//@version=6 strategy("Trader - (FX/CFD) – V 3.6", overlay=true, calc_on_every_tick=false, process_orders_on_close=true, max_labels_count=500, max_lines_count=500, initial_capital=50000, default_qty_type=strategy.percent_of_equity, pyramiding=0, commission_type=strategy.commission.percent, commission_value=0.0) // -------------------------- GROUPS -------------------------- gPC = "------------------- PineConnector Alerts Configuration ----------------" gStyle = "------------------------- Trade Style — Control -----------------------" gEMA = "------MAIN • EMA (20/50/200)-------------------------------------------" gADX = "------MAIN • ADX + DI--------------------------------------------------" gST = "------MAIN • Supertrend------------------------------------------------" gRSI = "------MAIN • RSI-------------------------------------------------------" gMACD = "------MAIN • MACD------------------------------------------------------" gSTOCH = "------OPTIONAL • Stochastic--------------------------------------------" gVOL = "------OPTIONAL • Volume spike------------------------------------------" gROC = "------OPTIONAL • ROC Momentum------------------------------------------" gMETA = "------OPTIONAL • Trend / Momentum / Volatility-------------------------" gSess = "------Sessions (Optional)----------------------------------------------" gGates = "------Macro Gates (Optional)-------------------------------------------" gRisk = "------Risk Caps (Optional)---------------------------------------------" gMoneySL = "------Money Management — SL--------------------------------------------" gMoneyTP = "------Money Management — TP--------------------------------------------" gTPbt = "------Backtest / Strategy-only-----------------------------------------" gVisual = "------Visuals + Diagnostics--------------------------------------------" // ================================================================================================= // 1) PINECONNECTOR (FIRST) // ================================================================================================= pairOverride = input.string("", "Pair override (blank = chart)", group=gPC) licenseID = input.string("", "License ID", group=gPC) alertUnitsOpt = input.string("Pips", "Alert Units (sl=, tp=)", options=["Pips","Price"], group=gPC) sendAsPrice = alertUnitsOpt == "Price" // Pip-size override (fix JPY / Metals / Indices) pipOverride = input.float(0.0, "Pip size override (0 = auto)", step=0.00001, group=gPC) isJPY = str.contains(syminfo.ticker, "JPY") isXAU = str.contains(syminfo.ticker, "XAU") or str.contains(syminfo.ticker, "GOLD") isXAG = str.contains(syminfo.ticker, "XAG") or str.contains(syminfo.ticker, "SILVER") fxGuess = isJPY ? 0.01 : 0.0001 metal = isXAU ? 0.10 : (isXAG ? 0.01 : na) pipGuess = na(metal) ? fxGuess : metal pipSize = pipOverride > 0 ? pipOverride : pipGuess pipSize := na(pipSize) ? syminfo.mintick : pipSize // fallback // Risk fraction (EA side; strategy uses header % with qty=positionSizePct) riskPctInput = input.float(0.01, "Risk fraction (0.01 = 1% of equity)", step=0.005, group=gPC) // Optional fields incComment = input.bool(true, "Include comment= score? (<=20 chars)", group=gPC) incBreakeven = input.bool(true, "Include automatic breakeven params?", group=gPC) beTrigger = input.int(15, "betrigger (pips)", minval=1, group=gPC) beOffset = input.int(3, "beoffset (pips)", minval=0, group=gPC) incATRTrail = input.bool(false, "Include ATR trailing params?", group=gPC) atrTimeframeM = input.int(5, "atrtimeframe (minutes)", minval=1, group=gPC) atrPeriodPC = input.int(14, "atrperiod", minval=1, group=gPC) atrMultPC = input.float(2.0, "atrmultiplier", step=0.1, group=gPC) atrShiftPC = input.int(0, "atrshift", minval=0, group=gPC) atrTriggerPC = input.int(20, "atrtrigger (pips)", minval=0, group=gPC) amEnable = input.bool(true, "Enable automated management alerts?", group=gPC) amBE_on = input.bool(true, "Auto breakeven when profit ≥ %", group=gPC) amBE_trigger_pct = input.float(1.5, "Breakeven trigger %", step=0.1, group=gPC) amBE_offset_pips = input.int(0, "Breakeven offset (pips)", group=gPC) amPC_on = input.bool(true, "Partial close at targets? (uses EA %)", group=gPC) amPC_tp1 = input.bool(true, "Close at TP1", group=gPC) amPC_tp2 = input.bool(false, "Close at TP2", group=gPC) amPC_tp3 = input.bool(false, "Close at TP3", group=gPC) amTrail_on = input.bool(false, "Start ATR trailing when profit ≥ %", group=gPC) amTrail_trigger = input.float(2.0, "ATR trailing trigger %", step=0.1, group=gPC) amRaiseSL_on = input.bool(true, "Raise SL to prior TP as targets hit", group=gPC) // ON by default pineSymbol = pairOverride != "" ? pairOverride : syminfo.ticker // ================================================================================================= // 2) TRADE STYLE – CONTROL // ================================================================================================= controlMode = input.string("Preset", "Control Mode", options=["Preset","Manual"], group=gStyle) stylePreset = input.string("Scalper (EMA20)", "Trade Style", options=["Scalper (Naked)","Scalper (EMA20)","Swing","Position","Custom"], group=gStyle) tradeSide = input.string("both", "Trade Direction", options=["both","long","short"], group=gStyle) minPass = input.int(4, "Min # base filters required (TOTAL)", minval=1, maxval=10, group=gStyle) minPassMain = input.int(3, "Min # MAIN filters (EMA/ADX/ST/RSI/MACD)", minval=0, maxval=10, group=gStyle) minPassOpt = input.int(0, "Min # OPTIONAL filters (Stoch/Vol/ROC/Trend/Momentum)", minval=0, maxval=10, group=gStyle) enableScoreExits = input.bool(true, "Enable score-based exits?", group=gStyle) exitBuffer = input.int(1, "Exit buffer (score exits)", minval=0, maxval=3, group=gStyle) shortMomentumGuard = input.bool(true, "Short momentum guard (extra)", group=gRSI) // ================================================================================================= // 3) MAIN FILTERS (Core) // ================================================================================================= // EMA useEMA = input.bool(true, "Enable EMA (master)", group=gEMA) useEMA20 = input.bool(true, "Use EMA 20", group=gEMA) useEMA50 = input.bool(true, "Use EMA 50", group=gEMA) useEMA200 = input.bool(true, "Use EMA 200", group=gEMA) ema20 = ta.ema(close, 20) ema50 = ta.ema(close, 50) ema200 = ta.ema(close, 200) emaTrendLong = ema20 > ema50 and ema50 > ema200 emaTrendShort = ema20 < ema50 and ema50 < ema200 priceAboveEMAs = close > ema20 and close > ema50 priceBelowEMAs = close < ema20 and close < ema50 emaOKlong_orig = (emaTrendLong and priceAboveEMAs) or (close > ema50 and ema20 > ema200 and close[1] <= ema50[1]) emaOKshort_orig = (emaTrendShort and priceBelowEMAs) or (close < ema50 and ema20 < ema200 and close[1] >= ema50[1]) ema20Roc = ta.roc(ema20, 5) ema50Roc = ta.roc(ema50, 5) trendConsistencyLong = ema20Roc > 0 and ema50Roc > 0 trendConsistencyShort = ema20Roc < 0 and ema50Roc < 0 // Bar strength bodySize = math.abs(close - open) avgBody = ta.sma(bodySize, 14) strongBullishBar = close > open and bodySize > avgBody and close > (high + low) / 2 strongBearishBar = close < open and bodySize > avgBody and close < (high + low) / 2 // ADX + DI (manual) useADX = input.bool(true, "Enable?", group=gADX) adxLen = input.int(14, "ADX Length", minval=2, group=gADX) adxThr = input.float(25.0, "ADX Threshold", step=0.5, group=gADX) up = ta.change(high) down = -ta.change(low) plusDM = (up > down and up > 0) ? up : 0.0 minusDM = (down > up and down > 0) ? down : 0.0 tr = math.max(high - low, math.max(math.abs(high - close[1]), math.abs(low - close[1]))) rmaTr = ta.rma(tr, adxLen) rmaPlusDM = ta.rma(plusDM, adxLen) rmaMinusDM = ta.rma(minusDM, adxLen) plusDI = (rmaTr == 0) ? 0.0 : 100 * rmaPlusDM / rmaTr minusDI = (rmaTr == 0) ? 0.0 : 100 * rmaMinusDM / rmaTr dx = (plusDI + minusDI == 0) ? 0.0 : 100 * math.abs(plusDI - minusDI) / (plusDI + minusDI) adx = ta.rma(dx, adxLen) adxThrShort = adxThr * 1.10 adxOKlong = (adx > adxThr) and (plusDI > minusDI) and (plusDI > plusDI[1]) and (minusDI < minusDI[1]) adxOKshort = (adx > adxThrShort) and (minusDI > plusDI) and (minusDI > minusDI[1]) and (plusDI < plusDI[1]) // Supertrend (proxy) useST = input.bool(true, "Enable?", group=gST) stMult = input.float(2.5, "ATR Multiplier", step=0.1, group=gST) stLen = input.int(14, "ATR Length", minval=1, group=gST) atrST = ta.atr(stLen) upperBand = hl2 + stMult * atrST lowerBand = hl2 - stMult * atrST stOKlong = (close > lowerBand) and (low > lowerBand) and (low[1] > lowerBand[1]) stOKshort = (close < upperBand) and (high < upperBand) and (high[1] < upperBand[1]) // RSI useRSI = input.bool(true, "Enable?", group=gRSI) rsiLen = input.int(14, "RSI Length", group=gRSI) rsiOB = input.int(70, "RSI Overbought", minval=50, maxval=95, group=gRSI) rsiOS = input.int(30, "RSI Oversold", minval=5, maxval=50, group=gRSI) rsiVal = ta.rsi(close, rsiLen) rsiEma = ta.ema(rsiVal, 5) rsiOKlong = (rsiVal > rsiOS) and (rsiVal < rsiOB - 5) and (rsiVal > rsiVal[1]) and (rsiVal > rsiEma) rsiOKshort = (rsiVal < rsiOB) and (rsiVal > rsiOS + 5) and (rsiVal < rsiVal[1]) and (rsiVal < rsiEma) // MACD useMACD = input.bool(false, "Enable?", group=gMACD) fast = input.int(12, "Fast", group=gMACD) slow = input.int(26, "Slow", group=gMACD) sig = input.int(9, "Signal", group=gMACD) [macdLine, macdSig, macdHist] = ta.macd(close, fast, slow, sig) macdOKlong = (macdLine > macdSig) and (macdLine[1] <= macdSig[1]) and (macdHist > macdHist[1]) macdOKshort = (macdLine < macdSig) and (macdLine[1] >= macdSig[1]) and (macdHist < macdHist[1]) // ================================================================================================= // 4) OPTIONAL FILTERS // ================================================================================================= useSTOCH = input.bool(false, "Enable?", group=gSTOCH) kLen = input.int(14, "%K Length", group=gSTOCH) kSm = input.int(1, "%K Smoothing", group=gSTOCH) dSm = input.int(3, "%D Smoothing", group=gSTOCH) stochOB = input.int(80, "OB", group=gSTOCH) stochOS = input.int(20, "OS", group=gSTOCH) kRaw = ta.stoch(high, low, close, kLen) stochK = ta.sma(kRaw, kSm) stochD = ta.sma(stochK, dSm) stochOKlong = (stochK < stochOS) and (stochD < stochOS) and (stochK > stochD) and (stochK[1] <= stochD[1]) stochOKshort = (stochK > stochOB) and (stochD > stochOB) and (stochK < stochD) and (stochK[1] >= stochD[1]) useVOL = input.bool(false, "Enable?", group=gVOL) volLen = input.int(20, "SMA Length", minval=1, group=gVOL) volMult = input.float(2.0, "Spike x SMA", step=0.1, group=gVOL) volSMA = ta.sma(volume, volLen) volOK = (volume > volSMA * volMult) and (volume[1] <= volSMA[1] * volMult) useROC = input.bool(true, "Enable?", group=gROC) rocLength = input.int(12, "ROC Length", minval=1, group=gROC) rocThresh = input.float(1.5, "Min |ROC| %", step=0.1, group=gROC) rocValue = ta.roc(close, rocLength) rocMA = ta.ema(rocValue, 5) rocOKlong = (rocValue >= rocThresh) and (rocValue > rocMA) and (rocValue[1] < rocThresh) rocOKshort = (rocValue <= -rocThresh) and (rocValue < rocMA) and (rocValue[1] > -rocThresh) useTrendFilter = input.bool(true, "Use Trend Strength bonus", group=gMETA) useVolatilityFilter = input.bool(true, "Use Enhanced Volatility", group=gMETA) useMomentumFilter = input.bool(true, "Use Momentum Confirmation", group=gMETA) minAtrPctExtra = input.float(0.10, "Min ATR% for entries (when filter ON)", step=0.01, group=gMETA) useRisingATR = input.bool(true, "Require rising ATR? (when filter ON)", group=gMETA) // ATRs & momentum atr14 = ta.atr(14) atr5 = ta.atr(5) emaTrendStrength = math.abs(ema20 - ema50) / close * 100 strongTrend = emaTrendStrength > 0.15 atrPercent = atr14 / close * 100 atrRising = atr5 > atr14 // Base (FX/CFD) baseMinAtrPct = input.float(0.08, "Base Min ATR% when volatility filter is OFF (FX default)", step=0.01, group=gMETA) baseVolatilityOK = atrPercent > baseMinAtrPct enhancedVolatilityOK = (atrPercent > minAtrPctExtra) and (useRisingATR ? atrRising : true) volatilityOK = useVolatilityFilter ? enhancedVolatilityOK : baseVolatilityOK // Momentum confirm momentumROC = ta.roc(close, 5) momentumOKlong = momentumROC > 0.1 momentumOKshort = momentumROC < -0.1 useSpikeSkip = input.bool(true, "Skip spike candles? (range > ATR x)", group=gMETA) spikeATRx = input.float(2.0, "Spike threshold: ATR x", step=0.1, group=gMETA) spikeBar = (high - low) > spikeATRx * atr14 // ---------------- CRYPTO-ONLY ENTRY RELAX (BTC) isCrypto = str.contains(str.lower(syminfo.ticker), "btc") or str.contains(str.lower(syminfo.root), "btc") autoRelaxCrypto = input.bool(false, "Crypto-only: relax entry gates (BTC)", group=gMETA) baseMinAtrPctCrypto = input.float(0.00, "Crypto-only: Base Min ATR% when filter OFF", step=0.01, group=gMETA) minAtrPctExtraCrypto = input.float(0.03, "Crypto-only: Min ATR% when filter ON", step=0.01, group=gMETA) cryptoMinPassOffset = input.int(0, "Crypto-only: MinPass offset (-3..+3)", minval=-3, maxval=3, group=gMETA) cryptoBonusScore = input.int(0, "Crypto-only: bonus score points (0..3)", minval=0, maxval=3, group=gMETA) // Bar/Vol gates barGateLongBase = (trendConsistencyLong or strongBullishBar) barGateShortBase = (trendConsistencyShort or strongBearishBar) var bool barGateLongOK = true var bool barGateShortOK = true var bool volOK_entry = true volOK_entry := volatilityOK barGateLongOK := barGateLongBase barGateShortOK:= barGateShortBase if isCrypto and autoRelaxCrypto volOK_entry := useVolatilityFilter ? (atrPercent > minAtrPctExtraCrypto) : (atrPercent > baseMinAtrPctCrypto) barGateLongOK := true barGateShortOK:= true // -------------------------- SESSIONS (Optional) enableSessionFilter = input.bool(true, "Enable session filter?", group=gSess) useLondon = input.bool(true, "Include London (08:00–17:00)", group=gSess) useNY = input.bool(true, "Include New York (13:00–22:00)", group=gSess) useAsia = input.bool(false, "Include Asia/Tokyo (00:00–09:00)", group=gSess) inSess(s) => not na(time(timeframe.period, s)) inLondon = inSess("0800-1700") inNY = inSess("1300-2200") inAsia = inSess("0000-0900") activeSession = (useLondon and inLondon) or (useNY and inNY) or (useAsia and inAsia) // -------------------------- MANUAL ENGINE (split MAIN vs OPTIONAL) longMain = 0 shortMain = 0 longOpt = 0 shortOpt = 0 // MAIN if useEMA longMain += emaOKlong_orig ? 1 : 0 shortMain += emaOKshort_orig ? 1 : 0 if useADX longMain += adxOKlong ? 1 : 0 shortMain += adxOKshort ? 1 : 0 if useST longMain += stOKlong ? 1 : 0 shortMain += stOKshort ? 1 : 0 if useRSI longMain += rsiOKlong ? 1 : 0 shortMain += rsiOKshort ? 1 : 0 if useMACD longMain += macdOKlong ? 1 : 0 shortMain += macdOKshort ? 1 : 0 // OPTIONAL if useSTOCH longOpt += stochOKlong ? 1 : 0 shortOpt += stochOKshort ? 1 : 0 if useVOL longOpt += volOK ? 1 : 0 shortOpt += volOK ? 1 : 0 if useROC longOpt += rocOKlong ? 1 : 0 shortOpt += rocOKshort ? 1 : 0 if useTrendFilter longOpt += strongTrend ? 1 : 0 shortOpt += strongTrend ? 1 : 0 if useMomentumFilter longOpt += momentumOKlong ? 1 : 0 shortOpt += momentumOKshort ? 1 : 0 finalLongScore_manual = longMain + longOpt finalShortScore_manual = shortMain + shortOpt enhancedShortSignal = (rsiVal < rsiOB) and (rsiVal > rsiOS) and (rsiVal < rsiVal[1]) // -------------------------- PRESET ENGINE crossUp50 = ta.crossover(close, ema50) crossDn50 = ta.crossunder(close, ema50) crossUp20 = ta.crossover(close, ema20) crossDn20 = ta.crossunder(close, ema20) emaOKLong_custom(_u20, _u50, _u200) => allOn = _u20 and _u50 and _u200 only2050 = _u20 and _u50 and (not _u200) only50200 = (not _u20) and _u50 and _u200 only20 = _u20 and (not _u50) and (not _u200) only50 = (not _u20) and _u50 and (not _u200) only200 = (not _u20) and (not _u50) and _u200 r = true if allOn r := emaOKlong_orig else if only2050 r := (ema20 > ema50 and close > ema20 and close > ema50) or crossUp50 else if only50200 r := ema50 > ema200 and close > ema50 else if only50 r := (close > ema50) or crossUp50 else if only20 r := (close > ema20) or crossUp20 else if only200 r := (close > ema200) else r := true r emaOKShort_custom(_u20, _u50, _u200) => allOn = _u20 and _u50 and _u200 only2050 = _u20 and _u50 and (not _u200) only50200 = (not _u20) and _u50 and _u200 only20 = _u20 and (not _u50) and (not _u200) only50 = (not _u20) and _u50 and (not _u200) only200 = (not _u20) and (not _u50) and _u200 r = true if allOn r := emaOKshort_orig else if only2050 r := (ema20 < ema50 and close < ema20 and close < ema50) or crossDn50 else if only50200 r := ema50 < ema200 and close < ema50 else if only50 r := (close < ema50) or crossDn50 else if only20 r := (close < ema20) or crossDn20 else if only200 r := (close < ema200) else r := true r usePresetEngine = (controlMode == "Preset" and stylePreset != "Custom") longMain_p = 0 shortMain_p = 0 longOpt_p = 0 shortOpt_p = 0 // ===================== PRESET ENGINE (revised EMA mapping) ===================== if usePresetEngine // EMA usage per preset: // - Scalper (Naked): 20 only // - Scalper (EMA20): 20 + 50 // - Swing / Position: 20 + 50 + 200 effUseEMA = true eff20 = true eff50 = (stylePreset == "Scalper (EMA20)") or (stylePreset == "Swing") or (stylePreset == "Position") eff200 = (stylePreset == "Swing") or (stylePreset == "Position") // Count EMA as MAIN according to the chosen combination longMain_p += emaOKLong_custom(eff20, eff50, eff200) ? 1 : 0 shortMain_p += emaOKShort_custom(eff20, eff50, eff200) ? 1 : 0 // Other MAINs in presets (unchanged) longMain_p += adxOKlong ? 1 : 0 shortMain_p += adxOKshort ? 1 : 0 // Supertrend only for Swing/Position (unchanged) effST = (stylePreset == "Swing") or (stylePreset == "Position") if effST longMain_p += stOKlong ? 1 : 0 shortMain_p += stOKshort ? 1 : 0 // RSI always part of MAIN in presets (unchanged) longMain_p += rsiOKlong ? 1 : 0 shortMain_p += rsiOKshort ? 1 : 0 // OPTIONAL / GLOBAL (leave as-is) if useSTOCH longOpt_p += stochOKlong ? 1 : 0 shortOpt_p += stochOKshort ? 1 : 0 if useMACD longMain_p += macdOKlong ? 1 : 0 shortMain_p += macdOKshort ? 1 : 0 if useVOL longOpt_p += volOK ? 1 : 0 shortOpt_p += volOK ? 1 : 0 if useROC longOpt_p += rocOKlong ? 1 : 0 shortOpt_p += rocOKshort ? 1 : 0 if useTrendFilter longOpt_p += strongTrend ? 1 : 0 shortOpt_p += strongTrend ? 1 : 0 if useMomentumFilter longOpt_p += momentumOKlong ? 1 : 0 shortOpt_p += momentumOKshort ? 1 : 0 finalLongScore_preset = (longMain_p + longOpt_p) finalShortScore_preset = (shortMain_p + shortOpt_p) // ===================== END PRESET ENGINE (revised) ===================== // -------------------------- MACRO GATES (Optional) macroGateMode = input.string("Off", "Macro Gate Mode", options=["Off","Bonus","Filter"], group=gGates) gateTFopt = input.string("1h", "Gate TF", options=["Chart","1m","5m","15m","1h","4h","1d","1w"], group=gGates) tfGate = gateTFopt=="Chart" ? timeframe.period : gateTFopt=="1m" ? "1" : gateTFopt=="5m" ? "5" : gateTFopt=="15m" ? "15" : gateTFopt=="1h" ? "60" : gateTFopt=="4h" ? "240" : gateTFopt=="1d" ? "D" : "W" gateMALen = input.int(50, "Gate MA length", minval=1, group=gGates) secCloseEma(sym, _tf, _len) => [c, e] = request.security(sym, _tf, [close, ta.ema(close, _len)], lookahead=barmerge.lookahead_off) [c, e] useDollarGates = input.bool(false, "Use Dollar Gates (DXY & US02Y)?", group=gGates) dxySym = input.string("TVC:DXY", "DXY symbol", group=gGates) us2ySym = input.string("TVC:US02Y","US02Y symbol",group=gGates) var float dxy = na var float dxyMA = na var float us2y = na var float us2yMA= na if useDollarGates [dxy_raw, dxyMA_raw] = secCloseEma(dxySym, tfGate, gateMALen) [us2y_raw, us2yMA_raw] = secCloseEma(us2ySym, tfGate, gateMALen) dxy := dxy_raw[1] us2y := us2y_raw[1] dxyMA := dxyMA_raw[1] us2yMA:= us2yMA_raw[1] dollarLongOK = (not useDollarGates) or (not na(dxy) and not na(dxyMA) and not na(us2y) and not na(us2yMA) and dxy < dxyMA and us2y < us2yMA) dollarShortOK = (not useDollarGates) or (not na(dxy) and not na(dxyMA) and not na(us2y) and not na(us2yMA) and dxy > dxyMA and us2y > us2yMA) useMainGates = input.bool(false, "Use Currency Index Gates?", group=gGates) incEXI = input.bool(true, "Include EXI (Euro/EXY)?", group=gGates) incJXI = input.bool(true, "Include JXI (Yen/JXY)?", group=gGates) incGXI = input.bool(true, "Include GXI (BXY)?", group=gGates) exiSym = input.string("TVC:EXY", "EXI symbol", group=gGates) jxiSym = input.string("TVC:JXY", "JXI symbol", group=gGates) gxiSym = input.string("TVC:BXY", "GXI symbol", group=gGates) mainAllMustAgree = input.bool(true, "Main Gates: require ALL selected to agree?", group=gGates) var float exi = na var float exiMA = na var float jxi = na var float jxiMA = na var float gxi = na var float gxiMA = na if useMainGates and incEXI [exi_raw, exiMA_raw] = secCloseEma(exiSym, tfGate, gateMALen) exi := exi_raw[1] exiMA := exiMA_raw[1] if useMainGates and incJXI [jxi_raw, jxiMA_raw] = secCloseEma(jxiSym, tfGate, gateMALen) jxi := jxi_raw[1] jxiMA := jxiMA_raw[1] if useMainGates and incGXI [gxi_raw, gxiMA_raw] = secCloseEma(gxiSym, tfGate, gateMALen) gxi := gxi_raw[1] gxiMA := gxiMA_raw[1] selEXI = useMainGates and incEXI selJXI = useMainGates and incJXI selGXI = useMainGates and incGXI selectedCount = (selEXI?1:0) + (selJXI?1:0) + (selGXI?1:0) exiHas = selEXI and not na(exi) and not na(exiMA) jxiHas = selJXI and not na(jxi) and not na(jxiMA) gxiHas = selGXI and not na(gxi) and not na(gxiMA) exiPassL = selEXI ? (exiHas and exi > exiMA) : true jxiPassL = selJXI ? (jxiHas and jxi > jxiMA) : true gxiPassL = selGXI ? (gxiHas and gxi > gxiMA) : true exiPassS = selEXI ? (exiHas and exi < exiMA) : true jxiPassS = selJXI ? (jxiHas and jxi < jxiMA) : true gxiPassS = selGXI ? (gxiHas and gxi < gxiMA) : true anyLong = (selEXI and exiHas and exi > exiMA) or (selJXI and jxiHas and jxi > jxiMA) or (selGXI and gxiHas and gxi > gxiMA) anyShort = (selEXI and exiHas and exi < exiMA) or (selJXI and jxiHas and jxi < jxiMA) or (selGXI and gxiHas and gxi < gxiMA) mainLongOK = (not useMainGates) or (selectedCount==0) or (mainAllMustAgree ? (exiPassL and jxiPassL and gxiPassL) : anyLong) mainShortOK = (not useMainGates) or (selectedCount==0) or (mainAllMustAgree ? (exiPassS and jxiPassS and gxiPassS) : anyShort) macroLongOK = dollarLongOK and mainLongOK macroShortOK = dollarShortOK and mainShortOK macroBonusLong = ((useDollarGates and dollarLongOK) ? 1 : 0) + ((useMainGates and mainLongOK) ? 1 : 0) macroBonusShort = ((useDollarGates and dollarShortOK) ? 1 : 0) + ((useMainGates and mainShortOK) ? 1 : 0) cryptoIgnoreMacroWhenRelaxed = input.bool(false, "Crypto-only: ignore Macro Gates while relaxed", group=gGates) // -------------------------- ENGINE SELECT + GATES/SESSIONS/SPIKES baseLongMain = usePresetEngine ? longMain_p : longMain baseShortMain = usePresetEngine ? shortMain_p : shortMain baseLongOpt = usePresetEngine ? longOpt_p : longOpt baseShortOpt = usePresetEngine ? shortOpt_p : shortOpt baseLongScore = baseLongMain + baseLongOpt baseShortScore = baseShortMain + baseShortOpt minPassUsed = (isCrypto and autoRelaxCrypto) ? math.max(1, minPass + cryptoMinPassOffset) : minPass finalLongScore = baseLongScore + (macroGateMode == "Bonus" ? macroBonusLong : 0) + ((isCrypto and autoRelaxCrypto) ? cryptoBonusScore : 0) finalShortScore = baseShortScore + (macroGateMode == "Bonus" ? macroBonusShort : 0) + ((isCrypto and autoRelaxCrypto) ? cryptoBonusScore : 0) sessOK = (not enableSessionFilter) or activeSession spikeOK = (not useSpikeSkip) or (not spikeBar) macroFilterLongOK = (macroGateMode != "Filter" or macroLongOK) macroFilterShortOK = (macroGateMode != "Filter" or macroShortOK) if isCrypto and autoRelaxCrypto and cryptoIgnoreMacroWhenRelaxed macroFilterLongOK := true macroFilterShortOK := true // STRICT: per-bucket minima effMinPassMain = math.min(minPassMain, minPassUsed) effMinPassOpt = math.min(minPassOpt, minPassUsed) mainOKLong = baseLongMain >= effMinPassMain mainOKShort = baseShortMain >= effMinPassMain optOKLong = baseLongOpt >= effMinPassOpt optOKShort = baseShortOpt >= effMinPassOpt validLong = (tradeSide != "short") and mainOKLong and optOKLong and (finalLongScore >= minPassUsed) and volOK_entry and barGateLongOK and macroFilterLongOK and sessOK and spikeOK validShort = (tradeSide != "long") and mainOKShort and optOKShort and (finalShortScore >= minPassUsed) and volOK_entry and barGateShortOK and (not shortMomentumGuard or enhancedShortSignal) and macroFilterShortOK and sessOK and spikeOK // -------------------------- RISK CAPS (Optional) enableRiskHalt = input.bool(true, "Enable Drawdown Halt (peak %)?", group=gRisk) maxDDpct = input.float(8.0, "Max Equity Drawdown % (halt)", step=0.5, group=gRisk) enableDailyCap = input.bool(true, "Enable Daily Loss Cap (%)?", group=gRisk) maxDailyLossPct = input.float(2.0, "Daily Loss Cap %", step=0.1, group=gRisk) enableConsecCap = input.bool(true, "Enable Consecutive Loss Cap?", group=gRisk) maxConsecLoss = input.int(2, "Max consecutive losing trades", minval=1, group=gRisk) streakResetMode = input.string("Daily", "Consecutive Loss Reset Mode", options=["Daily","Never","Session"], group=gRisk) var float peakEq = strategy.initial_capital curEq = strategy.initial_capital + strategy.netprofit peakEq := math.max(peakEq, curEq) ddPct = peakEq > 0 ? (peakEq - curEq) / peakEq * 100.0 : 0.0 riskHalt = enableRiskHalt and (ddPct >= maxDDpct) var float dayStartEq = na newDay = ta.change(time("D")) != 0 if newDay dayStartEq := curEq dayStartEq := na(dayStartEq) ? curEq : dayStartEq dayLossPct = dayStartEq > 0 ? math.max(0.0, (dayStartEq - curEq) / dayStartEq * 100.0) : 0.0 dailyHalt = enableDailyCap and (dayLossPct >= maxDailyLossPct) var int consecLoss = 0 if (streakResetMode == "Daily" and newDay) or (streakResetMode == "Session" and ta.change(activeSession) and activeSession) consecLoss := 0 closedNow = strategy.closedtrades > strategy.closedtrades[1] var bool lossJustTriggered = false lossJustTriggered := false if closedNow lastProfit = nz(strategy.closedtrades.profit(strategy.closedtrades - 1), 0.0) if (lastProfit < 0) consecLoss += 1 lossJustTriggered := true else if (lastProfit > 0) consecLoss := 0 consecHalt = enableConsecCap and (consecLoss >= maxConsecLoss) canTrade = not (riskHalt or dailyHalt or consecHalt) // ================================================================================================= // 5) MONEY MANAGEMENT // ================================================================================================= slMode = input.string("ATRx", "SL mode", options=["Percent","ATRx"], group=gMoneySL) slPct = input.float(1.0, "SL % (if Percent)", step=0.1, group=gMoneySL) slATRm = input.float(2.0, "SL ATRx", step=0.1, group=gMoneySL) atrSLp = input.int(14, "ATR period (SL)", minval=1, group=gMoneySL) alertMinATRx = input.float(1.00, "Min SL = ATRx", step=0.05, group=gMoneySL) alertMaxATRx = input.float(3.00, "Max SL = ATRx", step=0.05, group=gMoneySL) tpVsSLratio = input.float(1.25, "TP : SL ratio", step=0.05, group=gMoneyTP) // ================================================================================================= // 6) BACKTEST / STRATEGY-ONLY // ================================================================================================= isStrategy = input.bool(true, "Run as Strategy? (strategy-only)", group=gTPbt) useVolAdjSize = input.bool(true, "Volatility-adjust position size? (strategy-only)", group=gTPbt) volAdjRefAtrPct = input.float(0.50, "VolSize: target ATR% (≈0.50)", step=0.05, group=gTPbt) volAdjMin = input.float(0.50, "VolSize: min scale", step=0.05, group=gTPbt) volAdjMax = input.float(1.50, "VolSize: max scale", step=0.05, group=gTPbt) btEnableTrail = input.bool(true, "Backtest: enable trailing stop?", group=gTPbt) btTrailATRx = input.float(0.60, "Backtest: trail offset ATRx", step=0.05, group=gTPbt) btEnableMaxHold = input.bool(true, "Backtest: enable max holding bars?", group=gTPbt) btMaxHoldBars = input.int(80, "Backtest: max holding bars", minval=5, group=gTPbt) tp1 = input.float(0.25, "TP1 %", step=0.05, group=gTPbt) tp2 = input.float(0.50, "TP2 %", step=0.05, group=gTPbt) tp3 = input.float(0.80, "TP3 %", step=0.05, group=gTPbt) tp4 = input.float(1.00, "TP4 %", step=0.05, group=gTPbt) btcBacktestFix = input.bool(false, "BTC backtest fixes (size floor & risk x)", group=gTPbt) btcRiskMultiplier = input.float(3.0, "BTC risk multiplier (x)", minval=1.0, maxval=10.0, step=0.5, group=gTPbt) btcMinPositionUSD = input.float(100.0,"BTC minimum position value (USD)", minval=10.0, step=10.0, group=gTPbt) baseRiskPct = riskPctInput if isCrypto and btcBacktestFix baseRiskPct *= btcRiskMultiplier baseSizePct = math.max(0.01, baseRiskPct * 100.0) if isCrypto and btcBacktestFix minPctByValue = (btcMinPositionUSD / (strategy.initial_capital + strategy.netprofit)) * 100.0 baseSizePct := math.max(baseSizePct, minPctByValue) baseSizePct := math.min(baseSizePct, 95.0) atrPctNow = (atr14 / close) * 100.0 volScaleRaw = volAdjRefAtrPct / math.max(0.05, atrPctNow) volScaleClp = math.min(volAdjMax, math.max(volAdjMin, volScaleRaw)) positionSizePct = useVolAdjSize ? baseSizePct * volScaleClp : baseSizePct if isStrategy if canTrade and validLong and strategy.position_size <= 0 strategy.entry("LONG", strategy.long, qty=positionSizePct) if canTrade and validShort and strategy.position_size >= 0 strategy.entry("SHORT", strategy.short, qty=positionSizePct) exitThresholdLong = math.max(0, minPassUsed - exitBuffer) exitThresholdShort = math.max(0, minPassUsed - exitBuffer) longHoldOK = (finalLongScore >= exitThresholdLong) and volOK_entry shortHoldOK = (finalShortScore >= exitThresholdShort) and volOK_entry if enableScoreExits and strategy.position_size > 0 and (not longHoldOK) strategy.close("LONG") if enableScoreExits and strategy.position_size < 0 and (not shortHoldOK) strategy.close("SHORT") atrSLvalCalc = ta.atr(atrSLp) var float trailStopL = na var float trailStopS = na if strategy.position_size != strategy.position_size[1] if strategy.position_size > 0 trailStopL := close - btTrailATRx * atrSLvalCalc trailStopS := na else if strategy.position_size < 0 trailStopS := close + btTrailATRx * atrSLvalCalc trailStopL := na if btEnableTrail and strategy.position_size > 0 trailStopL := math.max(trailStopL, high - btTrailATRx * atrSLvalCalc) strategy.exit("BT Trail L", from_entry="LONG", stop=trailStopL) if btEnableTrail and strategy.position_size < 0 trailStopS := math.min(trailStopS, low + btTrailATRx * atrSLvalCalc) strategy.exit("BT Trail S", from_entry="SHORT", stop=trailStopS) var int entryBar = na if strategy.position_size != strategy.position_size[1] entryBar := bar_index if btEnableMaxHold and not na(entryBar) and (bar_index - entryBar > btMaxHoldBars) strategy.close_all() if (riskHalt or dailyHalt or consecHalt) if strategy.position_size > 0 strategy.close("LONG") if strategy.position_size < 0 strategy.close("SHORT") // -------------------------- AUTO MANAGEMENT (follow-up PineConnector commands) var bool am_sentBE = false var bool am_sentPC1 = false var bool am_sentPC2 = false var bool am_sentPC3 = false var bool am_sentTrail= false var bool am_raised1 = false var bool am_raised2 = false if strategy.position_size == 0 or strategy.position_size != strategy.position_size[1] am_sentBE := false am_sentPC1 := false am_sentPC2 := false am_sentPC3 := false am_sentTrail := false am_raised1 := false am_raised2 := false entryPrice = strategy.position_avg_price profitPctLong = entryPrice > 0 ? (close - entryPrice) / entryPrice * 100.0 : 0.0 profitPctShort = entryPrice > 0 ? (entryPrice - close) / entryPrice * 100.0 : 0.0 TP1_price_L = entryPrice * (1 + tp1/100.0) TP2_price_L = entryPrice * (1 + tp2/100.0) TP3_price_L = entryPrice * (1 + tp3/100.0) TP1_price_S = entryPrice * (1 - tp1/100.0) TP2_price_S = entryPrice * (1 - tp2/100.0) TP3_price_S = entryPrice * (1 - tp3/100.0) fmtPrice(v) => str.tostring(v, format.mintick) if amEnable and strategy.position_size > 0 if amBE_on and not am_sentBE and profitPctLong >= amBE_trigger_pct beStr = amBE_offset_pips > 0 ? ",sl=0,beoffset=" + str.tostring(amBE_offset_pips) : ",sl=0" alert(licenseID + ",newsltplong," + pineSymbol + beStr, alert.freq_once_per_bar) am_sentBE := true if amPC_on and not am_sentPC1 and amPC_tp1 and profitPctLong >= tp1 alert(licenseID + ",closelongpct," + pineSymbol, alert.freq_once_per_bar) am_sentPC1 := true if amPC_on and not am_sentPC2 and amPC_tp2 and profitPctLong >= tp2 alert(licenseID + ",closelongpct," + pineSymbol, alert.freq_once_per_bar) am_sentPC2 := true if amPC_on and not am_sentPC3 and amPC_tp3 and profitPctLong >= tp3 alert(licenseID + ",closelongpct," + pineSymbol, alert.freq_once_per_bar) am_sentPC3 := true if amTrail_on and not am_sentTrail and profitPctLong >= amTrail_trigger alert(licenseID + ",newsltplong," + pineSymbol + ",atrtimeframe=" + str.tostring(atrTimeframeM) + ",atrperiod=" + str.tostring(atrPeriodPC) + ",atrmultiplier=" + str.tostring(atrMultPC) + ",atrshift=" + str.tostring(atrShiftPC) + ",atrtrigger=0", alert.freq_once_per_bar) am_sentTrail := true if amRaiseSL_on and not am_raised1 and close >= TP2_price_L alert(licenseID + ",newsltplong," + pineSymbol + ",sl=" + fmtPrice(TP1_price_L), alert.freq_once_per_bar) am_raised1 := true if amRaiseSL_on and not am_raised2 and close >= TP3_price_L alert(licenseID + ",newsltplong," + pineSymbol + ",sl=" + fmtPrice(TP2_price_L), alert.freq_once_per_bar) am_raised2 := true if amEnable and strategy.position_size < 0 if amBE_on and not am_sentBE and profitPctShort >= amBE_trigger_pct beStrS = amBE_offset_pips > 0 ? ",sl=0,beoffset=" + str.tostring(amBE_offset_pips) : ",sl=0" alert(licenseID + ",newsltpshort," + pineSymbol + beStrS, alert.freq_once_per_bar) am_sentBE := true if amPC_on and not am_sentPC1 and amPC_tp1 and profitPctShort >= tp1 alert(licenseID + ",closeshortpct," + pineSymbol, alert.freq_once_per_bar) am_sentPC1 := true if amPC_on and not am_sentPC2 and amPC_tp2 and profitPctShort >= tp2 alert(licenseID + ",closeshortpct," + pineSymbol, alert.freq_once_per_bar) am_sentPC2 := true if amPC_on and not am_sentPC3 and amPC_tp3 and profitPctShort >= tp3 alert(licenseID + ",closeshortpct," + pineSymbol, alert.freq_once_per_bar) am_sentPC3 := true if amTrail_on and not am_sentTrail and profitPctShort >= amTrail_trigger alert(licenseID + ",newsltpshort," + pineSymbol + ",atrtimeframe=" + str.tostring(atrTimeframeM) + ",atrperiod=" + str.tostring(atrPeriodPC) + ",atrmultiplier=" + str.tostring(atrMultPC) + ",atrshift=" + str.tostring(atrShiftPC) + ",atrtrigger=0", alert.freq_once_per_bar) am_sentTrail := true if amRaiseSL_on and not am_raised1 and close <= TP2_price_S alert(licenseID + ",newsltpshort," + pineSymbol + ",sl=" + fmtPrice(TP1_price_S), alert.freq_once_per_bar) am_raised1 := true if amRaiseSL_on and not am_raised2 and close <= TP3_price_S alert(licenseID + ",newsltpshort," + pineSymbol + ",sl=" + fmtPrice(TP2_price_S), alert.freq_once_per_bar) am_raised2 := true // -------------------------- ENTRY ALERTS (PineConnector format) atrSLvalA = ta.atr(atrSLp) cur = close slDistPrice = slMode == "Percent" ? cur * (slPct / 100.0) : slATRm * atrSLvalA slDistClamp = math.max(alertMinATRx * atrSLvalA, math.min(alertMaxATRx * atrSLvalA, slDistPrice)) tpDistPrice = slDistClamp * tpVsSLratio var string slFieldLong = na var string tpFieldLong = na var string slFieldShort = na var string tpFieldShort = na if sendAsPrice slFieldLong := str.tostring(cur - slDistClamp, format.mintick) tpFieldLong := str.tostring(cur + tpDistPrice, format.mintick) slFieldShort := str.tostring(cur + slDistClamp, format.mintick) tpFieldShort := str.tostring(cur - tpDistPrice, format.mintick) if not sendAsPrice slPips = math.max(1, math.round(slDistClamp / pipSize)) tpPips = math.max(1, math.round(tpDistPrice / pipSize)) slFieldLong := str.tostring(slPips) tpFieldLong := str.tostring(tpPips) slFieldShort := str.tostring(slPips) tpFieldShort := str.tostring(tpPips) // Effective threshold in comment rawComment = "L=" + str.tostring(finalLongScore) + "/" + str.tostring(minPassUsed) + " S=" + str.tostring(finalShortScore) + "/" + str.tostring(minPassUsed) comment20 = str.substring(str.replace_all(rawComment, " ", ""), 0, 20) makeOthers(_risk, _sl, _tp) => var string parts = "" parts := "risk=" + str.tostring(_risk) + ",sl=" + _sl + ",tp=" + _tp if incComment parts += ",comment=" + comment20 if incBreakeven parts += ",betrigger=" + str.tostring(beTrigger) + ",beoffset=" + str.tostring(beOffset) if incATRTrail parts += ",atrtimeframe=" + str.tostring(atrTimeframeM) + ",atrperiod=" + str.tostring(atrPeriodPC) + ",atrmultiplier=" + str.tostring(atrMultPC) + ",atrshift=" + str.tostring(atrShiftPC) + ",atrtrigger=" + str.tostring(atrTriggerPC) parts var bool amBlockThisBar = false amBlockThisBar := lossJustTriggered // avoid re-firing same bar right after a loss closes longEntryCondition = canTrade and (not amBlockThisBar) and validLong and (strategy.position_size <= 0 or strategy.position_size[1] <= 0) shortEntryCondition = canTrade and (not amBlockThisBar) and validShort and (strategy.position_size >= 0 or strategy.position_size[1] >= 0) if longEntryCondition othersL = makeOthers(riskPctInput, slFieldLong, tpFieldLong) alert(licenseID + ",buy," + pineSymbol + "," + othersL, alert.freq_once_per_bar) if shortEntryCondition othersS = makeOthers(riskPctInput, slFieldShort, tpFieldShort) alert(licenseID + ",sell," + pineSymbol + "," + othersS, alert.freq_once_per_bar) // -------------------------- VISUALS + DIAGNOSTICS showEMAs = input.bool(true, "Show EMAs", group=gVisual) showSignals = input.bool(true, "Show signal markers", group=gVisual) showSessionBg = input.bool(false, "Shade active sessions (if session filter is on)", group=gVisual) debugPanelOn = input.bool(false, "Show Debug Panel (which gate is blocking)", group=gVisual) dbgBypassVol = input.bool(false, "DEBUG: Bypass volatility gate", group=gVisual) dbgBypassMacro = input.bool(false, "DEBUG: Ignore Macro gates", group=gVisual) dbgBypassSess = input.bool(false, "DEBUG: Ignore Session/Spike gates", group=gVisual) volOK_entry := dbgBypassVol ? true : volOK_entry macroFilterLongOK := dbgBypassMacro ? true : macroFilterLongOK macroFilterShortOK := dbgBypassMacro ? true : macroFilterShortOK sessOK := dbgBypassSess ? true : sessOK spikeOK := dbgBypassSess ? true : spikeOK plot(showEMAs ? ema20 : na, "EMA20", color=color.yellow, linewidth=1) plot(showEMAs ? ema50 : na, "EMA50", color=color.blue, linewidth=2) plot(showEMAs ? ema200 : na, "EMA200", color=color.white, linewidth=2) plotshape(showSignals and longEntryCondition, title="Long", style=shape.triangleup, location=location.belowbar, color=color.new(color.green,0), size=size.tiny) plotshape(showSignals and shortEntryCondition, title="Short", style=shape.triangledown, location=location.abovebar, color=color.new(color.red,0), size=size.tiny) presetShadeColor = input.string("Amber", "Preset shade color", options=["Amber","Blue","Gray","Purple"], group=gVisual) presetShadeAlpha = input.int(80, "Preset shade transparency (0=solid, 100=hidden)", minval=0, maxval=100, group=gVisual) showPresetShade = input.bool(true, "Show preset shade when Control Mode = Preset?", group=gVisual) shadeColor = presetShadeColor == "Amber" ? color.rgb(255,191,0) : presetShadeColor == "Blue" ? color.blue : presetShadeColor == "Gray" ? color.gray : color.purple doPresetBg = showPresetShade and (controlMode == "Preset") sessionBg = showSessionBg and enableSessionFilter and activeSession ? color.new(color.aqua, 88) : na finalBg = doPresetBg ? color.new(shadeColor, presetShadeAlpha) : sessionBg bgcolor(finalBg) // Debug panel var table dbg = na if debugPanelOn and na(dbg) dbg := table.new(position.top_right, 2, 18, border_width=1) table.cell(dbg, 0,0, "Gate", text_color=color.white, bgcolor=color.new(color.gray,20)) table.cell(dbg, 1,0, "Value", text_color=color.white, bgcolor=color.new(color.gray,20)) if debugPanelOn table.cell(dbg, 0,1, "finalLongScore") table.cell(dbg, 1,1, str.tostring(finalLongScore)) table.cell(dbg, 0,2, "finalShortScore") table.cell(dbg, 1,2, str.tostring(finalShortScore)) table.cell(dbg, 0,3, "minPassUsed") table.cell(dbg, 1,3, str.tostring(minPassUsed)) table.cell(dbg, 0,4, "MAIN L/OPT L") table.cell(dbg, 1,4, str.tostring(baseLongMain) + "/" + str.tostring(baseLongOpt)) table.cell(dbg, 0,5, "MAIN S/OPT S") table.cell(dbg, 1,5, str.tostring(baseShortMain) + "/" + str.tostring(baseShortOpt)) table.cell(dbg, 0,6, "mainOKL/optOKL") table.cell(dbg, 1,6, (mainOKLong ? "✔" : "✖") + "/" + (optOKLong ? "✔" : "✖"), bgcolor=(mainOKLong and optOKLong) ? color.new(color.green,85) : color.new(color.red,85)) table.cell(dbg, 0,7, "mainOKS/optOKS") table.cell(dbg, 1,7, (mainOKShort ? "✔" : "✖") + "/" + (optOKShort ? "✔" : "✖"), bgcolor=(mainOKShort and optOKShort) ? color.new(color.green,85) : color.new(color.red,85)) table.cell(dbg, 0,8, "volOK_entry") table.cell(dbg, 1,8, volOK_entry ? "✔" : "✖", bgcolor=volOK_entry ? color.new(color.green,85) : color.new(color.red,85)) table.cell(dbg, 0,9, "barGateLongOK") table.cell(dbg, 1,9, barGateLongOK ? "✔" : "✖", bgcolor=barGateLongOK ? color.new(color.green,85) : color.new(color.red,85)) table.cell(dbg, 0,10, "barGateShortOK") table.cell(dbg, 1,10, barGateShortOK ? "✔" : "✖", bgcolor=barGateShortOK ? color.new(color.green,85) : color.new(color.red,85)) table.cell(dbg, 0,11, "macroFilterLongOK") table.cell(dbg, 1,11, macroFilterLongOK ? "✔" : "✖", bgcolor=macroFilterLongOK ? color.new(color.green,85) : color.new(color.red,85)) table.cell(dbg, 0,12, "macroFilterShortOK") table.cell(dbg, 1,12, macroFilterShortOK ? "✔" : "✖", bgcolor=macroFilterShortOK ? color.new(color.green,85) : color.new(color.red,85)) table.cell(dbg, 0,13, "sessOK & spikeOK") table.cell(dbg, 1,13, (sessOK and spikeOK) ? "✔" : "✖", bgcolor=(sessOK and spikeOK) ? color.new(color.green,85) : color.new(color.red,85)) table.cell(dbg, 0,14, "consecLoss/max") table.cell(dbg, 1,14, str.tostring(consecLoss) + "/" + str.tostring(maxConsecLoss)) table.cell(dbg, 0,15, "halts (dd/daily/consec)") table.cell(dbg, 1,15, (riskHalt? "1" : "0") + "/" + (dailyHalt? "1":"0") + "/" + (consecHalt? "1":"0")) table.cell(dbg, 0,16, "posSize%") table.cell(dbg, 1,16, str.tostring(positionSizePct, format.mintick)) table.cell(dbg, 0,17, "resetMode") table.cell(dbg, 1,17, streakResetMode)