Zeiierman’s Machine Learning RSI | AI Classification & Ranking is one of the most downloaded “AI” oscillators on TradingView, and unlike many indicators that wear the machine-learning label as marketing, this one earns it. It does not read the RSI through fixed 70/30 levels. Instead it turns every bar into an 8-dimensional fingerprint of momentum behaviour, stores those fingerprints in a memory bank together with what the market did next, and then — for the current bar — searches that bank for the closest historical analogues and lets them vote on the directional bias. The vote drives an adaptive RSI line, a rank/confidence quality score, long/short signals, and a Supertrend whose band width tightens or loosens with the model’s conviction.
The engine never looks at “RSI = 63”. It builds eight normalised features from a base RSI (length 14 by default), each describing a different facet of momentum behaviour:
1. value = RSI / 100
2. slope = scale01( RSI - RSI[3] )
3. accel = scale01( (RSI - RSI[3]) - (RSI[3] - RSI[6]) )
4. mid = |RSI - 50| / 50
5. percentile= percentrank(RSI, 100) / 100
6. churn = scale01( stdev(RSI, 14) ) // RSI's own volatility
7. spread = scale01( RSI(7) - RSI(28) ) // fast vs slow RSI
8. regime = scale01( EMA(RSI, 20) - 50 )
scale01(x) is a rolling min-max normalisation over a 100-bar window: (x – lowest) / (highest – lowest), mapping every feature into [0, 1] so they are directly comparable in distance space. The result is that each bar becomes a point in an 8-dimensional momentum space rather than a single oscillator value.
Every bar (subsampled to decorrelate neighbours) is stored with its fingerprint and the outcome that followed it. The outcome is the forward price move over a fixed horizon, bucketed by ATR into seven classes:
moveFwd = close - close[horizon]
band = atrFactor × ATR[horizon]
outcome = +3 if moveFwd > 2·band ... down to -3 if moveFwd < -2·band (0 if flat)
The fingerprint stored is the one from horizon bars ago, paired with the outcome measured up to now — i.e. “given this momentum state, here is what happened next”. That is the labelled training set, built continuously as the chart prints.
For the current bar, the engine scans the bank and computes a weighted distance to every stored fingerprint:
dist = Σ w_i × log(1 + |feature_i_now − feature_i_stored|)
The log(1 + |d|) compression (a Lorentzian-style metric) is the key idea: it stops any single feature with a huge mismatch from dominating the comparison, which makes analogue matching robust to outliers. The k nearest fingerprints (k = 8 by default) are retained via a manual top-K — there is no array.sort in ProBuilder, so a parallel array of distances is kept and the worst of the current k is replaced whenever a closer one appears.
The k neighbours vote, weighted by closeness:
weight = 1 / (1 + dist)
score += class × weight // class is the stored outcome, -3..+3
The aggregate produces analogScore (the normalised directional bias), agreeFrac (how unanimous the neighbours are), and gapTight (how close, on average, the matches were). Together these answer not just “which way?” but “how well-supported is this read?”.
A flip of bias is not enough to fire a signal. Each setup is scored twice, on a 0–100 scale:
A long or short only prints when the bias flips and rank ≥ 60 and confidence ≥ 50 and the volatility/trend/chop gates pass and a cooldown of 5 bars has elapsed. This is what turns a noisy classifier into a selective one.
A trailing stop whose ATR multiplier is modulated by the model’s conviction:
mlDrive = |convSmoothed|·0.5 + gapTight·0.3 + agreeFrac·0.2 (×0.35 in chop)
adaptMult= baseMult × (1 + responsiveness × (1 − mlDrive))
High conviction tightens the bands (faster trailing); low conviction or chop widens them (fewer whipsaws). It is a Supertrend that listens to the classifier instead of to volatility alone.
| Feature | Behaviour |
|---|---|
| 8-feature RSI fingerprint | Value, slope, accel, midpoint distance, percentile, churn, fast/slow spread, regime |
| Memory bank | Circular buffer of past fingerprints + ATR-bucketed forward outcome |
| KNN classifier | k nearest analogues by weighted Lorentzian distance, manual top-K |
| Distance-weighted vote | Closer analogues count more; yields bias, agreement, tightness |
| Rank & confidence | Two independent 0–100 quality scores gating the signals |
| Adaptive Supertrend | ATR band width modulated by model conviction |
| ML RSI oscillator | RSI tilted by conviction × rank, coloured by a bull/bear gradient |
| Signal line + BB | Switchable MA (SMA/EMA/SMMA/WMA/VWMA) with optional Bollinger Bands |
// Indicator 1/2 — ML RSI Oscillator (panel)
| Parameter | Default | Description |
|---|---|---|
| `rsiBase` | 14 | Base RSI length; the whole feature set is seeded from it |
| `memoryDepth` | 400 | Span of bars the bank covers |
| `kNeighbors` | 8 | Number of analogues that vote |
| `maType` | 1 | Signal line: 0=None,1=SMA,2=SMA+BB,3=EMA,4=SMMA,5=WMA,6=VWMA |
| `maLen` / `bbMult` | 14 / 2.0 | Signal line length / Bollinger width |
// Indicator 2/2 — ML Supertrend + Signals (overlay)
| Parameter | Default | Description |
|---|---|---|
| `stMultBase` | 1.5 | Base ATR multiplier of the Supertrend |
| `stMlResp` | 1.0 | How strongly conviction reshapes the band (0 = fixed Supertrend) |
| `gateRank` / `gateConf` | 60 / 50 | Minimum rank / confidence for a signal |
| `coolBars` | 5 | Minimum bars between signals |
| `atrFactor` | 0.5 | Learning sensitivity: how big a move counts as an outcome |
The indicator ships as two scripts. Load the first in a separate panel and the second on the price chart, with matching key inputs (rsiBase, memoryDepth, kNeighbors).
// ============================================================
// PRC_Machine Learning RSI (Zeiierman)
// version = 0
// 05.06.2026
// Iván González @ www.prorealcode.com
// Sharing ProRealTime knowledge
// ============================================================
rsiBase = 14
memoryDepth = 400
kNeighbors = 8
winLen = 100
spacingBars = 4
horizonBars = 4
atrFactor = 0.5
wVal = 1.0
wSlp = 1.0
wAcc = 1.0
wMid = 1.0
wPct = 1.0
wChn = 1.0
wSpr = 1.0
wReg = 1.0
stMultBase = 1.5
stMlResp = 1.0
stAtrLen = 10
trendLen = 50
chopCut = 0.5
useChop = 1
smoothLen = 10
useTrendGate = 1
useVolBand = 1
volBandLo = 20
volBandHi = 85
maType = 1 // 0=None, 1=SMA, 2=SMA+BB, 3=EMA, 4=SMMA, 5=WMA, 6=VWMA
maLen = 14
bbMult = 2.0
bullR = 41
bullG = 98
bullB = 255
bearR = 255
bearG = 93
bearB = 0
bankCap = round(memoryDepth / spacingBars)
src = close
stSrc = (high + low) / 2
rOsc = rsi[rsiBase](src)
rfastL = max(2, round(rsiBase / 2))
rOscF = rsi[rfastL](src)
rOscS = rsi[rsiBase * 2](src)
atrVal = averagetruerange[14]
stepLen = 3
fVal = rOsc / 100.0
slopeRaw = rOsc - rOsc[stepLen]
slLo = lowest[winLen](slopeRaw)
slHi = highest[winLen](slopeRaw)
if slHi = slLo then
fSlp = 0.5
else
fSlp = (slopeRaw - slLo) / (slHi - slLo)
endif
accRaw = rOsc - rOsc[stepLen] - (rOsc[stepLen] - rOsc[2 * stepLen])
acLo = lowest[winLen](accRaw)
acHi = highest[winLen](accRaw)
if acHi = acLo then
fAcc = 0.5
else
fAcc = (accRaw - acLo) / (acHi - acLo)
endif
fMid = abs(rOsc - 50.0) / 50.0
prCnt = 0
for p = 1 to winLen do
if rOsc[p] <= rOsc then
prCnt = prCnt + 1
endif
next
fPct = prCnt / winLen
churnRaw = std[14](rOsc)
chLo = lowest[winLen](churnRaw)
chHi = highest[winLen](churnRaw)
if chHi = chLo then
fChn = 0.5
else
fChn = (churnRaw - chLo) / (chHi - chLo)
endif
spreadRaw = rOscF - rOscS
spLo = lowest[winLen](spreadRaw)
spHi = highest[winLen](spreadRaw)
if spHi = spLo then
fSpr = 0.5
else
fSpr = (spreadRaw - spLo) / (spHi - spLo)
endif
emaR20 = average[20, 1](rOsc)
regRaw = emaR20 - 50.0
rgLo = lowest[winLen](regRaw)
rgHi = highest[winLen](regRaw)
if rgHi = rgLo then
fReg = 0.5
else
fReg = (regRaw - rgLo) / (rgHi - rgLo)
endif
moveFwd = src - src[horizonBars]
bandFwd = atrFactor * atrVal[horizonBars]
if moveFwd > 2 * bandFwd then
outcome = 3
elsif moveFwd > bandFwd then
outcome = 2
elsif moveFwd > 0 then
outcome = 1
elsif moveFwd < -2 * bandFwd then
outcome = -3
elsif moveFwd < -bandFwd then
outcome = -2
elsif moveFwd < 0 then
outcome = -1
else
outcome = 0
endif
once bankPtr = 0
once bankRows = 0
once writeCnt = 0
if barindex > horizonBars + winLen then
writeCnt = writeCnt + 1
if writeCnt >= spacingBars then
writeCnt = 0
$bVal[bankPtr] = fVal[horizonBars]
$bSlp[bankPtr] = fSlp[horizonBars]
$bAcc[bankPtr] = fAcc[horizonBars]
$bMid[bankPtr] = fMid[horizonBars]
$bPct[bankPtr] = fPct[horizonBars]
$bChn[bankPtr] = fChn[horizonBars]
$bSpr[bankPtr] = fSpr[horizonBars]
$bReg[bankPtr] = fReg[horizonBars]
$bOut[bankPtr] = outcome
bankPtr = bankPtr + 1
if bankPtr >= bankCap then
bankPtr = 0
endif
if bankRows < bankCap then
bankRows = bankRows + 1
endif
endif
endif
for n = 0 to kNeighbors - 1 do
$kgap[n] = 10000000000
$kcls[n] = 0
next
if bankRows > 1 then
for idx = 0 to bankRows - 1 do
dist = wVal * log(1 + abs(fVal - $bVal[idx])) + wSlp * log(1 + abs(fSlp - $bSlp[idx])) + wAcc * log(1 + abs(fAcc - $bAcc[idx])) + wMid * log(1 + abs(fMid - $bMid[idx])) + wPct * log(1 + abs(fPct - $bPct[idx])) + wChn * log(1 + abs(fChn - $bChn[idx])) + wSpr * log(1 + abs(fSpr - $bSpr[idx])) + wReg * log(1 + abs(fReg - $bReg[idx]))
worst = 0
worstGap = $kgap[0]
for j = 1 to kNeighbors - 1 do
if $kgap[j] > worstGap then
worstGap = $kgap[j]
worst = j
endif
next
if dist < worstGap then
$kgap[worst] = dist
$kcls[worst] = $bOut[idx]
endif
next
endif
voteTotal = 0.0
voteScore = 0.0
voteBull = 0.0
voteBear = 0.0
kCount = 0
gapSum = 0.0
for n = 0 to kNeighbors - 1 do
if $kgap[n] < 10000000000 then
wgt = 1.0 / (1.0 + $kgap[n])
voteTotal = voteTotal + wgt
voteScore = voteScore + $kcls[n] * wgt
if $kcls[n] > 0 then
voteBull = voteBull + wgt
elsif $kcls[n] < 0 then
voteBear = voteBear + wgt
endif
gapSum = gapSum + $kgap[n]
kCount = kCount + 1
endif
next
if voteTotal > 0 then
analogScore = voteScore / voteTotal
else
analogScore = 0.0
endif
if voteTotal > 0 and analogScore > 0.15 then
biasDir = 1
elsif voteTotal > 0 and analogScore < -0.15 then
biasDir = -1
else
biasDir = 0
endif
if voteTotal > 0 and biasDir = 1 then
agreeFrac = voteBull / voteTotal
elsif voteTotal > 0 and biasDir = -1 then
agreeFrac = voteBear / voteTotal
else
agreeFrac = 0.0
endif
if kCount > 0 then
avgGap = gapSum / kCount
else
avgGap = 0.0
endif
wSum = wVal + wSlp + wAcc + wMid + wPct + wChn + wSpr + wReg
gapScale = wSum * 0.45 + 0.000000001
gapTight = max(0.0, min(1.0, 1.0 - avgGap / gapScale))
emaTrend = average[trendLen, 1](src)
emaQuick = average[5, 1](src)
if atrVal > 0 then
trendForce = abs(emaQuick - emaTrend) / atrVal
else
trendForce = 0
endif
chopRaw = trendForce < chopCut
if useChop then
chopNow = chopRaw
else
chopNow = 0
endif
convInst = max(-1.0, min(1.0, analogScore / 1.5))
convSmoothed = average[smoothLen, 1](convInst)
mlDriveBase = abs(convSmoothed) * 0.5 + gapTight * 0.3 + agreeFrac * 0.2
mlDrive = max(0.0, min(1.0, mlDriveBase))
if chopNow then
mlDrive = mlDrive * 0.35
endif
adaptMult = stMultBase * (1.0 + stMlResp * (1.0 - mlDrive))
stAtr = averagetruerange[stAtrLen]
upBand = stSrc - adaptMult * stAtr
dnBand = stSrc + adaptMult * stAtr
if barindex <= 1 then
stLong = upBand
stShort = dnBand
stDir = 1
else
if close[1] > stLong[1] then
stLong = max(upBand, stLong[1])
else
stLong = upBand
endif
if close[1] < stShort[1] then
stShort = min(dnBand, stShort[1])
else
stShort = dnBand
endif
if stDir[1] = -1 and close > stShort[1] then
stDir = 1
elsif stDir[1] = 1 and close < stLong[1] then
stDir = -1
else
stDir = stDir[1]
endif
endif
atrPctCnt = 0
for q = 1 to 100 do
if atrVal[q] <= atrVal then
atrPctCnt = atrPctCnt + 1
endif
next
atrPct = atrPctCnt
slopeUp = rOsc > rOsc[stepLen]
oscReg = emaR20
volHealthy = atrPct >= volBandLo and atrPct <= volBandHi
slopeFit = (biasDir = 1 and slopeUp) or (biasDir = -1 and slopeUp = 0)
stretched = (biasDir = 1 and rOsc > 70) or (biasDir = -1 and rOsc < 30)
emaR5 = average[5, 1](rOsc)
oscSmoothUp = emaR5 > emaR5[1]
trendAligned = (biasDir = 1 and stDir = 1) or (biasDir = -1 and stDir = -1)
once stanceState = 0
once stanceAge = 0
if useTrendGate then
gateTrend = trendAligned
else
gateTrend = 1
endif
if useVolBand then
gateVol = volHealthy
else
gateVol = 1
endif
gatesPass = gateTrend and gateVol and (chopNow = 0)
if biasDir = 1 and gatesPass then
stanceState = 1
elsif biasDir = -1 and gatesPass then
stanceState = -1
else
stanceState = stanceState[1]
endif
stanceChanged = stanceState <> stanceState[1]
if stanceChanged then
stanceAge = 0
else
stanceAge = stanceAge[1] + 1
endif
earlyFlip = stanceChanged and (stanceChanged[1] or stanceChanged[2] or stanceChanged[3])
pAgree = 25.0 * agreeFrac
pGap = 15.0 * gapTight
if slopeFit then
pStructA = 10.0
else
pStructA = 0.0
endif
if stretched then
pStructB = 0.0
else
pStructB = 5.0
endif
pStruct = pStructA + pStructB
if trendAligned then
pTrend = 10.0
else
pTrend = 0.0
endif
if volHealthy then
pVol = 10.0
elsif atrPct < volBandLo then
pVol = 5.0
else
pVol = 3.0
endif
regFit = (biasDir = 1 and oscReg > 55) or (biasDir = -1 and oscReg < 45)
if regFit then
pReg = 10.0
elsif oscReg >= 45 and oscReg <= 55 then
pReg = 4.0
else
pReg = 6.0
endif
if (biasDir = 1 and oscSmoothUp) or (biasDir = -1 and oscSmoothUp = 0) then
pSmooth = 5.0
else
pSmooth = 0.0
endif
pHold = min(5.0, stanceAge)
penChop = 0.0
if chopRaw then
penChop = 8.0
endif
penStr = 0.0
if stretched then
penStr = 6.0
endif
penFlip = 0.0
if earlyFlip then
penFlip = 6.0
endif
penK = 0.0
if kCount < kNeighbors then
penK = 5.0 * (kNeighbors - kCount) / kNeighbors
endif
pPen = min(20.0, penChop + penStr + penFlip + penK)
rawRank = pAgree + pGap + pStruct + pTrend + pVol + pReg + pSmooth + pHold - pPen
if biasDir = 0 then
rankVal = 0.0
else
rankVal = max(0.0, min(100.0, rawRank))
endif
intensity = max(0.0, min(1.0, average[3, 1](rankVal) / 100.0))
mlTilt = max(-1.0, min(1.0, convSmoothed)) * intensity * 18.0
mlRSI = average[3, 1](max(0.0, min(100.0, rOsc + mlTilt)))
gradPos = max(0.0, min(1.0, (mlRSI - 28.0) / 44.0))
rCol = round(bearR + (bullR - bearR) * gradPos)
gCol = round(bearG + (bullG - bearG) * gradPos)
bCol = round(bearB + (bullB - bearB) * gradPos)
if maType = 1 or maType = 2 then
sigLine = average[maLen](mlRSI)
elsif maType = 3 then
sigLine = average[maLen, 1](mlRSI)
elsif maType = 4 then
sigLine = average[maLen, 3](mlRSI)
elsif maType = 5 then
sigLine = average[maLen, 2](mlRSI)
elsif maType = 6 then
volSum = summation[maLen](volume)
if volSum > 0 then
sigLine = summation[maLen](mlRSI * volume) / volSum
else
sigLine = undefined
endif
else
sigLine = undefined
endif
if maType = 2 then
bbDev = std[maLen](mlRSI) * bbMult
bbUp = sigLine + bbDev
bbDn = sigLine - bbDev
else
bbUp = undefined
bbDn = undefined
endif
return mlRSI as "ML RSI" coloured(rCol, gCol, bCol) style(line, 2), sigLine as "Signal" coloured(227, 177, 58), bbUp as "BB Up" coloured(150, 150, 150), bbDn as "BB Dn" coloured(150, 150, 150), 70 as "OB", 50 as "Mid", 30 as "OS"
Load on the price chart with matching rsiBase, memoryDepth, kNeighbors.
// ============================================================
// PRC_Machine Learning RSI (Zeiierman)
// version = 0
// 05.06.2026
// Iván González @ www.prorealcode.com
// Sharing ProRealTime knowledge
// ============================================================
rsiBase = 14
memoryDepth = 400
kNeighbors = 8
winLen = 100
spacingBars = 4
horizonBars = 4
atrFactor = 0.5
wVal = 1.0
wSlp = 1.0
wAcc = 1.0
wMid = 1.0
wPct = 1.0
wChn = 1.0
wSpr = 1.0
wReg = 1.0
stMultBase = 1.5
stMlResp = 1.0
stAtrLen = 10
trendLen = 50
chopCut = 0.5
useChop = 1
smoothLen = 10
gateRank = 60
gateConf = 50
useTrendGate = 1
useVolBand = 1
volBandLo = 20
volBandHi = 85
coolBars = 5
bankCap = round(memoryDepth / spacingBars)
src = close
stSrc = (high + low) / 2
rOsc = rsi[rsiBase](src)
rfastL = max(2, round(rsiBase / 2))
rOscF = rsi[rfastL](src)
rOscS = rsi[rsiBase * 2](src)
atrVal = averagetruerange[14]
stepLen = 3
fVal = rOsc / 100.0
slopeRaw = rOsc - rOsc[stepLen]
slLo = lowest[winLen](slopeRaw)
slHi = highest[winLen](slopeRaw)
if slHi = slLo then
fSlp = 0.5
else
fSlp = (slopeRaw - slLo) / (slHi - slLo)
endif
accRaw = rOsc - rOsc[stepLen] - (rOsc[stepLen] - rOsc[2 * stepLen])
acLo = lowest[winLen](accRaw)
acHi = highest[winLen](accRaw)
if acHi = acLo then
fAcc = 0.5
else
fAcc = (accRaw - acLo) / (acHi - acLo)
endif
fMid = abs(rOsc - 50.0) / 50.0
prCnt = 0
for p = 1 to winLen do
if rOsc[p] <= rOsc then
prCnt = prCnt + 1
endif
next
fPct = prCnt / winLen
churnRaw = std[14](rOsc)
chLo = lowest[winLen](churnRaw)
chHi = highest[winLen](churnRaw)
if chHi = chLo then
fChn = 0.5
else
fChn = (churnRaw - chLo) / (chHi - chLo)
endif
spreadRaw = rOscF - rOscS
spLo = lowest[winLen](spreadRaw)
spHi = highest[winLen](spreadRaw)
if spHi = spLo then
fSpr = 0.5
else
fSpr = (spreadRaw - spLo) / (spHi - spLo)
endif
emaR20 = average[20, 1](rOsc)
regRaw = emaR20 - 50.0
rgLo = lowest[winLen](regRaw)
rgHi = highest[winLen](regRaw)
if rgHi = rgLo then
fReg = 0.5
else
fReg = (regRaw - rgLo) / (rgHi - rgLo)
endif
moveFwd = src - src[horizonBars]
bandFwd = atrFactor * atrVal[horizonBars]
if moveFwd > 2 * bandFwd then
outcome = 3
elsif moveFwd > bandFwd then
outcome = 2
elsif moveFwd > 0 then
outcome = 1
elsif moveFwd < -2 * bandFwd then
outcome = -3
elsif moveFwd < -bandFwd then
outcome = -2
elsif moveFwd < 0 then
outcome = -1
else
outcome = 0
endif
once bankPtr = 0
once bankRows = 0
once writeCnt = 0
if barindex > horizonBars + winLen then
writeCnt = writeCnt + 1
if writeCnt >= spacingBars then
writeCnt = 0
$bVal[bankPtr] = fVal[horizonBars]
$bSlp[bankPtr] = fSlp[horizonBars]
$bAcc[bankPtr] = fAcc[horizonBars]
$bMid[bankPtr] = fMid[horizonBars]
$bPct[bankPtr] = fPct[horizonBars]
$bChn[bankPtr] = fChn[horizonBars]
$bSpr[bankPtr] = fSpr[horizonBars]
$bReg[bankPtr] = fReg[horizonBars]
$bOut[bankPtr] = outcome
bankPtr = bankPtr + 1
if bankPtr >= bankCap then
bankPtr = 0
endif
if bankRows < bankCap then
bankRows = bankRows + 1
endif
endif
endif
for n = 0 to kNeighbors - 1 do
$kgap[n] = 10000000000
$kcls[n] = 0
next
if bankRows > 1 then
for idx = 0 to bankRows - 1 do
dist = wVal * log(1 + abs(fVal - $bVal[idx])) + wSlp * log(1 + abs(fSlp - $bSlp[idx])) + wAcc * log(1 + abs(fAcc - $bAcc[idx])) + wMid * log(1 + abs(fMid - $bMid[idx])) + wPct * log(1 + abs(fPct - $bPct[idx])) + wChn * log(1 + abs(fChn - $bChn[idx])) + wSpr * log(1 + abs(fSpr - $bSpr[idx])) + wReg * log(1 + abs(fReg - $bReg[idx]))
worst = 0
worstGap = $kgap[0]
for j = 1 to kNeighbors - 1 do
if $kgap[j] > worstGap then
worstGap = $kgap[j]
worst = j
endif
next
if dist < worstGap then
$kgap[worst] = dist
$kcls[worst] = $bOut[idx]
endif
next
endif
voteTotal = 0.0
voteScore = 0.0
voteBull = 0.0
voteBear = 0.0
kCount = 0
gapSum = 0.0
for n = 0 to kNeighbors - 1 do
if $kgap[n] < 10000000000 then
wgt = 1.0 / (1.0 + $kgap[n])
voteTotal = voteTotal + wgt
voteScore = voteScore + $kcls[n] * wgt
if $kcls[n] > 0 then
voteBull = voteBull + wgt
elsif $kcls[n] < 0 then
voteBear = voteBear + wgt
endif
gapSum = gapSum + $kgap[n]
kCount = kCount + 1
endif
next
if voteTotal > 0 then
analogScore = voteScore / voteTotal
else
analogScore = 0.0
endif
if voteTotal > 0 and analogScore > 0.15 then
biasDir = 1
elsif voteTotal > 0 and analogScore < -0.15 then
biasDir = -1
else
biasDir = 0
endif
if voteTotal > 0 and biasDir = 1 then
agreeFrac = voteBull / voteTotal
elsif voteTotal > 0 and biasDir = -1 then
agreeFrac = voteBear / voteTotal
else
agreeFrac = 0.0
endif
if kCount > 0 then
avgGap = gapSum / kCount
else
avgGap = 0.0
endif
wSum = wVal + wSlp + wAcc + wMid + wPct + wChn + wSpr + wReg
gapScale = wSum * 0.45 + 0.000000001
gapTight = max(0.0, min(1.0, 1.0 - avgGap / gapScale))
emaTrend = average[trendLen, 1](src)
emaQuick = average[5, 1](src)
if atrVal > 0 then
trendForce = abs(emaQuick - emaTrend) / atrVal
else
trendForce = 0
endif
chopRaw = trendForce < chopCut
if useChop then
chopNow = chopRaw
else
chopNow = 0
endif
convInst = max(-1.0, min(1.0, analogScore / 1.5))
convSmoothed = average[smoothLen, 1](convInst)
mlDriveBase = abs(convSmoothed) * 0.5 + gapTight * 0.3 + agreeFrac * 0.2
mlDrive = max(0.0, min(1.0, mlDriveBase))
if chopNow then
mlDrive = mlDrive * 0.35
endif
adaptMult = stMultBase * (1.0 + stMlResp * (1.0 - mlDrive))
stAtr = averagetruerange[stAtrLen]
upBand = stSrc - adaptMult * stAtr
dnBand = stSrc + adaptMult * stAtr
if barindex <= 1 then
stLong = upBand
stShort = dnBand
stDir = 1
else
if close[1] > stLong[1] then
stLong = max(upBand, stLong[1])
else
stLong = upBand
endif
if close[1] < stShort[1] then
stShort = min(dnBand, stShort[1])
else
stShort = dnBand
endif
if stDir[1] = -1 and close > stShort[1] then
stDir = 1
elsif stDir[1] = 1 and close < stLong[1] then
stDir = -1
else
stDir = stDir[1]
endif
endif
if stDir = 1 then
stLine = stLong
r = 33
g = 87
b = 243
else
stLine = stShort
r = 242
g = 54
b = 69
endif
atrPctCnt = 0
for q = 1 to 100 do
if atrVal[q] <= atrVal then
atrPctCnt = atrPctCnt + 1
endif
next
atrPct = atrPctCnt
slopeUp = rOsc > rOsc[stepLen]
oscReg = emaR20
volHealthy = atrPct >= volBandLo and atrPct <= volBandHi
slopeFit = (biasDir = 1 and slopeUp) or (biasDir = -1 and slopeUp = 0)
stretched = (biasDir = 1 and rOsc > 70) or (biasDir = -1 and rOsc < 30)
emaR5 = average[5, 1](rOsc)
oscSmoothUp = emaR5 > emaR5[1]
trendAligned = (biasDir = 1 and stDir = 1) or (biasDir = -1 and stDir = -1)
once stanceState = 0
once stanceAge = 0
once lastEntryBar = -1000
if useTrendGate then
gateTrend = trendAligned
else
gateTrend = 1
endif
if useVolBand then
gateVol = volHealthy
else
gateVol = 1
endif
gatesPass = gateTrend and gateVol and (chopNow = 0)
if biasDir = 1 and gatesPass then
stanceState = 1
elsif biasDir = -1 and gatesPass then
stanceState = -1
else
stanceState = stanceState[1]
endif
stanceChanged = stanceState <> stanceState[1]
if stanceChanged then
stanceAge = 0
else
stanceAge = stanceAge[1] + 1
endif
earlyFlip = stanceChanged and (stanceChanged[1] or stanceChanged[2] or stanceChanged[3])
pAgree = 25.0 * agreeFrac
pGap = 15.0 * gapTight
if slopeFit then
pStructA = 10.0
else
pStructA = 0.0
endif
if stretched then
pStructB = 0.0
else
pStructB = 5.0
endif
pStruct = pStructA + pStructB
if trendAligned then
pTrend = 10.0
else
pTrend = 0.0
endif
if volHealthy then
pVol = 10.0
elsif atrPct < volBandLo then
pVol = 5.0
else
pVol = 3.0
endif
regFit = (biasDir = 1 and oscReg > 55) or (biasDir = -1 and oscReg < 45)
if regFit then
pReg = 10.0
elsif oscReg >= 45 and oscReg <= 55 then
pReg = 4.0
else
pReg = 6.0
endif
if (biasDir = 1 and oscSmoothUp) or (biasDir = -1 and oscSmoothUp = 0) then
pSmooth = 5.0
else
pSmooth = 0.0
endif
pHold = min(5.0, stanceAge)
penChop = 0.0
if chopRaw then
penChop = 8.0
endif
penStr = 0.0
if stretched then
penStr = 6.0
endif
penFlip = 0.0
if earlyFlip then
penFlip = 6.0
endif
penK = 0.0
if kCount < kNeighbors then
penK = 5.0 * (kNeighbors - kCount) / kNeighbors
endif
pPen = min(20.0, penChop + penStr + penFlip + penK)
rawRank = pAgree + pGap + pStruct + pTrend + pVol + pReg + pSmooth + pHold - pPen
if biasDir = 0 then
rankVal = 0.0
else
rankVal = max(0.0, min(100.0, rawRank))
endif
if slopeFit then
confSlope = 10.0
else
confSlope = 0.0
endif
confFlipPen = 0.0
if earlyFlip then
confFlipPen = 15.0
endif
confKPen = 0.0
if kCount < kNeighbors then
confKPen = 10.0 * (kNeighbors - kCount) / kNeighbors
endif
confRaw = 40.0 * agreeFrac + 25.0 * gapTight + 15.0 * min(1.0, stanceAge / 5.0) + confSlope - confFlipPen - confKPen
if biasDir = 0 then
confVal = 0.0
else
confVal = max(0.0, min(100.0, confRaw))
endif
flipLong = stanceState = 1 and stanceState[1] <> 1
flipShort = stanceState = -1 and stanceState[1] <> -1
qualifies = rankVal >= gateRank and confVal >= gateConf
coolOK = barindex - lastEntryBar >= coolBars
triggerLong = flipLong and qualifies and coolOK
triggerShort = flipShort and qualifies and coolOK
if triggerLong or triggerShort then
lastEntryBar = barindex
endif
placebuy = lowest[5](low) - 0.25 * atrVal
placesell = highest[5](high) + 0.25 * atrVal
if triggerLong then
drawtext("▲", barindex, placebuy) coloured(50, 205, 50)
endif
if triggerShort then
drawtext("▼", barindex, placesell) coloured(242, 54, 69)
endif
return stLine as "ML Supertrend" coloured(r, g, b)