//@version=6
strategy(
     title="CNPS36 NEON KELTNER MULTI ENGINE",
     shorttitle="CNPS36 NEON KC",
     overlay=true,
     initial_capital=100000000,
     default_qty_type=strategy.fixed,
     default_qty_value=3,
     pyramiding=0,
     margin_long=0,
     margin_short=0,
     slippage=3,
     commission_type=strategy.commission.cash_per_contract,
     commission_value=10000,
     fill_orders_on_standard_ohlc=true,
     process_orders_on_close=true,
     calc_on_order_fills=true,
     max_labels_count=200,
     max_lines_count=100,
     max_bars_back=5000
)


// ============================================================================
// CNPS36 NEON KELTNER MULTI ENGINE
//
// SOURCE-DERIVED SIGNAL FAMILIES:
//
// 1. REVERSAL
//
// Price leaves deviation band
// -> %B re-enters
// -> Keltner excursion tier
// -> Long EMA slope filter
// -> WaveTrend confirmation
// -> Entry
//
// 2. PULLBACK
//
// Strong long-term trend
// -> pullback to Midline / Inner Band
// -> price rejects back in trend direction
// -> WaveTrend direction confirmation
// -> Entry
//
// 3. BAND BREAKOUT
//
// Close crosses EMA20 +/- 2 ATR
// -> next bar OPENS beyond breakout band
// -> breakout confirmed
// -> structure stop
// -> hold until EMA20 basis exit or timeout
//
// IMPORTANT CNPS ADAPTATION:
//
// - Adaptive Auto-Hide is not used for order execution.
// - Built-in LRETF benchmark gate is not used.
// - This makes backtests deterministic and avoids SPY being used as the
//   default benchmark for VN30.
//
// - Current chart timeframe.
// - 100M VND.
// - 3 contracts.
// - Commission 10,000 VND / contract.
// - Slippage 3 ticks.
// - Entry session 09:00 - 14:30.
// - Force close 14:45.
// ============================================================================


// ============================================================================
// SECTION 1: SIGNAL ENGINE
// ============================================================================

signalMode = input.string(
     "Reversal + Pullback",
     title="Signal Mode",
     options=[
         "Reversal",
         "Pullback",
         "Reversal + Pullback",
         "Band Breakout"
     ],
     group="Signal Engine"
)

bool useReversal = (
     signalMode == "Reversal" or
     signalMode == "Reversal + Pullback"
)

bool usePullback = (
     signalMode == "Pullback" or
     signalMode == "Reversal + Pullback"
)

bool useBandBreakout = (
     signalMode == "Band Breakout"
)


// ============================================================================
// SECTION 2: DIRECTION
// ============================================================================

tradeDirection = input.string(
     "Both",
     title="Trade Direction",
     options=["Long", "Short", "Both"],
     group="Trade Control"
)

bool allowLong = (
     tradeDirection == "Long" or
     tradeDirection == "Both"
)

bool allowShort = (
     tradeDirection == "Short" or
     tradeDirection == "Both"
)


// ============================================================================
// SECTION 3: KELTNER MIDLINE
// ============================================================================

midlineType = input.string(
     "EMA",
     title="Midline Type",
     options=[
         "EMA",
         "SMA",
         "Kijun"
     ],
     group="Keltner Channel"
)

midlineLength = input.int(
     34,
     title="Midline Length",
     minval=1,
     group="Keltner Channel"
)

kijunLength = input.int(
     26,
     title="Kijun Length",
     minval=2,
     group="Keltner Channel"
)

atrSmoothingType = input.string(
     "EMA",
     title="TR Smoothing",
     options=["EMA", "SMA"],
     group="Keltner Channel"
)

keltnerATRLength = input.int(
     88,
     title="Keltner TR Length",
     minval=1,
     group="Keltner Channel"
)

innerMultiplier = input.float(
     1.5,
     title="Inner Band Multiplier",
     minval=0.1,
     step=0.1,
     group="Keltner Channel"
)

outerMultiplier = input.float(
     3.5,
     title="Outer Band Multiplier",
     minval=0.1,
     step=0.1,
     group="Keltner Channel"
)


// ============================================================================
// SECTION 4: DEVIATION BAND
// ============================================================================

deviationLength = input.int(
     34,
     title="Deviation Length",
     minval=2,
     group="Reversal"
)

deviationMultiplier = input.float(
     2.0,
     title="Deviation Multiplier",
     minval=0.5,
     step=0.1,
     group="Reversal"
)

percentBUpper = input.float(
     1.0,
     title="%B Upper Level",
     step=0.05,
     group="Reversal"
)

percentBLower = input.float(
     0.0,
     title="%B Lower Level",
     step=0.05,
     group="Reversal"
)

reversalCooldownBars = input.int(
     5,
     title="Same Direction Cooldown",
     minval=0,
     group="Reversal"
)


// ============================================================================
// SECTION 5: KELTNER TIER
// ============================================================================
//
// Source v23 default:
// "Exclude Outer Band"
//
// Therefore Tier 0 and Tier 1 are accepted.
// Tier 2 is rejected.
//
// ============================================================================

reversalTierMode = input.string(
     "Exclude Outer Band",
     title="Reversal Tier",
     options=[
         "All",
         "Inner Or Stronger",
         "Outer Only",
         "Exclude Outer Band"
     ],
     group="Reversal"
)


// ============================================================================
// SECTION 6: TREND FILTER
// ============================================================================

useLongTrendFilter = input.bool(
     true,
     title="Use Long EMA Trend Filter?",
     group="Trend Filter"
)

longEMALength = input.int(
     200,
     title="Long EMA Length",
     minval=10,
     group="Trend Filter"
)

trendSlopeDistance = input.int(
     50,
     title="EMA Slope Distance",
     minval=1,
     group="Trend Filter"
)


// ============================================================================
// SECTION 7: WAVETREND
// ============================================================================

waveTrendMode = input.string(
     "Flexible",
     title="WaveTrend Confirmation",
     options=[
         "Off",
         "Extreme",
         "Cross",
         "Flexible",
         "Auto"
     ],
     group="WaveTrend"
)

wtChannelLength = input.int(
     10,
     title="WT Channel Length",
     minval=1,
     group="WaveTrend"
)

wtAverageLength = input.int(
     21,
     title="WT Average Length",
     minval=1,
     group="WaveTrend"
)

wtSignalLength = input.int(
     4,
     title="WT Signal Length",
     minval=1,
     group="WaveTrend"
)

wtLookback = input.int(
     5,
     title="WT Confirmation Lookback",
     minval=1,
     group="WaveTrend"
)

dynamicWTThreshold = input.bool(
     true,
     title="Dynamic WT Threshold?",
     group="WaveTrend"
)

fixedWTOverbought = input.float(
     53.0,
     title="Fixed WT Overbought",
     group="WaveTrend"
)

fixedWTOversold = input.float(
     -53.0,
     title="Fixed WT Oversold",
     group="WaveTrend"
)

wtPercentileLength = input.int(
     200,
     title="Dynamic WT History",
     minval=20,
     group="WaveTrend"
)

wtTailPercent = input.float(
     10.0,
     title="Dynamic WT Tail %",
     minval=1.0,
     maxval=40.0,
     step=1.0,
     group="WaveTrend"
)

wtMinimumThreshold = input.float(
     20.0,
     title="Dynamic Minimum Threshold",
     minval=0.0,
     group="WaveTrend"
)


// ============================================================================
// SECTION 8: AUTO REGIME
// ============================================================================

regimeMeasure = input.string(
     "Efficiency Ratio",
     title="Auto WT Regime",
     options=[
         "Efficiency Ratio",
         "ADX"
     ],
     group="WaveTrend"
)

efficiencyLength = input.int(
     20,
     title="Efficiency Length",
     minval=2,
     group="WaveTrend"
)

efficiencyTrendThreshold = input.float(
     0.30,
     title="Efficiency Trend Threshold",
     minval=0.05,
     maxval=0.90,
     step=0.05,
     group="WaveTrend"
)

adxLength = input.int(
     14,
     title="ADX Length",
     minval=2,
     group="WaveTrend"
)

adxTrendThreshold = input.float(
     25.0,
     title="ADX Trend Threshold",
     minval=5.0,
     step=1.0,
     group="WaveTrend"
)


// ============================================================================
// SECTION 9: PULLBACK
// ============================================================================

pullbackZone = input.string(
     "Midline",
     title="Pullback Touch Zone",
     options=[
         "Midline",
         "Inner Band"
     ],
     group="Pullback"
)

pullbackLookback = input.int(
     5,
     title="Pullback Touch Lookback",
     minval=1,
     group="Pullback"
)

pullbackUseWT = input.bool(
     true,
     title="Use WT Direction Confirmation?",
     group="Pullback"
)

pullbackCooldown = input.int(
     10,
     title="Pullback Cooldown",
     minval=0,
     group="Pullback"
)

minimumTrendStrengthATR = input.float(
     1.0,
     title="Minimum Trend Strength ATR",
     minval=0.0,
     step=0.1,
     group="Pullback"
)


// ============================================================================
// SECTION 10: SOURCE STOP
// ============================================================================

stopBufferATR = input.float(
     0.5,
     title="Structure Stop Buffer ATR",
     minval=0.0,
     step=0.1,
     group="Risk Management"
)


// ============================================================================
// SECTION 11: CORE EXIT
// ============================================================================

coreExitMode = input.string(
     "Source H2",
     title="Reversal / Pullback Exit",
     options=[
         "Source H2",
         "Structure RR",
         "Fixed Points"
     ],
     group="Risk Management"
)

rewardRisk = input.float(
     2.0,
     title="Structure Reward / Risk",
     minval=0.5,
     maxval=5.0,
     step=0.25,
     group="Risk Management"
)

maximumStructureRisk = input.float(
     0.0,
     title="Maximum Structure Risk (0 = Off)",
     minval=0.0,
     step=0.5,
     group="Risk Management"
)

minimumSourceRR = input.float(
     0.0,
     title="Minimum Source Target R:R (0 = Off)",
     minval=0.0,
     maxval=10.0,
     step=0.1,
     group="Risk Management"
)


// ============================================================================
// SECTION 12: FIXED EXIT
// ============================================================================

fixedStopLossPoints = input.float(
     5.0,
     title="Fixed Stop Loss",
     minval=0.5,
     step=0.5,
     group="Fixed Exit"
)

fixedTakeProfitPoints = input.float(
     15.0,
     title="Fixed Take Profit",
     minval=0.5,
     step=0.5,
     group="Fixed Exit"
)


// ============================================================================
// SECTION 13: BREAKOUT MODULE
// ============================================================================

bandBreakoutShorts = input.bool(
     false,
     title="Enable Short Breakouts?",
     group="Band Breakout"
)

breakoutEMALength = input.int(
     20,
     title="Breakout EMA",
     minval=2,
     group="Band Breakout"
)

breakoutATRLength = input.int(
     14,
     title="Breakout ATR",
     minval=1,
     group="Band Breakout"
)

breakoutATRMultiplier = input.float(
     2.0,
     title="Breakout Band x ATR",
     minval=0.1,
     step=0.1,
     group="Band Breakout"
)

breakoutStopMode = input.string(
     "Wick",
     title="Breakout Stop",
     options=[
         "Wick",
         "Open"
     ],
     group="Band Breakout"
)

breakoutMinimumGap = input.int(
     20,
     title="Minimum Bars Between Breakouts",
     minval=0,
     group="Band Breakout"
)

breakoutMaximumBars = input.int(
     200,
     title="Maximum Bars In Breakout Trade",
     minval=1,
     group="Band Breakout"
)


// ============================================================================
// SECTION 14: OPTIONAL BREAK EVEN
// ============================================================================

useBreakEven = input.bool(
     false,
     title="Use Break Even?",
     group="Break Even"
)

breakEvenTriggerR = input.float(
     1.0,
     title="Break Even Trigger R",
     minval=0.25,
     maxval=5.0,
     step=0.25,
     group="Break Even"
)

breakEvenOffsetPoints = input.float(
     0.5,
     title="Break Even Offset",
     minval=0.0,
     step=0.5,
     group="Break Even"
)


// ============================================================================
// SECTION 15: TRADE CONTROL
// ============================================================================

maximumTradesPerDay = input.int(
     4,
     title="Maximum Trades Per Day",
     minval=1,
     maxval=20,
     group="Trade Control"
)

globalCooldownBars = input.int(
     0,
     title="Global Cooldown After Exit",
     minval=0,
     maxval=100,
     group="Trade Control"
)


// ============================================================================
// SECTION 16: SESSION
// ============================================================================

strategyTimezone = input.string(
     "Asia/Ho_Chi_Minh",
     title="Timezone",
     group="Session"
)

entryStartHour = input.int(
     9,
     title="Entry Start Hour",
     minval=0,
     maxval=23,
     group="Session"
)

entryStartMinute = input.int(
     0,
     title="Entry Start Minute",
     minval=0,
     maxval=59,
     group="Session"
)

lastEntryHour = input.int(
     14,
     title="Last Entry Hour",
     minval=0,
     maxval=23,
     group="Session"
)

lastEntryMinute = input.int(
     30,
     title="Last Entry Minute",
     minval=0,
     maxval=59,
     group="Session"
)

forceCloseHour = input.int(
     14,
     title="Force Close Hour",
     minval=0,
     maxval=23,
     group="Session"
)

forceCloseMinute = input.int(
     45,
     title="Force Close Minute",
     minval=0,
     maxval=59,
     group="Session"
)


// ============================================================================
// SECTION 17: ALERTS
// ============================================================================

disableSLTPAlerts = input.bool(
     false,
     title="Disable SL / TP Alerts?",
     group="Alerts"
)


// ============================================================================
// SECTION 18: DISPLAY
// ============================================================================

showChannel = input.bool(
     false,
     title="Show Keltner Channel?",
     group="Display"
)

showLongEMA = input.bool(
     false,
     title="Show Long EMA?",
     group="Display"
)

showBreakoutBand = input.bool(
     false,
     title="Show Breakout Band?",
     group="Display"
)

showSignals = input.bool(
     false,
     title="Show Signal Markers?",
     group="Display"
)

showActiveSLTP = input.bool(
     true,
     title="Show Active SL / TP?",
     group="Display"
)

showDiagnostics = input.bool(
     false,
     title="Show Diagnostics?",
     group="Display"
)


// ============================================================================
// SECTION 19: HELPER FUNCTIONS
// ============================================================================

f_ma(string mode, float source, int length) =>

    float result = na

    if mode == "SMA"
        result := ta.sma(source, length)
    else
        result := ta.ema(source, length)

    result


// ============================================================================
// SECTION 20: KELTNER CHANNEL
// ============================================================================

float kijun = (ta.highest(high, kijunLength) + ta.lowest(low, kijunLength)) / 2.0

float mid = na


if midlineType == "Kijun"
    mid := kijun
else
    mid := f_ma(midlineType, close, midlineLength)


float trueRange = ta.tr(true)

float channelRange = f_ma(
     atrSmoothingType,
     trueRange,
     keltnerATRLength
)

float topInner = mid + channelRange * innerMultiplier

float topOuter = mid + channelRange * outerMultiplier

float bottomInner = mid - channelRange * innerMultiplier

float bottomOuter = mid - channelRange * outerMultiplier


// ============================================================================
// SECTION 21: DEVIATION BAND / %B
// ============================================================================

float deviation = deviationMultiplier * ta.stdev(close, deviationLength)

float deviationUpper = mid + deviation

float deviationLower = mid - deviation

float percentB = 0.5


if deviationUpper != deviationLower
    percentB := (close - deviationLower) / (deviationUpper - deviationLower)


float atr14 = ta.atr(14)


// ============================================================================
// SECTION 22: LONG TREND
// ============================================================================

float longEMA = ta.ema(
     close,
     longEMALength
)

bool longTrendUp = (
     not useLongTrendFilter or
     longEMA > longEMA[trendSlopeDistance]
)

bool longTrendDown = (
     not useLongTrendFilter or
     longEMA < longEMA[trendSlopeDistance]
)


// ============================================================================
// SECTION 23: WAVETREND
// ============================================================================

float wtAP = hlc3

float wtESA = ta.ema(
     wtAP,
     wtChannelLength
)

float wtDeviation = ta.ema(
     math.abs(wtAP - wtESA),
     wtChannelLength
)

float wtCI = 0.0


if wtDeviation != 0
    wtCI := (wtAP - wtESA) / (0.015 * wtDeviation)


float wt1 = ta.ema(
     wtCI,
     wtAverageLength
)

float wt2 = ta.sma(
     wt1,
     wtSignalLength
)

bool wtCrossUp = ta.crossover(
     wt1,
     wt2
)

bool wtCrossDown = ta.crossunder(
     wt1,
     wt2
)

int barsSinceWTCrossUp = ta.barssince(
     wtCrossUp
)

int barsSinceWTCrossDown = ta.barssince(
     wtCrossDown
)

float wtRecentLow = ta.lowest(
     wt2,
     wtLookback
)

float wtRecentHigh = ta.highest(
     wt2,
     wtLookback
)


// ============================================================================
// SECTION 24: WT DYNAMIC THRESHOLDS
// ============================================================================

float wtDynamicOversold = ta.percentile_linear_interpolation(
     wt2,
     wtPercentileLength,
     wtTailPercent
)

float wtDynamicOverbought = ta.percentile_linear_interpolation(
     wt2,
     wtPercentileLength,
     100.0 - wtTailPercent
)

float wtOversoldLimit = fixedWTOversold

float wtOverboughtLimit = fixedWTOverbought


if dynamicWTThreshold

    wtOversoldLimit := math.min(
         nz(wtDynamicOversold, fixedWTOversold),
         -wtMinimumThreshold
    )

    wtOverboughtLimit := math.max(
         nz(wtDynamicOverbought, fixedWTOverbought),
         wtMinimumThreshold
    )


// ============================================================================
// SECTION 25: REGIME
// ============================================================================

float efficiencyNumerator = math.abs(
     close - close[efficiencyLength]
)

float efficiencyPathAverage = ta.sma(
     math.abs(close - close[1]),
     efficiencyLength
)

float efficiencyDenominator = efficiencyPathAverage * efficiencyLength

float efficiencyRaw = 0.0


if efficiencyDenominator > 0
    efficiencyRaw := efficiencyNumerator / efficiencyDenominator


float efficiencyRatio = ta.sma(
     efficiencyRaw,
     3
)

[diPlus, diMinus, adxValue] = ta.dmi(
     adxLength,
     adxLength
)

bool trendRegime = false


if regimeMeasure == "ADX"
    trendRegime := adxValue >= adxTrendThreshold
else
    trendRegime := efficiencyRatio >= efficiencyTrendThreshold


string effectiveWTMode = waveTrendMode


if waveTrendMode == "Auto"

    if trendRegime
        effectiveWTMode := "Cross"
    else
        effectiveWTMode := "Extreme"


// ============================================================================
// SECTION 26: WT CONFIRMATION
// ============================================================================

bool wtBuyExtreme = (
     wtRecentLow <= wtOversoldLimit
)

bool wtSellExtreme = (
     wtRecentHigh >= wtOverboughtLimit
)

bool wtBuyCross = (
     not na(barsSinceWTCrossUp) and
     barsSinceWTCrossUp <= wtLookback
)

bool wtSellCross = (
     not na(barsSinceWTCrossDown) and
     barsSinceWTCrossDown <= wtLookback
)


bool waveTrendBuyOK = true

bool waveTrendSellOK = true


if effectiveWTMode == "Extreme"

    waveTrendBuyOK := wtBuyExtreme
    waveTrendSellOK := wtSellExtreme


else if effectiveWTMode == "Cross"

    waveTrendBuyOK := wtBuyCross
    waveTrendSellOK := wtSellCross


else if effectiveWTMode == "Flexible"

    waveTrendBuyOK := wtBuyExtreme or wtBuyCross
    waveTrendSellOK := wtSellExtreme or wtSellCross


else if effectiveWTMode == "Off"

    waveTrendBuyOK := true
    waveTrendSellOK := true


// ============================================================================
// SECTION 27: REVERSAL EXCURSION STATE
// ============================================================================

var float excursionLow = na

var float excursionHigh = na

var int excursionBuyTier = 0

var int excursionSellTier = 0


bool reversalBuyRaw = ta.crossover(
     percentB,
     percentBLower
)

bool reversalSellRaw = ta.crossunder(
     percentB,
     percentBUpper
)


float reversalSwingLow = math.min(
     nz(excursionLow, low[1]),
     low
)

float reversalSwingHigh = math.max(
     nz(excursionHigh, high[1]),
     high
)


int currentBuyTier = excursionBuyTier

int currentSellTier = excursionSellTier


// ============================================================================
// SECTION 28: RESET EXCURSION ON RE-ENTRY
// ============================================================================

if reversalBuyRaw

    excursionLow := na
    excursionBuyTier := 0


if reversalSellRaw

    excursionHigh := na
    excursionSellTier := 0


// ============================================================================
// SECTION 29: TRACK EXCURSION
// ============================================================================

if percentB < percentBLower

    if na(excursionLow)
        excursionLow := low
    else
        excursionLow := math.min(
             excursionLow,
             low
        )


    int newTier = 0


    if low <= bottomOuter
        newTier := 2

    else if low <= bottomInner
        newTier := 1


    excursionBuyTier := math.max(
         excursionBuyTier,
         newTier
    )


if percentB > percentBUpper

    if na(excursionHigh)
        excursionHigh := high
    else
        excursionHigh := math.max(
             excursionHigh,
             high
        )


    int newTier = 0


    if high >= topOuter
        newTier := 2

    else if high >= topInner
        newTier := 1


    excursionSellTier := math.max(
         excursionSellTier,
         newTier
    )


// ============================================================================
// SECTION 30: TIER FILTER
// ============================================================================

bool buyTierOK = true

bool sellTierOK = true


if reversalTierMode == "Inner Or Stronger"

    buyTierOK := currentBuyTier >= 1
    sellTierOK := currentSellTier >= 1


else if reversalTierMode == "Outer Only"

    buyTierOK := currentBuyTier == 2
    sellTierOK := currentSellTier == 2


else if reversalTierMode == "Exclude Outer Band"

    buyTierOK := currentBuyTier <= 1
    sellTierOK := currentSellTier <= 1


// ============================================================================
// SECTION 31: REVERSAL COOLDOWN
// ============================================================================

var int lastReversalBuyBar = na

var int lastReversalSellBar = na


bool reversalBuyCooldownOK = (
     na(lastReversalBuyBar) or
     bar_index - lastReversalBuyBar > reversalCooldownBars
)

bool reversalSellCooldownOK = (
     na(lastReversalSellBar) or
     bar_index - lastReversalSellBar > reversalCooldownBars
)


// ============================================================================
// SECTION 32: QUALIFIED REVERSAL
// ============================================================================

bool reversalLongSignal = (
     useReversal and
     reversalBuyRaw and
     buyTierOK and
     longTrendUp and
     waveTrendBuyOK and
     reversalBuyCooldownOK and
     barstate.isconfirmed
)

bool reversalShortSignal = (
     useReversal and
     reversalSellRaw and
     sellTierOK and
     longTrendDown and
     waveTrendSellOK and
     reversalSellCooldownOK and
     barstate.isconfirmed
)


if reversalLongSignal
    lastReversalBuyBar := bar_index


if reversalShortSignal
    lastReversalSellBar := bar_index


// ============================================================================
// SECTION 33: REVERSAL LEVELS
// ============================================================================

float reversalLongStop = reversalSwingLow - atr14 * stopBufferATR

float reversalShortStop = reversalSwingHigh + atr14 * stopBufferATR

float reversalLongTarget = topInner

float reversalShortTarget = bottomInner


// ============================================================================
// SECTION 34: PULLBACK TREND STRENGTH
// ============================================================================

float trendStrengthATR = na


if atr14 > 0

    trendStrengthATR := math.abs(
         longEMA - longEMA[trendSlopeDistance]
    ) / atr14


bool pullbackTrendStrong = (
     minimumTrendStrengthATR == 0 or
     (
         not na(trendStrengthATR) and
         trendStrengthATR >= minimumTrendStrengthATR
     )
)


// ============================================================================
// SECTION 35: PULLBACK ZONES
// ============================================================================

float pullbackSellZone = mid

float pullbackBuyZone = mid


if pullbackZone == "Inner Band"

    pullbackSellZone := topInner
    pullbackBuyZone := bottomInner


float recentPullbackHigh = ta.highest(
     high,
     pullbackLookback
)

float recentPullbackLow = ta.lowest(
     low,
     pullbackLookback
)


bool freshSellTouch = (
     high >= mid or
     close[1] >= mid[1]
)

bool freshBuyTouch = (
     low <= mid or
     close[1] <= mid[1]
)


// ============================================================================
// SECTION 36: PULLBACK COOLDOWN
// ============================================================================

var int lastPullbackBuyBar = na

var int lastPullbackSellBar = na


bool pullbackBuyCooldownOK = (
     na(lastPullbackBuyBar) or
     bar_index - lastPullbackBuyBar > pullbackCooldown
)

bool pullbackSellCooldownOK = (
     na(lastPullbackSellBar) or
     bar_index - lastPullbackSellBar > pullbackCooldown
)


// ============================================================================
// SECTION 37: PULLBACK SIGNAL
// ============================================================================

bool pullbackLongSignal = (
     usePullback and
     longTrendUp and
     recentPullbackLow <= pullbackBuyZone and
     freshBuyTouch and
     close > mid and
     close > open and
     (
         not pullbackUseWT or
         wt1 > wt2
     ) and
     pullbackTrendStrong and
     pullbackBuyCooldownOK and
     not reversalLongSignal and
     barstate.isconfirmed
)


bool pullbackShortSignal = (
     usePullback and
     longTrendDown and
     recentPullbackHigh >= pullbackSellZone and
     freshSellTouch and
     close < mid and
     close < open and
     (
         not pullbackUseWT or
         wt1 < wt2
     ) and
     pullbackTrendStrong and
     pullbackSellCooldownOK and
     not reversalShortSignal and
     barstate.isconfirmed
)


if pullbackLongSignal
    lastPullbackBuyBar := bar_index


if pullbackShortSignal
    lastPullbackSellBar := bar_index


// ============================================================================
// SECTION 38: PULLBACK LEVELS
// ============================================================================

float pullbackLongStop = recentPullbackLow - atr14 * stopBufferATR

float pullbackShortStop = recentPullbackHigh + atr14 * stopBufferATR

float pullbackLongTarget = topOuter

float pullbackShortTarget = bottomOuter


// ============================================================================
// SECTION 39: BREAKOUT BAND
// ============================================================================

float breakoutBasis = ta.ema(
     close,
     breakoutEMALength
)

float breakoutATR = ta.atr(
     breakoutATRLength
)

float breakoutUpper = breakoutBasis + breakoutATRMultiplier * breakoutATR

float breakoutLower = breakoutBasis - breakoutATRMultiplier * breakoutATR


bool breakoutCrossUp = (
     close > breakoutUpper and
     close[1] <= breakoutUpper[1]
)

bool breakoutCrossDown = (
     close < breakoutLower and
     close[1] >= breakoutLower[1]
)


// ============================================================================
// SECTION 40: BREAKOUT PENDING STATE
// ============================================================================

var int breakoutPendingDirection = 0

var int breakoutPendingBar = na

var float breakoutPendingStop = na

var int lastBreakoutEntryBar = na


bool breakoutGapOK = (
     na(lastBreakoutEntryBar) or
     bar_index - lastBreakoutEntryBar >= breakoutMinimumGap
)


// ============================================================================
// SECTION 41: REGISTER BREAKOUT
// ============================================================================

if (
     useBandBreakout and
     breakoutGapOK and
     barstate.isconfirmed
)

    if breakoutCrossUp

        breakoutPendingDirection := 1

        breakoutPendingBar := bar_index


        if breakoutStopMode == "Wick"
            breakoutPendingStop := low
        else
            breakoutPendingStop := open


    else if (
         breakoutCrossDown and
         bandBreakoutShorts
    )

        breakoutPendingDirection := -1

        breakoutPendingBar := bar_index


        if breakoutStopMode == "Wick"
            breakoutPendingStop := high
        else
            breakoutPendingStop := open


// ============================================================================
// SECTION 42: BREAKOUT NEXT-BAR CONFIRMATION
// ============================================================================
//
// Historical strategy limitation:
//
// Source defines entry at the OPEN of this confirmation bar.
//
// This CNPS conversion can only know the bar's open after the bar exists,
// while process_orders_on_close=true.
//
// Therefore the source pattern is preserved:
//
// - breakout bar
// - next bar open remains beyond previous band
//
// but the market order is placed at the confirmation BAR CLOSE.
//
// ============================================================================

bool breakoutLongSignal = (
     useBandBreakout and
     breakoutPendingDirection == 1 and
     not na(breakoutPendingBar) and
     bar_index == breakoutPendingBar + 1 and
     open > breakoutUpper[1] and
     breakoutPendingStop < open and
     barstate.isconfirmed
)


bool breakoutShortSignal = (
     useBandBreakout and
     bandBreakoutShorts and
     breakoutPendingDirection == -1 and
     not na(breakoutPendingBar) and
     bar_index == breakoutPendingBar + 1 and
     open < breakoutLower[1] and
     breakoutPendingStop > open and
     barstate.isconfirmed
)


// ============================================================================
// SECTION 43: EXPIRE BREAKOUT PENDING SETUP
// ============================================================================

if (
     not na(breakoutPendingBar) and
     bar_index > breakoutPendingBar + 1
)

    breakoutPendingDirection := 0

    breakoutPendingBar := na

    breakoutPendingStop := na


// ============================================================================
// SECTION 44: SESSION
// ============================================================================

int currentMinute = hour(time, strategyTimezone) * 60 + minute(time, strategyTimezone)

int currentCloseMinute = hour(time_close, strategyTimezone) * 60 + minute(time_close, strategyTimezone)

int entryStartTotal = entryStartHour * 60 + entryStartMinute

int lastEntryTotal = lastEntryHour * 60 + lastEntryMinute

int forceCloseTotal = forceCloseHour * 60 + forceCloseMinute


bool entrySession = (
     currentMinute >= entryStartTotal and
     currentMinute <= lastEntryTotal
)


// ============================================================================
// SECTION 45: DAILY COUNTER
// ============================================================================

int dayKey = year(time, strategyTimezone) * 10000 + month(time, strategyTimezone) * 100 + dayofmonth(time, strategyTimezone)

bool newTradingDay = (
     bar_index == 0 or
     dayKey != nz(dayKey[1], dayKey)
)


var int tradesToday = 0


if newTradingDay

    tradesToday := 0

    breakoutPendingDirection := 0

    breakoutPendingBar := na

    breakoutPendingStop := na


// ============================================================================
// SECTION 46: POSITION STATE
// ============================================================================

bool flat = strategy.position_size == 0

bool inLong = strategy.position_size > 0

bool inShort = strategy.position_size < 0


bool newLongPosition = (
     inLong and
     strategy.position_size[1] <= 0
)


bool newShortPosition = (
     inShort and
     strategy.position_size[1] >= 0
)


bool positionJustClosed = (
     flat and
     strategy.position_size[1] != 0
)


// ============================================================================
// SECTION 47: GLOBAL COOLDOWN
// ============================================================================

var int lastExitBar = na


if positionJustClosed
    lastExitBar := bar_index


bool globalCooldownOK = (
     na(lastExitBar) or
     bar_index - lastExitBar > globalCooldownBars
)


bool tradeLimitOK = (
     tradesToday < maximumTradesPerDay
)


// ============================================================================
// SECTION 48: SELECT RAW SETUP
// ============================================================================
//
// Priority:
//
// Band Breakout
// Pullback
// Reversal
//
// Reversal already has priority over Pullback inside source logic when both
// occur in the same direction.
//
// ============================================================================

int selectedDirection = 0

string selectedSetup = ""

float selectedStop = na

float selectedSourceTarget = na


if breakoutLongSignal

    selectedDirection := 1

    selectedSetup := "BREAKOUT"

    selectedStop := breakoutPendingStop


else if breakoutShortSignal

    selectedDirection := -1

    selectedSetup := "BREAKOUT"

    selectedStop := breakoutPendingStop


else if pullbackLongSignal

    selectedDirection := 1

    selectedSetup := "PULLBACK"

    selectedStop := pullbackLongStop

    selectedSourceTarget := pullbackLongTarget


else if pullbackShortSignal

    selectedDirection := -1

    selectedSetup := "PULLBACK"

    selectedStop := pullbackShortStop

    selectedSourceTarget := pullbackShortTarget


else if reversalLongSignal

    selectedDirection := 1

    selectedSetup := "REVERSAL"

    selectedStop := reversalLongStop

    selectedSourceTarget := reversalLongTarget


else if reversalShortSignal

    selectedDirection := -1

    selectedSetup := "REVERSAL"

    selectedStop := reversalShortStop

    selectedSourceTarget := reversalShortTarget


// ============================================================================
// SECTION 49: STRUCTURE RISK
// ============================================================================

float selectedRisk = na


if selectedDirection == 1 and not na(selectedStop)

    selectedRisk := close - selectedStop


else if selectedDirection == -1 and not na(selectedStop)

    selectedRisk := selectedStop - close


bool riskValid = (
     not na(selectedRisk) and
     selectedRisk > syminfo.mintick
)


if (
     riskValid and
     maximumStructureRisk > 0 and
     selectedRisk > maximumStructureRisk
)

    riskValid := false


// ============================================================================
// SECTION 50: SOURCE TARGET R:R
// ============================================================================

float selectedSourceReward = na

float selectedSourceRR = na


if (
     selectedDirection == 1 and
     not na(selectedSourceTarget)
)

    selectedSourceReward := selectedSourceTarget - close


else if (
     selectedDirection == -1 and
     not na(selectedSourceTarget)
)

    selectedSourceReward := close - selectedSourceTarget


if (
     not na(selectedSourceReward) and
     not na(selectedRisk) and
     selectedRisk > 0
)

    selectedSourceRR := selectedSourceReward / selectedRisk


bool sourceRROK = true


if (
     selectedSetup != "BREAKOUT" and
     coreExitMode == "Source H2"
)

    sourceRROK := (
         not na(selectedSourceReward) and
         selectedSourceReward > 0 and
         not na(selectedSourceRR)
    )


    if (
         sourceRROK and
         minimumSourceRR > 0
    )

        sourceRROK := selectedSourceRR >= minimumSourceRR


// ============================================================================
// SECTION 51: ENTRY APPROVAL
// ============================================================================

bool commonEntryOK = (
     flat and
     entrySession and
     currentCloseMinute < forceCloseTotal and
     globalCooldownOK and
     tradeLimitOK and
     barstate.isconfirmed
)


bool finalLongSignal = (
     commonEntryOK and
     allowLong and
     selectedDirection == 1 and
     riskValid and
     sourceRROK
)


bool finalShortSignal = (
     commonEntryOK and
     allowShort and
     selectedDirection == -1 and
     riskValid and
     sourceRROK
)


// ============================================================================
// SECTION 52: PENDING TRADE CONTEXT
// ============================================================================

var float pendingStop = na

var float pendingTarget = na

var float pendingRisk = na

var string pendingSetup = ""

var int pendingEntryBar = na


// ============================================================================
// SECTION 53: LONG ENTRY
// ============================================================================

if finalLongSignal

    pendingStop := selectedStop

    pendingRisk := selectedRisk

    pendingSetup := selectedSetup

    pendingEntryBar := bar_index


    if selectedSetup == "BREAKOUT"

        pendingTarget := na


    else if coreExitMode == "Source H2"

        pendingTarget := selectedSourceTarget


    else if coreExitMode == "Structure RR"

        pendingTarget := close + selectedRisk * rewardRisk


    else

        pendingStop := close - fixedStopLossPoints

        pendingRisk := fixedStopLossPoints

        pendingTarget := close + fixedTakeProfitPoints


    strategy.entry(
         "Long",
         strategy.long,
         qty=3,
         comment=pendingSetup,
         alert_message="ENTRY_LONG_CNPS36_" + pendingSetup
    )


    tradesToday += 1


    if selectedSetup == "BREAKOUT"

        lastBreakoutEntryBar := bar_index

        breakoutPendingDirection := 0

        breakoutPendingBar := na

        breakoutPendingStop := na


// ============================================================================
// SECTION 54: SHORT ENTRY
// ============================================================================

if finalShortSignal

    pendingStop := selectedStop

    pendingRisk := selectedRisk

    pendingSetup := selectedSetup

    pendingEntryBar := bar_index


    if selectedSetup == "BREAKOUT"

        pendingTarget := na


    else if coreExitMode == "Source H2"

        pendingTarget := selectedSourceTarget


    else if coreExitMode == "Structure RR"

        pendingTarget := close - selectedRisk * rewardRisk


    else

        pendingStop := close + fixedStopLossPoints

        pendingRisk := fixedStopLossPoints

        pendingTarget := close - fixedTakeProfitPoints


    strategy.entry(
         "Short",
         strategy.short,
         qty=3,
         comment=pendingSetup,
         alert_message="ENTRY_SHORT_CNPS36_" + pendingSetup
    )


    tradesToday += 1


    if selectedSetup == "BREAKOUT"

        lastBreakoutEntryBar := bar_index

        breakoutPendingDirection := 0

        breakoutPendingBar := na

        breakoutPendingStop := na


// ============================================================================
// SECTION 55: ACTIVE TRADE STATE
// ============================================================================

var float activeEntryPrice = na

var float activeInitialRisk = na

var float activeStop = na

var float activeTarget = na

var string activeSetup = ""

var int activeEntryBar = na

var bool breakEvenActivated = false


// ============================================================================
// SECTION 56: INITIALIZE LONG
// ============================================================================

if newLongPosition

    activeEntryPrice := strategy.position_avg_price

    activeStop := pendingStop

    activeTarget := pendingTarget

    activeSetup := pendingSetup

    activeEntryBar := bar_index

    breakEvenActivated := false


    if (
         na(activeStop) or
         activeStop >= activeEntryPrice
    )

        activeStop := activeEntryPrice - fixedStopLossPoints


    activeInitialRisk := activeEntryPrice - activeStop


    if (
         activeSetup != "BREAKOUT" and
         coreExitMode == "Structure RR"
    )

        activeTarget := activeEntryPrice + activeInitialRisk * rewardRisk


    else if (
         activeSetup != "BREAKOUT" and
         coreExitMode == "Fixed Points"
    )

        activeStop := activeEntryPrice - fixedStopLossPoints

        activeInitialRisk := fixedStopLossPoints

        activeTarget := activeEntryPrice + fixedTakeProfitPoints


    pendingStop := na

    pendingTarget := na

    pendingRisk := na

    pendingSetup := ""

    pendingEntryBar := na


// ============================================================================
// SECTION 57: INITIALIZE SHORT
// ============================================================================

if newShortPosition

    activeEntryPrice := strategy.position_avg_price

    activeStop := pendingStop

    activeTarget := pendingTarget

    activeSetup := pendingSetup

    activeEntryBar := bar_index

    breakEvenActivated := false


    if (
         na(activeStop) or
         activeStop <= activeEntryPrice
    )

        activeStop := activeEntryPrice + fixedStopLossPoints


    activeInitialRisk := activeStop - activeEntryPrice


    if (
         activeSetup != "BREAKOUT" and
         coreExitMode == "Structure RR"
    )

        activeTarget := activeEntryPrice - activeInitialRisk * rewardRisk


    else if (
         activeSetup != "BREAKOUT" and
         coreExitMode == "Fixed Points"
    )

        activeStop := activeEntryPrice + fixedStopLossPoints

        activeInitialRisk := fixedStopLossPoints

        activeTarget := activeEntryPrice - fixedTakeProfitPoints


    pendingStop := na

    pendingTarget := na

    pendingRisk := na

    pendingSetup := ""

    pendingEntryBar := na


// ============================================================================
// SECTION 58: BREAK EVEN LONG
// ============================================================================

if (
     inLong and
     useBreakEven and
     not breakEvenActivated and
     not na(activeEntryPrice) and
     not na(activeInitialRisk)
)

    float triggerPrice = activeEntryPrice + activeInitialRisk * breakEvenTriggerR


    if high >= triggerPrice

        float candidateStop = activeEntryPrice + breakEvenOffsetPoints

        activeStop := math.max(
             activeStop,
             candidateStop
        )

        breakEvenActivated := true


// ============================================================================
// SECTION 59: BREAK EVEN SHORT
// ============================================================================

if (
     inShort and
     useBreakEven and
     not breakEvenActivated and
     not na(activeEntryPrice) and
     not na(activeInitialRisk)
)

    float triggerPrice = activeEntryPrice - activeInitialRisk * breakEvenTriggerR


    if low <= triggerPrice

        float candidateStop = activeEntryPrice - breakEvenOffsetPoints

        activeStop := math.min(
             activeStop,
             candidateStop
        )

        breakEvenActivated := true


// ============================================================================
// SECTION 60: PROTECTIVE LONG EXIT
// ============================================================================

if (
     inLong and
     not na(activeStop)
)

    strategy.exit(
         "Long Protective",
         from_entry="Long",
         stop=activeStop,
         limit=activeTarget,
         alert_profit="EXIT_LONG_TP_CNPS36",
         alert_loss=breakEvenActivated ? "EXIT_LONG_BE_CNPS36" : "EXIT_LONG_SL_CNPS36",
         disable_alert=disableSLTPAlerts
    )


// ============================================================================
// SECTION 61: PROTECTIVE SHORT EXIT
// ============================================================================

if (
     inShort and
     not na(activeStop)
)

    strategy.exit(
         "Short Protective",
         from_entry="Short",
         stop=activeStop,
         limit=activeTarget,
         alert_profit="EXIT_SHORT_TP_CNPS36",
         alert_loss=breakEvenActivated ? "EXIT_SHORT_BE_CNPS36" : "EXIT_SHORT_SL_CNPS36",
         disable_alert=disableSLTPAlerts
    )


// ============================================================================
// SECTION 62: SOURCE BREAKOUT BASIS EXIT
// ============================================================================

bool longBreakoutBasisExit = (
     inLong and
     activeSetup == "BREAKOUT" and
     not na(activeEntryBar) and
     bar_index > activeEntryBar and
     close < breakoutBasis and
     barstate.isconfirmed
)


bool shortBreakoutBasisExit = (
     inShort and
     activeSetup == "BREAKOUT" and
     not na(activeEntryBar) and
     bar_index > activeEntryBar and
     close > breakoutBasis and
     barstate.isconfirmed
)


if longBreakoutBasisExit

    strategy.close(
         "Long",
         comment="Basis Exit",
         alert_message="BASIS_EXIT_LONG_CNPS36"
    )


if shortBreakoutBasisExit

    strategy.close(
         "Short",
         comment="Basis Exit",
         alert_message="BASIS_EXIT_SHORT_CNPS36"
    )


// ============================================================================
// SECTION 63: BREAKOUT TIME EXIT
// ============================================================================

bool breakoutTimeExit = (
     strategy.position_size != 0 and
     activeSetup == "BREAKOUT" and
     not na(activeEntryBar) and
     bar_index - activeEntryBar >= breakoutMaximumBars
)


if breakoutTimeExit

    strategy.close_all(
         comment="Breakout Time",
         alert_message="BREAKOUT_TIME_EXIT_CNPS36"
    )


// ============================================================================
// SECTION 64: CLEANUP
// ============================================================================

if positionJustClosed

    activeEntryPrice := na

    activeInitialRisk := na

    activeStop := na

    activeTarget := na

    activeSetup := ""

    activeEntryBar := na

    breakEvenActivated := false


// ============================================================================
// SECTION 65: FORCE CLOSE
// ============================================================================

bool forceCloseBar = (
     currentMinute < forceCloseTotal and
     currentCloseMinute >= forceCloseTotal
)


if forceCloseBar

    breakoutPendingDirection := 0

    breakoutPendingBar := na

    breakoutPendingStop := na


    if strategy.position_size != 0

        strategy.close_all(
             immediately=true,
             comment="EOD",
             alert_message="SESSION_END_CNPS36"
        )


// ============================================================================
// SECTION 66: CHANNEL VISUALS
// ============================================================================

plot(
     showChannel ? mid : na,
     title="Keltner Midline",
     color=color.gray,
     linewidth=1
)


plot(
     showChannel ? topInner : na,
     title="Keltner Upper Inner",
     color=color.new(color.red, 55),
     linewidth=1
)


plot(
     showChannel ? topOuter : na,
     title="Keltner Upper Outer",
     color=color.new(color.red, 20),
     linewidth=1
)


plot(
     showChannel ? bottomInner : na,
     title="Keltner Lower Inner",
     color=color.new(color.green, 55),
     linewidth=1
)


plot(
     showChannel ? bottomOuter : na,
     title="Keltner Lower Outer",
     color=color.new(color.green, 20),
     linewidth=1
)


// ============================================================================
// SECTION 67: LONG EMA
// ============================================================================

plot(
     showLongEMA ? longEMA : na,
     title="Long EMA",
     color=color.orange,
     linewidth=1
)


// ============================================================================
// SECTION 68: BREAKOUT BAND
// ============================================================================

plot(
     showBreakoutBand and useBandBreakout ? breakoutUpper : na,
     title="Breakout Upper Band",
     color=color.new(color.orange, 25),
     linewidth=1
)


plot(
     showBreakoutBand and useBandBreakout ? breakoutBasis : na,
     title="Breakout EMA",
     color=color.blue,
     linewidth=1
)


plot(
     showBreakoutBand and useBandBreakout ? breakoutLower : na,
     title="Breakout Lower Band",
     color=color.new(color.orange, 25),
     linewidth=1
)


// ============================================================================
// SECTION 69: ACTIVE SL / TP
// ============================================================================

plot(
     showActiveSLTP and strategy.position_size != 0 ? activeStop : na,
     title="Active Stop",
     color=color.red,
     linewidth=2,
     style=plot.style_linebr
)


plot(
     showActiveSLTP and strategy.position_size != 0 ? activeTarget : na,
     title="Active Target",
     color=color.green,
     linewidth=2,
     style=plot.style_linebr
)


// ============================================================================
// SECTION 70: ENTRY SIGNAL MARKERS
// ============================================================================

plotshape(
     showSignals and finalLongSignal,
     title="Long Entry",
     style=shape.triangleup,
     location=location.belowbar,
     color=color.green,
     size=size.small,
     text="L"
)


plotshape(
     showSignals and finalShortSignal,
     title="Short Entry",
     style=shape.triangledown,
     location=location.abovebar,
     color=color.red,
     size=size.small,
     text="S"
)


// ============================================================================
// SECTION 71: COUNTERS
// ============================================================================

var int reversalLongCount = 0

var int reversalShortCount = 0

var int pullbackLongCount = 0

var int pullbackShortCount = 0

var int breakoutLongCount = 0

var int breakoutShortCount = 0


if reversalLongSignal
    reversalLongCount += 1


if reversalShortSignal
    reversalShortCount += 1


if pullbackLongSignal
    pullbackLongCount += 1


if pullbackShortSignal
    pullbackShortCount += 1


if breakoutLongSignal
    breakoutLongCount += 1


if breakoutShortSignal
    breakoutShortCount += 1


// ============================================================================
// SECTION 72: DIAGNOSTICS
// ============================================================================

var table diagnostics = table.new(
     position.top_right,
     2,
     20,
     border_width=1
)


if showDiagnostics and barstate.islast

    string positionText = "FLAT"


    if inLong
        positionText := "LONG"

    else if inShort
        positionText := "SHORT"


    string trendText = "FLAT"


    if longTrendUp
        trendText := "UP"

    else if longTrendDown
        trendText := "DOWN"


    table.cell(diagnostics, 0, 0, "CNPS36")
    table.cell(diagnostics, 1, 0, timeframe.period)


    table.cell(diagnostics, 0, 1, "Mode")
    table.cell(diagnostics, 1, 1, signalMode)


    table.cell(diagnostics, 0, 2, "Trend")
    table.cell(diagnostics, 1, 2, trendText)


    table.cell(diagnostics, 0, 3, "%B")
    table.cell(diagnostics, 1, 3, str.tostring(percentB, "0.00"))


    table.cell(diagnostics, 0, 4, "WT1 / WT2")
    table.cell(diagnostics, 1, 4, str.tostring(wt1, "0.0") + " / " + str.tostring(wt2, "0.0"))


    table.cell(diagnostics, 0, 5, "WT Mode")
    table.cell(diagnostics, 1, 5, effectiveWTMode)


    table.cell(diagnostics, 0, 6, "ER")
    table.cell(diagnostics, 1, 6, str.tostring(efficiencyRatio, "0.00"))


    table.cell(diagnostics, 0, 7, "ADX")
    table.cell(diagnostics, 1, 7, str.tostring(adxValue, "0.0"))


    table.cell(diagnostics, 0, 8, "Trend Strength")
    table.cell(diagnostics, 1, 8, str.tostring(trendStrengthATR, "0.00") + " ATR")


    table.cell(diagnostics, 0, 9, "REV L / S")
    table.cell(diagnostics, 1, 9, str.tostring(reversalLongCount) + " / " + str.tostring(reversalShortCount))


    table.cell(diagnostics, 0, 10, "PB L / S")
    table.cell(diagnostics, 1, 10, str.tostring(pullbackLongCount) + " / " + str.tostring(pullbackShortCount))


    table.cell(diagnostics, 0, 11, "BO L / S")
    table.cell(diagnostics, 1, 11, str.tostring(breakoutLongCount) + " / " + str.tostring(breakoutShortCount))


    table.cell(diagnostics, 0, 12, "Selected Setup")
    table.cell(diagnostics, 1, 12, selectedSetup == "" ? "NONE" : selectedSetup)


    table.cell(diagnostics, 0, 13, "Prospective Risk")
    table.cell(diagnostics, 1, 13, str.tostring(selectedRisk, "0.00"))


    table.cell(diagnostics, 0, 14, "Source R:R")
    table.cell(diagnostics, 1, 14, str.tostring(selectedSourceRR, "0.00"))


    table.cell(diagnostics, 0, 15, "Active Setup")
    table.cell(diagnostics, 1, 15, activeSetup == "" ? "NONE" : activeSetup)


    table.cell(diagnostics, 0, 16, "Active Risk")
    table.cell(diagnostics, 1, 16, str.tostring(activeInitialRisk, "0.00"))


    table.cell(diagnostics, 0, 17, "Trades Today")
    table.cell(diagnostics, 1, 17, str.tostring(tradesToday) + " / " + str.tostring(maximumTradesPerDay))


    table.cell(diagnostics, 0, 18, "Core Exit")
    table.cell(diagnostics, 1, 18, coreExitMode)


    table.cell(diagnostics, 0, 19, "Position")
    table.cell(diagnostics, 1, 19, positionText)