Appearance
Personalize your experience
Regression Pullback
Admin's ChoiceFreeBuys pullbacks to the lower edge of clean regression channels
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
Buys pullbacks to the lower edge of a clean rising regression channel and sells rallies to the upper edge of a clean falling one. "Clean" means the channel explains price well (R squared above the quality level), so the edges are statistically meaningful; pullbacks in noisy channels are ignored. Entry: R squared above the level, slope in the trade's direction, low touching the lower 2-sigma band and the bar closing back inside (mirror for shorts). Exit: target at the channel centre plus half the band, stop beyond the 3-sigma band, time stop. Risk: 1 % per trade.
Settings
Channel window: 100
Minimum R squared: 0.55
Entry band (sigma): 2
Maximum bars held: 40
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
- ▸ R squared above the level, slope in the trade's direction, low touching the lower 2-sigma band and the bar closing back inside (mirror for shorts)
Exit Conditions
- ▸ target at the channel centre plus half the band, stop beyond the 3-sigma band, time stop
Measured backtest
BTCUSDT 1h (Binance), 2025-11-01 to 2026-11-01 · backtest engine, script costs included
-4.4%
Return
-0.72
Sharpe
-6.6%
Max DD
42.4%
Win Rate
33
Trades
| Market | Trades | Return | Sharpe | Max DD | Win rate | Profit factor |
|---|---|---|---|---|---|---|
| BTCUSDT 1h | 33 | -4.4% | -0.72 | -6.6% | 42.4% | 0.73 |
| ETHUSDT 1h | 29 | -4.5% | -0.73 | -5.8% | 41.4% | 0.70 |
Past results on historical data do not predict future returns.
Source Code
Edit in IDE| 1 | strategy "Regression Pullback" { |
| 2 | overlay = true |
| 3 | capital = 100000 |
| 4 | commission = 0.05 |
| 5 | slippage = 1 |
| 6 | sizing = risk_percent |
| 7 | max_leverage = 1 |
| 8 | size = 1 |
| 9 | pyramiding = 1 |
| 10 | } |
| 11 | /// Buys pullbacks to the lower edge of a clean rising regression channel and sells rallies to the |
| 12 | /// upper edge of a clean falling one. "Clean" means the channel explains price well (R squared |
| 13 | /// above the quality level), so the edges are statistically meaningful; pullbacks in noisy |
| 14 | /// channels are ignored. |
| 15 | /// Entry: R squared above the level, slope in the trade's direction, low touching the lower 2-sigma |
| 16 | /// band and the bar closing back inside (mirror for shorts). Exit: target at the channel centre |
| 17 | /// plus half the band, stop beyond the 3-sigma band, time stop. Risk: 1 % per trade. |
| 18 | use "viz/premium" as pr |
| 19 | len = param.int(100, title = "Channel window", min = 20, max = 1000, group = "Channel") |
| 20 | q_min = param.float(0.55, title = "Minimum R squared", min = 0.1, max = 0.95, step = 0.01, group = "Channel") |
| 21 | k_in = param.float(2.0, title = "Entry band (sigma)", min = 0.5, max = 4, step = 0.1, group = "Entry") |
| 22 | hold = param.int(40, title = "Maximum bars held", min = 2, max = 500, group = "Exit") |
| 23 | (v, slope, _) = stat.linreg(close, len, 0) |
| 24 | sd = stat.stdev(close - v, len) |
| 25 | q = stat.r_squared(close, len) |
| 26 | clean = q > q_min |
| 27 | up = slope > 0 |
| 28 | lo = v - k_in * sd |
| 29 | hi = v + k_in * sd |
| 30 | flat = position.size == 0 |
| 31 | long_sig = clean and up and low <= lo and close > lo |
| 32 | short_sig = clean and not up and high >= hi and close < hi |
| 33 | if flat and long_sig { |
| 34 | order.market("L", side.long) |
| 35 | exit.bracket("L exit", entry = "L", stop = v - 3 * sd, target = v + 0.5 * sd) |
| 36 | } |
| 37 | if flat and short_sig { |
| 38 | order.market("S", side.short) |
| 39 | exit.bracket("S exit", entry = "S", stop = v + 3 * sd, target = v - 0.5 * sd) |
| 40 | } |
| 41 | if position.size != 0 and position.bars_held >= hold { exit.close_all() } |
| 42 | risk.max_drawdown(25) |
| 43 | pu = render.line(hi, "Upper band", color = color.translucent(color.amber.300, 40), width = 1) |
| 44 | pc = render.line(v, "Regression", color = clean ? (up ? color.sky.300 : color.amber.300) : color.slate.500, width = 2) |
| 45 | pl = render.line(lo, "Lower band", color = color.translucent(color.sky.300, 40), width = 1) |
| 46 | render.band(pu, pc, texture = "gradient", direction = "edges", upper_color = color.amber.500, lower_color = color.slate.900, opacity = 0.18, title = "Upper half") |
| 47 | render.band(pc, pl, texture = "gradient", direction = "edges", upper_color = color.slate.900, lower_color = color.sky.500, opacity = 0.18, title = "Lower half") |
| 48 | render.mark(long_sig, shape = shape.triangle_up, at = anchor.below_bar, color = color.sky.300, size = 0.7, title = "Pullback buy") |
| 49 | render.mark(short_sig, shape = shape.triangle_down, at = anchor.above_bar, color = color.amber.300, size = 0.7, title = "Rally sell") |
| 50 | persist t = draw.table.create(anchor.top_right, 4, 2) |
| 51 | if bar.is_last { |
| 52 | pr.heading(t, 2, "Regression Pullback", "Strategy · channel-edge entries") |
| 53 | pr.row(t, 2, "R squared", str.format("{:.2f}", q), clean ? pr.BULL : pr.LABEL) |
| 54 | pr.row(t, 3, "Channel", clean ? (up ? "Clean rising" : "Clean falling") : "Noisy", color.white) |
| 55 | } |
| 56 |
Reviews
No reviews yet. Be the first to review this listing.