Overview
A Pine strategy simulates fills on historical bars and feeds the Strategy Tester report. By default the broker emulator fills an order at the open of the bar after the script created it, so a signal computed at a bar’s close executes on the next bar. Use an indicator (pine-script) when you only need a visual signal.
Declare costs and sizing before writing signal logic
Every cost parameter changes every performance number, so set them first.
//@version=6
strategy("Donchian Breakout", overlay = true,
initial_capital = 10000,
default_qty_type = strategy.percent_of_equity, default_qty_value = 10,
commission_type = strategy.commission.percent, commission_value = 0.1,
slippage = 2, calc_on_every_tick = false)slippageis in ticks. A zero-cost backtest of a high-frequency strategy overstates results by a wide margin.- In v6 the default
margin_longandmargin_shortare 100, so margin calls and fund checks apply by default. A strategy that backtested in v5 can trade differently after conversion. - Keep
calc_on_every_tick = false. Withtrue, realtime bars execute on every tick while history executes at the close, so live and backtest results diverge. - Set
pyramidingdeliberately. Same-direction add-ons inflate returns by scaling into winners.
Place orders with if blocks, not when
v6 removed the when parameter from strategy.entry(), strategy.order(), strategy.exit(), and related calls. Gate them with if.
length = input.int(20, "Channel length")
upper = ta.highest(high, length)
lower = ta.lowest(low, length)
if close > upper[1]
strategy.entry("long", strategy.long)
if close < lower[1]
strategy.close("long")The [1] offset compares against the channel as it stood before the current bar; without it, close can never exceed a highest(high) that includes the current bar.
strategy.entryreverses an open opposite position automatically.strategy.orderplaces an order regardless of position state.- Give
strategy.exiteither relative values (profit,loss, in ticks) or absolute prices (limit,stop). Passing both families evaluates both, and whichever triggers first fills. - Passing
limitandstoptogether creates two exit orders (take-profit and stop-loss), not a stop-limit order.
atr = ta.atr(14)
if strategy.position_size > 0
strategy.exit("long exit", "long",
stop = strategy.position_avg_price - 2 * atr,
limit = strategy.position_avg_price + 3 * atr)Keep signals repaint-free
Compute signals on closed bars and request higher-timeframe data with the pattern in pine-script-security. alertcondition() cannot create alerts from a strategy; send alerts from order calls with alert_message or from alert().
Read the Strategy Tester skeptically
- Compare against buy and hold. A long-only strategy that beats nothing in a bull market is beta.
- Distrust a high profit factor on a small trade count; few trades prove little.
- Judge max drawdown against what you would tolerate live.
- Develop on one date range and validate on another that the parameters never saw.
- v6 trims the oldest orders past 9,000 instead of erroring, so a long backtest may silently omit early trades. Deep Backtesting raises the cap to 1,000,000 orders.
Enforce hard limits inside the simulation:
strategy.risk.max_drawdown(20, strategy.percent_of_equity)
strategy.risk.max_position_size(5)