//@version=6 // KONATUS Flow 0.2.0 — Kutty Stauder. 5-minute multitrend research edition. // Native TradingView compilation, chart execution and alert delivery unverified. // A descriptive filter, not an order, protective stop or profitability claim. indicator("KONATUS Flow · 5m Multitrend 0.2.0", shorttitle="Flow 5m", overlay=true, max_bars_back=500, dynamic_requests=true) string quality = input.string("Balanced", "Filtering", options=["Balanced", "Strict"], tooltip="5-minute charts only. Both presets require closed 15m and 1h agreement. Strict raises strength/efficiency thresholds and reduces allowed extension. Settings are not performance-optimized.") int confirmation = input.int(2, "Confirmation closes", minval=1, maxval=5) bool paintBars = input.bool(true, "Color closed candles", group="Display") bool showStructure = input.bool(true, "Show structural line and ribbon", group="Display") bool showZone = input.bool(false, "Show quiet zone", group="Display") bool showMarks = input.bool(true, "Show new strong trends", group="Display") bool showPanel = input.bool(true, "Show explanation panel", group="Display") color strongUp = input.color(#21CE91, "Strong up", group="Candle colors") color weakUp = input.color(#689C91, "Weak up", group="Candle colors") color strongDown = input.color(#F05F6D, "Strong down", group="Candle colors") color weakDown = input.color(#AE858D, "Weak down", group="Candle colors") color conflictColor = input.color(#E9B85D, "Timeframe conflict", group="Candle colors") color pauseColor = input.color(#AE91E8, "Volatility pause", group="Candle colors") color neutralColor = input.color(#939DAA, "Neutral", group="Candle colors") f_valid() => not na(open) and not na(high) and not na(low) and not na(close) and high >= math.max(open, close) and low <= math.min(open, close) and high >= low f_range(bool valid, int count) => valid ? (count > 1 ? math.max(high - low, math.max(math.abs(high - close[1]), math.abs(low - close[1]))) : high - low) : na f_kama(int length, float fastLength, float slowLength, bool valid, int count) => float step = valid and count > 1 ? math.abs(close - close[1]) : 0.0 float path = math.sum(step, length) float er = count > length and path > 0 ? math.max(0.0, math.min(1.0, math.abs(close - close[length]) / path)) : 0.0 float alpha = math.pow(er * (2.0 / (fastLength + 1.0) - 2.0 / (slowLength + 1.0)) + 2.0 / (slowLength + 1.0), 2) var float average = na average := not valid ? na : na(average[1]) ? close : average[1] + alpha * (close - average[1]) [average, er] // Seed and reset explicitly so missing OHLC never carries an old strength estimate. f_wilder(float value, int length, int count) => float total = math.sum(value, length) var float result = na result := count < length or na(value) ? na : count == length ? total / length : (result[1] * (length - 1) + value) / length result f_context() => bool valid = f_valid() var int count = 0 count := valid ? count + 1 : 0 var float fast = na var float slow = na fast := not valid ? na : na(fast[1]) ? close : fast[1] + (2.0 / 22.0) * (close - fast[1]) slow := not valid ? na : na(slow[1]) ? close : slow[1] + (2.0 / 56.0) * (close - slow[1]) float scale = ta.sma(f_range(valid, count), 14) float slope = (fast - fast[3]) / 3.0 bool ready = count >= 90 and not na(scale) and not na(slope) int direction = not ready or scale <= 0 ? 0 : fast > slow and close > fast and slope > 0.02 * scale ? 1 : fast < slow and close < fast and slope < -0.02 * scale ? -1 : 0 // Every field is offset: lookahead_on must NEVER expose an unfinished HTF bar. [direction[1], ready[1], time_close[1]] f_direction(int direction) => direction == 1 ? "UP" : direction == -1 ? "DOWN" : "NEUTRAL" f_state(int state) => switch state 2 => "STRONG UP" 1 => "WEAK UP" -2 => "STRONG DOWN" -1 => "WEAK DOWN" 3 => "TIMEFRAME CONFLICT" 4 => "VOLATILITY PAUSE" => "NEUTRAL" f_color(int state) => switch state 2 => strongUp 1 => weakUp -2 => strongDown -1 => weakDown 3 => conflictColor 4 => pauseColor => neutralColor bool supported = chart.is_standard and timeframe.isminutes and timeframe.multiplier == 5 int direction15 = 0 int direction60 = 0 bool ready15 = false bool ready60 = false int closed15 = na int closed60 = na // Unsupported charts do not issue accidental lower-timeframe requests. if supported [d15, r15, t15] = request.security(syminfo.tickerid, "15", f_context(), gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_on) [d60, r60, t60] = request.security(syminfo.tickerid, "60", f_context(), gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_on) direction15 := d15 direction60 := d60 ready15 := r15 ready60 := r60 closed15 := t15 closed60 := t60 bool valid = f_valid() var int count = 0 count := valid ? count + 1 : 0 [adaptive, efficiency] = f_kama(10, 2.0, 30.0, valid, count) [structure, _] = f_kama(30, 5.0, 60.0, valid, count) float trueRange = f_range(valid, count) float scale = ta.sma(trueRange, 20) float fastSlope = (adaptive - adaptive[3]) / 3.0 float slowSlope = (structure - structure[5]) / 5.0 float halfZone = 0.15 * scale float upward = count > 1 ? high - high[1] : 0.0 float downward = count > 1 ? low[1] - low : 0.0 float plusDM = valid ? (upward > downward and upward > 0 ? upward : 0.0) : na float minusDM = valid ? (downward > upward and downward > 0 ? downward : 0.0) : na float smoothTR = f_wilder(trueRange, 14, count) float smoothPlus = f_wilder(plusDM, 14, count) float smoothMinus = f_wilder(minusDM, 14, count) float plusDI = smoothTR > 0 ? 100.0 * smoothPlus / smoothTR : 0.0 float minusDI = smoothTR > 0 ? 100.0 * smoothMinus / smoothTR : 0.0 float dx = plusDI + minusDI > 0 ? 100.0 * math.abs(plusDI - minusDI) / (plusDI + minusDI) : 0.0 float adx = f_wilder(count >= 14 ? dx : na, 14, math.max(0, count - 13)) bool localReady = count >= 120 and not na(adx) and not na(scale) and not na(slowSlope) int localDirection = not localReady or scale <= 0 ? 0 : adaptive > structure and close > adaptive + halfZone and fastSlope > 0.02 * scale and slowSlope > 0 ? 1 : adaptive < structure and close < adaptive - halfZone and fastSlope < -0.02 * scale and slowSlope < 0 ? -1 : 0 bool fresh15 = not na(closed15) and closed15 <= time and time - closed15 <= 30 * 60 * 1000 bool fresh60 = not na(closed60) and closed60 <= time and time - closed60 <= 120 * 60 * 1000 bool ready = supported and localReady and ready15 and ready60 and fresh15 and fresh60 bool shock = valid and count > 20 and scale[1] > 0 and trueRange > 3.0 * scale[1] var int cooldown = 0 cooldown := not valid ? 0 : shock ? 3 : math.max(0, cooldown - 1) bool paused = cooldown > 0 bool strict = quality == "Strict" float minimumER = strict ? 0.40 : 0.30 float minimumADX = strict ? 25.0 : 20.0 float minimumSpread = strict ? 8.0 : 5.0 float maxExtension = strict ? 1.0 : 1.5 float extension = scale > 0 ? math.abs(close - adaptive) / scale : 0.0 bool opposing = localDirection != 0 and (direction15 == -localDirection or direction60 == -localDirection) bool aligned = localDirection != 0 and direction15 == localDirection and direction60 == localDirection bool efficient = efficiency >= minimumER bool strength = adx >= minimumADX and localDirection * (plusDI - minusDI) >= minimumSpread bool stretched = extension > maxExtension int qualified = ready and not paused and aligned and efficient and strength and not stretched ? localDirection : 0 var int run = 0 run := qualified == 0 ? 0 : qualified == qualified[1] ? run + 1 : 1 int nextState = not ready ? 0 : paused ? 4 : localDirection == 0 ? 0 : opposing ? 3 : qualified != 0 and run >= confirmation ? 2 * localDirection : localDirection string nextReason = not supported ? "Use standard 5-minute candles" : not valid ? "Invalid OHLC: rebuilding history" : not localReady ? "Warming up: 120 valid 5m bars" : not ready15 or not ready60 ? "Warming up: 90 bars on 15m and 1h" : not fresh15 or not fresh60 ? "Waiting for fresh closed higher-timeframe bars" : paused ? "Range shock: " + str.tostring(cooldown) + " pause bars including this close" : localDirection == 0 ? "Local structure is mixed or inside quiet zone" : opposing ? "Higher timeframe opposes the 5m direction" : not aligned ? "Higher timeframe neutral: strong withheld" : not efficient ? "Price path too inefficient" : not strength ? "ADX / directional spread below threshold" : stretched ? "Too far from adaptive line" : run < confirmation ? "Confirming: " + str.tostring(run) + "/" + str.tostring(confirmation) + " closes" : "All filters aligned" // Publish one snapshot at each close. The open candle is intentionally not painted. var int state = 0 var string reason = "Waiting for first closed bar" var string snapshotDirections = "5m — / 15m — / 1h —" var string snapshotStrength = "ADX — / ER —" var string snapshotFilter = "Waiting for data" if barstate.isconfirmed state := nextState reason := nextReason snapshotDirections := "5m " + f_direction(localDirection) + " / 15m " + (ready15 and fresh15 ? f_direction(direction15) : "WAIT") + " / 1h " + (ready60 and fresh60 ? f_direction(direction60) : "WAIT") snapshotStrength := "ADX " + (localReady ? str.tostring(adx, "#.0") : "—") + " / ER " + (localReady ? str.tostring(efficiency, "#.00") : "—") snapshotFilter := quality + " · " + str.tostring(confirmation) + " closes · last closed bar" bool eligibleEvent = barstate.isconfirmed and ready and ready[1] bool strongUpEvent = eligibleEvent and state == 2 and state[1] != 2 bool strongDownEvent = eligibleEvent and state == -2 and state[1] != -2 bool lostEvent = eligibleEvent and math.abs(state[1]) == 2 and math.abs(state) != 2 bool conflictEvent = eligibleEvent and state == 3 and state[1] != 3 bool pauseEvent = eligibleEvent and state == 4 and state[1] != 4 bool changeEvent = eligibleEvent and state != state[1] bool shownLocalReady = supported and (barstate.isconfirmed ? localReady : localReady[1]) bool shownReady = supported and (barstate.isconfirmed ? ready : ready[1]) float shownAdaptive = shownLocalReady ? (barstate.isconfirmed ? adaptive : adaptive[1]) : na float shownStructure = shownLocalReady ? (barstate.isconfirmed ? structure : structure[1]) : na float shownZone = barstate.isconfirmed ? halfZone : halfZone[1] color stateColor = f_color(state) fastPlot = plot(shownAdaptive, "Adaptive trend", color=stateColor, linewidth=3, style=plot.style_linebr) slowPlot = plot(showStructure ? shownStructure : na, "Structural trend", color=color.new(#D9BD7F, 15), linewidth=1, style=plot.style_linebr) fill(fastPlot, slowPlot, color=showStructure ? color.new(stateColor, 93) : na, title="Trend structure ribbon", fillgaps=false) zoneUpper = plot(showZone ? shownAdaptive + shownZone : na, "Quiet zone upper", color=color.new(neutralColor, 80), style=plot.style_linebr) zoneLower = plot(showZone ? shownAdaptive - shownZone : na, "Quiet zone lower", color=color.new(neutralColor, 80), style=plot.style_linebr) fill(zoneUpper, zoneLower, color=color.new(neutralColor, 94), title="Quiet zone", fillgaps=false) barcolor(paintBars and ready and barstate.isconfirmed ? stateColor : na, title="Confirmed trend strength") plotshape(showMarks and strongUpEvent, title="New strong up", style=shape.triangleup, location=location.belowbar, color=strongUp, text="S+", textcolor=strongUp, size=size.tiny) plotshape(showMarks and strongDownEvent, title="New strong down", style=shape.triangledown, location=location.abovebar, color=strongDown, text="S-", textcolor=strongDown, size=size.tiny) var table panel = table.new(position.bottom_right, 1, 6, bgcolor=#11191D, frame_color=#404D4E, frame_width=1) if barstate.islast and showPanel table.cell(panel, 0, 0, "KONATUS FLOW / 5m · 0.2", text_color=#CFB98A, text_size=size.small) table.cell(panel, 0, 1, shownReady ? f_state(state) : "WAIT / DATA", text_color=stateColor, text_size=size.large) table.cell(panel, 0, 2, reason, text_color=#D5DEDF, text_size=size.small) table.cell(panel, 0, 3, snapshotDirections, text_color=#D5DEDF, text_size=size.small) table.cell(panel, 0, 4, snapshotStrength, text_color=#B4BEC0, text_size=size.small) table.cell(panel, 0, 5, snapshotFilter, text_color=#B4BEC0, text_size=size.tiny) alertcondition(strongUpEvent, "Flow: strong up", "Flow 0.2: new confirmed STRONG UP on {{ticker}} / 5m. Descriptive state, not an order.") alertcondition(strongDownEvent, "Flow: strong down", "Flow 0.2: new confirmed STRONG DOWN on {{ticker}} / 5m. Descriptive state, not an order.") alertcondition(lostEvent, "Flow: strong trend lost", "Flow 0.2: strong conditions no longer hold on {{ticker}} / 5m. Review the panel; not an exit order.") alertcondition(conflictEvent, "Flow: timeframe conflict", "Flow 0.2: 5m direction opposed by a closed higher-timeframe reading on {{ticker}}.") alertcondition(pauseEvent, "Flow: volatility pause", "Flow 0.2: confirmed volatility pause on {{ticker}} / 5m.") alertcondition(changeEvent, "Flow: any state change", "Flow 0.2: confirmed state change on {{ticker}} / 5m. Check the explanation panel.")