Strategy · Pine Script v6 · On the chart

Kernal Cipher Strategy

by kdricha0602

Kernal Cipher Strategy is a price-chart overlay that backtests the Kernal Cipher confirmed-bar signal engine.

View on GitHub

GitHub stars1on the repository
Pine versionv6current
Lines of code518
Settings40inputs
LicenceMIT
Last update5 weeks ago21 Sep 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

518 lines of Pine Script v6 from Kernal-Cipher-Strategy.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.

Kernal-Cipher-Strategy.pine
//@version=6
// ═══════════════════════════════════════════════════════════════════════════
//  KERNAL CIPHER STRATEGY — backtest reference (built 2026-09-20).
//  Companion: "Kernal Cipher" (all-in-one overlay indicator).
//  The KC2 signal engine (Kernal Cipher 2 panel) rebuilt as a tradable
//  strategy: same confirmed-bar engine, entries at the next bar's open,
//  2x ATR stop + opposite-signal exit.
//
//  HONEST STATUS: this engine was backtested twice (6 mega-cap tech names
//  intraday; then 26 symbols x 5 timeframes, daily 2020-2026). No tested
//  configuration was profitable out-of-sample or stable walk-forward, so
//  nothing here is a proven system. Defaults below are the most robust
//  choices the data supported — plateaus, never sharp peaks. Trade the
//  findings, not the hype.
//  2026-09-20 FIX: the opposite-signal exit was dead code (its conditions
//  could never be true inside the flatOrLong/flatOrShort guards), so the
//  strategy only ever exited on the ATR stop. Order management rewritten:
//  opposite exits issue first (old stop cancelled), entries account for the
//  pending exit, and reversals now fill cleanly at the next bar's open.
//  2026-09-20 FEAR/GREED: added the QK Fear/Greed gauge — an internal
//  0-100 gauge replicating the published CNN Fear & Greed methodology
//  (7 components, z-score normalized, equally weighted) computed from
//  chart data only, since Pine cannot fetch the CNN index over HTTP.
//  Simulator test (26 daily symbols, 2020-2026, base = bear-gated shorts):
//  blocking entries against extreme crowd sentiment (FG2) improved pooled
//  profit factor 3.05 -> 3.54 and expectancy 1.68R -> 2.02R, in both bull
//  and bear regimes. Conviction sizing at contrarian extremes (FG1) tested
//  neutral and ships default OFF.
//  2026-09-20 VIX: tested VIX as a direct decision input on top of the FG
//  winner. Calm-only longs (VIX < SMA50, VIX1) improved pooled profit
//  factor 3.54 -> 5.18 and expectancy 2.02R -> 3.77R — default ON. Panic
//  spike filter (skip entries on >15% VIX day-over-day jumps, VIX2) helped
//  mildly and ships default OFF. Elevated-vol short tilt (VIX4) also helped
//  mildly but is superseded by VIX1; not wired in.
// ═══════════════════════════════════════════════════════════════════════════

strategy("Kernal Cipher Strategy", shorttitle="KCS", overlay=true,
     initial_capital=100000, currency=currency.USD,
     commission_type=strategy.commission.percent, commission_value=0.01, slippage=1,
     pyramiding=0, calc_on_every_tick=false, process_orders_on_close=false,
     max_bars_back=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_X   = "11 · Strategy"
GRP_FG  = "12 · Fear & Greed"

// ── Inputs: engine (defaults = most robust choice per backtests) ──
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)
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)
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)
wtN1 = input.int(10, "Channel Length", minval=1, group=GRP_WT)
wtN2 = input.int(21, "Average Length", minval=1, group=GRP_WT)
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)
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)
// RS filter defaults OFF: backtests showed the filter as coded discards good
// longs (removing it helped); every positive threshold tested hurt.
useRSFilter = input.bool(false, "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)
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)
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)
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)
strongOnly = input.bool(false, "Strong Signals Only", group=GRP_SG)

// ── Inputs: strategy wrapper ──
tradeDir = input.string("Long + Short", "Trade Direction", options=["Long + Short", "Long Only", "Short Only"], group=GRP_X)
riskPct = input.float(1.0, "Risk Per Trade (% of equity)", minval=0.1, maxval=10, step=0.1, group=GRP_X)
useOppExit = input.bool(true, "Exit on Opposite Signal", group=GRP_X)
useTarget = input.bool(false, "Use Fixed R Target", group=GRP_X)
targetR = input.float(2.0, "R Target Multiple", minval=0.5, step=0.5, group=GRP_X)
useTrailing = input.bool(false, "Use ATR Trailing Stop", group=GRP_X)
trailMult = input.float(2.0, "Trailing ATR Multiple", minval=0.5, step=0.1, group=GRP_X)
// Bear-regime short gate: shorts only work in bear regimes (dial-in tests:
// short sleeve PF 0.34 pooled, 0.04 in bull years; gating shorts to
// SPY < SMA(200) lifted pooled PF 2.41 -> 3.05). Default ON.
useBearGate = input.bool(true, "Gate Shorts on Bear Regime (benchmark < SMA)", group=GRP_X)
bearLen = input.int(200, "Bear Gate SMA Length", minval=20, group=GRP_X)

// ── Inputs: Fear/Greed gauge ──
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)
// Backtest winner (FG2): block entries that chase extreme crowd sentiment —
// no new longs in Extreme Greed, no new shorts in Extreme Fear, unless the
// signal is strong. Pooled PF 3.05 -> 3.54, expectancy 1.68R -> 2.02R.
useFGBlock = input.bool(true, "Block Entries Against Extreme Crowd", group=GRP_FG)
// Tested neutral in the simulator: +1 score conviction at contrarian
// extremes (treated as strong sooner) plus bigger size on convicted trades.
useFGConv = input.bool(false, "Fear/Greed Conviction Boost", group=GRP_FG)
convSizeMult = input.float(1.5, "Conviction Size Multiplier", minval=1.0, maxval=3.0, step=0.25, group=GRP_FG)
// VIX as a direct decision input (separate from its gauge component).
// VIX1 (backtest winner on top of FG2): longs only when VIX is calm —
// pooled PF 3.54 -> 5.18, expectancy 2.02R -> 3.77R. Default ON.
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)
// VIX2 (mild help in tests): skip new entries on VIX panic spikes;
// exits and stops still fire. Default OFF.
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)

// ── Engine (identical logic to KC2 panel; only defaults/flags changed) ──
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
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
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
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
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
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
volBaseline = ta.sma(nz(volume, 0), volLen)
rvol = volBaseline > 0 ? nz(volume, 0) / volBaseline : 0
volOK = not useVolFilter or rvol >= rvolMin
volStrong = rvol >= rvolStrong

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)

// Bear-regime gate for shorts (benchmark daily close below its SMA)
mktD = request.security(bench1, "D", close)
mktSMA = request.security(bench1, "D", ta.sma(close, bearLen))
bearMarket = not na(mktD) and not na(mktSMA) and mktD < mktSMA
canLong = tradeDir != "Short Only"
canShort = tradeDir != "Long Only" and (not useBearGate or bearMarket)

// ── 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.
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"

longGate = bullReg and not rangeReg and volOK and rsLongOK and betaLongOK and volatilityOK and notChasing and srBull and k < stochLongMax
shortGate = bearReg and not rangeReg and volOK and rsShortOK and volatilityOK and notChasing and srBear and k > stochShortMin
rawLong = scoreSafe >= longThresh and longGate and canLong
rawShort = scoreSafe <= -shortThresh and shortGate and canShort
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
tradeLong = (strongOnly ? strongLong : newLong) and not fgBlockLong and (not useVIXCalm or vixCalm)
tradeShort = (strongOnly ? strongShort : newShort) and not fgBlockShort

// ── Order management ──
// Opposite-signal exits are issued BEFORE entries. Entries use effLong /
// effShort (the position once pending opposite exits fill) because
// strategy.position_size still shows the pre-fill position on the signal
// bar. A reversal therefore fills cleanly: old stop cancelled, old position
// closed, new position opened — all at the next bar's open.
var float longStopPx = na
var float shortStopPx = na
var float posRiskDist = na

stopDist = nz(atrV, 0) * stopMultEff
isConvicted = (tradeLong and fgConvLong) or (tradeShort and fgConvShort)
riskCash = strategy.equity * riskPct / 100 * (useFGConv and isConvicted ? convSizeMult : 1.0)
qty = stopDist > 0 ? math.max(1, math.floor(riskCash / stopDist)) : 1

isLong = strategy.position_size > 0
isShort = strategy.position_size < 0
exitShortOnOpp = useOppExit and tradeLong and isShort
exitLongOnOpp = useOppExit and tradeShort and isLong
if exitShortOnOpp
    strategy.cancel("KC3 Short Exit")
    strategy.close("KC3 Short", comment="Opp Exit")
if exitLongOnOpp
    strategy.cancel("KC3 Long Exit")
    strategy.close("KC3 Long", comment="Opp Exit")
// Position once the pending opposite exits fill:
effLong = isLong and not exitLongOnOpp
effShort = isShort and not exitShortOnOpp

if tradeLong and not effShort and not vixPanic
    longStopPx := close - stopDist
    posRiskDist := stopDist
    strategy.entry("KC3 Long", strategy.long, qty=qty, comment="KC3 BUY")
    if useTrailing
        strategy.exit("KC3 Long Exit", from_entry="KC3 Long", stop=longStopPx, trail_offset=nz(atrV, 0) * trailMult / syminfo.mintick)
    else if useTarget
        strategy.exit("KC3 Long Exit", from_entry="KC3 Long", stop=longStopPx, limit=close + stopDist * targetR)
    else
        strategy.exit("KC3 Long Exit", from_entry="KC3 Long", stop=longStopPx)
    alert("KC3 LONG " + syminfo.ticker + " (" + timeframe.period + ") qty " + str.tostring(qty) + " stop " + str.tostring(longStopPx, format.mintick), alert.freq_once_per_bar_close)

if tradeShort and not effLong and not vixPanic
    shortStopPx := close + stopDist
    posRiskDist := stopDist
    strategy.entry("KC3 Short", strategy.short, qty=qty, comment="KC3 SELL")
    if useTrailing
        strategy.exit("KC3 Short Exit", from_entry="KC3 Short", stop=shortStopPx, trail_offset=nz(atrV, 0) * trailMult / syminfo.mintick)
    else if useTarget
        strategy.exit("KC3 Short Exit", from_entry="KC3 Short", stop=shortStopPx, limit=close - stopDist * targetR)
    else
        strategy.exit("KC3 Short Exit", from_entry="KC3 Short", stop=shortStopPx)
    alert("KC3 SHORT " + syminfo.ticker + " (" + timeframe.period + ") qty " + str.tostring(qty) + " stop " + str.tostring(shortStopPx, format.mintick), alert.freq_once_per_bar_close)

if strategy.position_size == 0
    posRiskDist := na
    longStopPx := na
    shortStopPx := na

// ── Markers: triangles, green = buy, red = sell, large = strong ──
buyCol = color.new(#00e5a0, 0)
sellCol = color.new(#ff4d6d, 0)
plotshape(tradeLong and not strongLong ? 1 : na, "KC3 Buy", shape.triangleup, location.belowbar, buyCol, size=size.small, text="BUY", textcolor=color.new(#ffffff, 0))
plotshape(strongLong ? 1 : na, "KC3 Strong Buy", shape.triangleup, location.belowbar, buyCol, size=size.large, text="BUY", textcolor=color.new(#ffffff, 0))
plotshape(tradeShort and not strongShort ? 1 : na, "KC3 Sell", shape.triangledown, location.abovebar, sellCol, size=size.small, text="SELL", textcolor=color.new(#ffffff, 0))
plotshape(strongShort ? 1 : na, "KC3 Strong Sell", shape.triangledown, location.abovebar, sellCol, size=size.large, text="SELL", textcolor=color.new(#ffffff, 0))

// Active stop line while in a position
activeStop = strategy.position_size > 0 ? longStopPx : strategy.position_size < 0 ? shortStopPx : na
plot(activeStop, "Active Stop", color=color.new(#ff9f1c, 0), style=plot.style_linebr, linewidth=1)

// ── Fear/Greed gauge (separate pane) ──
fgLineCol = fgExtGreed ? sellCol : fgExtFear ? buyCol : color.new(#e8ecf1, 10)
plot(useFG ? fgGauge : na, "QK Fear/Greed", fgLineCol, 2, display=display.pane)
fgHiP = plot(useFG ? fgGreed * 1.0 : na, "FG Greed Line", color.new(#ff4d6d, 60), display=display.pane)
fgLoP = plot(useFG ? fgFear * 1.0 : na, "FG Fear Line", color.new(#00e5a0, 60), display=display.pane)
fgMidP = plot(useFG ? 50.0 : na, "FG Neutral Line", color.new(#8a8f98, 80), display=display.pane)
fill(fgHiP, fgMidP, color.new(#ff4d6d, 92), "FG Greed Fill")
fill(fgMidP, fgLoP, color.new(#00e5a0, 92), "FG Fear Fill")

// ── Compact strategy dashboard ──
vixTxt = vixPanic ? "PANIC" : vixCalm ? "calm" : "ELEVATED"
var table dash = table.new(position.top_right, 2, 8, border_width=1, border_color=color.new(#8a8f98, 70))
if barstate.islast
    sigTxt = strongLong ? "STRONG BUY" : strongShort ? "STRONG SELL" : newLong ? "BUY" : newShort ? "SELL" : "WAIT"
    sigAge = na(lastSignalBar) ? "—" : str.tostring(bar_index - lastSignalBar) + "b ago"
    posTxt = strategy.position_size > 0 ? "LONG " + str.tostring(strategy.position_size) : strategy.position_size < 0 ? "SHORT " + str.tostring(math.abs(strategy.position_size)) : "FLAT"
    entryTxt = strategy.position_size != 0 ? str.tostring(strategy.position_avg_price, format.mintick) : "—"
    openR = strategy.position_size != 0 and not na(posRiskDist) and posRiskDist != 0 ? (strategy.position_size > 0 ? (close - strategy.position_avg_price) : (strategy.position_avg_price - close)) / posRiskDist : na
    openRTxt = na(openR) ? "—" : (openR >= 0 ? "+" : "") + str.tostring(openR, "#.#") + "R"
    regTxt = bullReg ? "BULL" : bearReg ? "BEAR" : "RANGE"
    table.cell(dash, 0, 0, "Signal", text_color=color.new(#8a8f98, 0), text_size=size.small)
    table.cell(dash, 1, 0, sigTxt + " · " + sigAge, text_color=color.new(#e8ecf1, 0), text_size=size.small)
    table.cell(dash, 0, 1, "Score", text_color=color.new(#8a8f98, 0), text_size=size.small)
    table.cell(dash, 1, 1, str.tostring(scoreSafe), text_color=color.new(#e8ecf1, 0), text_size=size.small)
    table.cell(dash, 0, 2, "Regime", text_color=color.new(#8a8f98, 0), text_size=size.small)
    table.cell(dash, 1, 2, regTxt + " · RVOL " + str.tostring(rvol, "#.#"), text_color=color.new(#e8ecf1, 0), text_size=size.small)
    table.cell(dash, 0, 3, "Position", text_color=color.new(#8a8f98, 0), text_size=size.small)
    table.cell(dash, 1, 3, posTxt, text_color=color.new(#e8ecf1, 0), text_size=size.small)
    table.cell(dash, 0, 4, "Entry", text_color=color.new(#8a8f98, 0), text_size=size.small)
    table.cell(dash, 1, 4, entryTxt, text_color=color.new(#e8ecf1, 0), text_size=size.small)
    table.cell(dash, 0, 5, "Open R", text_color=color.new(#8a8f98, 0), text_size=size.small)
    table.cell(dash, 1, 5, openRTxt, text_color=color.new(#e8ecf1, 0), text_size=size.small)
    table.cell(dash, 0, 6, "Net P&L", text_color=color.new(#8a8f98, 0), text_size=size.small)
    table.cell(dash, 1, 6, str.tostring(strategy.netprofit, format.mintick), text_color=strategy.netprofit >= 0 ? color.new(#00e5a0, 0) : color.new(#ff4d6d, 0), text_size=size.small)
    table.cell(dash, 0, 7, "Fear/Greed", text_color=color.new(#8a8f98, 0), text_size=size.small)
    table.cell(dash, 1, 7, str.tostring(fgGauge, "#") + " · " + fgZoneTxt + " · VIX " + vixTxt, text_color=fgExtGreed ? sellCol : fgExtFear ? buyCol : color.new(#e8ecf1, 0), text_size=size.small)

// ═══════════════════════════════════════════════════════════════════════════
//  DATA NOTES (what the backtests actually said — defaults follow this)
//  · Score thresholds 10/10 (C1 winner, 100-symbol 1h): long thr 10 +
//    0.75x intraday ATR stop -> n=352, PF 2.25, expR +1.44, won all 3
//    walk-forward folds. Edge is five trades deep (drop top 5 -> breakeven)
//    — treat as live experiment until out-of-sample earns it. Short thr 10:
//    pooled daily 33 trades PF 11.88; short sleeve PF 0.70 (was 0.50 at 8).
//    Threshold is not a robustness lever — cooldown provably inert, keep 5.
//  · RS filter as coded discards good longs: default OFF. Any positive
//    minimum threshold (2/5/10/20%) hurt in validation; "70" produced zero
//    longs. Beta filter: no robust value — default OFF.
//  · Volume filter (RVOL 1.20) is the most valuable filter — keep ON.
//    StochRSI restriction and divergence: small positive — keep.
//  · Anti-chase 1.5 ATR: keep. Blocked entries trade more and lose more.
//  · Exits: 2x ATR stop + opposite-signal exit beat 2R/3R targets and ATR
//    trailing in every test — targets/trailing ship default OFF.
//  · BO/REV setup tags never fire on accepted signals (5 of 957) — removed
//    from trade logic rather than displayed as dead tags.
//  · Shorts: PF 0.34 pooled (0.04 in bull years). Bear-regime gate (SPY <
//    SMA(200)) lifted pooled PF 2.41 -> 3.05 — default ON.
//  · Fear/Greed gauge (CNN 7-component method, chart data only): blocking
//    entries against extreme crowd sentiment (no longs in Extreme Greed,
//    no shorts in Extreme Fear, unless strong) lifted pooled PF 3.05 ->
//    3.54 and expectancy 1.68R -> 2.02R in both regimes — default ON.
//    Conviction boost (+1 score, 1.5x size at contrarian extremes) tested
//    neutral — default OFF.
//  · VIX as a direct input: longs only when VIX < SMA(50) lifted pooled
//    PF 3.54 -> 5.18, expectancy 2.02R -> 3.77R (bull PF 8.08; bear year
//    thins to 11 trades, PF 1.00) — default ON. Panic-spike entry filter
//    (>15% VIX day-over-day) helped mildly — default OFF.
//  · Intraday timeframes showed no edge in either test; the engine's best
//    behavior is on daily bars.
// ═══════════════════════════════════════════════════════════════════════════

Overview

Kernal Cipher Strategy enters long or short at the next bar's open when trend, regime, and oscillator filters agree, then exits on an adaptive ATR stop or an opposite signal. Fast, slow, and long EMAs, slope, Supertrend, ADX, WaveTrend, StochRSI, pivot divergence, relative strength versus benchmarks, and beta change which setups qualify. A Fear and Greed gauge and optional VIX calm filter can block entries. The README reports an unresolved invalid-symbol runtime error.

Details

Published by

kdricha0602 on GitHub

Draws

On the price chart

Markets

Stocks, Indices

Timeframes

Any timeframe

Risk Notes

No repainting, lookahead or stacked entries were found in its code or README. That says nothing about how well it works: test it first.

Topics

pine-scripttechnical-analysistradingtradingview-pro-2026

Settings

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

SettingTypeDefaultVariable
EMA Fastint21emaFastLen
EMA Slowint50emaSlowLen
EMA Long / Regimeint200emaLongLen
EMA Slope Lookbackint5slopeLen
Minimum EMA Slope %float0.05minSlopePct
ATR Lengthint10stLen
Multiplierfloat3.0stMult
ADX Lengthint14adxLen
ADX Minimumint20adxThresh
Require DI AlignmentbooltruerequireDiAlignment
Channel Lengthint10wtN1
Average Lengthint21wtN2
Show all 40 settings
SettingTypeDefaultVariable
RSI Lengthint14rsiLen
Stochastic Lengthint14stochLen
Smooth Kint3smoothK
Smooth Dint3smoothD
Maximum K for Long Entryfloat80stochLongMax
Minimum K for Short Entryfloat20stochShortMin
Pivot Lookback Leftint5lbL
Pivot Lookback Rightint5lbR
Min Bars Between Pivotsint5divMinBars
Max Bars Between Pivotsint60divMaxBars
Use Relative Strength FilterboolfalseuseRSFilter
Primary BenchmarksymbolAMEX:SPYbench1
Secondary BenchmarksymbolNASDAQ:QQQbench2
RS Lookbackint20rsLen
Minimum RS % for Longsfloat0.0rsLongMin
Maximum RS % for Shortsfloat0.0rsShortMax
Beta Lookbackint60betaLen
Minimum Beta for Longsfloat0.7betaMin
Require Minimum Beta for LongsboolfalseuseBetaFilter
ATR Lengthint14atrLen
ATR Stop Multiplierfloat2.0atrStopMult
Timeframe-Aware Stop (0.75x intraday / 2.0x daily+)booltrueuseTFStop
Intraday Stop Multiplierfloat0.75tfStopMult
ATR Regime Baselineint50atrRegimeLen
Min ATR / ATR Baselinefloat0.80atrExpansionMin
Max ATR / ATR Baselinefloat3.00atrExpansionMax
Max Distance From Fast EMA (ATR)float1.5maxChaseATR
Volume Baseline Lengthint20volLen
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

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

Momentum Rider DAILY FINAL

by greenpeas007
58 stars

This strategy combines exponential moving averages (EMA 9, 21, 50), RSI, MACD, and Supertrend indicators to enter long positions. It triggers trades using six dynamic setups based on momentum breakouts, EMA crossovers, and pullbacks. Position sizing is compute

Strategy v6 TrendOscillators & momentum Repaints
Updated 4 months ago · Custom licence GitHub repository of Momentum Rider DAILY FINAL

CombinedBinHAndClucV6

by viktors1982
36 stars

CombinedBinHAndClucV6 is a trading strategy based on the popular BinHV45 and ClucMay72018 algorithms. It calculates dual Bollinger Bands, Exponential Moving Averages, Relative Strength Index, Average True Range, and volume metrics across primary and one-hour h

Strategy v4 Volatility & bandsOscillators & momentum Repaints
Updated 2 years ago · None stated GitHub repository of CombinedBinHAndClucV6

BB Swing

by benso87
24 stars

This strategy calculates Bollinger Bands, a configurable moving average slope, Stochastic RSI, ATR, and ADX directly on the price chart. It evaluates trend bias and momentum conditions to generate long and short swing entries using percent-of-equity sizing. Th

Strategy v5 Volatility & bandsOscillators & momentum
Updated 4 years ago · MPL-2.0 GitHub repository of BB Swing

This strategy trades long and short on the price chart using a multi-timeframe TradingLatino setup. It requires EMA 10 to cross or sit above or below EMA 55 on the one-hour, four-hour, and daily charts, together with a positive ADX slope, a directional monitor

Strategy v5 TrendMoving averages
Updated 1 year ago · None stated GitHub repository of Ruckard ABCBitcoin TradingLatino A010

More Trend scripts

Related on ForexR

Trade Your Charts With a Regulated Broker

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

Compare brokers