//@version=6 indicator("Ocs Ai Trader", shorttitle = "Ocs Ai Trader", overlay = true, max_bars_back = 500, max_lines_count = 500, max_labels_count = 500, max_boxes_count = 500) const string GRP_GEN = "General Settings" const string GRP_TSP = "Time Series Processing Layer" const string GRP_SPL = "Signal Processing Layer" const string GRP_FLT = "Filters" const string GRP_ML = "Machine Learning (KNN)" const string GRP_GAU = "Gaussian Structure" const string GRP_VTE = "Virtual Trade Emulator" const string GRP_SR = "Support & Resistance" const string GRP_DSP = "Display" const string GRP_TBL = "Backtesting Compiler (Stats Table)" const string GRP_COL = "Colors" const string TXT_TINY = "Tiny" const string TXT_SMALL = "Small" const string TXT_NORMAL = "Normal" const string TXT_LARGE = "Large" const string HIT_BOOKED = "✓Booked" const string HIT_PLAIN = "Hit" i_confirmBar = input.bool(true, "Show Trade Signal only when bar is confirmed", group = GRP_GEN, tooltip = "When enabled, signals are produced on bar close only (no repaint).") i_isPremium = input.bool(true, "Is your TradingView Subscription Premium and above ?", group = GRP_GEN, tooltip = "Premium plans can read second based intrabar data for a precise Volume Delta. If your plan is not Premium, UNCHECK this option.") i_ltf = input.timeframe("1", "Lower Timeframe to use if TradingView Subscription is not premium !", group = GRP_GEN, tooltip = "Lower timeframe used for the Volume Delta when the Premium option is unchecked. Ignored if it is not lower than the chart timeframe.") i_textSize = input.string(TXT_SMALL, "Text Size :", options = [TXT_TINY, TXT_SMALL, TXT_NORMAL, TXT_LARGE], group = GRP_GEN) i_sensitivity = input.float(1.0, "Signal Sensitivity", minval = 0.1, maxval = 5.0, step = 0.1, group = GRP_GEN, tooltip = "Exponential Probability Squeeze factor. Higher values produce more decisive probabilities and more signals.") i_alertPass = input.string("", "Alert Passcode", group = GRP_GEN, tooltip = "Optional passcode appended to every alert() message (useful for webhook authentication).") i_dcMin = input.int(8, "Dominant Cycle Period Min", minval = 4, maxval = 50, group = GRP_TSP, inline = "dc") i_dcMax = input.int(48, "Max", minval = 10, maxval = 100, group = GRP_TSP, inline = "dc", tooltip = "Bounds of the Ehlers Homodyne Dominant Cycle. The detected cycle is used as the dynamic length of the adaptive calculations.") i_amaFast = input.int(2, "Adaptive Moving Average Fast", minval = 1, group = GRP_TSP, inline = "ama") i_amaSlow = input.int(30, "Slow", minval = 2, group = GRP_TSP, inline = "ama") i_stFactor = input.float(3.0, "Supertrend Factor", minval = 0.5, step = 0.1, group = GRP_TSP, inline = "st") i_stAtrLen = input.int(10, "ATR", minval = 1, group = GRP_TSP, inline = "st") i_stochLen = input.int(14, "Relative Strength Stochastics Length", minval = 2, group = GRP_TSP, inline = "sto") i_stochSmooth = input.int(3, "Smooth", minval = 1, group = GRP_TSP, inline = "sto") i_cvdLen = input.int(21, "Cumulative Volume Delta Length", minval = 2, group = GRP_TSP) i_trixLen = input.int(14, "TRIX Length", minval = 2, group = GRP_TSP, inline = "trix") i_trixSig = input.int(9, "Signal", minval = 1, group = GRP_TSP, inline = "trix") i_conf = input.float(0.60, "Probability Distribution Confidence", minval = 0.50, maxval = 0.95, step = 0.01, group = GRP_SPL, tooltip = "Minimum bullish probability for long intents (and maximum for short intents = 1 - value).") i_probAvg = input.int(3, "Probability Average Length", minval = 1, maxval = 20, group = GRP_SPL) i_probVarMax = input.float(0.30, "Probability Variable Filter (Max StDev)", minval = 0.01, maxval = 1.0, step = 0.01, group = GRP_SPL) i_lmsLen = input.int(16, "Adaptive LMS Filter Order", minval = 2, maxval = 64, group = GRP_SPL, inline = "lms") i_lmsMu = input.float(0.05, "Step (mu)", minval = 0.001, maxval = 1.0, step = 0.001, group = GRP_SPL, inline = "lms") i_fisherTol = input.float(0.25, "Fisher Transform Tolerance", minval = 0.0, maxval = 3.0, step = 0.05, group = GRP_SPL) i_qrLen = input.int(20, "Quadratic Regression / Trimean Length", minval = 5, maxval = 200, group = GRP_SPL) i_useCvdDiv = input.bool(true, "CVD Divergence Signal Structure", group = GRP_SPL, inline = "div") i_divLen = input.int(14, "Lookback", minval = 3, group = GRP_SPL, inline = "div") i_useRegime = input.bool(true, "Regime Filter", group = GRP_FLT, inline = "reg") i_regimeThr = input.float(-0.1, "Threshold", minval = -10.0, maxval = 10.0, step = 0.1, group = GRP_FLT, inline = "reg") i_useVolFilter = input.bool(true, "Volatility Probability Filter", group = GRP_FLT) i_useAtrImpulse = input.bool(true, "Impulsive ATR Filter", group = GRP_FLT, inline = "imp") i_atrImpMult = input.float(0.75, "Multiplier", minval = 0.1, step = 0.05, group = GRP_FLT, inline = "imp") i_useVolDeriv = input.bool(true, "Volatility Derivative Filter (Elder)", group = GRP_FLT, inline = "vd") i_volDerivTol = input.float(0.15, "Tolerance", minval = 0.0, step = 0.01, group = GRP_FLT, inline = "vd") i_useTrix = input.bool(true, "TRIX Strength / Weakness Analyser", group = GRP_FLT) i_useCvd = input.bool(true, "Cumulative Volume Delta Regime Filter", group = GRP_FLT) i_useMarubozu = input.bool(true, "Marubozu Abnormality Detection", group = GRP_FLT, inline = "maru") i_maruBody = input.float(0.85, "Body Ratio", minval = 0.5, maxval = 1.0, step = 0.01, group = GRP_FLT, inline = "maru") i_useKnn = input.bool(true, "K-Nearest Neighbour Classification", group = GRP_ML) i_knnK = input.int(8, "Neighbours (K)", minval = 1, maxval = 50, group = GRP_ML) i_knnWindow = input.int(1000, "Training Window (Samples)", minval = 50, maxval = 5000, group = GRP_ML) i_knnStride = input.int(4, "Sampling Stride", minval = 1, maxval = 20, group = GRP_ML) i_knnHorizon = input.int(4, "Prediction Horizon (Bars)", minval = 1, maxval = 50, group = GRP_ML) i_knnWeight = input.float(0.50, "KNN Weight in Final Probability", minval = 0.0, maxval = 1.0, step = 0.05, group = GRP_ML) i_gaussAdaptive = input.bool(true, "Adaptive Length (Dominant Cycle)", group = GRP_GAU) i_gaussLen = input.int(21, "Gaussian Length", minval = 2, group = GRP_GAU, inline = "gau") i_gaussPoles = input.int(4, "Poles", minval = 1, maxval = 4, group = GRP_GAU, inline = "gau") i_gaussMult = input.float(1.5, "Band ATR Multiplier", minval = 0.1, step = 0.1, group = GRP_GAU) i_baseLen = input.int(144, "Gaussian Baseline Length", minval = 10, group = GRP_GAU) i_atrLenTrade = input.int(14, "ATR Length", minval = 1, group = GRP_VTE) i_tMult = input.float(1.0, "Target Step (ATR)", minval = 0.1, step = 0.05, group = GRP_VTE) i_stopMult = input.float(2.25, "Stop Distance (ATR)", minval = 0.1, step = 0.05, group = GRP_VTE) i_showT45 = input.bool(false, "Dynamic Target 4 & Target 5", group = GRP_VTE) i_recoveryBars = input.int(0, "Recovery Window (Bars)", minval = 0, maxval = 100, group = GRP_VTE, tooltip = "Bars a stopped-out trade stays on the chart before it is cleared. It no longer affects the statistics: once the stop is touched the trade is a loss, whatever price does afterwards. Leave at 0 unless you want to watch where it went.") i_labelOffset = input.int(4, "Levels Label Offset (Bars)", minval = 1, maxval = 100, group = GRP_VTE, tooltip = "Distance, in bars, between the last bar and the level labels.") i_hitStyle = input.string(HIT_BOOKED, "Target Hit Text", options = [HIT_BOOKED, HIT_PLAIN], group = GRP_VTE) i_showVolSR = input.bool(true, "Volume Float Support & Resistance", group = GRP_SR) i_srPivotLen = input.int(10, "Pivot Length", minval = 2, group = GRP_SR, inline = "vsr") i_srVolMult = input.float(1.2, "Volume Multiplier", minval = 0.1, step = 0.1, group = GRP_SR, inline = "vsr") i_srMaxBars = input.int(60, "Max Extension (Bars)", minval = 5, maxval = 500, group = GRP_SR) i_showPivots = input.bool(true, "Pivotal Points & Lines", group = GRP_SR) i_pivLen = input.int(20, "Pivot Length", minval = 2, group = GRP_SR, inline = "piv") i_pivMaxBars = input.int(50, "Max Extension (Bars)", minval = 5, maxval = 500, group = GRP_SR, inline = "piv") i_maxZones = input.int(20, "Max Zones Kept On Chart", minval = 2, maxval = 100, group = GRP_SR) i_showBand = input.bool(true, "Gaussian Trend Band", group = GRP_DSP, inline = "band") i_bandWidth = input.int(10, "Thickness", minval = 1, maxval = 30, group = GRP_DSP, inline = "band") i_showBaseline = input.bool(true, "Gaussian Baseline", group = GRP_DSP) i_showDots = input.bool(true, "Probability Triangles", group = GRP_DSP) i_showBarColor = input.bool(true, "Colour Signal Candles", group = GRP_DSP) i_showHistory = input.bool(false, "Historical Buy Sell Positions by Algo", group = GRP_DSP) i_showLevels = input.bool(true, "Targets, Strength & Stop Levels", group = GRP_DSP) i_showTable = input.bool(true, "Show Stats Table", group = GRP_TBL) i_tablePos = input.string("Bottom Left", "Position", options = ["Top Left", "Top Center", "Top Right", "Middle Left", "Middle Right", "Bottom Left", "Bottom Center", "Bottom Right"], group = GRP_TBL, inline = "tbl") i_tableSize = input.string(TXT_TINY, "Size", options = [TXT_TINY, TXT_SMALL, TXT_NORMAL, TXT_LARGE], group = GRP_TBL, inline = "tbl") i_colBandBull = input.color(color.new(#4caf50, 82), "Trend Band Bull", group = GRP_COL, inline = "c1") i_colBandBear = input.color(color.new(#f23645, 82), "Bear", group = GRP_COL, inline = "c1") i_colDotBull = input.color(color.new(#81c784, 30), "Triangles Bull", group = GRP_COL, inline = "c2") i_colDotBear = input.color(color.new(#ef9a9a, 20), "Bear", group = GRP_COL, inline = "c2") i_colDotNeut = input.color(color.new(#9598a1, 25), "Triangles Neutral", group = GRP_COL, inline = "c2b") i_colDotWarn = input.color(color.new(#ffb74d, 10), "Caution", group = GRP_COL, inline = "c2b") i_colBaseline = input.color(color.new(#a5d6a7, 40), "Gaussian Baseline", group = GRP_COL) i_colBuyBar = input.color(#7ee8c4, "Signal Candle Buy", group = GRP_COL, inline = "c3") i_colSellBar = input.color(#f4a6d7, "Sell", group = GRP_COL, inline = "c3") i_colBuyTgt = input.color(#2962ff, "Buy Targets Text", group = GRP_COL, inline = "b1") i_colBuyTgtBg = input.color(color.new(#2962ff, 90), "Bg", group = GRP_COL, inline = "b1") i_colBuyTgtLn = input.color(color.new(#4caf50, 30), "Line", group = GRP_COL, inline = "b1") i_colBuyEnt = input.color(#089981, "Buy Strength Text", group = GRP_COL, inline = "b2") i_colBuyEntBg = input.color(color.new(#4caf50, 88), "Bg", group = GRP_COL, inline = "b2") i_colBuyEntLn = input.color(#2e9d4a, "Line", group = GRP_COL, inline = "b2") i_colBuyStp = input.color(#f23645, "Buy Stop Text", group = GRP_COL, inline = "b3") i_colBuyStpBg = input.color(color.new(#f7525f, 88), "Bg", group = GRP_COL, inline = "b3") i_colBuyStpLn = input.color(color.new(#f23645, 30), "Line", group = GRP_COL, inline = "b3") i_colSellTgt = input.color(#f23645, "Sell Targets Text", group = GRP_COL, inline = "s1") i_colSellTgtBg = input.color(color.new(#f23645, 90), "Bg", group = GRP_COL, inline = "s1") i_colSellTgtLn = input.color(color.new(#f23645, 30), "Line", group = GRP_COL, inline = "s1") i_colSellEnt = input.color(#f23645, "Sell Weakness Text", group = GRP_COL, inline = "s2") i_colSellEntBg = input.color(color.new(#ff9800, 85), "Bg", group = GRP_COL, inline = "s2") i_colSellEntLn = input.color(#f23645, "Line", group = GRP_COL, inline = "s2") i_colSellStp = input.color(#2962ff, "Sell Stop Text", group = GRP_COL, inline = "s3") i_colSellStpBg = input.color(color.new(#4caf50, 88), "Bg", group = GRP_COL, inline = "s3") i_colSellStpLn = input.color(color.new(#2962ff, 30), "Line", group = GRP_COL, inline = "s3") i_colBrand = input.color(#f7525f, "Brand Text", group = GRP_COL, inline = "c7") i_colBrandBg = input.color(color.new(#ff9800, 88), "Background", group = GRP_COL, inline = "c7") i_colVolZone = input.color(color.new(#787b86, 85), "Volume Float Zones", group = GRP_COL) i_colPivZone = input.color(color.new(#2196f3, 85), "Pivotal Zones", group = GRP_COL) i_colLong = input.color(#089981, "Stats Long", group = GRP_COL, inline = "c8") i_colShort = input.color(#f23645, "Short", group = GRP_COL, inline = "c8") i_colTblBg = input.color(#ffffff, "Table Background", group = GRP_COL, inline = "c9") i_colTblText = input.color(#1e3a8a, "Text", group = GRP_COL, inline = "c9") i_colTblBorder = input.color(#b2b5be, "Border", group = GRP_COL, inline = "c9") const string VERSION_TXT = "Ocs AI ML V.312 (corrected)" const string BRAND_TXT = "Ocs Ai Trader" const string RECOVERY_TXT = "(stop = loss)" const float EPS = 1e-10 const int WARMUP_MULT = 2 const float ZONE_MIN_ATR = 0.25 const float PIV_ZONE_ATR = 0.50 const float BRAND_GAP = 0.60 const string ARROW_UP = "▲ " const string ARROW_DN = "▼ " const string WARN_ICON = "⚠ " const color TXT_DARK = #131722 type TradeState int dir = 0 int sigBar = 0 float trigger = na float stop = na float t1 = na float t2 = na float t3 = na float t4 = na float t5 = na bool triggered = false bool t1Hit = false bool t2Hit = false bool t3Hit = false bool t4Hit = false bool t5Hit = false bool stopHit = false int stopBar = na bool closed = false type Stats int longTotal = 0 int shortTotal = 0 int longWins = 0 int shortWins = 0 type Zone box bx float top float bottom int dir int startBar bool active = true f_tanh(float x) => float xc = math.max(math.min(nz(x), 20.0), -20.0) float e2 = math.exp(2.0 * xc) (e2 - 1.0) / (e2 + 1.0) f_smooth(float src, float alpha) => float s = na s := na(s[1]) ? src : alpha * src + (1.0 - alpha) * s[1] s f_rma(float src, float len) => f_smooth(src, 1.0 / math.max(len, 1.0)) f_rsi(float src, int len) => float chg = src - nz(src[1], src) float up = f_rma(math.max(chg, 0.0), len) float dn = f_rma(math.max(-chg, 0.0), len) dn <= EPS ? 100.0 : up <= EPS ? 0.0 : 100.0 - 100.0 / (1.0 + up / dn) f_normalize(float src, float lo, float hi) => var float hMin = 1e10 var float hMax = -1e10 hMin := math.min(nz(src, hMin), hMin) hMax := math.max(nz(src, hMax), hMax) lo + (hi - lo) * (nz(src) - hMin) / math.max(hMax - hMin, EPS) f_vwma(float src, float vol, int len) => float den = math.sum(vol, len) den > EPS ? math.sum(src * vol, len) / den : src f_kama(float src, int len, int fastLen, int slowLen) => float change = math.abs(src - nz(src[len], src)) float noise = math.sum(math.abs(src - nz(src[1], src)), len) float er = noise > EPS ? change / noise : 0.0 float fastSc = 2.0 / (fastLen + 1.0) float slowSc = 2.0 / (slowLen + 1.0) float sc = math.pow(er * (fastSc - slowSc) + slowSc, 2) float k = na k := na(k[1]) ? src : k[1] + sc * (src - k[1]) k f_dominantCycle(float src, int minP, int maxP) => float period = 0.0 float smoothP = 0.0 float i2 = 0.0 float q2 = 0.0 float re = 0.0 float im = 0.0 float prevPeriod = nz(period[1], 15.0) float smooth = (4.0 * src + 3.0 * nz(src[1], src) + 2.0 * nz(src[2], src) + nz(src[3], src)) / 10.0 float adj = 0.075 * prevPeriod + 0.54 float detrender = (0.0962 * smooth + 0.5769 * nz(smooth[2]) - 0.5769 * nz(smooth[4]) - 0.0962 * nz(smooth[6])) * adj float q1 = (0.0962 * detrender + 0.5769 * nz(detrender[2]) - 0.5769 * nz(detrender[4]) - 0.0962 * nz(detrender[6])) * adj float i1 = nz(detrender[3]) float jI = (0.0962 * i1 + 0.5769 * nz(i1[2]) - 0.5769 * nz(i1[4]) - 0.0962 * nz(i1[6])) * adj float jQ = (0.0962 * q1 + 0.5769 * nz(q1[2]) - 0.5769 * nz(q1[4]) - 0.0962 * nz(q1[6])) * adj i2 := 0.2 * (i1 - jQ) + 0.8 * nz(i2[1]) q2 := 0.2 * (q1 + jI) + 0.8 * nz(q2[1]) re := 0.2 * (i2 * nz(i2[1]) + q2 * nz(q2[1])) + 0.8 * nz(re[1]) im := 0.2 * (i2 * nz(q2[1]) - q2 * nz(i2[1])) + 0.8 * nz(im[1]) float p = im != 0.0 and re != 0.0 ? 2.0 * math.pi / math.atan(im / re) : prevPeriod p := math.min(p, 1.5 * prevPeriod) p := math.max(p, 0.67 * prevPeriod) p := math.max(math.min(p, maxP), minP) period := 0.2 * p + 0.8 * prevPeriod smoothP := 0.33 * period + 0.67 * nz(smoothP[1], period) smoothP f_gaussian(float src, float len, int poles) => float beta = (1.0 - math.cos(2.0 * math.pi / math.max(len, 2.0))) / (math.pow(2.0, 1.0 / poles) - 1.0) float alpha = -beta + math.sqrt(beta * beta + 2.0 * beta) float x = 1.0 - alpha float f = na float f1 = nz(f[1], src) float f2 = nz(f[2], f1) float f3 = nz(f[3], f2) float f4 = nz(f[4], f3) f := switch poles 1 => alpha * src + x * f1 2 => math.pow(alpha, 2) * src + 2.0 * x * f1 - math.pow(x, 2) * f2 3 => math.pow(alpha, 3) * src + 3.0 * x * f1 - 3.0 * math.pow(x, 2) * f2 + math.pow(x, 3) * f3 => math.pow(alpha, 4) * src + 4.0 * x * f1 - 6.0 * math.pow(x, 2) * f2 + 4.0 * math.pow(x, 3) * f3 - math.pow(x, 4) * f4 f f_trailBand(float base, float atrv, float mult, float src) => float a = nz(atrv, high - low) float upRaw = base - mult * a float dnRaw = base + mult * a float upT = na float dnT = na int dir = 1 float upPrev = nz(upT[1], upRaw) float dnPrev = nz(dnT[1], dnRaw) upT := nz(src[1], src) > upPrev ? math.max(upRaw, upPrev) : upRaw dnT := nz(src[1], src) < dnPrev ? math.min(dnRaw, dnPrev) : dnRaw int prevDir = nz(dir[1], 1) dir := prevDir == -1 and src > dnPrev ? 1 : prevDir == 1 and src < upPrev ? -1 : prevDir float trail = dir == 1 ? upT : dnT [trail, dir] f_fisher(float src, int len) => float hh = ta.highest(src, len) float ll = ta.lowest(src, len) float rng = hh - ll float v = 0.0 v := 0.66 * (rng > EPS ? (src - ll) / rng - 0.5 : 0.0) + 0.67 * nz(v[1]) v := math.max(math.min(v, 0.999), -0.999) float fish = 0.0 fish := 0.5 * math.log((1.0 + v) / (1.0 - v)) + 0.5 * nz(fish[1]) fish f_lmsPredict(float src, simple int len, float mu) => var array w = array.new_float(len, 0.0) float pred = 0.0 float energy = EPS for i = 0 to len - 1 float xi = nz(src[i + 1]) pred += w.get(i) * xi energy += xi * xi float err = nz(src) - pred for i = 0 to len - 1 w.set(i, w.get(i) + mu * err * nz(src[i + 1]) / energy) float nxt = 0.0 for i = 0 to len - 1 nxt += w.get(i) * nz(src[i]) nxt f_quadReg(float src, simple int n) => float sx = 0.0 float sx2 = 0.0 float sx3 = 0.0 float sx4 = 0.0 float sy = 0.0 float sxy = 0.0 float sx2y = 0.0 for i = 0 to n - 1 float x = n - 1.0 - i float y = nz(src[i], src) sx += x sx2 += x * x sx3 += x * x * x sx4 += x * x * x * x sy += y sxy += x * y sx2y += x * x * y float det = n * (sx2 * sx4 - sx3 * sx3) - sx * (sx * sx4 - sx3 * sx2) + sx2 * (sx * sx3 - sx2 * sx2) float a = src float b = 0.0 float c = 0.0 if math.abs(det) > EPS a := (sy * (sx2 * sx4 - sx3 * sx3) - sx * (sxy * sx4 - sx3 * sx2y) + sx2 * (sxy * sx3 - sx2 * sx2y)) / det b := (n * (sxy * sx4 - sx3 * sx2y) - sy * (sx * sx4 - sx3 * sx2) + sx2 * (sx * sx2y - sxy * sx2)) / det c := (n * (sx2 * sx2y - sxy * sx3) - sx * (sx * sx2y - sxy * sx2) + sy * (sx * sx3 - sx2 * sx2)) / det float xn = n - 1.0 [a + b * xn + c * xn * xn, b + 2.0 * c * xn] f_trimean(float src, int len) => float q1 = ta.percentile_linear_interpolation(src, len, 25) float q2 = ta.median(src, len) float q3 = ta.percentile_linear_interpolation(src, len, 75) (q1 + 2.0 * q2 + q3) / 4.0 f_regimeFilter(float src, float threshold) => float value1 = 0.0 float value2 = 0.0 float klmf = 0.0 value1 := 0.2 * (src - nz(src[1], src)) + 0.8 * nz(value1[1]) value2 := 0.1 * (high - low) + 0.8 * nz(value2[1]) float omega = value2 != 0.0 ? math.abs(value1 / value2) : 0.0 float alpha = (-omega * omega + math.sqrt(math.pow(omega, 4) + 16.0 * math.pow(omega, 2))) / 8.0 klmf := alpha * src + (1.0 - alpha) * nz(klmf[1], src) float absSlope = math.abs(klmf - nz(klmf[1], klmf)) float avgSlope = nz(ta.ema(absSlope, 200)) float normDecline = avgSlope != 0.0 ? (absSlope - avgSlope) / avgSlope : 0.0 normDecline >= threshold f_knnPredict(array a1, array a2, array a3, array a4, array a5, array lbl, float q1, float q2, float q3, float q4, float q5, int k, int stride) => array dists = array.new_float() array votes = array.new_int() int n = lbl.size() if n > 0 for i = 0 to n - 1 by stride float d = math.log(1.0 + math.abs(a1.get(i) - q1)) + math.log(1.0 + math.abs(a2.get(i) - q2)) + math.log(1.0 + math.abs(a3.get(i) - q3)) + math.log(1.0 + math.abs(a4.get(i) - q4)) + math.log(1.0 + math.abs(a5.get(i) - q5)) int sz = dists.size() bool accept = sz < k if not accept accept := d < dists.get(sz - 1) if accept int pos = 0 while pos < sz if dists.get(pos) > d break pos += 1 dists.insert(pos, d) votes.insert(pos, lbl.get(i)) if dists.size() > k dists.pop() votes.pop() float s = 0.0 for v in votes s += v votes.size() > 0 ? s / votes.size() : 0.0 f_reached(int dir, float lvl) => dir == 1 ? high >= lvl : low <= lvl f_stopped(int dir, float lvl) => dir == 1 ? low <= lvl : high >= lvl f_updateZones(array zones, int maxBars) => for z in zones if z.active z.bx.set_right(bar_index) bool broken = z.dir == 1 ? close < z.bottom : close > z.top if broken or bar_index - z.startBar >= maxBars z.active := false zones.size() f_addZone(array zones, Zone z, int maxKeep) => zones.push(z) if zones.size() > maxKeep Zone old = zones.shift() old.bx.delete() zones.size() f_drawLevel(array lns, array