//@version=6 indicator("Konatus Bearing · Cross-market 0.3.1", "Konatus031", overlay = true, max_bars_back = 2000) // Copyright (c) 2026 Kontaus. Experimental kb-0.3; no established market edge. bool allowShort = input.bool(false, "Enable short entries", group = "00 · Entry direction", tooltip = "Long entries are enabled. Turn this on to also allow red short-entry triangles and short plans. Signals still require all setup and risk conditions. This control is in the cross-market 0.3.1 indicator; the old 0.2 baseline is long-only.") string profileInput = input.string("Auto", "Instrument profile", options = ["Auto", "Stocks", "Bonds", "Futures", "Forex", "Crypto", "Generic"], group = "01 · Market") string profile = profileInput == "Auto" ? (syminfo.type == "stock" or syminfo.type == "fund" ? "Stocks" : syminfo.type == "futures" ? "Futures" : syminfo.type == "forex" ? "Forex" : syminfo.type == "crypto" ? "Crypto" : "Generic") : profileInput string volumePolicy = input.string("Profile", "Participation policy", options = ["Profile", "Required", "Price only"], group = "01 · Market", tooltip = "Profile requires volume for Stocks/Futures/Crypto. Bonds/Forex/Generic use price only. Missing required volume blocks new plans.") string volumeMode = input.string("Prior EW", "Volume baseline", options = ["Prior EW", "Session slot"], group = "01 · Market", tooltip = "Prior EW uses 20 earlier volume observations. Session slot uses at least 5 earlier trading days at this exchange-local minute. Minute or hourly charts only; no current-bar volume in the baseline.") bool vwapGate = input.bool(false, "Require session VWAP alignment", group = "01 · Market", tooltip = "Requires a fully observed trading-day anchor and positive current volume. VWAP is context, not order flow.") int resetAt = input.time(0, "Reset model at contract/data boundary", group = "01 · Market", tooltip = "Optional timestamp. Resets analytical history once; never cancels an open illustrative stop. Does not construct or adjust a futures roll.") string setupMode = input.string("Breakout", "Setup", options = ["Breakout", "Pullback", "Either"], group = "02 · Signals") float decay = input.float(0.9, "Evidence memory", minval = 0.01, maxval = 0.99, step = 0.01, group = "02 · Signals") float persistenceMin = input.float(20, "Minimum directional evidence", minval = 0, maxval = 100, group = "02 · Signals") float extensionLimit = input.float(1.5, "Maximum entry extension · ATR", minval = 0.1, group = "02 · Signals") float efficiencyMin = input.float(0.25, "Minimum directional efficiency", minval = 0, maxval = 1, group = "02 · Signals") float shockAtr = input.float(4, "Shock threshold · prior ATR", minval = 0.1, group = "02 · Signals") int cooldown = input.int(3, "Shock cooldown · bars", minval = 0, maxval = 100, group = "02 · Signals") float account = input.float(100000, "Reference account", minval = 1, group = "03 · Risk") float riskPct = input.float(1, "Planned risk %", minval = 0, maxval = 100, group = "03 · Risk") float stopAtr = input.float(3, "Frozen distance · ATR", minval = 0.1, group = "03 · Risk") float conversion = input.float(1, "Account currency per quote currency", minval = 0.00000001, group = "03 · Risk", tooltip = "Explicit fixed conversion for the illustration, not an FX feed. Use 1 only when account and quote currency match. Recompute when conversion changes.") float pointOverride = input.float(0, "Point-value override · 0 = symbol", minval = 0, group = "03 · Risk") float pointValue = pointOverride > 0 ? pointOverride : syminfo.pointvalue float tick = syminfo.mintick float lot = input.float(1, "Quantity increment", minval = 0.00000001, group = "03 · Risk", tooltip = "Quantity uses symbol units/contracts, not broker-defined FX lots. Confirm the point value and increment for this instrument.") float margin = input.float(0, "Capacity per unit · account currency", minval = 0, group = "03 · Risk", tooltip = "Required for Futures and nonpositive-price plans. Enter a conservative per-contract allocation. It is not a live broker margin model. Zero uses cash notional for other positive-price instruments.") float capitalFraction = input.float(1, "Maximum capacity · account fraction", minval = 0, maxval = 1, group = "03 · Risk") float costBps = input.float(5, "One-way notional costs · bps", minval = 0, group = "03 · Risk") float feePerUnit = input.float(0, "One-way fee per unit · quote currency", minval = 0, group = "03 · Risk") int slipTicks = input.int(1, "Adverse slippage · ticks", minval = 0, maxval = 100, group = "03 · Risk") string theme = input.string("Auto", "Theme", options = ["Auto", "Dark", "Light"], group = "04 · Appearance") string density = input.string("Essential", "Dashboard", options = ["Essential", "Detailed", "Hidden"], group = "04 · Appearance") bool candleColors = input.bool(true, "Color confirmed candles", group = "04 · Appearance") bool showVwap = input.bool(false, "Show session VWAP", group = "04 · Appearance") if not chart.is_standard or timeframe.isticks runtime.error("Konatus requires standard time-based OHLC charts.") if na(pointValue) or na(tick) or math.sign(pointValue) <= 0 or math.sign(tick) <= 0 runtime.error("A positive tick and point value are required.") if volumeMode == "Session slot" and (not timeframe.isintraday or timeframe.in_seconds() < 60) runtime.error("Session-slot volume needs intraday time bars of at least one minute.") bool needsVolume = volumePolicy == "Required" or (volumePolicy == "Profile" and (profile == "Stocks" or profile == "Futures" or profile == "Crypto")) if vwapGate and not timeframe.isintraday runtime.error("Session VWAP gating requires intraday time bars. Disable it on daily and higher charts.") // Shared confirmed-bar analytical engine. Price units allow zero/negative prices. var float level = na var float drift = 0 var float p00 = 0 var float p01 = 0 var float p11 = 0 var float atr = 0 var float trSum = 0 var float previous = na var float evidence = 0 var int count = 0 var int direction = 0 var int runLength = 0 var int pause = 0 var array highs = array.new_float() var array lows = array.new_float() var array closes = array.new_float() var array slotMean = array.new_float(1440, 0) var array slotCount = array.new_int(1440, 0) var array slotAnchor = array.new_int(1440, na) var float volMean = 0 var int volCount = 0 var int anchor = na var bool anchorComplete = false var bool anchorValid = true var float pv = 0 var float vSum = 0 var float vwap = na var float relativeVolume = na var float efficiency = 0 var float extension = na var float surprise = na var float predictiveSd = na var float distance = 0 var float quantity = 0 var string volumeStatus = "WARMUP" var string condition = "WARMUP" var string entryStatus = "WARMUP" var string setupName = "NONE" var int breakoutSide = 0 var float breakoutLevel = na var bool ready = false int signal = 0 bool valid = not na(open) and not na(high) and not na(low) and not na(close) and (math.min(open, close) - low) / tick >= 0 and (high - math.max(open, close)) / tick >= 0 bool resetNow = resetAt > 0 and time >= resetAt and (na(time[1]) or time[1] < resetAt) if barstate.isconfirmed if not valid or resetNow level := na drift := 0 p00 := 0 p01 := 0 p11 := 0 atr := 0 trSum := 0 previous := na evidence := 0 count := 0 direction := 0 runLength := 0 pause := 0 array.clear(highs) array.clear(lows) array.clear(closes) array.fill(slotMean, 0) array.fill(slotCount, 0) array.fill(slotAnchor, na) volMean := 0 volCount := 0 anchor := na anchorComplete := false anchorValid := true pv := 0 vSum := 0 vwap := na breakoutSide := 0 ready := false quantity := 0 distance := 0 extension := na efficiency := 0 surprise := na predictiveSd := na relativeVolume := na volumeStatus := "UNAVAILABLE" condition := "INVALID DATA" entryStatus := "INVALID DATA" setupName := "NONE" if valid float priorLevel = level float tr = na(previous) ? high - low : math.max(high - low, math.abs(high - previous), math.abs(low - previous)) float scale = math.max(count >= 14 ? atr : count > 0 ? trSum / count : tr, tick) float variance = scale * scale float observation = 0.25 * variance if na(level) level := close p00 := observation p11 := observation surprise := 0 predictiveSd := math.sqrt(2 * observation) else float a = p00 + 2 * p01 + p11 + 0.01 * variance float b = p01 + p11 float d = p11 + 0.0001 * variance float predictive = a + observation float innovation = close - level - drift surprise := innovation / math.sqrt(predictive) predictiveSd := math.sqrt(predictive) float k0 = a / predictive float k1 = b / predictive float m = 1 - k0 level += drift + k0 * innovation drift += k1 * innovation p00 := m * m * a + k0 * k0 * observation p01 := m * (b - k1 * a) + k0 * k1 * observation p11 := d - 2 * k1 * b + k1 * k1 * a + k1 * k1 * observation count += 1 if count <= 14 trSum += tr atr := trSum / count else atr += (tr - atr) / 14 int priorDirection = direction direction := drift / tick > 0.000001 ? 1 : drift / tick < -0.000001 ? -1 : 0 runLength := direction != 0 and direction == priorDirection ? runLength + 1 : direction != 0 ? 1 : 0 evidence := decay * evidence + (1 - decay) * math.max(-1, math.min(1, drift / (0.1 * scale))) array.push(closes, close) if array.size(closes) > 21 array.shift(closes) float travel = 0 if array.size(closes) > 1 for j = 1 to array.size(closes) - 1 travel += math.abs(array.get(closes, j) - array.get(closes, j - 1)) efficiency := travel > 0 ? math.abs(close - array.get(closes, 0)) / travel : 0 extension := math.abs(close - level) / math.max(atr, tick) bool shock = not na(previous) and math.abs(close - previous) / scale > shockAtr pause := shock ? cooldown : math.max(0, pause - 1) ready := count >= 40 and atr / tick > 0.000001 condition := count < 40 ? "WARMUP" : not ready ? "FLAT" : shock or pause > 0 ? "SHOCK" : efficiency < efficiencyMin ? "CONGESTION" : "TREND" bool haveVolume = not na(volume) and volume >= 0 int slot = hour(time, syminfo.timezone) * 60 + minute(time, syminfo.timezone) int dayAnchor = time_tradingday float baseline = volumeMode == "Session slot" ? array.get(slotMean, slot) : volMean int samples = volumeMode == "Session slot" ? array.get(slotCount, slot) : volCount int lastAnchor = array.get(slotAnchor, slot) bool duplicateSlot = volumeMode == "Session slot" and not na(lastAnchor) and lastAnchor == dayAnchor bool enough = samples >= (volumeMode == "Session slot" ? 5 : 20) relativeVolume := haveVolume and enough and baseline > 0 and not duplicateSlot ? volume / baseline : na volumeStatus := not haveVolume ? "VOLUME MISSING" : duplicateSlot ? "SLOT REPEATED" : volume == 0 ? "ZERO VOLUME" : na(relativeVolume) ? "VOLUME WARMUP" : "AVAILABLE" if haveVolume and not duplicateSlot float updated = samples == 0 ? volume : baseline + (volume - baseline) * 2 / 21 if volumeMode == "Session slot" array.set(slotMean, slot, updated) array.set(slotCount, slot, samples + 1) array.set(slotAnchor, slot, dayAnchor) else volMean := updated volCount += 1 if not needsVolume volumeStatus := "PRICE ONLY" bool volumeOk = not needsVolume or (volumeStatus == "AVAILABLE" and relativeVolume >= 1) if na(anchor) or dayAnchor != anchor anchorComplete := not na(anchor) anchor := dayAnchor anchorValid := true pv := 0 vSum := 0 if not haveVolume anchorValid := false if haveVolume and volume > 0 pv += hlc3 * volume vSum += volume vwap := vSum > 0 ? pv / vSum : na bool contextOk = not vwapGate or (anchorValid and anchorComplete and not na(vwap) and haveVolume and volume > 0 and direction * (close - vwap) / tick > 0) int fire = 0 if not ready or shock or pause > 0 breakoutSide := 0 else if breakoutSide != 0 if breakoutSide * (close - breakoutLevel) / tick > 0 fire := breakoutSide breakoutSide := 0 else if array.size(highs) == 20 if (close - array.max(highs)) / tick > 0 breakoutSide := 1 breakoutLevel := array.max(highs) else if (array.min(lows) - close) / tick > 0 breakoutSide := -1 breakoutLevel := array.min(lows) int pull = not na(previous) and not na(priorLevel) and direction * (previous - priorLevel) / tick <= 0 and direction * (close - level) / tick > 0 ? direction : 0 int setupSide = setupMode == "Breakout" ? fire : setupMode == "Pullback" ? pull : fire != 0 ? fire : pull setupName := setupSide != 0 and setupSide == fire ? "BREAKOUT" : setupSide != 0 ? "PULLBACK" : "NONE" distance := math.ceil(stopAtr * atr / tick - 1e-9) * tick float slip = slipTicks * tick float fees = 2 * feePerUnit + (2 * math.abs(close) + distance + 2 * slip) * pointValue * costBps / 10000 float unitRisk = ((distance + slip + tick) * pointValue + fees) * conversion float capacity = profile == "Futures" or math.sign(close) <= 0 ? margin : margin > 0 ? margin : (math.abs(close) + slip) * pointValue * conversion float capacityWithFees = capacity + (feePerUnit + (math.abs(close) + slip) * pointValue * costBps / 10000) * conversion quantity := math.sign(capacity) > 0 and math.sign(distance) > 0 and math.sign(unitRisk) > 0 ? math.max(0, math.floor(math.min(account * riskPct / 100 / unitRisk, account * capitalFraction / capacityWithFees) / lot) * lot) : 0 entryStatus := count < 40 ? "WARMUP" : not ready ? "FLAT" : condition == "SHOCK" ? "SHOCK PAUSE" : condition == "CONGESTION" ? "CONGESTION" : direction == 0 or direction * evidence * 100 < persistenceMin ? "WEAK EVIDENCE" : direction < 0 and not allowShort ? "SHORT DISABLED" : not volumeOk and volumeStatus != "AVAILABLE" ? volumeStatus : not volumeOk ? "LOW PARTICIPATION" : not contextOk ? "VWAP CONTEXT" : extension > extensionLimit ? "EXTENDED" : quantity <= 0 ? "SIZE UNAVAILABLE" : setupSide == direction ? "READY" : "WAIT SETUP" signal := entryStatus == "READY" ? direction : 0 array.push(highs, high) array.push(lows, low) if array.size(highs) > 20 array.shift(highs) array.shift(lows) previous := close // Illustrative next-open ledger. Orders are not sent to a broker. var int heldSide = 0 var int pendingSide = 0 var float pendingQuantity = 0 var float pendingDistance = 0 var float heldQuantity = 0 var float entryPrice = na var float stopPrice = na var float heldDistance = na var int reversalCount = 0 var bool pendingExit = false var bool capacityBreached = false bool entryIntent = false bool exitIntent = false bool entered = false bool exited = false string exitReason = "" if barstate.isconfirmed and valid bool dueExit = pendingExit pendingExit := false float slip = slipTicks * tick if heldSide != 0 if heldSide * (open - stopPrice) / tick <= 0 exited := true exitReason := "GAP_STOP" else if dueExit exited := true exitReason := "REVERSAL" if heldSide == 0 and pendingSide != 0 heldSide := pendingSide heldQuantity := pendingQuantity heldDistance := pendingDistance entryPrice := open + heldSide * slip float rawStop = entryPrice - heldSide * heldDistance stopPrice := (heldSide > 0 ? math.floor(rawStop / tick + 1e-9) : math.ceil(rawStop / tick - 1e-9)) * tick float entryFee = heldQuantity * (feePerUnit + math.abs(entryPrice) * pointValue * costBps / 10000) * conversion float filledCapacity = margin > 0 ? margin : math.abs(entryPrice) * pointValue * conversion capacityBreached := filledCapacity * heldQuantity + entryFee > account * capitalFraction pendingSide := 0 entered := true if heldSide != 0 and not exited and (heldSide > 0 ? (stopPrice - low) / tick >= 0 : (high - stopPrice) / tick >= 0) exited := true exitReason := "STOP" if exited heldSide := 0 reversalCount := 0 if heldSide != 0 reversalCount := direction == -heldSide ? reversalCount + 1 : 0 if reversalCount >= 2 pendingExit := true exitIntent := true else if not exited and signal != 0 pendingSide := signal pendingQuantity := quantity pendingDistance := distance entryIntent := true // Alerts are decision events. Namespace separates configurations/alert instances. string alertNamespace = input.string("default", "Alert instance ID", group = "05 · Alerts", tooltip = "Use a distinct ID for each configuration. Allowed characters: letters, numbers, dash and underscore. Recreate TradingView alerts after settings change.") if str.length(alertNamespace) == 0 or str.match(alertNamespace, "^[A-Za-z0-9_-]+$") != alertNamespace runtime.error("Alert instance ID must contain only letters, numbers, dash or underscore.") if entryIntent or exitIntent string eventName = entryIntent ? "ENTRY_INTENT" : "EXIT_INTENT" int eventSide = entryIntent ? pendingSide : heldSide string eventId = "kb-0.3:" + alertNamespace + ":" + syminfo.tickerid + ":" + timeframe.period + ":" + str.tostring(time_close) + ":" + eventName + ":" + str.tostring(eventSide) string payload = '{"version":"kb-0.3-experimental","id":"' + eventId + '","event":"' + eventName + '","symbol":"' + syminfo.tickerid + '","timeframe":"' + timeframe.period + '","time_close":' + str.tostring(time_close) + ',"side":' + str.tostring(eventSide) + ',"quantity":' + str.tostring(entryIntent ? pendingQuantity : heldQuantity, "#.########") + ',"distance":' + str.tostring(entryIntent ? pendingDistance : heldDistance, "#.########") + ',"illustrative":true}' alert(payload, alert.freq_once_per_bar_close) bool dark = theme == "Dark" or (theme == "Auto" and color.r(chart.bg_color) + color.g(chart.bg_color) + color.b(chart.bg_color) < 384) color surface = dark ? #151B18 : #FCFCF8 color ink = dark ? #E8ECE6 : #202E25 color secondary = dark ? #A5B0A7 : #546559 color up = dark ? #A6CF9A : #37672C color down = dark ? #EDABA0 : #9E453A color amber = dark ? #E7C184 : #835B1C color edge = dark ? #344238 : #BDC7BB color bearingInk = not ready or math.abs(evidence * 100) < persistenceMin ? secondary : direction > 0 ? up : down plot(level, "Bearing price", color = bearingInk, linewidth = 2) plot(showVwap ? vwap : na, "Session VWAP", color = secondary) plot(heldSide != 0 ? entryPrice : na, "Illustrative entry", color = secondary, style = plot.style_linebr) plot(heldSide != 0 ? stopPrice : na, "Frozen protective stop", color = amber, linewidth = 2, style = plot.style_linebr) barcolor(candleColors and barstate.isconfirmed ? bearingInk : na) plotshape(entryIntent and pendingSide == 1, "Long intent", style = shape.triangleup, location = location.belowbar, color = color.green, size = size.tiny) plotshape(entryIntent and pendingSide == -1, "Short intent", style = shape.triangledown, location = location.abovebar, color = color.red, size = size.tiny) plotshape(exited, "Illustrative exit", style = shape.xcross, location = location.abovebar, color = amber, size = size.tiny) bool displayValid = barstate.isconfirmed ? valid : valid[1] string state = not displayValid ? "DATA UNAVAILABLE" : pendingExit ? "EXIT QUEUED" : heldSide != 0 ? (heldSide > 0 ? "LONG PLAN" : "SHORT PLAN") : pendingSide != 0 ? "ENTRY QUEUED" : entryStatus string reason = heldSide != 0 ? (not displayValid ? "Data invalid; last stop retained" : capacityBreached ? "Capacity exceeded at fill" : "Frozen stop active") : pendingSide != 0 ? "Illustrative next-open fill" : entryStatus == "EXTENDED" ? "Trend persists; wait for entry" : entryStatus == "SIZE UNAVAILABLE" ? "Check contract / margin / risk" : entryStatus == "SHOCK PAUSE" ? "New entries paused" : entryStatus == "WAIT SETUP" ? "Evidence ready; wait for setup" : "Experimental · closed-bar state" var table panel = table.new(position.top_right, 2, 15, bgcolor = surface, frame_color = edge, frame_width = 1) row(table t, int n, string label, string value, color valueInk, color bg, color labelInk) => table.cell(t, 0, n, " " + label + " ", bgcolor = bg, text_color = labelInk, text_halign = text.align_left, text_size = size.small) table.cell(t, 1, n, " " + value + " ", bgcolor = bg, text_color = valueInk, text_halign = text.align_right, text_size = size.small) if barstate.isfirst table.merge_cells(panel, 0, 1, 1, 1) table.merge_cells(panel, 0, 2, 1, 2) table.merge_cells(panel, 0, 14, 1, 14) if barstate.islast table.clear(panel, 0, 0, 1, 14) if density != "Hidden" or (heldSide != 0 and (capacityBreached or not displayValid)) row(panel, 0, "KONATUS BEARING", profile, ink, surface, secondary) table.cell(panel, 0, 1, " " + state + " ", text_color = heldSide != 0 and not pendingExit ? bearingInk : amber, bgcolor = surface, text_size = size.large, text_halign = text.align_left) table.cell(panel, 0, 2, " " + reason + " ", text_color = secondary, bgcolor = surface, text_size = size.small, text_halign = text.align_left) row(panel, 3, "DIRECTION", direction > 0 ? "Upward" : direction < 0 ? "Downward" : "Unclear", bearingInk, surface, secondary) row(panel, 4, "PERSISTENCE", str.tostring(evidence * 100, "0.0") + " / 100", ink, surface, secondary) row(panel, 5, "CONDITIONS", condition, ink, surface, secondary) row(panel, 6, "NEW ENTRY", entryStatus, ink, surface, secondary) row(panel, 7, "EXTENSION", na(extension) ? "—" : str.tostring(extension, "0.00") + " ATR", ink, surface, secondary) row(panel, 8, "PARTICIPATION", volumeStatus, secondary, surface, secondary) row(panel, 9, "FROZEN STOP", heldSide != 0 ? str.tostring(stopPrice, format.mintick) : "—", amber, surface, secondary) row(panel, 10, "ACTIVE / QUEUED QTY", str.tostring(heldSide != 0 ? heldQuantity : pendingSide != 0 ? pendingQuantity : 0, "#.########"), ink, surface, secondary) if density == "Detailed" row(panel, 11, "DRIFT / MODEL SD", str.tostring(drift, "#.########") + " / " + str.tostring(math.sqrt(math.max(p11, 0)), "#.########"), ink, surface, secondary) row(panel, 12, "RUN / EFFICIENCY", str.tostring(runLength) + " / " + str.tostring(efficiency, "0.00"), ink, surface, secondary) row(panel, 13, "QUOTE → ACCOUNT", syminfo.currency + " × " + str.tostring(conversion, "#.########"), secondary, surface, secondary) table.cell(panel, 0, 14, " kb-0.3 · experimental · illustrative ", text_color = secondary, bgcolor = surface, text_size = size.small) plot(evidence * 100, "Persistence", display = display.data_window) plot(direction, "Direction", display = display.data_window) plot(extension, "Extension ATR", display = display.data_window) plot(efficiency, "Efficiency", display = display.data_window) plot(atr, "ATR", display = display.data_window) plot(relativeVolume, "Prior relative volume", display = display.data_window) plot(surprise, "Standardized forecast innovation", display = display.data_window) plot(predictiveSd, "Prior predictive SD", display = display.data_window) plot(drift, "Price drift", display = display.data_window) plot(math.sqrt(math.max(p11, 0)), "Model drift SD", display = display.data_window) plot(signal, "Setup signal", display = display.data_window) plot(entryIntent ? pendingSide : 0, "Entry intent", display = display.data_window) plot(exitIntent ? 1 : 0, "Exit intent", display = display.data_window) plot(quantity, "Planned quantity", display = display.data_window) plot(distance, "Planned distance", display = display.data_window) plot(ready ? 1 : 0, "Ready", display = display.data_window) // Raw observation exports allow an actual chart-to-Python parity check. plot(bar_index, "Observation index", display = display.data_window) plot(open, "Input open", display = display.data_window) plot(high, "Input high", display = display.data_window) plot(low, "Input low", display = display.data_window) plot(close, "Input close", display = display.data_window) plot(volume, "Input volume", display = display.data_window) plot(time_tradingday, "Input anchor", display = display.data_window) plot(hour(time, syminfo.timezone) * 60 + minute(time, syminfo.timezone), "Input slot", display = display.data_window) plot(anchorComplete ? 1 : 0, "Input anchor complete", display = display.data_window) plot(resetNow ? 1 : 0, "Input reset", display = display.data_window) plot(heldSide, "Held side", display = display.data_window) plot(heldSide != 0 ? heldQuantity : 0, "Held quantity", display = display.data_window) plot(exited ? 1 : 0, "Illustrative exit flag", display = display.data_window) plot(exitReason == "STOP" ? 1 : exitReason == "GAP_STOP" ? 2 : exitReason == "REVERSAL" ? 3 : 0, "Illustrative exit code", display = display.data_window)