TradingView strategy research

Linear regression trading strategy: breakout or reversion?

A linear regression trading strategy needs a trading hypothesis in addition to a fitted channel. A breakout model buys a confirmed move above the upper band; a reversion model buys a recovery through the lower band toward the center. The research protocol below tests those alternatives separately, calculates each threshold from a completed prior window, and freezes the stop and target when the signal occurs. It establishes no profitable settings.

See the decision guide

One channel definition for both experiments

The existing regression-channel guide explains the indicator calculation. Here the channel becomes an order reference. Use standard candles, close prices, equal weights and a fixed N. At the close of bar t, fit only t-N through t-1, indexed x = 0 through N-1. No price from bar t enters the fit. TradingView documents source, count and deviation inputs; this page specifies its own residual-RMS convention rather than assuming numerical parity with every indicator.

  • The divisor is N, not N-2. NIST describes the latter as the residual standard deviation estimate for a fitted straight line. Changing the divisor changes the channel and therefore the test.
  • The fit rolls on each new decision bar; N and the formula remain fixed within the experiment. An open trade keeps its original absolute stop and target even while the displayed reference channel updates.
  • A two-deviation band is not a demonstrated 95% price interval. A small fitting error does not establish forecasting accuracy.
QuantityDefinition used in this protocol
Fitb = Σ[(x - mean(x))(y - mean(y))] / Σ[(x - mean(x))²]; a = mean(y) - b × mean(x).
Widthd = sqrt(Σ[y - (a + bx)]² / N). Every residual uses the same a and b.
Signal-bar projectionC = a + bN; U = C + kd; L = C - kd. These prices are known before the signal bar closes.
Previous endpointUprev = a + b(N - 1) + kd, using this same prior-window fit.
BaselineN = 20, k = 2, one long position of 10 shares, no pyramiding, maximum holding period 10 bars.
EligibilityRequire N positive, nonmissing closes, N ≥ 3, and d at least one minimum tick. Skip incomplete or effectively straight-line windows.

Two mutually exclusive rulebooks

Choose a mode before running the sample. Both examples are long-only, for a stock or unleveraged ETF with sufficient simulated capital. They test different entries with the same fitting method. The settings below are research assumptions; neither mode has been performance-tested here.

  • Equality at U is not a breakout; equality at L is not a confirmed recovery. A wick touching L is eligible only if the close recovers above it and stays below the target.
  • Breakout uses two points on the same fitted line. Comparing the close with the previous value of a separately rolling upper-band series defines a different crossing rule.
  • Reversion can buy into a persistent decline because this baseline has no trend filter. Breakout can enter an exhaustion move. Keep those losses instead of retrospectively switching modes.
  • Short trades, automatic regime switching, trailing exits and averaging down are outside this baseline. Each would require a separate rulebook and cost model.
RuleBreakout modeReversion mode
HypothesisAn upside break of a rising fitted channel continues.A lower-band rejection returns toward the fitted center.
Confirmed signalb > 0, prior close ≤ Uprev, and current close > U.Current low ≤ L, current close > L, and current close < C. No slope filter.
EntryMarket buy after the qualifying close.Market buy after the qualifying close.
Frozen stopC at the signal bar.L - d at the signal bar.
Frozen limit targetU + 2d at the signal bar.C at the signal bar.
Entry guardRound stop and target down to the minimum tick. Require 0 < stop < signal close < target.Apply the same rounding and strict ordering guard.
Position lifecycleOne position at a time. Skip a new signal on an observed exit bar.One position at a time. Skip a new signal on an observed exit bar.
Time exitCount the fill bar as bar one. Request a market exit at the close of bar ten if still open.Use the same holding limit. Keep the bracket active until the exit fills.

Why a redrawn channel can erase an apparent breakout

Reuse the indicator guide's hypothetical closes 100, 102, 101, 105 and 107 with N = 5 and k = 2. Least squares gives a = 99.6, b = 1.7, SSE = 5.10 and d = sqrt(1.02) = 1.00995049. The next bar has a hypothetical close of 112. The arithmetic compares information available before that close with a later refit; it is not a trade result.

  • For the original fit, 107 ≤ Uprev = 108.41990099 and 112 > 110.11990099, so the breakout comparison passes. With a 0.01 tick, its frozen stop is 108.10 and target is 112.13. The small remaining target distance is a reason to inspect costs and the next fill, not a reason to rewrite the past threshold.
  • Save the signal timestamp, window start and end, a, b, d, projected U/L/C, mode, rounded exits and actual fill. A later channel redraw must not replace this record.
  • The script plots a saved projection value on each bar rather than drawing the newest fitted line backward through old bars. Changing data or settings can still recalculate the entire test.
Measurement at the new barFit excludes 112Fit includes 112, drops 100
Fitted coefficientsa = 99.6; b = 1.7a = 100.2; b = 2.6
Center at the new bar108.1, projected at x = 5110.6, fitted at x = 4
Upper boundary110.11990099113.37128129
Close 112 above upper?YesNo
Center at the old 107 bar106.4108.0 after the window rolls

Pine Script baseline with fixed trade exits

Set the mode, dates and parameters explicitly before adding this draft to a standard candle chart. It uses a prior-window fit, bar-close decisions and a bracket submitted with the entry. The signal-date filter uses bar opening time at or after the start and closing time strictly before the end. Earlier bars may supply warm-up data, but cannot open positions. The source passed Pineify syntax validation on October 17, 2026. Review its chart behavior and order timing before use; compilation alone does not establish numerical or execution accuracy.

  • The default order timing fills a market order on the next available tick, normally the next bar open in a historical close-based test. The signal-close price is not an assumed fill.
  • If an entry fills outside its frozen bracket and remains open at the next calculation, the script requests a market close. It cannot undo the fill; bracket orders may already have executed. Retain these trades.
  • A date or holding-period exit is a market request, so its fill can occur after the stated boundary. Export that spillover explicitly. Each separate date-window run begins flat; it does not inherit positions from a previous run.
  • Commission is 0.05% on each filled order and slippage is one tick on applicable orders. These are placeholders, not estimates for a particular broker. Replace them and the fixed share quantity before evaluating another instrument.
Pine v6 research example; syntax checked October 17, 2026. No return or live-execution claim.pine
//@version=6
strategy("Frozen regression hypotheses", overlay=true, initial_capital=10000, default_qty_type=strategy.fixed, default_qty_value=10, pyramiding=0, commission_type=strategy.commission.percent, commission_value=0.05, slippage=1, margin_long=100, calc_on_every_tick=false, calc_on_order_fills=false, process_orders_on_close=false, max_bars_back=501)
mode = input.string("Breakout", "Hypothesis", options=["Breakout", "Reversion"])
n = input.int(20, "Fit bars", minval=3, maxval=500)
k = input.float(2.0, "Band multiplier", minval=0.5, step=0.5)
hold = input.int(10, "Maximum holding bars", minval=1)
startTime = input.time(timestamp("01 Jan 2024 00:00 +0000"), "Signal start")
endTime = input.time(timestamp("01 Jan 2025 00:00 +0000"), "Signal end (exclusive)")

// One fit from the n completed bars before the signal bar.
float meanX = (n - 1) / 2.0
float sumY = 0.0
float numerator = 0.0
bool valid = bar_index >= n
for x = 0 to n - 1
    float y = close[n - x]
    valid := valid and not na(y) and y > 0
    sumY += y
    numerator += (x - meanX) * y
float slope = numerator / (n * (n * n - 1) / 12.0)
float intercept = sumY / n - slope * meanX
float sse = 0.0
for x = 0 to n - 1
    float residual = close[n - x] - (intercept + slope * x)
    sse += residual * residual
float d = math.sqrt(sse / n)
float center = intercept + slope * n
float upper = center + k * d
float lower = center - k * d
float previousUpper = intercept + slope * (n - 1) + k * d
valid := valid and d >= syminfo.mintick

bool breakout = valid and slope > 0 and close[1] <= previousUpper and close > upper
bool reversion = valid and low <= lower and close > lower and close < center
float rawStop = mode == "Breakout" ? center : lower - d
float rawTarget = mode == "Breakout" ? upper + 2 * d : center
float stopPrice = math.floor(rawStop / syminfo.mintick) * syminfo.mintick
float targetPrice = math.floor(rawTarget / syminfo.mintick) * syminfo.mintick
bool selected = mode == "Breakout" ? breakout : reversion
bool inDates = time >= startTime and time_close < endTime
bool exitedThisBar = strategy.closedtrades > nz(strategy.closedtrades[1])
var float frozenStop = na
var float frozenTarget = na
bool signal = barstate.isconfirmed and inDates and strategy.position_size == 0 and not exitedThisBar and selected and stopPrice > 0 and stopPrice < close and close < targetPrice
if signal
    frozenStop := stopPrice
    frozenTarget := targetPrice
    strategy.entry("Long", strategy.long)
    strategy.exit("Bracket", "Long", stop=frozenStop, limit=frozenTarget)

if strategy.position_size > 0
    strategy.exit("Bracket", "Long", stop=frozenStop, limit=frozenTarget)
    int barsHeld = bar_index - strategy.opentrades.entry_bar_index(0) + 1
    bool gapOutside = strategy.position_avg_price <= frozenStop or strategy.position_avg_price >= frozenTarget
    if gapOutside or barsHeld >= hold or time_close >= endTime
        strategy.close("Long", comment="Gap, time or date exit")

plot(valid ? center : na, "Prior-fit projection", color=color.gray)
plot(valid ? upper : na, "Prior-fit upper", color=color.blue)
plot(valid ? lower : na, "Prior-fit lower", color=color.blue)
plot(strategy.position_size > 0 or signal ? frozenStop : na, "Frozen stop", color=color.red, style=plot.style_linebr)
plot(strategy.position_size > 0 or signal ? frozenTarget : na, "Frozen target", color=color.green, style=plot.style_linebr)
plotshape(signal, title="Confirmed signal", style=shape.triangleup, location=location.belowbar, color=color.blue, size=size.tiny)

Cost arithmetic can overturn a small channel target

For a separate hypothetical breakout, suppose the prior fit gives C = 100, d = 1, k = 2, positive slope and a qualifying previous close. Then U = 102, stop = 100 and target = 104. A signal closes at 102.20. Assume the next open is 102.30 and a 0.01 adverse entry tick produces a fill at 102.31 for 10 shares. Commission is 0.0005 times notional on each side. These are assumed fills, not observed results.

  • The target branch assumes an exact limit fill; the stop branches already include the adverse tick in their fill prices. Do not subtract that tick again. Spread, financing and other charges are not included.
  • With only the exact-target and ordinary-stop outcomes, arithmetic break-even win rate is 24.21150 / (15.86845 + 24.21150), about 60.41%. This is not an estimated win rate.
  • A separate reversion fixture with C = 100, d = 1 and k = 2 has L = 98, stop = 97 and target = 100. A bar with low 97.80 and close 98.20 qualifies; close 98 exactly or close 100 does not. No slope estimate is required for this boundary example.
  • When both stop and target occur inside one chart bar, the historical intrabar assumptions affect which fills first. Review available lower-timeframe detail; a clean-looking channel does not resolve that order.
Alternative outcomeNet calculation for 10 sharesNet P&L
Target fills at 10410 × (104 - 102.31) - 0.0005 × 10 × (102.31 + 104)+$15.86845
Stop fills at 99.99 after one adverse tick10 × (99.99 - 102.31) - 0.0005 × 10 × (102.31 + 99.99)-$24.21150
Gap through stop; open 98.50, fill 98.4910 × (98.49 - 102.31) - 0.0005 × 10 × (102.31 + 98.49)-$39.20400
Four targets and six ordinary stops4 × 15.86845 - 6 × 24.21150-$81.79520

Reserve later dates before comparing settings

Use a chronological development period and an untouched later evaluation period on the same stated feed, session and timeframe. Record both date ranges before comparing results. Historical dates in the draft are placeholders, not evidence of a tested sample. Parameter selection is part of the experiment and must not use the reserved results.

  • Report net average trade, profit factor, trade count and drawdown together with period length. A few trades or zero losing trades do not establish repeatable performance; an undefined or infinite ratio needs its denominator shown.
  • Separate breakout false starts, reversion trades that keep falling, gap exits and time exits. A combined total can hide dependence on one favorable period.
  • To test both hypotheses on the reserved period, predeclare that two-model comparison and report both. Choosing the better result afterward consumes the holdout for another selection decision.
StageReproducible decision
Development gridFor each mode separately, compare N in {20, 40, 60}, k in {1.5, 2, 2.5}, and holding bars in {5, 10}: 18 settings per mode, 36 total. Keep formula, share quantity and execution assumptions fixed.
Selection ruleWrite the ranking metric, minimum acceptable trade count and risk limit before running the grid. Report every attempted setting and neighboring results, including failures.
FreezeSelect at most one mode and one parameter set using development results only. Save source, inputs, costs, feed settings and chosen dates.
Later evaluationRun that exact choice from flat on the reserved later dates. Prior observations may warm up the fit; no earlier position carries into the sample. Report exit spillover after the date end.
Stress and rejectionRepeat the fixed choice with a declared higher-cost scenario. If it fails the stated criteria, report failure. Retuning against these dates converts them into development data.

Implement, compare and inspect the same rulebook

Pine Script AI Coding Agent can help revise the source from these explicit rules and request syntax diagnostics. Require the five-bar fixture and the equality, zero-width and gap cases before accepting a generated revision. Compilation alone cannot prove that order timing or the trading hypothesis is correct.

  • After the implementation passes review, use Strategy Optimizer to compare the exposed input combinations on development dates. Keep the mode choice and reserved-date procedure under your control; this workflow does not assume automatic out-of-sample selection.
  • Export each evaluated trade list for Backtest Deep Report. Its supported overview calculations include net profit, trade count, profit factor and drawdown from the closed-trade equity curve. That drawdown can differ from TradingView intrabar drawdown because open-trade excursions are not the same series.
  • Preserve the source and settings alongside each report. Neither parameter comparison nor a report verifies a future trading edge or supplies live order execution.
Where Pineify fits

Pine Script AI Coding Agent

Turn the frozen-window rulebook and numerical fixtures into a reviewed Pine Script implementation, then check syntax and order behavior.

Also useful: Strategy Optimizer. Compare declared strategy inputs on development dates, then validate the frozen choice separately on reserved dates.

Draft my regression strategy

Frequently asked questions

Educational research, not investment advice. Prices and fills are hypothetical; no strategy performance is claimed. Backtests do not guarantee future results, and gaps can exceed planned stop losses.

Sources and verification

Published by Pineify. Pineify is an independent product and is not affiliated with TradingView. Sources support the technical explanations; examples do not establish trading performance.