Skip to content
Algo Trade Analytics Docs

The Double Donchian Channel Breakout strategy coded for TradingView

TL;DR
Use a fast Donchian channel for entries, a slower one for exits, and optional pyramiding to capture sustained trends the way the original Turtle traders did.

DifficultyIntermediate
Time to implement20-25 min
CategoryStrategies
//@version=6
strategy("Double Donchian", overlay=true, pyramiding=2)
fastLen = input.int(20, "Fast channel")
slowLen = input.int(55, "Slow channel")
upperFast = ta.highest(high, fastLen)
lowerFast = ta.lowest(low, fastLen)
upperSlow = ta.highest(high, slowLen)
lowerSlow = ta.lowest(low, slowLen)
if ta.crossover(close, upperFast)
strategy.entry("Long", strategy.long)
if ta.crossunder(close, lowerFast)
strategy.entry("Short", strategy.short)
strategy.exit("Long Exit", "Long", stop=lowerSlow)
strategy.exit("Short Exit", "Short", stop=upperSlow)
Tip. Stacking entries works best when you size by volatility. Add ATR-based position sizing so each additional leg risks the same amount of capital.

The double Donchian technique adds structure to breakout trading: fast channels detect new highs/lows quickly, while slower channels keep you in trends until meaningful reversals occur. Pine Script makes the entire Turtle-style blueprint approachable in a few dozen lines.

  • Configure two Donchian channels and decide which powers entries vs. exits.
  • Implement pyramiding with ATR-based risk so add-on trades stay proportionate.
  • Align exits (slow channel, ATR stops, or both) with the trend you’re capturing.
  • Plot channels and annotate fills for easier debugging.
HelperPurpose
ta.highest / ta.lowestConstruct Donchian channels (fast and slow).
`strategy(entryexit)`
ta.atrSize positions and compute emergency stops.
input.int, input.float, input.boolExpose tunable channel lengths, pyramiding, risk.
plot, fillVisualise channels and the zones between them.
  1. Capture channel settings & risk
    Provide inputs for fast/slow lengths, pyramiding count, ATR risk per leg.

    fastLen = input.int(20, "Fast channel", minval=1)
    slowLen = input.int(55, "Slow channel", minval=1)
    pyramid = input.int(2, "Pyramiding", minval=0, maxval=4)
    riskATR = input.float(2.0, "Stop distance (ATR)", step=0.1)
    strategy("Double Donchian", overlay=true, pyramiding=pyramid)
  2. Compute channels and sizing
    Generate upper/lower bands and calculate quantity from ATR.

    upperFast = ta.highest(high, fastLen)
    lowerFast = ta.lowest(low, fastLen)
    upperSlow = ta.highest(high, slowLen)
    lowerSlow = ta.lowest(low, slowLen)
    atr = ta.atr(14)
    riskCapital = strategy.equity * 0.01 // risk 1% by default
    qty = atr > 0 ? math.floor(riskCapital / (atr * riskATR)) : 0
  3. Stage entries & exits
    Use fast-channel breaks for entries; trail stops at the slow channel (or ATR-based fallbacks).

    if ta.crossover(close, upperFast) and qty > 0
    strategy.entry("Long", strategy.long, qty=qty)
    if ta.crossunder(close, lowerFast) and qty > 0
    strategy.entry("Short", strategy.short, qty=qty)
    strategy.exit("LX", "Long", stop=math.min(lowerSlow, strategy.position_avg_price - atr * riskATR))
    strategy.exit("SX", "Short", stop=math.max(upperSlow, strategy.position_avg_price + atr * riskATR))
//@version=6
strategy("Double Donchian Turtle", overlay=true, pyramiding=3, initial_capital=100_000, commission_type=strategy.commission.percent, commission_value=0.02)
// Inputs
donchianFast = input.int(20, "Fast Donchian", minval=2)
donchianSlow = input.int(55, "Slow Donchian", minval=2)
atrLen = input.int(14, "ATR length", minval=1)
riskPct = input.float(0.5, "Risk per leg (%)", minval=0.1, step=0.1)
atrStopMult = input.float(2.0, "ATR stop multiple", step=0.1)
// Channels
upperFast = ta.highest(high, donchianFast)
lowerFast = ta.lowest(low, donchianFast)
upperSlow = ta.highest(high, donchianSlow)
lowerSlow = ta.lowest(low, donchianSlow)
plot(upperFast, "Fast upper", color=color.new(color.green, 0))
plot(lowerFast, "Fast lower", color=color.new(color.green, 0))
plot(upperSlow, "Slow upper", color=color.new(color.orange, 0))
plot(lowerSlow, "Slow lower", color=color.new(color.orange, 0))
fill(plot(upperFast, display=display.none), plot(lowerFast, display=display.none), color=color.new(color.green, 95))
aTR = ta.atr(atrLen)
riskCash = strategy.equity * (riskPct * 0.01)
qty = aTR > 0 ? math.floor(riskCash / (aTR * atrStopMult)) : 0
longBreak = ta.crossover(close, upperFast)
shortBreak = ta.crossunder(close, lowerFast)
if longBreak and qty > 0
strategy.entry("Long", strategy.long, qty=qty)
if shortBreak and qty > 0
strategy.entry("Short", strategy.short, qty=qty)
if strategy.position_size > 0
stopLong = math.min(lowerSlow, strategy.position_avg_price - aTR * atrStopMult)
strategy.exit("Long Exit", "Long", stop=stopLong)
if strategy.position_size < 0
stopShort = math.max(upperSlow, strategy.position_avg_price + aTR * atrStopMult)
strategy.exit("Short Exit", "Short", stop=stopShort)
bgcolor(strategy.position_size > 0 ? color.new(color.green, 90) : strategy.position_size < 0 ? color.new(color.red, 90) : na)
var label status = na
if barstate.islast
if not na(status)
label.delete(status)
status = label.new(bar_index, close,
text="Equity: " + str.tostring(strategy.equity, "##,###.00") +
"\nOpen trades: " + str.tostring(strategy.opentrades, "0"),
style=label.style_label_left,
textcolor=color.white,
bgcolor=color.new(color.blue, 70))
Why this works.
  • Dual Donchian channels separate breakout detection (fast) from trailing exits (slow).
  • ATR-driven position sizing keeps each leg’s risk consistent as volatility changes.
  • Visual overlays (plots, fills, background) make it easy to confirm entries, exits, and pyramiding behaviour.
  • Lower the fast-channel length to generate more trades, or raise it to filter noise—slow channel should be significantly longer to avoid premature exits.
  • When pyramiding, consider limiting the maximum number of concurrent legs or scaling ATR stops tighter for late-stage entries.
  • Add filters (trend, volatility, session) if you want to avoid choppy regimes where Donchian breakouts whipsaw.
  • Always account for slippage and commission; longer-term breakouts can still suffer if costs aren’t factored in.

Reduce pyramiding or require a minimum distance from the previous entry (strategy.position_avg_price vs. current breakout level).

Add a minimum stop distance based on syminfo.mintick or allow users to cap ATR multiples with a secondary input.

How do I prevent entries during sideways ranges?

Section titled “How do I prevent entries during sideways ranges?”

Combine Donchian breakouts with volatility filters (e.g., ta.atr percentile) or only allow trades when the slow channel width exceeds a threshold.

  • Double Donchian systems pair fast breakout detection with slow trailing exits—perfect for trend following.
  • Pine Script makes it straightforward to implement both channels, pyramiding, and ATR-based risk sizing.
  • Visualise channels and annotate status to validate logic before live deployment.
  • Fine-tune channel lengths, pyramiding, and stop multiples to match the market you’re trading.