Pine Script Boilerplate for CrossTrade
Copy the template below into the TradingView Pine editor. Replace the signal logic with your own (moving average cross, RSI, breakout — whatever). The webhook payload, position sizing, stop/target handling, and alert firing are already wired for CrossTrade. You just provide the edge.
Build the payload in one place, and let TradingView substitute the placeholders.
The full template (Pine Script v6)
//@version=6
strategy(
title = "CrossTrade Strategy Template",
overlay = true,
initial_capital = 50000,
default_qty_type = strategy.fixed,
default_qty_value = 1,
commission_type = strategy.commission.cash_per_contract,
commission_value = 2.50,
calc_on_every_tick = false,
process_orders_on_close = true
)
// ==========================================================
// SECTION 1 — CrossTrade webhook configuration
// These inputs appear in the script's Settings dialog.
// ==========================================================
grpCT = "CrossTrade Webhook"
secretKey = input.string("YOUR-SECRET-KEY", "Secret key", group=grpCT)
account = input.string("Sim101", "NT account", group=grpCT)
instrument = input.string("ES 12-26", "NT instrument", group=grpCT)
tif = input.string("day", "Time in force", options=["day","gtc"], group=grpCT)
syncStrat = input.bool(true, "Sync NT with strategy", group=grpCT)
// ==========================================================
// SECTION 2 — Position-sizing inputs
// ==========================================================
grpRisk = "Risk"
riskPct = input.float(1.0, "Risk % per trade", minval=0.1, step=0.1, group=grpRisk)
pointValue = input.float(50, "Point value per contract", group=grpRisk, tooltip="ES=50, NQ=20, MES=5, MNQ=2, CL=1000 per $, etc.")
stopAtrMlt = input.float(1.5, "Stop = N × ATR", group=grpRisk)
atrLen = input.int(14, "ATR length", group=grpRisk)
// ==========================================================
// SECTION 3 — REPLACE THIS BLOCK WITH YOUR LOGIC
// Provide two boolean series: longSignal and shortSignal.
// ==========================================================
fastLen = input.int(9, "Fast EMA length", group="Signal")
slowLen = input.int(21, "Slow EMA length", group="Signal")
fastEma = ta.ema(close, fastLen)
slowEma = ta.ema(close, slowLen)
longSignal = ta.crossover(fastEma, slowEma)
shortSignal = ta.crossunder(fastEma, slowEma)
plot(fastEma, color=color.yellow, linewidth=2, title="Fast EMA")
plot(slowEma, color=color.blue, linewidth=2, title="Slow EMA")
// ==========================================================
// SECTION 4 — Stop/target sizing from ATR
// ==========================================================
atrVal = ta.atr(atrLen)
stopDist = stopAtrMlt * atrVal
// Compute contracts from risk budget
riskDollars = strategy.equity * (riskPct / 100.0)
perContract = stopDist * pointValue
qty = perContract > 0 ? math.max(1, math.floor(riskDollars / perContract)) : 1
// ==========================================================
// SECTION 5 — Entries and exits
// ==========================================================
if longSignal and strategy.position_size <= 0
if strategy.position_size < 0
strategy.close("Short")
strategy.entry("Long", strategy.long, qty=qty)
strategy.exit("Long Exit", "Long", stop=close - stopDist)
if shortSignal and strategy.position_size >= 0
if strategy.position_size > 0
strategy.close("Long")
strategy.entry("Short", strategy.short, qty=qty)
strategy.exit("Short Exit", "Short", stop=close + stopDist)
// ==========================================================
// SECTION 6 — CrossTrade webhook payload
// One alert() call formatted for CrossTrade. Pair with an alert
// configured as "Any alert() function call" with the webhook URL
// set to your CrossTrade endpoint.
// ==========================================================
payload =
"key=" + secretKey +
";command=place" +
";account=" + account +
";instrument=" + instrument +
";action={{strategy.order.action}}" +
";qty={{strategy.order.contracts}}" +
";order_type=market" +
";tif=" + tif +
(syncStrat ?
";sync_strategy=true" +
";market_position={{strategy.market_position}}" +
";prev_market_position={{strategy.prev_market_position}}" +
";target_quantity={{strategy.position_size}}" +
";out_of_sync=resync"
: "")
if longSignal or shortSignal
alert(payload, alert.freq_once_per_bar_close)
How to use
- Copy the template into the TradingView Pine editor
- Replace Section 3 with your own signal logic — give the script two booleans named
longSignalandshortSignal. Everything else stays the same. - Add to chart and verify signals in the Strategy Tester panel
- Configure the CrossTrade webhook inputs — secret key, account, instrument
- Create an alert:
- Condition: your strategy
- "Any alert() function call"
- Frequency: "Once per bar close"
- Webhook URL: your CrossTrade endpoint
- Test in sim before going live
What the template gives you
- Position sizing based on account equity and ATR-scaled stops (1% default)
- ATR-based stops that adapt to volatility
- Reversal handling — closes the opposite position before opening a new one
- Sync flags so NinjaTrader follows TradingView's strategy state
out_of_sync=resync(with the requiredtarget_quantity) so NT auto-corrects to the strategy's position if the two ever disagree. Swap inout_of_sync=flattenif you'd rather be flattened than corrected.- A single
alert()call — simpler to wire than multiple alerts
Variations
Bracket with profit target:
Add a target alongside the stop in strategy.exit:
targetMult = input.float(2.0, "Target = N × stop distance")
// ...
strategy.exit("Long Exit", "Long", stop=close - stopDist, limit=close + targetMult * stopDist)
Time-filter (RTH only):
Wrap entry conditions in a session check:
inRTH = (hour * 100 + minute) >= 930 and (hour * 100 + minute) < 1600
if longSignal and inRTH and strategy.position_size <= 0
// ... entry logic
End-of-session flatten:
if hour == 15 and minute >= 55
strategy.close_all("EOD flatten")
Trailing stop:
trailAtrMult = input.float(2.0, "Trail = N × ATR")
if strategy.position_size > 0
trailStop = close - trailAtrMult * atrVal
strategy.exit("Trail", "Long", stop=trailStop)
Testing checklist before going live
- Backtest shows acceptable profit factor (>1.3) with commissions included
- Drawdown is within your account's tolerance
- Strategy fires fewer than ~50 alerts/month (else you'll burn through TradingView plan limits)
- CrossTrade Alert History shows successful payload parsing on sim for multiple sessions
- NinjaTrader sim account shows matching fills to TradingView's strategy tester
- 2FA is enabled on your TradingView account (required for webhooks)
- Your broker's day-margin cutoff is respected by the strategy's flatten logic
When to deviate from the template
- Scalping strategies (sub-minute) — disable
process_orders_on_closeand consider intra-bar signals. Use caution: intra-bar strategies often backtest optimistically. - Pyramiding strategies — set
pyramidingparameter on thestrategy()declaration. Update the entry logic to stack positions. - Portfolio strategies — TradingView's one-strategy-per-chart limit means you'll want separate charts per strategy or a more advanced setup.
Frequently Asked Questions
Can I just copy-paste this Pine Script and run it?
Yes — after updating the 'YOUR-SECRET-KEY' placeholder with your actual CrossTrade secret, configuring the account and instrument inputs, and creating an alert pointed at your CrossTrade webhook URL. The default 9/21 EMA logic is functional but not particularly profitable on its own — replace Section 3 with your own strategy.
Why does the template close opposite positions before entering?
To handle reversals cleanly. If you're long and a short signal fires, the template closes the long first, then opens the short. Without this, strategy.entry('Short') would flip the position but the CrossTrade sync_strategy logic handles the NT-side transition smoothly.
What's 'out_of_sync=resync' doing?
If CrossTrade detects that NinjaTrader's actual position doesn't match what the TradingView strategy thinks it is (e.g., a manual trade was placed, or an alert was missed), resync works out the difference and submits a corrective order so NT lands on the position the strategy expects. That's why the template also sends target_quantity: resync needs the exact size to correct to. If you'd rather CrossTrade close the position and wait for a clean entry, use out_of_sync=flatten instead (and drop target_quantity).
Do I need to change the template for MNQ vs ES?
Only the 'pointValue' input (ES=50, NQ=20, MES=5, MNQ=2) and the 'instrument' input. The logic, stops, and position sizing math work identically across instruments.