Kernal Cipher is a custom TradingView Pine Script v6 indicator that overlays multiple technical components on the price chart for discretionary momentum trading.
1Copy the scriptPress Copy script above, or the copy button on the code below.
2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.
Source Code
756 lines of Pine Script v6 from Kernal-Cipher.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.
Kernal-Cipher.pine
//@version=6
// ═══════════════════════════════════════════════════════════════════════════
// KERNAL CIPHER — All-in-one overlay (merged 2026-09-20;
// "Kernal Cipher 1" overlay + "Kernal Cipher 2" signal panel).
// Companion: "Kernal Cipher Strategy" (backtest reference).
// Trend (EMA stack + slope) · Supertrend · Regime · WaveTrend · StochRSI ·
// regular + hidden divergence · RS vs SPY/QQQ · ATR stop/regime/targets ·
// RVOL · rolling beta · setup classifier · virtual position.
// 2026-09-20 FEAR/GREED + VIX: the KC3-validated 7-component Fear/Greed gauge
// and both decision gates (FG block, VIX calm) are wired into the overlay's
// signal logic with identical math and defaults, so the overlay's signals,
// targets and virtual position agree with what KC3 trades. Gauge value +
// zone + VIX state on the dashboard; optional zone ribbon / background tint.
// All filters are chart-timeframe native — works on any timeframe.
// ═══════════════════════════════════════════════════════════════════════════
indicator("Kernal Cipher", shorttitle="KC", overlay=true, max_lines_count=500)
GRP_T = "01 · Trend"
GRP_ST = "02 · Supertrend"
GRP_RG = "03 · Regime"
GRP_WT = "04 · WaveTrend"
GRP_SR = "05 · StochRSI"
GRP_DV = "06 · Divergence"
GRP_RS = "07 · Relative Strength & Beta"
GRP_ATR = "08 · ATR / Volatility / Risk"
GRP_VL = "09 · Volume"
GRP_SG = "10 · Signals"
GRP_TG = "11 · Profit Targets"
GRP_FG = "12 · Fear & Greed"
GRP_DS = "13 · Display"
GRP_WC = "14 · White Candle Event"
// ── 01 Trend ──
emaFastLen = input.int(21, "EMA Fast", minval=2, group=GRP_T)
emaSlowLen = input.int(50, "EMA Slow", minval=3, group=GRP_T)
emaLongLen = input.int(200, "EMA Long / Regime", minval=20, group=GRP_T)
slopeLen = input.int(5, "EMA Slope Lookback", minval=1, group=GRP_T)
minSlopePct = input.float(0.05, "Minimum EMA Slope %", step=0.01, group=GRP_T)
htfTF = input.timeframe("D", "Higher Timeframe", group=GRP_T)
// ── 02 Supertrend ──
stLen = input.int(10, "ATR Length", minval=1, group=GRP_ST)
stMult = input.float(3.0, "Multiplier", minval=0.1, step=0.1, group=GRP_ST)
// ── 03 Regime ──
adxLen = input.int(14, "ADX Length", minval=2, group=GRP_RG)
adxThresh = input.int(20, "ADX Minimum", minval=5, group=GRP_RG)
requireDiAlignment = input.bool(true, "Require DI Alignment", group=GRP_RG)
// ── 04 WaveTrend ──
wtN1 = input.int(10, "Channel Length", minval=1, group=GRP_WT)
wtN2 = input.int(21, "Average Length", minval=1, group=GRP_WT)
// ── 05 StochRSI ──
rsiLen = input.int(14, "RSI Length", minval=2, group=GRP_SR)
stochLen = input.int(14, "Stochastic Length", minval=2, group=GRP_SR)
smoothK = input.int(3, "Smooth K", minval=1, group=GRP_SR)
smoothD = input.int(3, "Smooth D", minval=1, group=GRP_SR)
stochLongMax = input.float(80, "Maximum K for Long Entry", minval=1, maxval=100, group=GRP_SR)
stochShortMin = input.float(20, "Minimum K for Short Entry", minval=0, maxval=99, group=GRP_SR)
// ── 06 Divergence ──
lbL = input.int(5, "Pivot Lookback Left", minval=1, group=GRP_DV)
lbR = input.int(5, "Pivot Lookback Right", minval=1, group=GRP_DV)
divMinBars = input.int(5, "Min Bars Between Pivots", minval=1, group=GRP_DV)
divMaxBars = input.int(60, "Max Bars Between Pivots", minval=2, group=GRP_DV)
// ── 07 RS & Beta ──
useRSFilter = input.bool(true, "Use Relative Strength Filter", group=GRP_RS)
bench1 = input.symbol("AMEX:SPY", "Primary Benchmark", group=GRP_RS)
bench2 = input.symbol("NASDAQ:QQQ", "Secondary Benchmark", group=GRP_RS)
rsLen = input.int(20, "RS Lookback", minval=2, group=GRP_RS)
rsLongMin = input.float(0.0, "Minimum RS % for Longs", step=0.1, group=GRP_RS)
rsShortMax = input.float(0.0, "Maximum RS % for Shorts", step=0.1, group=GRP_RS)
betaLen = input.int(60, "Beta Lookback", minval=10, group=GRP_RS)
betaMin = input.float(0.7, "Minimum Beta for Longs", minval=0, step=0.1, group=GRP_RS)
useBetaFilter = input.bool(false, "Require Minimum Beta for Longs", group=GRP_RS)
// ── 08 ATR ──
atrLen = input.int(14, "ATR Length", minval=1, group=GRP_ATR)
atrStopMult = input.float(2.0, "ATR Stop Multiplier", minval=0.5, step=0.1, group=GRP_ATR)
useTFStop = input.bool(true, "Timeframe-Aware Stop (0.75x intraday / 2.0x daily+)", group=GRP_ATR)
tfStopMult = input.float(0.75, "Intraday Stop Multiplier", minval=0.5, step=0.1, group=GRP_ATR)
stopMultEff = useTFStop and timeframe.isintraday ? tfStopMult : atrStopMult
atrRegimeLen = input.int(50, "ATR Regime Baseline", minval=5, group=GRP_ATR)
atrExpansionMin = input.float(0.80, "Min ATR / ATR Baseline", minval=0.1, step=0.05, group=GRP_ATR)
atrExpansionMax = input.float(3.00, "Max ATR / ATR Baseline", minval=0.5, step=0.1, group=GRP_ATR)
maxChaseATR = input.float(1.5, "Max Distance From Fast EMA (ATR)", minval=0.25, step=0.25, group=GRP_ATR)
showATRTarget = input.bool(true, "Show ATR Target", group=GRP_ATR)
rngMult = input.float(1.5, "EMA Range Multiplier", minval=0.1, step=0.1, group=GRP_ATR)
showBands = input.bool(true, "Show ATR Range Bands", group=GRP_ATR)
// ── 09 Volume ──
volLen = input.int(20, "Volume Baseline Length", minval=2, group=GRP_VL)
rvolMin = input.float(1.20, "Minimum RVOL", minval=0.1, step=0.05, group=GRP_VL)
rvolStrong = input.float(2.00, "Strong RVOL", minval=0.5, step=0.1, group=GRP_VL)
useVolFilter = input.bool(true, "Require Volume Confirmation", group=GRP_VL)
// ── 14 White Candle Event ──
useWhiteCandle = input.bool(true, "Paint White-Candle Event", group=GRP_WC, tooltip="Flags the candle white when total volume is slightly below average but buying dominates the tape and price range stretches beyond ATR.")
wcVolLow = input.float(0.70, "Volume Lower Bound (× avg)", minval=0.1, step=0.05, group=GRP_WC)
wcVolHigh = input.float(1.00, "Volume Upper Bound (× avg)", minval=0.5, step=0.05, group=GRP_WC)
wcBuyFrac = input.float(0.65, "Min Buy-Share Fraction", minval=0.5, maxval=1.0, step=0.05, group=GRP_WC, tooltip="Estimated share of volume on the buy side: (close-low)/(high-low).")
wcAtrMult = input.float(1.00, "Range Expansion (× ATR)", minval=0.25, step=0.25, group=GRP_WC)
wcMirror = input.bool(true, "Paint Sell-Side Mirror (black)", group=GRP_WC)
// ── 10 Signals (weighted: regime/EMA/ST count double; max score 12) ──
regimeWeight = input.int(2, "Regime Weight", minval=1, maxval=4, group=GRP_SG)
emaWeight = input.int(2, "EMA Weight", minval=1, maxval=4, group=GRP_SG)
stWeight = input.int(2, "Supertrend Weight", minval=1, maxval=4, group=GRP_SG)
slopeWeight = input.int(1, "Slope Weight", minval=0, maxval=3, group=GRP_SG)
wtWeight = input.int(1, "WaveTrend Weight", minval=0, maxval=3, group=GRP_SG)
srWeight = input.int(1, "StochRSI Weight", minval=0, maxval=3, group=GRP_SG)
divWeight = input.int(1, "Divergence Weight", minval=0, maxval=3, group=GRP_SG)
rsWeight = input.int(1, "Relative Strength Weight", minval=0, maxval=3, group=GRP_SG)
atrWeight = input.int(1, "ATR Weight", minval=0, maxval=3, group=GRP_SG)
longThresh = input.int(10, "Long Score Threshold", minval=1, maxval=20, group=GRP_SG)
shortThresh = input.int(10, "Short Score Threshold", minval=1, maxval=20, group=GRP_SG)
strongOffset = input.int(2, "Strong Signal Offset (score above threshold)", minval=1, maxval=5, group=GRP_SG)
cooldownBars = input.int(5, "Signal Cooldown Bars", minval=0, group=GRP_SG)
breakoutLookback = input.int(20, "Breakout Lookback", minval=5, group=GRP_SG)
longOnly = input.bool(false, "Long Signals Only", group=GRP_SG)
// ── 11 Targets ──
showTargets = input.bool(true, "Show Profit Targets (1.5x / 2x / 3x ATR)", group=GRP_TG)
tgtBars = input.int(60, "Target Projection (bars)", minval=10, group=GRP_TG)
// ── 12 Fear/Greed gauge (identical math + defaults to KC3) ──
useFG = input.bool(true, "Show Fear/Greed Gauge", group=GRP_FG)
fgSymVIX = input.symbol("TVC:VIX", "VIX Symbol (volatility)", group=GRP_FG)
fgSymHYG = input.symbol("AMEX:HYG", "High-Yield Bond Symbol", group=GRP_FG)
fgSymIEF = input.symbol("AMEX:IEF", "Treasury Bond Symbol", group=GRP_FG)
fgZLen = input.int(252, "Normalization Window (bars)", minval=50, group=GRP_FG)
fgMomLen = input.int(125, "Momentum SMA Length", minval=20, group=GRP_FG)
fgVixLen = input.int(50, "VIX SMA Length", minval=10, group=GRP_FG)
fgRetLen = input.int(20, "Return / Breadth Window", minval=5, group=GRP_FG)
fgRangeLen = input.int(252, "Strength Range Length", minval=50, group=GRP_FG)
fgFear = input.int(24, "Extreme Fear Zone (≤)", minval=1, maxval=49, group=GRP_FG)
fgGreed = input.int(76, "Extreme Greed Zone (≥)", minval=51, maxval=99, group=GRP_FG)
fgW1 = input.float(1.0, "Weight: Market Momentum", minval=0, step=0.5, group=GRP_FG)
fgW2 = input.float(1.0, "Weight: Price Strength", minval=0, step=0.5, group=GRP_FG)
fgW3 = input.float(1.0, "Weight: Breadth", minval=0, step=0.5, group=GRP_FG)
fgW4 = input.float(1.0, "Weight: Put/Call Proxy", minval=0, step=0.5, group=GRP_FG)
fgW5 = input.float(1.0, "Weight: Junk Bonds", minval=0, step=0.5, group=GRP_FG)
fgW6 = input.float(1.0, "Weight: Volatility", minval=0, step=0.5, group=GRP_FG)
fgW7 = input.float(1.0, "Weight: Safe Haven", minval=0, step=0.5, group=GRP_FG)
useFGBlock = input.bool(true, "Block Entries Against Extreme Crowd", group=GRP_FG)
useFGConv = input.bool(false, "Fear/Greed Conviction Boost", group=GRP_FG)
useVIXCalm = input.bool(true, "Longs Only When VIX Calm (VIX < SMA)", group=GRP_FG)
vixCalmLen = input.int(50, "VIX Calm SMA Length", minval=10, group=GRP_FG)
useVIXPanic = input.bool(false, "Skip Entries on VIX Panic Spike", group=GRP_FG)
vixPanicPct = input.float(15.0, "VIX Panic Spike % (day-over-day)", minval=5, step=1, group=GRP_FG)
// ── 13 Display ──
showDash = input.bool(true, "Show Dashboard", group=GRP_DS)
showBg = input.bool(true, "Regime Background", group=GRP_DS)
showDiv = input.bool(true, "Show Divergence Markers", group=GRP_DS)
showFGRibbon = input.bool(true, "Show Fear/Greed Zone Ribbon", group=GRP_DS)
showFGTint = input.bool(false, "Fear/Greed Background Tint", group=GRP_DS)
showContract = input.bool(true, "Show Contract Strip (bottom-left fine print)", group=GRP_DS, tooltip="The decision contract: last signal, score vs trigger, and live gate state. Four rows, tiny text, bottom-left.")
dashStyleOpt = input.string("Upright Table", "Dashboard Style", options=["Upright Table", "Bottom Strip", "Far Left"], group=GRP_DS)
dashPosOpt = input.string("Top Right", "Dashboard Position", options=["Top Left", "Top Center", "Top Right", "Middle Left", "Middle Center", "Middle Right", "Bottom Left", "Bottom Center", "Bottom Right"], group=GRP_DS)
dashSizeOpt = input.string("Small", "Dashboard Text Size", options=["Tiny", "Small", "Normal", "Large"], group=GRP_DS)
dashPos = dashStyleOpt == "Far Left" ? position.middle_left : dashPosOpt == "Top Left" ? position.top_left : dashPosOpt == "Top Center" ? position.top_center : dashPosOpt == "Top Right" ? position.top_right : dashPosOpt == "Middle Left" ? position.middle_left : dashPosOpt == "Middle Center" ? position.middle_center : dashPosOpt == "Middle Right" ? position.middle_right : dashPosOpt == "Bottom Left" ? position.bottom_left : dashPosOpt == "Bottom Center" ? position.bottom_center : position.bottom_right
isStrip = dashStyleOpt == "Bottom Strip"
dashTxt = dashSizeOpt == "Tiny" ? size.tiny : dashSizeOpt == "Small" ? size.small : dashSizeOpt == "Normal" ? size.normal : size.large
// ═══ ENGINE ═══
emaFast = ta.ema(close, emaFastLen)
emaSlow = ta.ema(close, emaSlowLen)
emaLong = ta.ema(close, emaLongLen)
emaBull = emaFast > emaSlow
emaBear = emaFast < emaSlow
emaSlopePct = not na(emaFast[slopeLen]) and emaFast[slopeLen] != 0 ? ((emaFast - emaFast[slopeLen]) / emaFast[slopeLen]) * 100 : 0
slopeBull = emaSlopePct >= minSlopePct
slopeBear = emaSlopePct <= -minSlopePct
[stLine, stDir] = ta.supertrend(stMult, stLen)
stBull = not na(stDir) and stDir < 0
stBear = not na(stDir) and stDir > 0
[dmp, dmn, adxV] = ta.dmi(adxLen, adxLen)
bullReg = not na(emaLong) and not na(adxV) and not na(dmp) and not na(dmn) and close > emaLong and adxV >= adxThresh and (not requireDiAlignment or dmp > dmn)
bearReg = not na(emaLong) and not na(adxV) and not na(dmp) and not na(dmn) and close < emaLong and adxV >= adxThresh and (not requireDiAlignment or dmn > dmp)
rangeReg = not bullReg and not bearReg
ap = hlc3
esa = ta.ema(ap, wtN1)
dd = ta.ema(math.abs(ap - esa), wtN1)
ci = dd == 0 ? 0 : (ap - esa) / (0.015 * dd)
wt1 = ta.ema(ci, wtN2)
wt2 = ta.sma(wt1, 4)
wtBull = wt1 > wt2
wtBear = wt1 < wt2
wtBullCross = ta.crossover(wt1, wt2)
wtBearCross = ta.crossunder(wt1, wt2)
rsiV = ta.rsi(close, rsiLen)
k = ta.sma(ta.stoch(rsiV, rsiV, rsiV, stochLen), smoothK)
dSm = ta.sma(k, smoothD)
srBull = k > dSm
srBear = k < dSm
srBullCross = ta.crossover(k, dSm)
srBearCross = ta.crossunder(k, dSm)
// Divergence: regular + hidden (WaveTrend)
plFound = not na(ta.pivotlow(wt1, lbL, lbR))
phFound = not na(ta.pivothigh(wt1, lbL, lbR))
_inRange(cond) =>
b = ta.barssince(cond)
not na(b) and b >= divMinBars and b <= divMaxBars
inRangePl = _inRange(plFound[1])
inRangePh = _inRange(phFound[1])
prevPlO = ta.valuewhen(plFound, wt1[lbR], 1)
prevPlP = ta.valuewhen(plFound, low[lbR], 1)
prevPhO = ta.valuewhen(phFound, wt1[lbR], 1)
prevPhP = ta.valuewhen(phFound, high[lbR], 1)
curPlO = wt1[lbR]
curPlP = low[lbR]
curPhO = wt1[lbR]
curPhP = high[lbR]
validL = plFound and inRangePl and not na(prevPlO) and not na(prevPlP) and not na(curPlO) and not na(curPlP)
validH = phFound and inRangePh and not na(prevPhO) and not na(prevPhP) and not na(curPhO) and not na(curPhP)
bullDiv = validL and curPlO > prevPlO and curPlP < prevPlP
bearDiv = validH and curPhO < prevPhO and curPhP > prevPhP
hidBullDiv = validL and curPlO < prevPlO and curPlP > prevPlP
hidBearDiv = validH and curPhO > prevPhO and curPhP < prevPhP
// ATR / volatility
atrV = ta.atr(atrLen)
atrBaseline = ta.sma(atrV, atrRegimeLen)
atrRatio = not na(atrBaseline) and atrBaseline != 0 ? atrV / atrBaseline : 1.0
volatilityOK = atrRatio >= atrExpansionMin and atrRatio <= atrExpansionMax
atrBull = volatilityOK and close > emaFast
atrBear = volatilityOK and close < emaFast
distanceATR = atrV != 0 ? math.abs(close - emaFast) / atrV : 0
notChasing = distanceATR <= maxChaseATR
// ATR trailing stop
var float lStop = na
var float sStop = na
newL = close - atrV * stopMultEff
newS = close + atrV * stopMultEff
prevBull = bar_index > 0 and stBull[1]
prevBear = bar_index > 0 and stBear[1]
lStop := stBull and not prevBull ? newL : na(lStop[1]) ? newL : math.max(newL, lStop[1])
sStop := stBear and not prevBear ? newS : na(sStop[1]) ? newS : math.min(newS, sStop[1])
stopLine = stBull ? lStop : sStop
rngUp = emaSlow + atrV * rngMult
rngDn = emaSlow - atrV * rngMult
// RS vs SPY/QQQ
benchSPY = request.security(bench1, timeframe.period, close)
benchQQQ = request.security(bench2, timeframe.period, close)
assetReturn = close[rsLen] != 0 ? ((close / close[rsLen]) - 1) * 100 : na
spyReturn = benchSPY[rsLen] != 0 ? ((benchSPY / benchSPY[rsLen]) - 1) * 100 : na
qqqReturn = benchQQQ[rsLen] != 0 ? ((benchQQQ / benchQQQ[rsLen]) - 1) * 100 : na
rsSPY = assetReturn - spyReturn
rsQQQ = assetReturn - qqqReturn
rsBull = not na(rsSPY) and not na(rsQQQ) and rsSPY >= rsLongMin and rsQQQ >= rsLongMin
rsBear = not na(rsSPY) and not na(rsQQQ) and rsSPY <= rsShortMax and rsQQQ <= rsShortMax
rsLongOK = not useRSFilter or rsBull
rsShortOK = not useRSFilter or rsBear
// Rolling beta (log returns)
assetRet = math.log(close / close[1])
benchRet = math.log(benchSPY / benchSPY[1])
assetMean = ta.sma(assetRet, betaLen)
benchMean = ta.sma(benchRet, betaLen)
rollingCov = ta.sma(assetRet * benchRet, betaLen) - assetMean * benchMean
rollingVar = ta.sma(benchRet * benchRet, betaLen) - benchMean * benchMean
beta = rollingVar != 0 ? rollingCov / rollingVar : na
betaLongOK = not useBetaFilter or na(beta) or beta >= betaMin
// Higher-timeframe trend context (default: daily)
[htfC, htfE] = request.security(syminfo.tickerid, htfTF, [close, ta.ema(close, emaLongLen)])
htfBull = not na(htfC) and not na(htfE) and htfC > htfE
htfBear = not na(htfC) and not na(htfE) and htfC < htfE
htfState = htfBull ? 1 : htfBear ? -1 : 0
// Volume / RVOL
volBaseline = ta.sma(nz(volume, 0), volLen)
rvol = volBaseline > 0 ? nz(volume, 0) / volBaseline : 0
volOK = not useVolFilter or rvol >= rvolMin
volStrong = rvol >= rvolStrong
// ── White-candle event: light total volume, heavy buy-share, range beyond ATR ──
wcRng = high - low
wcBuyShare = wcRng > 0 ? (close - low) / wcRng : 0.5
wcVolRatio = volBaseline > 0 ? nz(volume, 0) / volBaseline : na
wcVolLight = not na(wcVolRatio) and wcVolRatio >= wcVolLow and wcVolRatio < wcVolHigh
wcExpand = not na(atrV) and atrV > 0 and wcRng > wcAtrMult * atrV
whiteCandle = useWhiteCandle and wcVolLight and wcExpand and close > open and wcBuyShare >= wcBuyFrac
blackCandle = useWhiteCandle and wcMirror and wcVolLight and wcExpand and close < open and (1 - wcBuyShare) >= wcBuyFrac
var int wcBar = na
var int bcBar = na
if whiteCandle
wcBar := bar_index
if blackCandle
bcBar := bar_index
barcolor(whiteCandle ? color.white : blackCandle ? color.black : na, title="White/Black Candle Event")
// ═══ WEIGHTED VOTES (max |12|) ═══
voteEMA = emaBull ? emaWeight : emaBear ? -emaWeight : 0
voteST = stBull ? stWeight : stBear ? -stWeight : 0
voteRegime = bullReg ? regimeWeight : bearReg ? -regimeWeight : 0
voteSlope = slopeBull ? slopeWeight : slopeBear ? -slopeWeight : 0
voteWT = wtBull ? wtWeight : wtBear ? -wtWeight : 0
voteSR = srBull ? srWeight : srBear ? -srWeight : 0
voteDiv = bullDiv or hidBullDiv ? divWeight : bearDiv or hidBearDiv ? -divWeight : 0
voteRS = rsBull ? rsWeight : rsBear ? -rsWeight : 0
voteATR = atrBull ? atrWeight : atrBear ? -atrWeight : 0
score = voteEMA + voteST + voteRegime + voteSlope + voteWT + voteSR + voteDiv + voteRS + voteATR
scoreSafe = nz(score, 0)
// ── QK Fear/Greed gauge: CNN 7-component methodology, chart data only ──
// Pine cannot fetch the CNN index over HTTP, so the gauge is rebuilt from
// market data. Construction mirrors CNN's published method (sources:
// investopedia.com/terms/f/fear-and-greed-index.asp and
// cnn.com/markets/fear-and-greed): 7 indicators, each scored 0-100, equally
// weighted (arithmetic mean), 50 = neutral. Normalization follows CNN's
// "how far they've veered from their average relative to how far they
// normally veer": z = (raw - trailing-window mean) / trailing-window stdev,
// score = 100 * Phi(z), inverted where high raw = fear. Zones: 0-24 Extreme
// Fear, 25-44 Fear, 45-55 Neutral, 56-75 Greed, 76-100 Extreme Greed.
// Calibrated for daily charts; market series are pulled on the daily
// timeframe regardless of chart timeframe.
mktD = request.security(bench1, "D", close)
fgZScore(raw) =>
zm = ta.sma(raw, fgZLen)
zs = ta.stdev(raw, fgZLen)
not na(zm) and not na(zs) and zs != 0 ? (raw - zm) / zs : 0.0
// Abramowitz & Stegun 7.1.26 — erf to <1.5e-7 (Pine has no math.erf)
kcErf(x) =>
a1 = 0.254829592
a2 = -0.284496736
a3 = 1.421413741
a4 = -1.453152027
a5 = 1.061405429
p = 0.3275911
sgn = x < 0 ? -1.0 : 1.0
ax = math.abs(x)
t = 1.0 / (1.0 + p * ax)
y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * math.exp(-ax * ax)
sgn * y
fgPhi(z) =>
0.5 * (1 + kcErf(z / 1.4142135623730951))
vixD = request.security(fgSymVIX, "D", close, barmerge.gaps_off, barmerge.lookahead_off)
hygD = request.security(fgSymHYG, "D", close, barmerge.gaps_off, barmerge.lookahead_off)
iefD = request.security(fgSymIEF, "D", close, barmerge.gaps_off, barmerge.lookahead_off)
// 1 · Market Momentum (CNN-exact): SPY vs its 125-day average
spySMA = ta.sma(mktD, fgMomLen)
fgR1 = not na(mktD) and not na(spySMA) and spySMA != 0 ? 100 * (mktD / spySMA - 1) : na
fgS1 = 100 * fgPhi(fgZScore(fgR1))
// 2 · Stock Price Strength (proxy for NYSE new highs minus new lows):
// where the close sits inside its 252-day high/low range, 0-100 as-is
hhR = ta.highest(high, fgRangeLen)
llR = ta.lowest(low, fgRangeLen)
fgR2 = not na(hhR) and not na(llR) and hhR != llR ? 100 * (close - llR) / (hhR - llR) : 50.0
fgS2 = math.max(0.0, math.min(100.0, fgR2))
// 3 · Stock Price Breadth (proxy for advancing/declining volume):
// share of 20-day volume printed on up days, z-scored
upV = close > close[1] ? nz(volume, 0) : 0.0
dnV = close < close[1] ? nz(volume, 0) : 0.0
upSum = math.sum(upV, fgRetLen)
dnSum = math.sum(dnV, fgRetLen)
fgR3 = upSum + dnSum > 0 ? upSum / (upSum + dnSum) : 0.5
fgS3 = 100 * fgPhi(fgZScore(fgR3))
// 4 · Put/Call proxy: distance from the 20-day high in ATRs.
// High hedging demand = fear, so the score is inverted.
hh20 = ta.highest(high, fgRetLen)
fgR4 = not na(hh20) and nz(atrV, 0) != 0 ? (hh20 - close) / nz(atrV, 0) : 0.0
fgS4 = 100 * (1 - fgPhi(fgZScore(fgR4)))
// 5 · Junk Bond Demand (real series): HYG 20-day return minus IEF 20-day return
rHYG = not na(hygD) and not na(hygD[fgRetLen]) and hygD[fgRetLen] != 0 ? hygD / hygD[fgRetLen] - 1 : na
rIEF = not na(iefD) and not na(iefD[fgRetLen]) and iefD[fgRetLen] != 0 ? iefD / iefD[fgRetLen] - 1 : na
fgR5 = not na(rHYG) and not na(rIEF) ? rHYG - rIEF : na
fgS5 = 100 * fgPhi(fgZScore(fgR5))
// 6 · Market Volatility (CNN-exact): VIX vs its 50-day average.
// High VIX = fear, so the score is inverted.
vixSMA = ta.sma(vixD, fgVixLen)
fgR6 = not na(vixD) and not na(vixSMA) and vixSMA != 0 ? vixD / vixSMA : na
fgS6 = 100 * (1 - fgPhi(fgZScore(fgR6)))
// VIX direct decision inputs (separate from the gauge component above)
vixCalmSMA = ta.sma(vixD, vixCalmLen)
vixCalm = not na(vixD) and not na(vixCalmSMA) and vixCalmSMA != 0 and vixD < vixCalmSMA
vixPanic = useVIXPanic and not na(vixD) and not na(vixD[1]) and vixD[1] != 0 and (vixD / vixD[1] - 1) * 100 > vixPanicPct
// 7 · Safe Haven Demand (real series): SPY 20-day return minus IEF 20-day return
rSPY = not na(mktD) and not na(mktD[fgRetLen]) and mktD[fgRetLen] != 0 ? mktD / mktD[fgRetLen] - 1 : na
fgR7 = not na(rSPY) and not na(rIEF) ? rSPY - rIEF : na
fgS7 = 100 * fgPhi(fgZScore(fgR7))
fgWSum = fgW1 + fgW2 + fgW3 + fgW4 + fgW5 + fgW6 + fgW7
fgGauge = fgWSum > 0 ? (fgW1 * fgS1 + fgW2 * fgS2 + fgW3 * fgS3 + fgW4 * fgS4 + fgW5 * fgS5 + fgW6 * fgS6 + fgW7 * fgS7) / fgWSum : 50.0
fgExtFear = fgGauge <= fgFear
fgExtGreed = fgGauge >= fgGreed
fgZoneTxt = fgExtFear ? "EXTREME FEAR" : fgExtGreed ? "EXTREME GREED" : fgGauge < 45 ? "FEAR" : fgGauge > 55 ? "GREED" : "NEUTRAL"
// ═══ SETUP CLASSIFICATION ═══
continuationLong = bullReg and emaBull and stBull and wtBull and srBull
continuationShort = bearReg and emaBear and stBear and wtBear and srBear
priorHigh = ta.highest(high[1], breakoutLookback)
priorLow = ta.lowest(low[1], breakoutLookback)
breakoutLong = close > priorHigh and volStrong and atrRatio > 1.0 and bullReg
breakoutShort = close < priorLow and volStrong and atrRatio > 1.0 and bearReg
reversalLong = bullDiv and (wtBullCross or srBullCross) and close > emaFast
reversalShort = bearDiv and (wtBearCross or srBearCross) and close < emaFast
setupText = breakoutLong ? "BREAKOUT LONG" : breakoutShort ? "BREAKOUT SHORT" : continuationLong ? "CONTINUATION LONG" : continuationShort ? "CONTINUATION SHORT" : reversalLong ? "REVERSAL LONG" : reversalShort ? "REVERSAL SHORT" : bullReg ? "BULL TREND" : bearReg ? "BEAR TREND" : "RANGE"
// ═══ SIGNAL GATES + COOLDOWN ═══
// ── Higher-timeframe alignment gate (non-repainting, backtest winner) ──
// HTF EMA21/EMA50 on the HTF chart's own closes, lookahead OFF: a bar only
// ever sees HTF values from fully confirmed HTF bars. Walk-forward PASS:
// V1 beat the shipped baseline on PF and expectancy in all 5 folds.
useHTFAlign = input.bool(true, "Require Higher-Timeframe Alignment", group=GRP_SG)
alignTF = input.timeframe("W", "Alignment Timeframe", group=GRP_SG)
alignClose = request.security(syminfo.tickerid, alignTF, close, lookahead=barmerge.lookahead_off)
alignEmaFast = ta.ema(alignClose, 21)
alignEmaSlow = ta.ema(alignClose, 50)
alignBull = not na(alignEmaFast) and not na(alignEmaSlow) and alignEmaFast > alignEmaSlow
alignBear = not na(alignEmaFast) and not na(alignEmaSlow) and alignEmaFast < alignEmaSlow
alignLongOK = not useHTFAlign or alignBull
alignShortOK = not useHTFAlign or alignBear
longGate = bullReg and not rangeReg and volOK and rsLongOK and betaLongOK and volatilityOK and notChasing and srBull and k < stochLongMax and alignLongOK
shortGate = bearReg and not rangeReg and volOK and rsShortOK and volatilityOK and notChasing and srBear and k > stochShortMin and not longOnly and alignShortOK
rawLong = scoreSafe >= longThresh and longGate
rawShort = scoreSafe <= -shortThresh and shortGate
var int lastSignalBar = na
cooldownOK = na(lastSignalBar) or bar_index - lastSignalBar > cooldownBars
newLong = barstate.isconfirmed and rawLong and not rawLong[1] and cooldownOK
newShort = barstate.isconfirmed and rawShort and not rawShort[1] and cooldownOK
if newLong or newShort
lastSignalBar := bar_index
// ── Fear/Greed conviction ──
// FG1 (optional, tested neutral): at contrarian extremes a signal scoring
// within 1 point of strong is treated as strong sooner.
fgConvLong = useFGConv and newLong and fgExtFear and scoreSafe >= longThresh + strongOffset - 1
fgConvShort = useFGConv and newShort and fgExtGreed and scoreSafe <= -(shortThresh + strongOffset - 1)
strongLong = newLong and (scoreSafe >= longThresh + strongOffset or fgConvLong)
strongShort = newShort and (scoreSafe <= -(shortThresh + strongOffset) or fgConvShort)
// FG2 (default ON, backtest winner): don't chase the crowd — no new longs in
// Extreme Greed and no new shorts in Extreme Fear unless the signal is strong.
fgBlockLong = useFGBlock and fgExtGreed and not strongLong
fgBlockShort = useFGBlock and fgExtFear and not strongShort
// VIX1 (default ON, backtest winner): longs only when VIX is calm. The panel
// folds KC3's panic-spike entry filter into the gated signal (same outcome as
// the strategy's order-layer check).
tradeLong = newLong and not fgBlockLong and (not useVIXCalm or vixCalm) and not vixPanic
tradeShort = newShort and not fgBlockShort and not vixPanic
tradeStrongLong = strongLong and (not useVIXCalm or vixCalm) and not vixPanic
tradeStrongShort = strongShort and not vixPanic
// ── Setup classification (drives the diamond tags) ──
isBO = (tradeLong and breakoutLong) or (tradeShort and breakoutShort)
isREV = (tradeLong and reversalLong) or (tradeShort and reversalShort)
isCONT = not isBO and not isREV
buySig = tradeLong and not tradeStrongLong
sellSig = tradeShort and not tradeStrongShort
// ── Chase-blocked: the score wanted a signal but price was too extended ──
longGateNoChase = bullReg and not rangeReg and volOK and rsLongOK and betaLongOK and volatilityOK and srBull and k < stochLongMax and alignLongOK
shortGateNoChase = bearReg and not rangeReg and volOK and rsShortOK and volatilityOK and srBear and k > stochShortMin and not longOnly and alignShortOK
rawLongNoChase = scoreSafe >= longThresh and longGateNoChase
rawShortNoChase = scoreSafe <= -shortThresh and shortGateNoChase
chaseBlockedLong = barstate.isconfirmed and rawLongNoChase and not rawLongNoChase[1] and cooldownOK and not rawLong
chaseBlockedShort = barstate.isconfirmed and rawShortNoChase and not rawShortNoChase[1] and cooldownOK and not rawShort
// ═══ PROFIT TARGETS 1.5x / 2x / 3x ATR ═══
var line lnEntry = na
var line lnT1 = na
var line lnT2 = na
var line lnT3 = na
if showTargets and (tradeLong or tradeShort)
line.delete(lnEntry)
line.delete(lnT1)
line.delete(lnT2)
line.delete(lnT3)
e = close
a = atrV
sgn = tradeLong ? 1 : -1
c1 = tradeLong ? color.new(#00e5a0, 20) : color.new(#ff4d6d, 20)
c2 = tradeLong ? color.new(#00e5a0, 45) : color.new(#ff4d6d, 45)
c3 = tradeLong ? color.new(#00e5a0, 65) : color.new(#ff4d6d, 65)
lnEntry := line.new(bar_index, e, bar_index + tgtBars, e, xloc=xloc.bar_index, extend=extend.none, color=color.new(#00d4ff, 0), style=line.style_dashed, width=1)
lnT1 := line.new(bar_index, e + sgn * 1.5 * a, bar_index + tgtBars, e + sgn * 1.5 * a, xloc=xloc.bar_index, extend=extend.none, color=c1, width=2)
lnT2 := line.new(bar_index, e + sgn * 2.0 * a, bar_index + tgtBars, e + sgn * 2.0 * a, xloc=xloc.bar_index, extend=extend.none, color=c2, width=2)
lnT3 := line.new(bar_index, e + sgn * 3.0 * a, bar_index + tgtBars, e + sgn * 3.0 * a, xloc=xloc.bar_index, extend=extend.none, color=c3, width=2)
// ═══ VIRTUAL POSITION TRACKER ═══
var int tradeDir = 0
var float trailStop = na
var float targetPrice = na
var float entryPx = na
var float entryRisk = na
if tradeLong
tradeDir := 1
trailStop := close - atrV * stopMultEff
targetPrice := close + atrV * stopMultEff
entryPx := close
entryRisk := atrV * stopMultEff
if tradeShort
tradeDir := -1
trailStop := close + atrV * stopMultEff
targetPrice := close - atrV * stopMultEff
entryPx := close
entryRisk := atrV * stopMultEff
if tradeDir == 1
newLongStop = close - atrV * stopMultEff
trailStop := math.max(nz(trailStop, newLongStop), newLongStop)
if tradeDir == -1
newShortStop = close + atrV * stopMultEff
trailStop := math.min(nz(trailStop, newShortStop), newShortStop)
virtualExit = (tradeDir == 1 and (close <= trailStop or close >= targetPrice)) or (tradeDir == -1 and (close >= trailStop or close <= targetPrice)) or (tradeDir == -1 and tradeLong) or (tradeDir == 1 and tradeShort)
if virtualExit
tradeDir := 0
trailStop := na
targetPrice := na
entryPx := na
entryRisk := na
activeStop = tradeDir != 0 ? trailStop : na
activeTarget = tradeDir != 0 ? targetPrice : na
// ═══ CHART PLOTS ═══
plot(emaFast, "EMA Fast", color.new(#00d4ff, 0), 2)
plot(emaSlow, "EMA Slow", color.new(#ff9f1c, 0), 2)
plot(emaLong, "EMA 200", color.new(#ffffff, 40), 1)
plot(stLine, "Supertrend", stBull ? color.new(#00e5a0, 0) : color.new(#ff4d6d, 0), 2)
plot(stopLine, "ATR Stop", color.new(#ffd60a, 0), 2, plot.style_linebr)
plot(activeStop, "Virtual Trail Stop", color.new(#ffd60a, 0), 2, plot.style_linebr)
plot(showATRTarget ? activeTarget : na, "Virtual Target", color.new(#ffffff, 35), 1, plot.style_linebr)
pU = plot(showBands ? rngUp : na, "ATR Range +", color.new(#9d4edd, 80))
pD = plot(showBands ? rngDn : na, "ATR Range -", color.new(#9d4edd, 80))
fill(pU, pD, color.new(#9d4edd, 93))
bgcolor(showBg ? bullReg ? color.new(#00e5a0, 93) : bearReg ? color.new(#ff4d6d, 93) : na : na)
bgcolor(showFGTint and useFG and fgExtGreed ? color.new(#ff4d6d, 94) : showFGTint and useFG and fgExtFear ? color.new(#00e5a0, 94) : na)
plotshape(showDiv and bullDiv, "Bullish Divergence", shape.diamond, location.belowbar, color.new(#00e5a0, 0), size=size.tiny)
plotshape(showDiv and bearDiv, "Bearish Divergence", shape.diamond, location.abovebar, color.new(#ff4d6d, 0), size=size.tiny)
plotshape(showDiv and hidBullDiv, "Hidden Bullish Div", shape.triangleup, location.belowbar, color.new(#00e5a0, 0), size=size.tiny)
plotshape(showDiv and hidBearDiv, "Hidden Bearish Div", shape.triangledown, location.abovebar, color.new(#ff4d6d, 0), size=size.tiny)
// ── Signal diamonds: green = BUY, red = SELL, large = STRONG ──
buyCol = color.new(#00e5a0, 0)
sellCol = color.new(#ff4d6d, 0)
diaTxtC = color.new(#e8ecf1, 0)
plotshape(buySig and isBO, "BUY · BO", shape.diamond, location.belowbar, buyCol, size=size.small, text="BO", textcolor=diaTxtC)
plotshape(buySig and isREV, "BUY · REV", shape.diamond, location.belowbar, buyCol, size=size.small, text="REV", textcolor=diaTxtC)
plotshape(buySig and isCONT, "BUY · CONT", shape.diamond, location.belowbar, buyCol, size=size.small, text="CONT", textcolor=diaTxtC)
plotshape(tradeStrongLong and isBO, "STRONG BUY · BO", shape.diamond, location.belowbar, buyCol, size=size.large, text="BO", textcolor=diaTxtC)
plotshape(tradeStrongLong and isREV, "STRONG BUY · REV", shape.diamond, location.belowbar, buyCol, size=size.large, text="REV", textcolor=diaTxtC)
plotshape(tradeStrongLong and isCONT, "STRONG BUY · CONT", shape.diamond, location.belowbar, buyCol, size=size.large, text="CONT", textcolor=diaTxtC)
plotshape(sellSig and isBO, "SELL · BO", shape.diamond, location.abovebar, sellCol, size=size.small, text="BO", textcolor=diaTxtC)
plotshape(sellSig and isREV, "SELL · REV", shape.diamond, location.abovebar, sellCol, size=size.small, text="REV", textcolor=diaTxtC)
plotshape(sellSig and isCONT, "SELL · CONT", shape.diamond, location.abovebar, sellCol, size=size.small, text="CONT", textcolor=diaTxtC)
plotshape(tradeStrongShort and isBO, "STRONG SELL · BO", shape.diamond, location.abovebar, sellCol, size=size.large, text="BO", textcolor=diaTxtC)
plotshape(tradeStrongShort and isREV, "STRONG SELL · REV", shape.diamond, location.abovebar, sellCol, size=size.large, text="REV", textcolor=diaTxtC)
plotshape(tradeStrongShort and isCONT, "STRONG SELL · CONT", shape.diamond, location.abovebar, sellCol, size=size.large, text="CONT", textcolor=diaTxtC)
// ── Chase-blocked ghosts: the setup was there but price was too extended ──
plotshape(chaseBlockedLong, "Chase-blocked Long", shape.diamond, location.belowbar, color.new(#8a8f98, 45), size=size.small, text="EXT", textcolor=color.new(#8a8f98, 0))
plotshape(chaseBlockedShort, "Chase-blocked Short", shape.diamond, location.abovebar, color.new(#8a8f98, 45), size=size.small, text="EXT", textcolor=color.new(#8a8f98, 0))
// ── Fear/Greed zone ribbon (short thick line at the recent high, colored by zone) ──
ribY = ta.highest(high, 50)
var line fgRibbon = na
if barstate.islast
line.delete(fgRibbon)
fgRibbon := na
if showFGRibbon and useFG
ribC = fgExtGreed ? color.new(#ff4d6d, 0) : fgExtFear ? color.new(#00e5a0, 0) : color.new(#8a8f98, 40)
fgRibbon := line.new(bar_index - 14, ribY, bar_index, ribY, xloc=xloc.bar_index, color=ribC, width=4)
// ── CONTRACT STRIP (fine print, bottom-left) ──
// The decision contract: last signal, score vs trigger, live gate state.
// Brief by design — three rows, tiny text, out of the way.
var string cSig = "—"
var string cSetup = ""
var int cSigBar = na
if tradeStrongLong or tradeLong
cSig := tradeStrongLong ? "STRONG BUY" : "BUY"
cSetup := isBO ? "BO" : isREV ? "REV" : "CONT"
cSigBar := bar_index
if tradeStrongShort or tradeShort
cSig := tradeStrongShort ? "STRONG SELL" : "SELL"
cSetup := isBO ? "BO" : isREV ? "REV" : "CONT"
cSigBar := bar_index
cAge = na(cSigBar) ? "" : " · " + str.tostring(bar_index - cSigBar) + " bars ago"
fgZ = fgExtGreed ? "X-GREED" : fgExtFear ? "X-FEAR" : fgGauge < 45 ? "FEAR" : fgGauge > 55 ? "GREED" : "NEUT"
gateTxt = (useFG ? "FG " + str.tostring(fgGauge, "#") + " " + fgZ + " " + (fgBlockLong or fgBlockShort ? "✗BLOCK" : "✓") : "FG off") + " · " + (vixPanic ? "VIX PANIC ✗" : useVIXCalm ? (vixCalm ? "VIX calm ✓" : "VIX high ✗") : "VIX off") + " · HTF " + (alignBull ? "bull ✓" : alignBear ? "bear ✓" : "—")
// ── Gate diagnostic: what's blocking a signal right now, failures only ──
dirUpD = bullReg or not bearReg
gRegime = dirUpD ? bullReg : bearReg
gScoreD = dirUpD ? scoreSafe >= longThresh : scoreSafe <= -shortThresh
gRvolD = volOK
gRSD = dirUpD ? rsLongOK : rsShortOK
gBetaD = dirUpD ? betaLongOK : true
gAtrD = volatilityOK
gChaseD = notChasing
gStochD = dirUpD ? (srBull and k < stochLongMax) : (srBear and k > stochShortMin)
gFGD = not (dirUpD ? fgBlockLong : fgBlockShort)
gVixD = not vixPanic and (not useVIXCalm or not dirUpD or vixCalm)
gHTFD = dirUpD ? alignLongOK : alignShortOK
gCoolD = cooldownOK
blkTxt = (gRegime ? "" : "REGIME ") + (gScoreD ? "" : "SCORE ") + (gRvolD ? "" : "RVOL ") + (gRSD ? "" : "RS ") + (gBetaD ? "" : "BETA ") + (gAtrD ? "" : "ATR ") + (gChaseD ? "" : "CHASE ") + (gStochD ? "" : "STOCH ") + (gFGD ? "" : "FG ") + (gVixD ? "" : "VIX ") + (gHTFD ? "" : "HTF ") + (gCoolD ? "" : "COOL ")
blkTxt := blkTxt == "" ? "all pass ✓" : "BLOCKED: " + blkTxt
var table contract = table.new(position.bottom_left, 2, 4, border_width=1, border_color=color.new(#ffffff, 88))
wcAge = na(wcBar) ? "" : " · WHITE " + str.tostring(bar_index - wcBar) + "b ago"
bcAge = na(bcBar) ? "" : " · BLACK " + str.tostring(bar_index - bcBar) + "b ago"
if barstate.islast and showContract
sigBull = str.contains(cSig, "BUY")
sigBear = str.contains(cSig, "SELL")
sigBg = sigBull ? color.new(#00e5a0, 80) : sigBear ? color.new(#ff4d6d, 80) : color.new(#8a8f98, 80)
table.cell(contract, 0, 0, "KC · " + syminfo.ticker + " · " + timeframe.period, text_color=color.new(#8a8f98, 0), text_size=size.tiny)
table.cell(contract, 1, 0, "SCORE " + (scoreSafe > 0 ? "+" : "") + str.tostring(scoreSafe) + "/" + str.tostring(scoreSafe >= 0 ? longThresh : shortThresh), text_color=color.new(#e8ecf1, 0), text_size=size.tiny)
table.cell(contract, 0, 1, "Signal", text_color=color.new(#8a8f98, 0), text_size=size.tiny)
table.cell(contract, 1, 1, cSig + (cSetup != "" ? " · " + cSetup : "") + cAge + wcAge + bcAge, bgcolor=sigBg, text_color=color.new(#e8ecf1, 0), text_size=size.tiny)
table.cell(contract, 0, 2, "Gates", text_color=color.new(#8a8f98, 0), text_size=size.tiny)
table.cell(contract, 1, 2, gateTxt, text_color=color.new(#e8ecf1, 0), text_size=size.tiny)
table.cell(contract, 0, 3, "Signal check", text_color=color.new(#8a8f98, 0), text_size=size.tiny)
table.cell(contract, 1, 3, blkTxt, bgcolor=str.contains(blkTxt, "BLOCKED") ? color.new(#ff4d6d, 80) : color.new(#00e5a0, 80), text_color=color.new(#e8ecf1, 0), text_size=size.tiny)
// ═══ DASHBOARD ═══
// Three styles: upright 2×20 table at any of 9 positions, a 20-pair strip
// flat across the bottom of the price pane, or the upright table pinned
// to the far left edge.
var table dash = na
if barstate.isfirst
dash := isStrip ? table.new(position.bottom_center, 40, 1, border_width=1, border_color=color.new(#ffffff, 85)) : table.new(dashPos, 2, 20, border_width=1, border_color=color.new(#ffffff, 85))
bullC = color.new(#00e5a0, 25)
bearC = color.new(#ff4d6d, 25)
flatC = color.new(#8a8f98, 25)
txtC = #e8ecf1
dimC = color.new(#8a8f98, 0)
stateTxt(s) => s > 0 ? "▲ BULL" : s < 0 ? "▼ BEAR" : "■ FLAT"
stateBg(s) => s > 0 ? bullC : s < 0 ? bearC : flatC
// Display values (shared by both layouts) — action first, then the why.
scoreTxt = (scoreSafe > 0 ? "+" : "") + str.tostring(scoreSafe)
scoreBg = scoreSafe >= longThresh ? bullC : scoreSafe <= -shortThresh ? bearC : flatC
// Remember the last signal direction so the dashboard keeps showing it (with age) between triggers.
var string lastSigDir = na
if tradeLong
lastSigDir := "BUY"
if tradeShort
lastSigDir := "SELL"
sigBase = tradeLong ? "BUY" : tradeShort ? "SELL" : na(lastSigDir) ? (rangeReg ? "STAND ASIDE" : "WAIT") : lastSigDir
sigAge = na(lastSignalBar) ? na : bar_index - lastSignalBar
sigValT = sigBase + (na(sigAge) ? "" : " · " + str.tostring(sigAge) + "b ago")
sigC = tradeLong or lastSigDir == "BUY" ? bullC : tradeShort or lastSigDir == "SELL" ? bearC : flatC
posT = tradeDir == 1 ? "LONG" : tradeDir == -1 ? "SHORT" : "FLAT"
posC = tradeDir == 1 ? bullC : tradeDir == -1 ? bearC : flatC
virtR = tradeDir != 0 and not na(entryPx) and not na(entryRisk) and entryRisk != 0 ? (tradeDir == 1 ? (close - entryPx) / entryRisk : (entryPx - close) / entryRisk) : na
posValT = posT + (na(virtR) ? "" : " " + (virtR >= 0 ? "+" : "") + str.tostring(virtR, "#.#") + "R")
stEma = emaBull ? 1 : emaBear ? -1 : 0
stSt = stBull ? 1 : stBear ? -1 : 0
stReg = bullReg ? 1 : bearReg ? -1 : 0
stSlope = slopeBull ? 1 : slopeBear ? -1 : 0
stWt = wtBull ? 1 : wtBear ? -1 : 0
stSr = srBull ? 1 : srBear ? -1 : 0
stDiv = bullDiv or hidBullDiv ? 1 : bearDiv or hidBearDiv ? -1 : 0
stRsSpy = rsSPY > 0 ? 1 : rsSPY < 0 ? -1 : 0
stRsQqq = rsQQQ > 0 ? 1 : rsQQQ < 0 ? -1 : 0
rvolTxt = str.tostring(rvol, "#.##") + "x"
atrTxt = str.tostring(atrRatio, "#.##") + "x"
betaTxt = na(beta) ? "n/a" : str.tostring(beta, "#.##")
setupBg = continuationLong or breakoutLong or reversalLong ? bullC : continuationShort or breakoutShort or reversalShort ? bearC : flatC
chaseTxt = notChasing ? "✓ OK" : "✗ EXTENDED"
chaseBg = notChasing ? bullC : bearC
srsiLbl = "SRSI " + str.tostring(math.round(nz(k, 50)))
emaLbl = "EMA " + str.tostring(emaFastLen) + "/" + str.tostring(emaSlowLen)
htfLbl = "HTF " + htfTF
alignLbl = "Align " + alignTF
alignSt = alignBull ? 1 : alignBear ? -1 : 0
vixTxt = vixPanic ? "PANIC" : vixCalm ? "calm" : "ELEVATED"
fgValT = useFG ? str.tostring(fgGauge, "#") + " · " + fgZoneTxt + " · VIX " + vixTxt : "OFF"
fgBg = not useFG ? flatC : fgExtGreed ? bearC : fgExtFear ? bullC : flatC
fgStripTxt = useFG ? str.tostring(fgGauge, "#") + " " + (fgExtFear ? "XF" : fgExtGreed ? "XG" : fgGauge < 45 ? "F" : fgGauge > 55 ? "G" : "N") : "OFF"
f_cell(r, lbl, s) =>
table.cell(dash, 0, r, lbl, text_color=txtC, text_size=dashTxt)
table.cell(dash, 1, r, stateTxt(s), bgcolor=stateBg(s), text_color=txtC, text_size=dashTxt)
s_pair(col, lbl, valT, valBg) =>
table.cell(dash, col, 0, lbl, text_color=dimC, text_size=size.tiny)
table.cell(dash, col + 1, 0, valT, bgcolor=valBg, text_color=txtC, text_size=size.tiny)
if barstate.islast and showDash
if isStrip
s_pair(0, "KC", scoreTxt, scoreBg)
s_pair(2, "Signal", sigBase, sigC)
s_pair(4, "Setup", setupText, setupBg)
s_pair(6, "VirtPos", posValT, posC)
s_pair(8, "Chase", chaseTxt, chaseBg)
s_pair(10, "HTF", stateTxt(htfState), stateBg(htfState))
s_pair(12, "EMA", stateTxt(stEma), stateBg(stEma))
s_pair(14, "ST", stateTxt(stSt), stateBg(stSt))
s_pair(16, "Regime", stateTxt(stReg), stateBg(stReg))
s_pair(18, "Slope", stateTxt(stSlope), stateBg(stSlope))
s_pair(20, "WT", stateTxt(stWt), stateBg(stWt))
s_pair(22, srsiLbl, stateTxt(stSr), stateBg(stSr))
s_pair(24, "Div", stateTxt(stDiv), stateBg(stDiv))
s_pair(26, "RS/SPY", stateTxt(stRsSpy), stateBg(stRsSpy))
s_pair(28, "RS/QQQ", stateTxt(stRsQqq), stateBg(stRsQqq))
s_pair(30, "RVOL", rvolTxt, volOK ? bullC : bearC)
s_pair(32, "ATR", atrTxt, volatilityOK ? bullC : bearC)
s_pair(34, "Beta", betaTxt, flatC)
s_pair(36, "FG", fgStripTxt, fgBg)
s_pair(38, "Align", stateTxt(alignSt), stateBg(alignSt))
else
table.cell(dash, 0, 0, "KC · " + syminfo.ticker, text_color=color.new(#00d4ff, 0), text_size=dashTxt)
table.cell(dash, 1, 0, "SCORE " + scoreTxt, bgcolor=scoreBg, text_color=txtC, text_size=dashTxt)
table.cell(dash, 0, 1, "Signal → Panel", text_color=txtC, text_size=dashTxt)
table.cell(dash, 1, 1, sigValT, bgcolor=sigC, text_color=txtC, text_size=dashTxt)
table.cell(dash, 0, 2, "Setup", text_color=txtC, text_size=dashTxt)
table.cell(dash, 1, 2, setupText, bgcolor=setupBg, text_color=txtC, text_size=dashTxt)
table.cell(dash, 0, 3, "Virtual Position", text_color=txtC, text_size=dashTxt)
table.cell(dash, 1, 3, posValT, bgcolor=posC, text_color=txtC, text_size=dashTxt)
table.cell(dash, 0, 4, "Chase", text_color=txtC, text_size=dashTxt)
table.cell(dash, 1, 4, chaseTxt, bgcolor=chaseBg, text_color=txtC, text_size=dashTxt)
f_cell(5, htfLbl, htfState)
f_cell(6, emaLbl, stEma)
f_cell(7, "Supertrend", stSt)
f_cell(8, "Regime", stReg)
f_cell(9, "EMA Slope", stSlope)
f_cell(10, "WaveTrend", stWt)
f_cell(11, "StochRSI " + str.tostring(math.round(nz(k, 50))), stSr)
f_cell(12, "Divergence", stDiv)
f_cell(13, "RS vs SPY", stRsSpy)
f_cell(14, "RS vs QQQ", stRsQqq)
table.cell(dash, 0, 15, "RVOL", text_color=txtC, text_size=dashTxt)
table.cell(dash, 1, 15, rvolTxt, bgcolor=volOK ? bullC : bearC, text_color=txtC, text_size=dashTxt)
table.cell(dash, 0, 16, "ATR Regime", text_color=txtC, text_size=dashTxt)
table.cell(dash, 1, 16, atrTxt, bgcolor=volatilityOK ? bullC : bearC, text_color=txtC, text_size=dashTxt)
table.cell(dash, 0, 17, "Beta vs SPY", text_color=txtC, text_size=dashTxt)
table.cell(dash, 1, 17, betaTxt, bgcolor=flatC, text_color=txtC, text_size=dashTxt)
table.cell(dash, 0, 18, "Fear/Greed", text_color=txtC, text_size=dashTxt)
table.cell(dash, 1, 18, fgValT, bgcolor=fgBg, text_color=txtC, text_size=dashTxt)
table.cell(dash, 0, 19, alignLbl, text_color=txtC, text_size=dashTxt)
table.cell(dash, 1, 19, stateTxt(alignSt), bgcolor=stateBg(alignSt), text_color=txtC, text_size=dashTxt)
// ── Alerts (one shot per signal) ──
alertcondition(tradeLong, "KC Long", "KC LONG on {{ticker}} ({{interval}}) at {{close}}")
alertcondition(tradeShort, "KC Short", "KC SHORT on {{ticker}} ({{interval}}) at {{close}}")
alertcondition(tradeStrongLong, "KC Strong Long", "KC STRONG LONG on {{ticker}} ({{interval}}) at {{close}}")
alertcondition(tradeStrongShort, "KC Strong Short", "KC STRONG SHORT on {{ticker}} ({{interval}}) at {{close}}")
alertcondition(whiteCandle, "KC White Candle", "KC WHITE CANDLE on {{ticker}} ({{interval}}) at {{close}}")
alertcondition(blackCandle, "KC Black Candle", "KC BLACK CANDLE on {{ticker}} ({{interval}}) at {{close}}")
Overview
Kernal Cipher calculates trend using EMA fast, slow, and long lengths with slope filters, regime detection via ADX and DI alignment, supertrend, WaveTrend oscillator, StochRSI with hidden divergence, relative strength versus benchmarks like SPY and QQQ, and volatility via ATR. The indicator plots breakout, reversal, and continuation signal diamonds, a 7-component Fear/Greed gauge, rolling beta markers, white-candle events, and a contract strip. Main settings adjust all component lengths and thresholds for trend, momentum, and risk overlays. It draws signals and visuals directly on the price chart with alerts.
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.
ADX Strategy (original) is an overlay indicator that identifies trend-following trade signals using the Average Directional Index (ADX) combined with a Simple Moving Average filter. A long signal is generated when the ADX rises above a user-defined trend level
Double Screened Decision is a multi-timeframe trend and momentum indicator overlaid directly on the price chart. It dynamically selects a higher timeframe based on the active chart resolution to evaluate higher-timeframe MACD, Bollinger Bands, RSI, and Stochas
This indicator plots Bollinger Bands on the price chart alongside a baseline moving average to identify trend-aligned swing setups. It calculates trend bias by comparing the Bollinger Bands basis to a customizable moving average, such as a 200 EMA. Trade signa
Index Intraday Analyst overlays CBOE cash indices such as SPX, NDX, RUT, and DJX. It plots a multi-EMA ribbon, a session-anchored TWAP with standard-deviation bands, and session levels such as prior-day high, low, and close, the opening range, and the high and
RePaNoCHa V4 is an overlay alert indicator that plots a Tillson T3 moving average on the price chart and colors it by whether the line is rising or falling. It combines that slope with a range filter, ADX, parabolic SAR, RSI, and MACD to mark long and short si
This indicator plots an exponential moving average and marks momentum-based trade sessions on the price chart. It treats RSI between 45 and 55 as chop and shades the background when momentum is present. Long and short labels appear when MACD crosses its signal