Appearance
Personalize your experience
Turtle Pyramid Breakout
Admin's ChoiceFreeChannel breakouts that pyramid up to four N-sized units
Live engine preview
The QuaScript engine's own output on the chart the terminal uses. Change the settings or edit the code and press Run to recompute it.
About
The classic channel-breakout trend system with volatility units and pyramiding. A close beyond the `entry_n`-bar channel opens one unit; every further N move in favour (N = the 20-bar ATR at the first entry) adds another unit, up to four. Every unit shares one stop, three N behind the newest entry, so the open risk stays bounded while winners compound. The trade ends on a close beyond the shorter `exit_n`-bar channel on the other side. Entry: close above the prior `entry_n`-bar high (below the low for shorts); add a unit each `add_n` N, up to `units`. Exit: shared stop at newest entry -/+ `stop_n` N (3 by default, wider than the daily-bar original because hourly noise is larger relative to N), or a close through the `exit_n`-bar opposite channel. Risk: 0.5 % of equity per unit at its stop. Method: public channel-breakout rules with N-based units (Donchian channels, ATR).
Settings
Entry channel: 55
Exit channel: 20
Maximum units: 4
Add every (N): 1
Stop (N): 3
Measured backtests
Run by the backtest engine on public candles, 2025-11-01 to 2026-11-01, with the script's own commission and slippage, on BTCUSDT 1h (Binance) and ETHUSDT 1h (Binance). The figures per market are in the Measured backtest panel below. Past results do not predict future returns.
Backtest it yourself
Open it on the backtesting page to run it on any symbol, interval and date range, then use Monte Carlo, optimisation, walk-forward and robustness tests.
Strategy Rules
Entry Conditions
- ▸ close above the prior `entry_n`-bar high (below the low for shorts); add a unit each `add_n` N, up to `units`
Exit Conditions
- ▸ shared stop at newest entry -/+ `stop_n` N (3 by default, wider than the daily-bar original because hourly noise is larger relative to N), or a close through the `exit_n`-bar opposite channel
Measured backtest
BTCUSDT 1h (Binance), 2025-11-01 to 2026-11-01 · backtest engine, script costs included
35.3%
Return
1.17
Sharpe
-27.5%
Max DD
28.5%
Win Rate
298
Trades
| Market | Trades | Return | Sharpe | Max DD | Win rate | Profit factor |
|---|---|---|---|---|---|---|
| BTCUSDT 1h | 298 | 35.3% | 1.17 | -27.5% | 28.5% | 1.42 |
| ETHUSDT 1h | 289 | -4.8% | -0.06 | -27.5% | 30.4% | 0.93 |
Past results on historical data do not predict future returns.
Source Code
Edit in IDE| 1 | strategy "Turtle Pyramid Breakout" { |
| 2 | overlay = true |
| 3 | capital = 100000 |
| 4 | commission = 0.05 |
| 5 | slippage = 1 |
| 6 | sizing = risk_percent |
| 7 | max_leverage = 1 |
| 8 | size = 0.5 |
| 9 | pyramiding = 4 |
| 10 | } |
| 11 | /// The classic channel-breakout trend system with volatility units and pyramiding. A close beyond |
| 12 | /// the `entry_n`-bar channel opens one unit; every further N move in favour (N = the 20-bar |
| 13 | /// ATR at the first entry) adds another unit, up to four. Every unit shares one stop, three N behind |
| 14 | /// the newest entry, so the open risk stays bounded while winners compound. The trade ends on a |
| 15 | /// close beyond the shorter `exit_n`-bar channel on the other side. |
| 16 | /// Entry: close above the prior `entry_n`-bar high (below the low for shorts); add a unit each |
| 17 | /// `add_n` N, up to `units`. Exit: shared stop at newest entry -/+ `stop_n` N (3 by default, wider |
| 18 | /// than the daily-bar original because hourly noise is larger relative to N), or a close through the |
| 19 | /// `exit_n`-bar opposite channel. Risk: 0.5 % of equity per unit at its stop. |
| 20 | /// Method: public channel-breakout rules with N-based units (Donchian channels, ATR). |
| 21 | use "viz/premium" as pr |
| 22 | entry_n = param.int(55, title = "Entry channel", min = 10, max = 500, group = "Channels") |
| 23 | exit_n = param.int(20, title = "Exit channel", min = 5, max = 300, group = "Channels") |
| 24 | units = param.int(4, title = "Maximum units", min = 1, max = 4, group = "Pyramid") |
| 25 | add_n = param.float(1.0, title = "Add every (N)", min = 0.1, max = 3, step = 0.1, group = "Pyramid") |
| 26 | stop_n = param.float(3.0, title = "Stop (N)", min = 0.5, max = 6, step = 0.1, group = "Risk") |
| 27 | n = ta.atr(20) |
| 28 | hh = ta.highest(high, entry_n) |
| 29 | ll = ta.lowest(low, entry_n) |
| 30 | xh = ta.highest(high, exit_n) |
| 31 | xl = ta.lowest(low, exit_n) |
| 32 | brk_up = close > hh[1] |
| 33 | brk_dn = close < ll[1] |
| 34 | persist count = 0 |
| 35 | persist last_px = na |
| 36 | persist unit_n = na |
| 37 | persist stop = na |
| 38 | if position.size == 0 { count := 0 } |
| 39 | // shared stop for every open unit |
| 40 | fn restop_long(count: int, stop: float) { |
| 41 | exit.bracket("L1 stop", entry = "L1", stop = stop) |
| 42 | if count >= 2 { exit.bracket("L2 stop", entry = "L2", stop = stop) } |
| 43 | if count >= 3 { exit.bracket("L3 stop", entry = "L3", stop = stop) } |
| 44 | if count >= 4 { exit.bracket("L4 stop", entry = "L4", stop = stop) } |
| 45 | } |
| 46 | fn restop_short(count: int, stop: float) { |
| 47 | exit.bracket("S1 stop", entry = "S1", stop = stop) |
| 48 | if count >= 2 { exit.bracket("S2 stop", entry = "S2", stop = stop) } |
| 49 | if count >= 3 { exit.bracket("S3 stop", entry = "S3", stop = stop) } |
| 50 | if count >= 4 { exit.bracket("S4 stop", entry = "S4", stop = stop) } |
| 51 | } |
| 52 | flat = position.size == 0 |
| 53 | if flat and brk_up { |
| 54 | unit_n := n |
| 55 | last_px := close |
| 56 | stop := close - stop_n * n |
| 57 | count := 1 |
| 58 | order.market("L1", side.long) |
| 59 | restop_long(count, stop) |
| 60 | } else if flat and brk_dn { |
| 61 | unit_n := n |
| 62 | last_px := close |
| 63 | stop := close + stop_n * n |
| 64 | count := 1 |
| 65 | order.market("S1", side.short) |
| 66 | restop_short(count, stop) |
| 67 | } else if position.size > 0 and count < units and close >= last_px + add_n * unit_n { |
| 68 | count := count + 1 |
| 69 | last_px := close |
| 70 | stop := close - stop_n * unit_n |
| 71 | if count == 2 { order.market("L2", side.long) } |
| 72 | if count == 3 { order.market("L3", side.long) } |
| 73 | if count == 4 { order.market("L4", side.long) } |
| 74 | restop_long(count, stop) |
| 75 | } else if position.size < 0 and count < units and close <= last_px - add_n * unit_n { |
| 76 | count := count + 1 |
| 77 | last_px := close |
| 78 | stop := close + stop_n * unit_n |
| 79 | if count == 2 { order.market("S2", side.short) } |
| 80 | if count == 3 { order.market("S3", side.short) } |
| 81 | if count == 4 { order.market("S4", side.short) } |
| 82 | restop_short(count, stop) |
| 83 | } |
| 84 | if position.size > 0 and close < xl[1] { exit.close_all() } |
| 85 | if position.size < 0 and close > xh[1] { exit.close_all() } |
| 86 | risk.max_drawdown(30) |
| 87 | up_p = render.line(hh, "Entry channel high", color = color.translucent(color.emerald.300, 45), width = 1) |
| 88 | dn_p = render.line(ll, "Entry channel low", color = color.translucent(color.rose.300, 45), width = 1) |
| 89 | render.band(up_p, dn_p, texture = "gradient", direction = "edges", upper_color = color.translucent(color.emerald.500, 88), lower_color = color.translucent(color.rose.500, 88), opacity = 1, title = "Channel") |
| 90 | render.line(xh, "Exit channel high", color = color.translucent(color.slate.300, 65), width = 1, stroke = style.dotted) |
| 91 | render.line(xl, "Exit channel low", color = color.translucent(color.slate.300, 65), width = 1, stroke = style.dotted) |
| 92 | render.step(position.size != 0 ? stop : na, "Shared stop", color = color.amber.300, width = 1.25, stroke = style.dashed) |
| 93 | render.mark(flat and brk_up, shape = shape.triangle_up, at = anchor.below_bar, color = color.emerald.300, text = "1", title = "First unit long") |
| 94 | render.mark(flat and brk_dn, shape = shape.triangle_down, at = anchor.above_bar, color = color.rose.300, text = "1", title = "First unit short") |
| 95 | render.mark(not flat and count > count[1], shape = shape.circle, at = anchor.below_bar, color = color.amber.200, size = 0.45, title = "Unit added") |
| 96 | persist t = draw.table.create(anchor.top_right, 5, 2) |
| 97 | if bar.is_last { |
| 98 | pr.heading(t, 2, "Turtle Pyramid Breakout", "Strategy · channel breakout, N units") |
| 99 | pr.row(t, 2, "Units open", str.format("{} / {}", position.size == 0 ? 0 : count, units), color.white) |
| 100 | pr.row(t, 3, "N (ATR 20)", str.format("{:.2f}", n), pr.LABEL) |
| 101 | pr.row(t, 4, "Shared stop", position.size != 0 ? str.format("{:.2f}", stop) : "Flat", pr.WARN) |
| 102 | } |
| 103 |
Reviews
No reviews yet. Be the first to review this listing.