Strategy · Pine Script v6 · On the chart

Seykota Alt 13: Vol-Gated Adds

by trustdan· code credited to danieltuckerrust

This Pine Script implements a trend-following strategy inspired by John Seykota's methods.

View on GitHub

GitHub stars16on the repository
Pine versionv6current
Lines of code260
Settings24inputs
LicenceNone statedfile: MPL-2.0
Last update11 months ago3 Nov 2025

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new strategy, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart. The trades and results appear in the Strategy Tester tab; adjust the settings with the gear icon.

Source Code

260 lines of Pine Script v6 from pine-scripts/21_PF-0.970_SPY_seykota_alt13_volatility_gated.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

21_PF-0.970_SPY_seykota_alt13_volatility_gated.pine
// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © danieltuckerrust
// ALTERNATIVE 13: Volatility-Gated Adds
// THEORY: Avoid adding during volatility explosions (often late-trend "blow-offs").
// The baseline adds whenever price extends by 0.5N, regardless of volatility regime. But volatility
// spikes often signal exhaustion: a sudden ATR expansion can indicate a climax move. This alternative
// blocks add-ons when current ATR > SMA(ATR, length), preventing pyramiding into parabolic phases.
// Still 100% price/volatility-based; no over-fitting. Initial entries are unaffected.
// Trade-off: Avoid pyramiding into exhaustion moves, but miss some legitimate strong trends.

//@version=6
strategy("Seykota Alt 13: Vol-Gated Adds v1.0",
     overlay=true,
     initial_capital=100000,
     commission_type=strategy.commission.percent, commission_value=0.005,
     slippage=2,
     default_qty_type=strategy.fixed, default_qty_value=0,
     pyramiding=10,
     max_bars_back=5000)

//================ Inputs
allowLong   = input.bool(true,  "Allow LONGs?")
allowShort  = input.bool(true,  "Allow SHORTs?")

entryLen    = input.int(55,     "Donchian ENTRY lookback",            minval=10)
exitLen     = input.int(10,     "Donchian EXIT lookback",             minval=5)
nLen        = input.int(20,     "N = ATR length",                     minval=5)
stopN       = input.float(2.0,  "Initial stop (in N)",                minval=0.5, step=0.25)
addStepN    = input.float(0.5,  "Add every X * N",                    minval=0.25, step=0.25)
maxUnits    = input.int(4,      "Max units (incl. initial)",          minval=1, maxval=10)
riskPct     = input.float(1.0,  "Risk % of equity PER UNIT",          minval=0.1, maxval=5, step=0.1)

// CHANGED: Volatility gate parameters
useVolGate  = input.bool(true,  "Block adds during vol spikes?")
volGateLen  = input.int(20,     "Vol gate MA length",                 minval=5)
volGateMult = input.float(1.0,  "Vol spike threshold (ATR > X * MA)", minval=0.5, step=0.1)

useMarket   = input.bool(false, "Use market regime filter?")
marketSym   = input.symbol("SPY", "Market symbol for regime (if used)")
marketTF    = input.timeframe("D", "Regime timeframe (if used)")
marketLen   = input.int(200,    "Market MA length (if used)",         minval=50)

timeExitMode= input.string("None","Time exit", options=["None","Close","Roll"])
timeExitBars= input.int(60,     "Time exit bars (if used)",           minval=5)

minVol      = input.int(0,      "Min 20-bar avg volume (chart TF)",   minval=0)
showSignals = input.bool(true,  "Plot signals & stops?")
plotDon     = input.bool(true,  "Plot Donchian bands?")

// Date-range filter
fromDate = input.time(defval=timestamp("2022-01-01T00:00:00"), title="Backtest FROM (yyyy-mm-dd)", confirm=true)
toDate   = input.time(defval=timestamp("2099-12-31T23:59:59"), title="Backtest TO (yyyy-mm-dd)",   confirm=true)
flatAtFrom = input.bool(true, "Force FLAT at range start?")
isRangeStart = (time[1] < fromDate) and (time >= fromDate)
if flatAtFrom and isRangeStart and strategy.position_size != 0
    strategy.close_all(comment="Flat at FROM")

//================ Helpers
sharesForUnit(_equity, _Nentry) =>
    riskDollars   = _equity * (riskPct/100.0)
    perShareRisk  = math.max(stopN * _Nentry, syminfo.mintick)
    math.max(1, math.floor(riskDollars / perShareRisk))

inRange = (time >= fromDate) and (time <= toDate)

//================ Core calcs with VOLATILITY GATE
N        = ta.atr(nLen)
volMA    = ta.sma(volume, 20)
liqOK    = volMA >= minVol

// CHANGED: Volatility gate logic
atrMA = ta.sma(N, volGateLen)
isVolSpike = N > (atrMA * volGateMult)
volGateOK = not useVolGate or not isVolSpike

// Donchian levels
donHi    = ta.highest(high, entryLen)
donLo    = ta.lowest(low,  entryLen)
donHiPrev= donHi[1]
donLoPrev= donLo[1]

exitHiPrev = ta.highest(high, exitLen)[1]
exitLoPrev = ta.lowest(low,  exitLen)[1]

// Market regime (optional)
mClose = request.security(marketSym, marketTF, close)
mMA    = request.security(marketSym, marketTF, ta.sma(close, marketLen))
longRegOK  = not useMarket or (mClose > mMA)
shortRegOK = not useMarket or (mClose < mMA)

//================ Signals (entries NOT affected by vol gate)
longBreak  = allowLong  and liqOK and longRegOK  and (close > donHiPrev)
shortBreak = allowShort and liqOK and shortRegOK and (close < donLoPrev)

//================ Position state
var float N_entry       = na
var float lastAddLong   = na
var float lastAddShort  = na
var int   units         = 0
var int   barsInPos     = 0
var bool  wantReenterL  = false
var bool  wantReenterS  = false
var int   blockedAdds   = 0  // Track how many adds were blocked

inPos     = strategy.position_size != 0
inLong    = strategy.position_size > 0
inShort   = strategy.position_size < 0

if inPos
    barsInPos += 1
else
    barsInPos    := 0
    units        := 0
    lastAddLong  := na
    lastAddShort := na
    N_entry      := na
    wantReenterL := false
    wantReenterS := false
    blockedAdds  := 0

//================ Entries
if inRange and not inPos and longBreak
    N_entry := N
    float sh = sharesForUnit(strategy.equity, N_entry)
    strategy.entry("L", strategy.long, qty=sh)
    units       := 1
    lastAddLong := close

if inRange and not inPos and shortBreak
    N_entry := N
    float sh = sharesForUnit(strategy.equity, N_entry)
    strategy.entry("S", strategy.short, qty=sh)
    units        := 1
    lastAddShort := close

//================ Add-ons (GATED by volatility)
priceConditionLong  = inRange and inLong  and units < maxUnits and high >= nz(lastAddLong)  + addStepN * N_entry
priceConditionShort = inRange and inShort and units < maxUnits and low  <= nz(lastAddShort) - addStepN * N_entry

// CHANGED: Add volGateOK condition to prevent adds during vol spikes
canAddLong  = priceConditionLong  and volGateOK
canAddShort = priceConditionShort and volGateOK

// Track blocked adds
if priceConditionLong and not volGateOK
    blockedAdds += 1

if priceConditionShort and not volGateOK
    blockedAdds += 1

if canAddLong
    float sh = sharesForUnit(strategy.equity, N_entry)
    strategy.entry("L", strategy.long, qty=sh)
    units       += 1
    lastAddLong := close

if canAddShort
    float sh = sharesForUnit(strategy.equity, N_entry)
    strategy.entry("S", strategy.short, qty=sh)
    units        += 1
    lastAddShort := close

//================ Exits
var float stopL = na
var float stopS = na

if inRange and inLong
    initStopL = strategy.position_avg_price - stopN * N_entry
    stopL     := math.max(initStopL, exitLoPrev)
    strategy.exit("L-EXIT", from_entry="L", stop=stopL)
else
    stopL := na

if inRange and inShort
    initStopS = strategy.position_avg_price + stopN * N_entry
    stopS     := math.min(initStopS, exitHiPrev)
    strategy.exit("S-EXIT", from_entry="S", stop=stopS)
else
    stopS := na

//================ Optional time exit
timeExitTrig = inRange and inPos and (timeExitMode != "None") and (barsInPos >= timeExitBars)

if timeExitTrig
    if inLong
        if timeExitMode == "Close"
            strategy.close("L", comment="Time Exit")
        else if timeExitMode == "Roll"
            wantReenterL := true
            strategy.close("L", comment="Time Exit (Roll)")
    if inShort
        if timeExitMode == "Close"
            strategy.close("S", comment="Time Exit")
        else if timeExitMode == "Roll"
            wantReenterS := true
            strategy.close("S", comment="Time Exit (Roll)")

// Re-enter after Roll
if inRange and not inPos and wantReenterL and longRegOK and (close > donHiPrev)
    N_entry := N
    float sh = sharesForUnit(strategy.equity, N_entry)
    strategy.entry("L", strategy.long, qty=sh)
    units       := 1
    lastAddLong := close
    wantReenterL := false

if inRange and not inPos and wantReenterS and shortRegOK and (close < donLoPrev)
    N_entry := N
    float sh = sharesForUnit(strategy.equity, N_entry)
    strategy.entry("S", strategy.short, qty=sh)
    units        := 1
    lastAddShort := close
    wantReenterS := false

//================ Plots
// Background shading for volatility regime
volBg = useVolGate ? (isVolSpike ? color.new(color.red, 95) : color.new(color.green, 95)) : na
bgcolor(volBg, title="Volatility Regime")

float dHiPlot = plotDon ? donHi : na
float dLoPlot = plotDon ? donLo : na
plot(dHiPlot, "Donchian High", color=color.new(color.blue, 40))
plot(dLoPlot, "Donchian Low",  color=color.new(color.blue, 40))

// Plot ATR and its MA for reference
plot(showSignals ? N : na, "ATR (N)", color=color.new(color.orange, 0), linewidth=1, display=display.data_window)
plot(showSignals ? atrMA : na, "ATR MA", color=color.new(color.purple, 0), linewidth=1, display=display.data_window)

plotshape(showSignals and longBreak,  title="Long Breakout",  style=shape.triangleup,   location=location.belowbar, size=size.tiny,  color=color.new(color.green, 0), text="Long")
plotshape(showSignals and shortBreak, title="Short Breakout", style=shape.triangledown, location=location.abovebar, size=size.tiny,  color=color.new(color.red,   0), text="Short")

// Mark successful adds (green) and blocked adds (red X)
plotshape(showSignals and canAddLong,              title="Add Long",      style=shape.circle, location=location.belowbar, size=size.tiny, color=color.new(color.green, 30))
plotshape(showSignals and canAddShort,             title="Add Short",     style=shape.circle, location=location.abovebar, size=size.tiny, color=color.new(color.red,   30))
plotshape(showSignals and priceConditionLong and not volGateOK,  title="Blocked Add (Long)",  style=shape.xcross, location=location.belowbar, size=size.tiny, color=color.new(color.red, 0))
plotshape(showSignals and priceConditionShort and not volGateOK, title="Blocked Add (Short)", style=shape.xcross, location=location.abovebar, size=size.tiny, color=color.new(color.red, 0))

plot(showSignals and inLong  ? stopL : na, "Long Stop",  color=color.new(color.red,  0), style=plot.style_linebr, linewidth=2)
plot(showSignals and inShort ? stopS : na, "Short Stop", color=color.new(color.red,  0), style=plot.style_linebr, linewidth=2)

// Info table
var table infoTable = table.new(position.top_right, 2, 5, border_width=1)
if barstate.islast and showSignals
    table.cell(infoTable, 0, 0, "Pyramid:", text_color=color.white, bgcolor=color.gray)
    table.cell(infoTable, 1, 0, "VOL-GATED", text_color=color.white, bgcolor=color.purple)

    table.cell(infoTable, 0, 1, "Vol Status:", text_color=color.white, bgcolor=color.gray)
    volStatus = isVolSpike ? "SPIKE (Adds Blocked)" : "Normal (Adds OK)"
    volColor = isVolSpike ? color.red : color.green
    table.cell(infoTable, 1, 1, volStatus, text_color=color.white, bgcolor=volColor)

    table.cell(infoTable, 0, 2, "ATR / MA:", text_color=color.white, bgcolor=color.gray)
    atrRatio = atrMA > 0 ? N / atrMA : 1.0
    table.cell(infoTable, 1, 2, str.tostring(atrRatio, "#.##") + "x", text_color=color.white, bgcolor=color.gray)

    table.cell(infoTable, 0, 3, "Blocked Adds:", text_color=color.white, bgcolor=color.gray)
    table.cell(infoTable, 1, 3, str.tostring(blockedAdds), text_color=color.white, bgcolor=blockedAdds > 0 ? color.orange : color.gray)

    table.cell(infoTable, 0, 4, "Units:", text_color=color.white, bgcolor=color.gray)
    table.cell(infoTable, 1, 4, str.tostring(units) + "/" + str.tostring(maxUnits), text_color=color.white, bgcolor=color.gray)

Overview

Seykota Alt 13: Vol-Gated Adds enters positions on Donchian channel breakouts using a longer lookback period of 55 bars and exits on shorter 10-bar signals. Volatility gating prevents adding to positions during ATR spikes above a moving average threshold, avoiding exhaustion moves. Initial stops and add increments are based on ATR multiples. The strategy supports pyramiding up to four units per direction and allows both long and short trades. Optional features include market regime filtering using a higher timeframe and time-based exits. Signals and stops are plotted directly on the price chart with customizable inputs for entry/exit parameters, risk per unit, and backtesting date ranges.

Details

Published by

trustdan on GitHub

Code credit

danieltuckerrust, as written in the script's header

Type

Strategy, Pine Script v6

Draws

On the price chart

Category

Trend, Moving averages, Volatility & bands

Markets

Stocks

Timeframes

H4 and above

Risk Notes

  • The strategy can add several entries in the same direction

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
Allow LONGs?booltrueallowLong
Allow SHORTs?booltrueallowShort
Donchian ENTRY lookbackint55entryLen
Donchian EXIT lookbackint10exitLen
N = ATR lengthint20nLen
Initial stop (in N)float2.0stopN
Add every X * Nfloat0.5addStepN
Max units (incl. initial)int4maxUnits
Risk % of equity PER UNITfloat1.0riskPct
Block adds during vol spikes?booltrueuseVolGate
Vol gate MA lengthint20volGateLen
Vol spike threshold (ATR > X * MA)float1.0volGateMult
Show all 24 settings
SettingTypeDefaultVariable
Use market regime filter?boolfalseuseMarket
Market symbol for regime (if used)symbolSPYmarketSym
Regime timeframe (if used)timeframeDmarketTF
Market MA length (if used)int200marketLen
Time exitstringNonetimeExitMode
Time exit bars (if used)int60timeExitBars
Min 20-bar avg volume (chart TF)int0minVol
Plot signals & stops?booltrueshowSignals
Plot Donchian bands?booltrueplotDon
Backtest FROM (yyyy-mm-dd)timetimestamp("2022-01-01T00:00:00")fromDate
Backtest TO (yyyy-mm-dd)timetimestamp("2099-12-31T23:59:59")toDate
Force FLAT at range start?booltrueflatAtFrom
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

This mean-reversion strategy plots Bollinger Bands and a long-term moving average directly on the price chart. It enters trades when price candles penetrate the outer Bollinger Bands beyond a user-defined body percentage, anticipating a price reversal. Entries

Strategy v5 Mean reversionVolatility & bands
Updated 2 months ago · None stated GitHub repository of Bollinger Bands Winner PRO [Alorse]
257 stars

This overlay strategy targets one-minute ES futures and plots volume-weighted moving-average bands from the open, scaled by a standard-deviation multiple, plus a slower band from ten-minute opens. A higher-timeframe exponential moving average can filter trades

Strategy v6 Moving averagesVolatility & bands
Updated 6 weeks ago · None stated GitHub repository of ES - Futures 1M No Repaint

STRG-BBForce

by harryguiacorn
219 stars

STRG-BBForce is a Bollinger Bands trend-following strategy. It calculates standard Bollinger Bands and tracks directional alignment across the upper, lower, and basis lines. When all three lines slope upward, it opens a long position and closes short positions

Strategy v5 TrendVolatility & bands
Updated 1 year ago · None stated GitHub repository of STRG-BBForce
106 stars

This trading strategy combines three Exponential Moving Averages (8, 14, and 50 periods by default) with Stochastic RSI and Average True Range (ATR). It enters a long position when EMA 1 is above EMA 2, EMA 2 is above EMA 3, and the Stochastic RSI %K line cros

Strategy v4 TrendOscillators & momentum
Updated 2 years ago · GPL-3.0 GitHub repository of 3 x EMA + Stochastic RSI + ATR

Bollinger Bands Strategy

by cklose2000
105 stars

This strategy generates buy and sell signals based on Bollinger Band crossovers combined with a simple moving average trend filter. It enters a long position when price crosses above the lower band while trading above the SMA filter line, and enters a short po

Strategy v5 Volatility & bandsMoving averages
Updated 1 year ago · None stated GitHub repository of Bollinger Bands Strategy
104 stars

This overlay strategy combines a Chandelier Exit with a zero-lag least squares moving average. Although the header mentions Heikin Ashi candles, the calculations use ordinary closes. An ATR trailing stop sets long and short levels, and direction flips when pri

Strategy v5 TrendMoving averages
Updated 3 years ago · MIT GitHub repository of Chandelier Exit & ZLSMA Strategy

More strategys

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers