Strategy · Pine Script v6 · On the chart

Hierarchical HTS v6 Markov-Proxy Regime Switcher

by rosijay17-cmd

A strategy that first classifies the market using ADX and a Bollinger-inside-Keltner squeeze check, then switches between two sub-strategies.

View on GitHub

GitHub stars1on the repository
Pine versionv6current
Lines of code243
Settings22inputs
LicenceGPL-3.0
Last update7 days ago22 Oct 2026

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

243 lines of Pine Script v6 from Hierarchical_HTS_v6_Markov_Proxy_Regime_Switcher.pine, published under GPL-3.0. Keep the author's credit and licence lines when you reuse it.

Hierarchical_HTS_v6_Markov_Proxy_Regime_Switcher.pine
//@version=6
strategy("Hierarchical HTS v6 (Markov-Proxy Regime Switcher)", overlay=true, initial_capital=10000, default_qty_type=strategy.percent_of_equity, default_qty_value=10)

// ==========================================
// --- INPUTS ---
// ==========================================
// 1. Layer 1: Markov-Proxy Regime Switcher Inputs
adxLength      = input.int(14, title="ADX Length", group="Layer 1: Regime Switcher")
adxThreshold   = input.int(25, title="ADX Trend Threshold", group="Layer 1: Regime Switcher")
bbSqueezeLen   = input.int(20, title="Squeeze BB Length", group="Layer 1: Regime Switcher")
bbSqueezeMult  = input.float(2.0, title="Squeeze BB Multiplier", group="Layer 1: Regime Switcher")
kcSqueezeMult  = input.float(1.5, title="Squeeze Keltner Multiplier", group="Layer 1: Regime Switcher")
cooldownBars   = input.int(5, title="Cooldown Bars", minval=0, group="Execution Settings")

// 2. Sub-Strategy A: Mean Reversion Inputs (Active in Ranging Regime)
bbLength       = input.int(20, title="BB Length", group="Sub-Strategy A: Mean Reversion")
baseMult       = input.float(2.5, title="Base BB Multiplier", group="Sub-Strategy A: Mean Reversion")
asymOffset     = input.float(0.5, title="Asymmetric Offset", group="Sub-Strategy A: Mean Reversion")
emaLength      = input.int(200, title="Trend Filter EMA", group="Sub-Strategy A: Mean Reversion")

// 3. Sub-Strategy B: Trend Following Inputs (Active in Trending Regime)
dcLength       = input.int(20, title="Donchian Breakout Length", group="Sub-Strategy B: Trend Following")
dcExitLength   = input.int(10, title="Donchian Exit Length", group="Sub-Strategy B: Trend Following")

// 4. Fakeout Breakout Filters (Applied to Sub-Strategy B)
useVolumeFilter = input.bool(true, title="Enable Volume Confirmation?", group="Sub-Strategy B: Breakout Filters")
volLength       = input.int(20, title="Volume Average Length", group="Sub-Strategy B: Breakout Filters")
volMultiplier   = input.float(1.5, title="Volume Multiplier (x SMA)", group="Sub-Strategy B: Breakout Filters")

useAtrFilter    = input.bool(true, title="Enable Range (ATR) Confirmation?", group="Sub-Strategy B: Breakout Filters")
atrLength       = input.int(14, title="ATR Length", group="Sub-Strategy B: Breakout Filters")
atrMultiplier   = input.float(1.0, title="Range Multiplier (x ATR)", group="Sub-Strategy B: Breakout Filters")

useWickFilter   = input.bool(true, title="Enable Rejection Wick Filter?", group="Sub-Strategy B: Breakout Filters")
maxWickPct      = input.float(35.0, title="Max Rejection Wick %", group="Sub-Strategy B: Breakout Filters")

useAtrPenetration  = input.bool(true, title="Enable ATR Penetration Filter?", group="Sub-Strategy B: Breakout Filters")
atrPenetrationMult = input.float(0.5, title="Min ATR Penetration Multiplier", group="Sub-Strategy B: Breakout Filters")

// ==========================================
// --- LAYER 1: REGIME SWITCHER ENGINE ---
// ==========================================
// A. Volatility Compression Calculation (Squeeze Detection)
basisSqueeze = ta.sma(close, bbSqueezeLen)
devSqueeze   = bbSqueezeMult * ta.stdev(close, bbSqueezeLen)
upperBB      = basisSqueeze + devSqueeze
lowerBB      = basisSqueeze - devSqueeze

atrValSqueeze = ta.atr(bbSqueezeLen)
upperKC       = basisSqueeze + (atrValSqueeze * kcSqueezeMult)
lowerKC       = basisSqueeze - (atrValSqueeze * kcSqueezeMult)

// A squeeze occurs when Bollinger Bands compress inside the Keltner Channels
bool isSqueezed = (upperBB < upperKC) and (lowerBB > lowerKC)

// B. Trend Momentum Calculation (ADX)
[diPlus, diMinus, adxValue] = ta.dmi(adxLength, adxLength)

// C. Dynamic Regime Assignment
// State 0 = Squeeze (Imminent breakout, lock out all entries)
// State 1 = Trending (High volatility breakout, run Sub-Strategy B)
// State 2 = Ranging (Sideways market waves, run Sub-Strategy A)
int regimeState = 2
if isSqueezed
    regimeState := 0
else if adxValue >= adxThreshold
    regimeState := 1
else
    regimeState := 2

// D. Routing Variables
bool isSqueezeState = regimeState == 0
bool isTrendState   = regimeState == 1
bool isRangeState   = regimeState == 2

// Highlight chart background based on active regime: Yellow (Squeeze), Teal (Trending), Orange (Ranging)
color bgRegimeColor = isSqueezeState ? color.new(color.yellow, 98) :
                      isTrendState   ? color.new(color.teal, 98)   :
                      color.new(color.orange, 98)
bgcolor(bgRegimeColor, title="Active Regime Background")

// ==========================================
// --- SUB-STRATEGY CALCULATIONS ---
// ==========================================
// Sub-Strategy A: Asymmetric Bollinger Bands
basisMR     = ta.sma(close, bbLength)
stdDevValMR = ta.stdev(close, bbLength)
trendEma    = ta.ema(close, emaLength)
bool isUptrend = close > trendEma

float longMult  = isUptrend ? (baseMult - asymOffset) : (baseMult + asymOffset)
float shortMult = isUptrend ? (baseMult + asymOffset) : (baseMult - asymOffset)

upperBB_MR = basisMR + (shortMult * stdDevValMR)
lowerBB_MR = basisMR - (longMult * stdDevValMR)

// Sub-Strategy B: Donchian Channels
dcUpper = ta.highest(high, dcLength)[1]
dcLower = ta.lowest(low, dcLength)[1]
dcExitUpper = ta.highest(high, dcExitLength)[1]
dcExitLower = ta.lowest(low, dcExitLength)[1]

// ==========================================
// --- SUB-STRATEGY B FAKEOUT FILTERS ---
// ==========================================
// 1. Volume Confirmation
volMa = ta.sma(volume, volLength)
bool isVolumeConfirmed = not useVolumeFilter or (volume > (volMa * volMultiplier))

// 2. Range Confirmation
atrVal = ta.atr(atrLength)
candleRange = high - low
bool isRangeConfirmed = not useAtrFilter or (candleRange > (atrVal * atrMultiplier))

// 3. Rejection Wick Filter
upperWick = high - math.max(open, close)
lowerWick = math.min(open, close) - low
bool longWickOK  = not useWickFilter or (candleRange > 0 ? (upperWick / candleRange) * 100 <= maxWickPct : true)
bool shortWickOK = not useWickFilter or (candleRange > 0 ? (lowerWick / candleRange) * 100 <= maxWickPct : true)

// 4. ATR Penetration Filter
bool longAtrPenetration  = not useAtrPenetration or ((close - dcUpper) >= (atrVal * atrPenetrationMult))
bool shortAtrPenetration = not useAtrPenetration or ((dcLower - close) >= (atrVal * atrPenetrationMult))

// Combined validated breakout pipelines
bool isLongBreakoutValid  = isVolumeConfirmed and isRangeConfirmed and longWickOK and longAtrPenetration
bool isShortBreakoutValid = isVolumeConfirmed and isRangeConfirmed and shortWickOK and shortAtrPenetration

// ==========================================
// --- SIGNALS & COOLDOWN ---
// ==========================================
var int lastSignalBar = -99999
bool isCooldownOver   = (bar_index - lastSignalBar) >= cooldownBars

// Raw Strategy Signals
bool rawMrLong  = ta.crossover(close, lowerBB_MR)
bool rawMrShort = ta.crossunder(close, upperBB_MR)

bool rawTfLong  = ta.crossover(close, dcUpper)
bool rawTfShort = ta.crossunder(close, dcLower)

// Final Conditions filtered strictly by the Markov-Proxy Active State
// During Squeeze State (0), both entry conditions evaluate to false (sidelined)
bool mrLongCondition  = isRangeState and rawMrLong and isCooldownOver
bool mrShortCondition = isRangeState and rawMrShort and isCooldownOver

bool tfLongCondition  = isTrendState and rawTfLong and isCooldownOver and isLongBreakoutValid
bool tfShortCondition = isTrendState and rawTfShort and isCooldownOver and isShortBreakoutValid

// Update Cooldown Tracker on any executed signal
if mrLongCondition or mrShortCondition or tfLongCondition or tfShortCondition
    lastSignalBar := bar_index

// ==========================================
// --- EXECUTION ENGINE ---
// ==========================================
// 1. Mean Reversion Execution (MR)
if mrLongCondition
    strategy.entry("MR Long", strategy.long, comment="MR Buy")

if mrShortCondition
    strategy.entry("MR Short", strategy.short, comment="MR Short")

// 2. Trend Following Execution (TF)
if tfLongCondition
    strategy.entry("TF Long", strategy.long, comment="TF Breakout Buy")

if tfShortCondition
    strategy.entry("TF Short", strategy.short, comment="TF Breakout Short")

// ==========================================
// --- EXIT ENGINE ---
// ==========================================
// MR Exits (Reversion to Mean)
if isRangeState
    if ta.crossover(close, basisMR) and (strategy.position_size < 0)
        strategy.close("MR Short", comment="MR Short Cover")
    if ta.crossunder(close, basisMR) and (strategy.position_size > 0)
        strategy.close("MR Long", comment="MR Long Sell")

// TF Exits (Trailing Highs/Lows)
if isTrendState
    if ta.crossunder(close, dcExitLower) and (strategy.position_size > 0)
        strategy.close("TF Long", comment="TF Trail Exit")
    if ta.crossover(close, dcExitUpper) and (strategy.position_size < 0)
        strategy.close("TF Short", comment="TF Trail Exit")

// ==========================================
// --- VISUALIZATIONS & HUD ---
// ==========================================
plot(basisMR, color=color.new(color.blue, 50), title="BB Basis (Mean)")
plot(trendEma, color=color.orange, linewidth=2, title="Trend Filter EMA")

plotshape(mrLongCondition, title="MR Long Signal", style=shape.triangleup, location=location.belowbar, color=color.green, size=size.small, text="MR")
plotshape(mrShortCondition, title="MR Short Signal", style=shape.triangledown, location=location.abovebar, color=color.red, size=size.small, text="MR")

plotshape(tfLongCondition, title="TF Long Signal", style=shape.triangleup, location=location.belowbar, color=color.teal, size=size.small, text="TF Breakout")
plotshape(tfShortCondition, title="TF Short Signal", style=shape.triangledown, location=location.abovebar, color=color.maroon, size=size.small, text="TF Breakout")

// Real-Time HUD Dashboard
var table hud = table.new(position.top_right, 2, 3, bgcolor=color.new(color.black, 30), border_width=1)
if barstate.islast
    table.clear(hud, 0, 0, 1, 2)
    string activeStateText = isSqueezeState ? "SQUEEZE (SIDELINED)" :
                             isTrendState   ? "TRENDING (TF ACTIVE)"  :
                             "RANGING (MR ACTIVE)"
    color activeStateColor = isSqueezeState ? color.yellow :
                             isTrendState   ? color.teal   :
                             color.orange

    // Cell 0: Regime State
    table.cell(hud, 0, 0, "HTS REGIME:", text_color=color.white, text_size=size.small)
    table.cell(hud, 1, 0, activeStateText, bgcolor=activeStateColor, text_color=color.white, text_size=size.small)

    // Cell 1: ADX Value
    table.cell(hud, 0, 1, "ADX POWER:", text_color=color.white, text_size=size.small)
    table.cell(hud, 1, 1, str.tostring(adxValue, "#.#"), text_color=color.white, text_size=size.small)

    // Cell 2: Volatility compression
    table.cell(hud, 0, 2, "VOL COMPRESSION:", text_color=color.white, text_size=size.small)
    table.cell(hud, 1, 2, isSqueezed ? "COILING" : "EXPANDED", bgcolor=isSqueezed ? color.new(color.yellow, 20) : color.new(color.green, 20), text_color=color.white, text_size=size.small)

// ==========================================
// --- ALERTS ---
// ==========================================
// Added when this script was brought into the repo -- the original pasted
// version had NO alertcondition()/alert() calls at all, which was the real
// bug behind "my alerts aren't working" (the only alert option available
// without these is TradingView's coarse built-in "Order fills", which
// can't distinguish MR from TF or long from short). Each of the four raw
// entry conditions gets its own named, explicitly bullish/bearish alert.
//
// NOTE: adding these does not by itself fix "alerts stopped after
// switching to the December contract" -- that symptom is TradingView
// binding an alert to the exact symbol it was created on, which no Pine
// code change can fix. Any alert made against this script still needs to
// be (re)created on the contract you're actually charting, or you trade/
// alert off the continuous contract (e.g. NQ1!/MNQ1!) to avoid repeating
// this every roll period.
alertcondition(mrLongCondition, title="MR Long Signal (Bullish)", message="BULLISH | Mean Reversion LONG entry -- {{ticker}} {{interval}} @ {{close}}")
alertcondition(mrShortCondition, title="MR Short Signal (Bearish)", message="BEARISH | Mean Reversion SHORT entry -- {{ticker}} {{interval}} @ {{close}}")
alertcondition(tfLongCondition, title="TF Long Breakout (Bullish)", message="BULLISH | Trend Breakout LONG entry -- {{ticker}} {{interval}} @ {{close}}")
alertcondition(tfShortCondition, title="TF Short Breakout (Bearish)", message="BEARISH | Trend Breakout SHORT entry -- {{ticker}} {{interval}} @ {{close}}")

Overview

In ranging conditions it fades asymmetric Bollinger Band extremes with a 200 EMA trend filter. In trending conditions it trades Donchian breakouts, with optional volume, range, rejection wick and ATR penetration filters. Exits use a Donchian exit channel and cooldown bars apply. Size is a percent of equity and no stop loss is set.

Details

Published by

rosijay17-cmd on GitHub

Type

Strategy, Pine Script v6

Draws

On the price chart

Category

Mean reversion, Breakout, Volatility & bands

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

  • The strategy enters trades without a stop loss

Settings

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

SettingTypeDefaultVariable
ADX Lengthint14adxLength
ADX Trend Thresholdint25adxThreshold
Squeeze BB Lengthint20bbSqueezeLen
Squeeze BB Multiplierfloat2.0bbSqueezeMult
Squeeze Keltner Multiplierfloat1.5kcSqueezeMult
Cooldown Barsint5cooldownBars
BB Lengthint20bbLength
Base BB Multiplierfloat2.5baseMult
Asymmetric Offsetfloat0.5asymOffset
Trend Filter EMAint200emaLength
Donchian Breakout Lengthint20dcLength
Donchian Exit Lengthint10dcExitLength
Show all 22 settings
SettingTypeDefaultVariable
Enable Volume Confirmation?booltrueuseVolumeFilter
Volume Average Lengthint20volLength
Volume Multiplier (x SMA)float1.5volMultiplier
Enable Range (ATR) Confirmation?booltrueuseAtrFilter
ATR Lengthint14atrLength
Range Multiplier (x ATR)float1.0atrMultiplier
Enable Rejection Wick Filter?booltrueuseWickFilter
Max Rejection Wick %float35.0maxWickPct
Enable ATR Penetration Filter?booltrueuseAtrPenetration
Min ATR Penetration Multiplierfloat0.5atrPenetrationMult
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

BBPullback

by hasnocool
106 stars

This strategy executes pullback trades based on internally computed Bollinger Bands and candle price action directly on the main price chart. It tracks breakout and pullback states to trigger long and short entries. Users can select among three distinct take p

Strategy v5 Volatility & bandsMean reversion
Updated 2 years ago · GPL-3.0 GitHub repository of BBPullback
3 stars

This strategy plots Bollinger Bands on the price chart and enters long only when flat. It buys on a strong bullish close above the middle band or on a small-body pullback that tags the middle band with a long lower wick. The stop sits below the signal bar by a

Strategy v5 Volatility & bandsBreakout
Updated 2 days ago · None stated GitHub repository of H4 布林ATR型態多單策略

KC Integration

by Sikako
1 stars

KC Integration is a trading strategy that calculates multiple sets of Keltner Channels tailored for both trend-following and counter-trend conditions. It plots upper, middle, and lower channel bands directly on the main price chart to identify potential breako

Strategy v5 Volatility & bandsBreakout
Updated 3 years ago · None stated GitHub repository of KC Integration
1 stars

The strategy detects range expansion by comparing the previous bar's range to its ATR multiple. It places pending stop entries above highs of up bars or below lows of down bars. On trigger it enters positions with take profit and stop loss in ticks using OCA g

Strategy v6 Volatility & bandsSupport & resistance
Updated 6 days ago · Apache-2.0 GitHub repository of PF probe 97 - tp/sl gap reversal oca

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