//@version=6
strategy(
     title="CNPS34 V2 SUPERTREND FILTER LAB COST AWARE",
     shorttitle="CNPS34 V2 ST",
     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=100,
     max_lines_count=100,
     max_bars_back=5000
)


// ============================================================================
// CNPS34 V2 SUPERTREND FILTER LAB COST AWARE
//
// SOURCE ENTRY ENGINES:
//
// 1. Plain Supertrend
// 2. Triple Supertrend
// 3. Supertrend + EMA 200
// 4. Supertrend + Kumo
//
// V2 DOES NOT REPLACE THE SOURCE SIGNAL.
//
// Source signal = setup event.
//
// Additional filters decide whether that source event is good enough to
// become a VN30 trade.
//
// DEFAULT V2 CONFIG:
// - Triple Supertrend
// - EMA 200 alignment ON
// - EMA slope ON
// - ADX + DI alignment ON
// - Candle quality ON
// - ATR regime filter ON
// - Risk must be 2.5 -> 7 points
// - 2 trades/day
// - 8-bar cooldown
// - No reversal
// - 2R target
// - Break-even OFF
//
// CNPS:
// - 100M VND
// - 3 contracts
// - 10,000 commission / contract
// - Slippage 3 ticks
// ============================================================================


// ============================================================================
// SECTION 1: SOURCE VARIANT
// ============================================================================

strategyVariant = input.string(
     "Triple Supertrend",
     title="Strategy Variant",
     options=[
         "Plain Supertrend",
         "Triple Supertrend",
         "Supertrend + 200 EMA",
         "Supertrend + Kumo"
     ],
     group="Source Engine"
)


// ============================================================================
// SECTION 2: DEFAULT SUPERTREND
// ============================================================================

defaultATRLength = input.int(
     10,
     title="Default ST ATR Length",
     minval=1,
     maxval=200,
     group="Source Engine"
)

defaultFactor = input.float(
     3.0,
     title="Default ST Factor",
     minval=0.1,
     maxval=20.0,
     step=0.1,
     group="Source Engine"
)


// ============================================================================
// SECTION 3: TRIPLE SUPERTREND
// ============================================================================

tripleATR1 = input.int(
     12,
     title="Triple ST1 ATR",
     minval=1,
     maxval=200,
     group="Triple Supertrend"
)

tripleFactor1 = input.float(
     3.0,
     title="Triple ST1 Factor",
     minval=0.1,
     maxval=20.0,
     step=0.1,
     group="Triple Supertrend"
)

tripleATR2 = input.int(
     10,
     title="Triple ST2 ATR",
     minval=1,
     maxval=200,
     group="Triple Supertrend"
)

tripleFactor2 = input.float(
     1.0,
     title="Triple ST2 Factor",
     minval=0.1,
     maxval=20.0,
     step=0.1,
     group="Triple Supertrend"
)

tripleATR3 = input.int(
     11,
     title="Triple ST3 ATR",
     minval=1,
     maxval=200,
     group="Triple Supertrend"
)

tripleFactor3 = input.float(
     2.0,
     title="Triple ST3 Factor",
     minval=0.1,
     maxval=20.0,
     step=0.1,
     group="Triple Supertrend"
)


// ============================================================================
// SECTION 4: EMA / KUMO SOURCE PARAMETERS
// ============================================================================

emaLength = input.int(
     200,
     title="EMA Length",
     minval=2,
     maxval=1000,
     group="Source Filters"
)

tenkanLength = input.int(
     9,
     title="Kumo Conversion Length",
     minval=1,
     maxval=200,
     group="Source Filters"
)

kijunLength = input.int(
     26,
     title="Kumo Base Length",
     minval=1,
     maxval=200,
     group="Source Filters"
)

spanBLength = input.int(
     52,
     title="Kumo Span B Length",
     minval=1,
     maxval=400,
     group="Source Filters"
)

kumoDisplacement = input.int(
     26,
     title="Kumo Displacement",
     minval=1,
     maxval=100,
     group="Source Filters"
)


// ============================================================================
// SECTION 5: 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 6: EMA TREND FILTER
// ============================================================================

useEMAAlignment = input.bool(
     true,
     title="Use EMA Alignment?",
     group="Entry Quality"
)

trendEMALength = input.int(
     200,
     title="Trend EMA Length",
     minval=10,
     maxval=1000,
     group="Entry Quality"
)

requireEMASlope = input.bool(
     true,
     title="Require EMA Slope?",
     group="Entry Quality"
)

emaSlopeLookback = input.int(
     5,
     title="EMA Slope Lookback",
     minval=1,
     maxval=50,
     group="Entry Quality"
)


// ============================================================================
// SECTION 7: ADX / DI FILTER
// ============================================================================

useADXFilter = input.bool(
     true,
     title="Use ADX / DI Filter?",
     group="Entry Quality"
)

adxLength = input.int(
     14,
     title="ADX Length",
     minval=2,
     maxval=100,
     group="Entry Quality"
)

adxSmoothing = input.int(
     14,
     title="ADX Smoothing",
     minval=2,
     maxval=100,
     group="Entry Quality"
)

adxThreshold = input.float(
     20.0,
     title="Minimum ADX",
     minval=1.0,
     maxval=80.0,
     step=1.0,
     group="Entry Quality"
)

requireDIDirection = input.bool(
     true,
     title="Require DI Direction?",
     group="Entry Quality"
)


// ============================================================================
// SECTION 8: CANDLE QUALITY
// ============================================================================

useCandleQuality = input.bool(
     true,
     title="Use Candle Quality?",
     group="Entry Quality"
)

minimumBodyRatio = input.float(
     0.45,
     title="Minimum Body / Candle Range",
     minval=0.10,
     maxval=1.0,
     step=0.05,
     group="Entry Quality"
)

requireDirectionalCandle = input.bool(
     true,
     title="Require Directional Candle?",
     group="Entry Quality"
)

requirePreviousBreak = input.bool(
     false,
     title="Require Previous High / Low Break?",
     group="Entry Quality"
)


// ============================================================================
// SECTION 9: ATR REGIME FILTER
// ============================================================================

useATRRegimeFilter = input.bool(
     true,
     title="Use ATR Regime Filter?",
     group="Entry Quality"
)

atrRegimeLength = input.int(
     50,
     title="ATR Regime Average Length",
     minval=10,
     maxval=500,
     group="Entry Quality"
)

minimumATRRatio = input.float(
     0.75,
     title="Minimum ATR / ATR Average",
     minval=0.10,
     maxval=3.0,
     step=0.05,
     group="Entry Quality"
)

maximumATRRatio = input.float(
     1.80,
     title="Maximum ATR / ATR Average",
     minval=0.50,
     maxval=5.0,
     step=0.05,
     group="Entry Quality"
)


// ============================================================================
// SECTION 10: VOLUME FILTER
// ============================================================================

useVolumeFilter = input.bool(
     false,
     title="Use Volume Filter?",
     group="Entry Quality"
)

volumeLength = input.int(
     20,
     title="Volume Average Length",
     minval=5,
     maxval=200,
     group="Entry Quality"
)

minimumRelativeVolume = input.float(
     1.0,
     title="Minimum Relative Volume",
     minval=0.1,
     maxval=5.0,
     step=0.1,
     group="Entry Quality"
)


// ============================================================================
// SECTION 11: RISK MODEL
// ============================================================================

exitMode = input.string(
     "Risk Line + RR",
     title="Exit Mode",
     options=[
         "Risk Line + RR",
         "Source Variant Exit",
         "Fixed Points"
     ],
     group="Risk Management"
)

minimumRiskPoints = input.float(
     2.5,
     title="Minimum Source Risk",
     minval=0.5,
     step=0.5,
     group="Risk Management"
)

maximumRiskPoints = input.float(
     7.0,
     title="Maximum Source Risk",
     minval=1.0,
     step=0.5,
     group="Risk Management"
)

rewardRisk = input.float(
     2.0,
     title="Reward / Risk",
     minval=1.0,
     maxval=5.0,
     step=0.25,
     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(
     12.0,
     title="Fixed Take Profit",
     minval=0.5,
     step=0.5,
     group="Fixed Exit"
)


// ============================================================================
// SECTION 13: BREAK EVEN
// ============================================================================

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

breakEvenTriggerR = input.float(
     1.25,
     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 14: TRADE CONTROL
// ============================================================================

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

cooldownBars = input.int(
     8,
     title="Cooldown Bars After Exit",
     minval=0,
     maxval=100,
     group="Trade Control"
)


// ============================================================================
// SECTION 15: SESSION
// ============================================================================

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

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

morningStartMinute = input.int(
     5,
     title="Morning Start Minute",
     minval=0,
     maxval=59,
     group="Session"
)

morningEndHour = input.int(
     11,
     title="Morning End Hour",
     minval=0,
     maxval=23,
     group="Session"
)

morningEndMinute = input.int(
     20,
     title="Morning End Minute",
     minval=0,
     maxval=59,
     group="Session"
)

afternoonStartHour = input.int(
     13,
     title="Afternoon Start Hour",
     minval=0,
     maxval=23,
     group="Session"
)

afternoonStartMinute = input.int(
     5,
     title="Afternoon 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(
     20,
     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 16: ALERTS
// ============================================================================

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


// ============================================================================
// SECTION 17: DISPLAY
// ============================================================================

showSourceLines = input.bool(
     true,
     title="Show Source Lines?",
     group="Display"
)

showTrendEMA = input.bool(
     false,
     title="Show Trend EMA?",
     group="Display"
)

showSignals = input.bool(
     false,
     title="Show Entry Signals?",
     group="Display"
)

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

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


// ============================================================================
// SECTION 18: SOURCE SUPERTRENDS
// ============================================================================

[st0, dir0] = ta.supertrend(defaultFactor, defaultATRLength)

[st1, dir1] = ta.supertrend(tripleFactor1, tripleATR1)

[st2, dir2] = ta.supertrend(tripleFactor2, tripleATR2)

[st3, dir3] = ta.supertrend(tripleFactor3, tripleATR3)


int u0 = na(dir0) ? 0 : dir0 < 0 ? 1 : -1

int u1 = na(dir1) ? 0 : dir1 < 0 ? 1 : -1

int u2 = na(dir2) ? 0 : dir2 < 0 ? 1 : -1

int u3 = na(dir3) ? 0 : dir3 < 0 ? 1 : -1


// ============================================================================
// SECTION 19: SOURCE STATES
// ============================================================================

bool defaultReady = u0 != 0 and u0[1] != 0

bool defaultFlip = defaultReady and u0 != u0[1]


bool tripleAgreeNow = u1 != 0 and u1 == u2 and u2 == u3

bool tripleAgreePrev = u1[1] != 0 and u1[1] == u2[1] and u2[1] == u3[1]

int tripleSide = tripleAgreeNow ? u1 : 0

int triplePrevSide = tripleAgreePrev ? u1[1] : 0


// ============================================================================
// SECTION 20: EMA SOURCE
// ============================================================================

float sourceEMA = ta.ema(close, emaLength)

bool sourceEMALong = u0 == 1 and close > sourceEMA

bool sourceEMAShort = u0 == -1 and close < sourceEMA


// ============================================================================
// SECTION 21: KUMO SOURCE
// ============================================================================

f_donchian(int length) =>
    math.avg(ta.lowest(low, length), ta.highest(high, length))


float conversionLine = f_donchian(tenkanLength)

float baseLine = f_donchian(kijunLength)

float leadA = math.avg(conversionLine, baseLine)

float leadB = f_donchian(spanBLength)

float cloudA = leadA[kumoDisplacement - 1]

float cloudB = leadB[kumoDisplacement - 1]

float cloudTop = math.max(cloudA, cloudB)

float cloudBottom = math.min(cloudA, cloudB)


bool sourceKumoLong = u0 == 1 and not na(cloudTop) and close > cloudTop

bool sourceKumoShort = u0 == -1 and not na(cloudBottom) and close < cloudBottom


// ============================================================================
// SECTION 22: RAW SOURCE SIGNAL
// ============================================================================

int sourceSignalSide = 0


if strategyVariant == "Plain Supertrend"

    if defaultFlip
        sourceSignalSide := u0


else if strategyVariant == "Triple Supertrend"

    if tripleAgreeNow and triplePrevSide != tripleSide
        sourceSignalSide := tripleSide


else if strategyVariant == "Supertrend + 200 EMA"

    if sourceEMALong and not sourceEMALong[1]
        sourceSignalSide := 1

    else if sourceEMAShort and not sourceEMAShort[1]
        sourceSignalSide := -1


else

    if sourceKumoLong and not sourceKumoLong[1]
        sourceSignalSide := 1

    else if sourceKumoShort and not sourceKumoShort[1]
        sourceSignalSide := -1


// ============================================================================
// SECTION 23: SOURCE VARIANT EXIT
// ============================================================================

bool sourceLongExit = false

bool sourceShortExit = false


if strategyVariant == "Plain Supertrend"

    sourceLongExit := defaultFlip
    sourceShortExit := defaultFlip


else if strategyVariant == "Triple Supertrend"

    sourceLongExit := not (tripleAgreeNow and tripleSide == 1)
    sourceShortExit := not (tripleAgreeNow and tripleSide == -1)


else if strategyVariant == "Supertrend + 200 EMA"

    sourceLongExit := defaultFlip
    sourceShortExit := defaultFlip


else

    sourceLongExit := not na(cloudTop) and close <= cloudTop
    sourceShortExit := not na(cloudBottom) and close >= cloudBottom


// ============================================================================
// SECTION 24: SOURCE RISK LINE
// ============================================================================

float sourceRiskLine = strategyVariant == "Triple Supertrend" ? st1 : st0

float sourceRiskDistance = na


if not na(sourceRiskLine)
    sourceRiskDistance := math.max(math.abs(close - sourceRiskLine), syminfo.mintick * 5.0)


// ============================================================================
// SECTION 25: TREND FILTERS
// ============================================================================

float trendEMA = ta.ema(close, trendEMALength)


bool longEMAAlignment = (
     not useEMAAlignment or
     close > trendEMA
)


bool shortEMAAlignment = (
     not useEMAAlignment or
     close < trendEMA
)


bool longEMASlope = (
     not requireEMASlope or
     trendEMA > trendEMA[emaSlopeLookback]
)


bool shortEMASlope = (
     not requireEMASlope or
     trendEMA < trendEMA[emaSlopeLookback]
)


// ============================================================================
// SECTION 26: ADX / DI
// ============================================================================

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


bool longADXOK = (
     not useADXFilter or
     (
         adxValue >= adxThreshold and
         (
             not requireDIDirection or
             diPlus > diMinus
         )
     )
)


bool shortADXOK = (
     not useADXFilter or
     (
         adxValue >= adxThreshold and
         (
             not requireDIDirection or
             diMinus > diPlus
         )
     )
)


// ============================================================================
// SECTION 27: CANDLE QUALITY
// ============================================================================

float candleRange = math.max(high - low, syminfo.mintick)

float candleBody = math.abs(close - open)

float bodyRatio = candleBody / candleRange


bool longCandleOK = (
     not useCandleQuality or
     (
         bodyRatio >= minimumBodyRatio and
         (
             not requireDirectionalCandle or
             close > open
         ) and
         (
             not requirePreviousBreak or
             close > high[1]
         )
     )
)


bool shortCandleOK = (
     not useCandleQuality or
     (
         bodyRatio >= minimumBodyRatio and
         (
             not requireDirectionalCandle or
             close < open
         ) and
         (
             not requirePreviousBreak or
             close < low[1]
         )
     )
)


// ============================================================================
// SECTION 28: ATR REGIME
// ============================================================================

float atrValue = ta.atr(defaultATRLength)

float atrAverage = ta.sma(atrValue, atrRegimeLength)

float atrRatio = 1.0


if not na(atrAverage) and atrAverage > 0
    atrRatio := atrValue / atrAverage


bool atrRegimeOK = (
     not useATRRegimeFilter or
     (
         atrRatio >= minimumATRRatio and
         atrRatio <= maximumATRRatio
     )
)


// ============================================================================
// SECTION 29: VOLUME
// ============================================================================

float volumeAverage = ta.sma(volume, volumeLength)

float relativeVolume = 1.0


if not na(volumeAverage) and volumeAverage > 0
    relativeVolume := volume / volumeAverage


bool volumeOK = (
     not useVolumeFilter or
     relativeVolume >= minimumRelativeVolume
)


// ============================================================================
// SECTION 30: SOURCE RISK VALIDATION
// ============================================================================

bool sourceRiskOK = (
     not na(sourceRiskDistance) and
     sourceRiskDistance >= minimumRiskPoints and
     sourceRiskDistance <= maximumRiskPoints
)


// ============================================================================
// SECTION 31: LONG QUALITY
// ============================================================================

bool longQualityOK = (
     longEMAAlignment and
     longEMASlope and
     longADXOK and
     longCandleOK and
     atrRegimeOK and
     volumeOK and
     sourceRiskOK
)


// ============================================================================
// SECTION 32: SHORT QUALITY
// ============================================================================

bool shortQualityOK = (
     shortEMAAlignment and
     shortEMASlope and
     shortADXOK and
     shortCandleOK and
     atrRegimeOK and
     volumeOK and
     sourceRiskOK
)


// ============================================================================
// SECTION 33: SESSION
// ============================================================================

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

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

int morningStartTotal = morningStartHour * 60 + morningStartMinute

int morningEndTotal = morningEndHour * 60 + morningEndMinute

int afternoonStartTotal = afternoonStartHour * 60 + afternoonStartMinute

int lastEntryTotal = lastEntryHour * 60 + lastEntryMinute

int forceCloseTotal = forceCloseHour * 60 + forceCloseMinute


bool morningSession = currentMinute >= morningStartTotal and currentMinute <= morningEndTotal

bool afternoonSession = currentMinute >= afternoonStartTotal and currentMinute <= lastEntryTotal

bool entrySession = morningSession or afternoonSession


// ============================================================================
// SECTION 34: DAILY STATE
// ============================================================================

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


// ============================================================================
// SECTION 35: 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 36: COOLDOWN
// ============================================================================

var int lastExitBar = na


if positionJustClosed
    lastExitBar := bar_index


bool cooldownOK = na(lastExitBar) or bar_index - lastExitBar > cooldownBars

bool tradeLimitOK = tradesToday < maximumTradesPerDay


// ============================================================================
// SECTION 37: ENTRY APPROVAL
// ============================================================================

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


bool finalLongSignal = (
     commonEntryOK and
     allowLong and
     sourceSignalSide == 1 and
     longQualityOK
)


bool finalShortSignal = (
     commonEntryOK and
     allowShort and
     sourceSignalSide == -1 and
     shortQualityOK
)


// ============================================================================
// SECTION 38: PENDING TRADE RISK
// ============================================================================

var float pendingRisk = na


// ============================================================================
// SECTION 39: LONG ENTRY
// ============================================================================

if finalLongSignal

    if exitMode == "Fixed Points"
        pendingRisk := fixedStopLossPoints
    else
        pendingRisk := sourceRiskDistance


    strategy.entry(
         "Long",
         strategy.long,
         qty=3,
         comment="ST L",
         alert_message="ENTRY_LONG_CNPS35_V2"
    )


    tradesToday += 1


// ============================================================================
// SECTION 40: SHORT ENTRY
// ============================================================================

if finalShortSignal

    if exitMode == "Fixed Points"
        pendingRisk := fixedStopLossPoints
    else
        pendingRisk := sourceRiskDistance


    strategy.entry(
         "Short",
         strategy.short,
         qty=3,
         comment="ST S",
         alert_message="ENTRY_SHORT_CNPS35_V2"
    )


    tradesToday += 1


// ============================================================================
// SECTION 41: ACTIVE TRADE STATE
// ============================================================================

var float activeEntryPrice = na

var float activeInitialRisk = na

var float activeStop = na

var float activeTarget = na

var int activeEntryBar = na

var bool breakEvenActivated = false


// ============================================================================
// SECTION 42: INITIALIZE LONG
// ============================================================================

if newLongPosition

    activeEntryPrice := strategy.position_avg_price

    activeEntryBar := bar_index

    activeInitialRisk := pendingRisk

    breakEvenActivated := false


    if na(activeInitialRisk) or activeInitialRisk <= 0
        activeInitialRisk := fixedStopLossPoints


    activeStop := activeEntryPrice - activeInitialRisk


    if exitMode == "Risk Line + RR"

        activeTarget := activeEntryPrice + activeInitialRisk * rewardRisk


    else if exitMode == "Fixed Points"

        activeTarget := activeEntryPrice + fixedTakeProfitPoints


    else

        activeTarget := na


    pendingRisk := na


// ============================================================================
// SECTION 43: INITIALIZE SHORT
// ============================================================================

if newShortPosition

    activeEntryPrice := strategy.position_avg_price

    activeEntryBar := bar_index

    activeInitialRisk := pendingRisk

    breakEvenActivated := false


    if na(activeInitialRisk) or activeInitialRisk <= 0
        activeInitialRisk := fixedStopLossPoints


    activeStop := activeEntryPrice + activeInitialRisk


    if exitMode == "Risk Line + RR"

        activeTarget := activeEntryPrice - activeInitialRisk * rewardRisk


    else if exitMode == "Fixed Points"

        activeTarget := activeEntryPrice - fixedTakeProfitPoints


    else

        activeTarget := na


    pendingRisk := na


// ============================================================================
// SECTION 44: 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 newStop = activeEntryPrice + breakEvenOffsetPoints

        activeStop := math.max(activeStop, newStop)

        breakEvenActivated := true


// ============================================================================
// SECTION 45: 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 newStop = activeEntryPrice - breakEvenOffsetPoints

        activeStop := math.min(activeStop, newStop)

        breakEvenActivated := true


// ============================================================================
// SECTION 46: LONG PROTECTIVE EXIT
// ============================================================================

if (
     inLong and
     not na(activeStop)
)

    strategy.exit(
         "Long Exit",
         from_entry="Long",
         stop=activeStop,
         limit=activeTarget,
         alert_profit="EXIT_LONG_TP_CNPS35_V2",
         alert_loss=breakEvenActivated ? "EXIT_LONG_BE_CNPS35_V2" : "EXIT_LONG_SL_CNPS35_V2",
         disable_alert=disableSLTPAlerts
    )


// ============================================================================
// SECTION 47: SHORT PROTECTIVE EXIT
// ============================================================================

if (
     inShort and
     not na(activeStop)
)

    strategy.exit(
         "Short Exit",
         from_entry="Short",
         stop=activeStop,
         limit=activeTarget,
         alert_profit="EXIT_SHORT_TP_CNPS35_V2",
         alert_loss=breakEvenActivated ? "EXIT_SHORT_BE_CNPS35_V2" : "EXIT_SHORT_SL_CNPS35_V2",
         disable_alert=disableSLTPAlerts
    )


// ============================================================================
// SECTION 48: SOURCE VARIANT EXIT MODE
// ============================================================================
//
// Only active when explicitly selected.
//
// Protective stop still remains active.
//
// ============================================================================

bool longVariantExit = (
     inLong and
     exitMode == "Source Variant Exit" and
     sourceLongExit and
     not na(activeEntryBar) and
     bar_index > activeEntryBar
)


bool shortVariantExit = (
     inShort and
     exitMode == "Source Variant Exit" and
     sourceShortExit and
     not na(activeEntryBar) and
     bar_index > activeEntryBar
)


if longVariantExit

    strategy.close(
         "Long",
         comment="ST Exit",
         alert_message="VARIANT_EXIT_LONG_CNPS35_V2"
    )


if shortVariantExit

    strategy.close(
         "Short",
         comment="ST Exit",
         alert_message="VARIANT_EXIT_SHORT_CNPS35_V2"
    )


// ============================================================================
// SECTION 49: CLEANUP
// ============================================================================

if positionJustClosed

    activeEntryPrice := na

    activeInitialRisk := na

    activeStop := na

    activeTarget := na

    activeEntryBar := na

    breakEvenActivated := false


// ============================================================================
// SECTION 50: FORCE CLOSE
// ============================================================================

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


if forceCloseBar

    if strategy.position_size != 0

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


// ============================================================================
// SECTION 51: SOURCE VISUALS
// ============================================================================

bool drawPlain = strategyVariant == "Plain Supertrend"

bool drawTriple = strategyVariant == "Triple Supertrend"

bool drawEMA = strategyVariant == "Supertrend + 200 EMA"

bool drawKumo = strategyVariant == "Supertrend + Kumo"

bool drawDefault = drawPlain or drawEMA or drawKumo


plot(
     showSourceLines and drawDefault ? st0 : na,
     title="Default Supertrend",
     color=u0 == 1 ? color.green : color.red,
     linewidth=2,
     style=plot.style_linebr
)


plot(
     showSourceLines and drawTriple ? st1 : na,
     title="Triple ST 12/3",
     color=u1 == 1 ? color.green : color.red,
     linewidth=2,
     style=plot.style_linebr
)


plot(
     showSourceLines and drawTriple ? st2 : na,
     title="Triple ST 10/1",
     color=color.new(u2 == 1 ? color.green : color.red, 55),
     linewidth=1,
     style=plot.style_linebr
)


plot(
     showSourceLines and drawTriple ? st3 : na,
     title="Triple ST 11/2",
     color=color.new(u3 == 1 ? color.green : color.red, 30),
     linewidth=1,
     style=plot.style_linebr
)


plot(
     showSourceLines and drawEMA ? sourceEMA : na,
     title="Source EMA",
     color=color.blue,
     linewidth=2
)


plot(
     showTrendEMA ? trendEMA : na,
     title="Trend EMA",
     color=color.orange,
     linewidth=1
)


// ============================================================================
// SECTION 52: KUMO VISUALS
// ============================================================================

plotA = plot(
     showSourceLines and drawKumo ? leadA : na,
     title="Kumo Lead A",
     color=color.new(color.green, 50),
     offset=kumoDisplacement - 1
)


plotB = plot(
     showSourceLines and drawKumo ? leadB : na,
     title="Kumo Lead B",
     color=color.new(color.red, 50),
     offset=kumoDisplacement - 1
)


fill(
     plotA,
     plotB,
     color=leadA > leadB ? color.new(color.green, 90) : color.new(color.red, 90)
)


// ============================================================================
// SECTION 53: 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 54: ENTRY 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 55: DIAGNOSTIC COUNTERS
// ============================================================================

var int rawLongSignals = 0

var int rawShortSignals = 0

var int qualifiedLongSignals = 0

var int qualifiedShortSignals = 0


if sourceSignalSide == 1
    rawLongSignals += 1


if sourceSignalSide == -1
    rawShortSignals += 1


if finalLongSignal
    qualifiedLongSignals += 1


if finalShortSignal
    qualifiedShortSignals += 1


// ============================================================================
// SECTION 56: 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"


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


    table.cell(diagnostics, 0, 1, "Variant")
    table.cell(diagnostics, 1, 1, strategyVariant)


    table.cell(diagnostics, 0, 2, "Raw Signal")
    table.cell(
         diagnostics,
         1,
         2,
         sourceSignalSide == 1 ? "LONG" : sourceSignalSide == -1 ? "SHORT" : "NONE"
    )


    table.cell(diagnostics, 0, 3, "EMA Long OK")
    table.cell(diagnostics, 1, 3, longEMAAlignment and longEMASlope ? "YES" : "NO")


    table.cell(diagnostics, 0, 4, "EMA Short OK")
    table.cell(diagnostics, 1, 4, shortEMAAlignment and shortEMASlope ? "YES" : "NO")


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


    table.cell(diagnostics, 0, 6, "DI+ / DI-")
    table.cell(diagnostics, 1, 6, str.tostring(diPlus, "0.0") + " / " + str.tostring(diMinus, "0.0"))


    table.cell(diagnostics, 0, 7, "Body Ratio")
    table.cell(diagnostics, 1, 7, str.tostring(bodyRatio, "0.00"))


    table.cell(diagnostics, 0, 8, "ATR Ratio")
    table.cell(diagnostics, 1, 8, str.tostring(atrRatio, "0.00"))


    table.cell(diagnostics, 0, 9, "Rel Volume")
    table.cell(diagnostics, 1, 9, str.tostring(relativeVolume, "0.00"))


    table.cell(diagnostics, 0, 10, "Source Risk")
    table.cell(diagnostics, 1, 10, str.tostring(sourceRiskDistance, "0.00"))


    table.cell(diagnostics, 0, 11, "Risk OK")
    table.cell(diagnostics, 1, 11, sourceRiskOK ? "YES" : "NO")


    table.cell(diagnostics, 0, 12, "Raw L / S")
    table.cell(diagnostics, 1, 12, str.tostring(rawLongSignals) + " / " + str.tostring(rawShortSignals))


    table.cell(diagnostics, 0, 13, "Entry L / S")
    table.cell(diagnostics, 1, 13, str.tostring(qualifiedLongSignals) + " / " + str.tostring(qualifiedShortSignals))


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


    table.cell(diagnostics, 0, 15, "Target R")
    table.cell(diagnostics, 1, 15, str.tostring(rewardRisk, "0.00"))


    table.cell(diagnostics, 0, 16, "Exit Mode")
    table.cell(diagnostics, 1, 16, exitMode)


    table.cell(diagnostics, 0, 17, "Cooldown")
    table.cell(diagnostics, 1, 17, str.tostring(cooldownBars))


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


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