//@version=6
strategy(
     title="CNPS22 BASTION V4 COST-AWARE",
     shorttitle="CNPS22 BASTION V4",
     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=300,
     max_lines_count=300,
     max_bars_back=5000
)


// ============================================================================
// CNPS22 BASTION V4 COST-AWARE
//
// CORE IDEA:
//
// TREND REGIME
// -> STRONG BOS
// -> DO NOT CHASE
// -> WAIT FOR BOS RETEST
// -> HOLD BOS
// -> CONTINUATION CONFIRMATION
// -> ENTRY
//
// COST-AWARE CHANGES:
// - Maximum 2 trades / day
// - One attempt per BOS
// - No break-even
// - No time stop
// - No regime market exit
// - No synthetic CVD
// - No SMI gate
// - Wider payoff: 2.5R default
//
// CURRENT CHART TIMEFRAME
// POSITION: 3 CONTRACTS
// ============================================================================


// ============================================================================
// SECTION 1: TRADE CONTROL
// ============================================================================

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"

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

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


// ============================================================================
// SECTION 2: TREND REGIME
// ============================================================================

fastEMALength = input.int(
     20,
     title="Fast EMA",
     minval=2,
     group="Trend Regime"
)

slowEMALength = input.int(
     50,
     title="Slow EMA",
     minval=5,
     group="Trend Regime"
)

emaSlopeLookback = input.int(
     5,
     title="Fast EMA Slope Lookback",
     minval=1,
     maxval=50,
     group="Trend Regime"
)

useVWAPFilter = input.bool(
     true,
     title="Require Price On Correct Side Of VWAP?",
     group="Trend Regime"
)


// ============================================================================
// SECTION 3: STRUCTURE
// ============================================================================

swingLength = input.int(
     4,
     title="Swing Lookback",
     minval=2,
     maxval=15,
     group="Structure"
)

minimumBOSDistanceATR = input.float(
     0.10,
     title="Minimum Close Beyond Swing (ATR)",
     minval=0.0,
     maxval=2.0,
     step=0.05,
     group="Structure"
)

setupLifetimeBars = input.int(
     12,
     title="Maximum Bars To Retest BOS",
     minval=2,
     maxval=50,
     group="Structure"
)


// ============================================================================
// SECTION 4: BOS QUALITY
// ============================================================================

minimumBOSBodyRatio = input.float(
     0.60,
     title="Minimum BOS Body / Range",
     minval=0.20,
     maxval=1.0,
     step=0.05,
     group="BOS Quality"
)

minimumBOSRangeATR = input.float(
     0.80,
     title="Minimum BOS Range / ATR",
     minval=0.20,
     maxval=5.0,
     step=0.10,
     group="BOS Quality"
)

useVolumeExpansion = input.bool(
     true,
     title="Require BOS Volume Expansion?",
     group="BOS Quality"
)

volumeLookback = input.int(
     20,
     title="Volume Average Length",
     minval=2,
     maxval=100,
     group="BOS Quality"
)

volumeMultiplier = input.float(
     1.15,
     title="BOS Volume Multiplier",
     minval=0.5,
     maxval=5.0,
     step=0.05,
     group="BOS Quality"
)


// ============================================================================
// SECTION 5: RETEST
// ============================================================================

retestToleranceATR = input.float(
     0.25,
     title="Retest Tolerance Above BOS (ATR)",
     minval=0.0,
     maxval=2.0,
     step=0.05,
     group="Retest"
)

maximumBOSFailureATR = input.float(
     0.35,
     title="Maximum Close Through BOS (ATR)",
     minval=0.0,
     maxval=2.0,
     step=0.05,
     group="Retest"
)


// ============================================================================
// SECTION 6: CONFIRMATION
// ============================================================================

minimumConfirmationBodyRatio = input.float(
     0.50,
     title="Confirmation Body / Range",
     minval=0.10,
     maxval=1.0,
     step=0.05,
     group="Confirmation"
)

requirePreviousHighLowBreak = input.bool(
     true,
     title="Require Previous Candle High / Low Break?",
     group="Confirmation"
)

requireFastEMA = input.bool(
     true,
     title="Require Close Back Beyond Fast EMA?",
     group="Confirmation"
)


// ============================================================================
// SECTION 7: RSI
// ============================================================================

useRSI = input.bool(
     true,
     title="Use RSI Confirmation?",
     group="Momentum"
)

rsiLength = input.int(
     14,
     title="RSI Length",
     minval=2,
     maxval=50,
     group="Momentum"
)

rsiLongMinimum = input.float(
     52.0,
     title="Long RSI Minimum",
     minval=40.0,
     maxval=80.0,
     step=1.0,
     group="Momentum"
)

rsiShortMaximum = input.float(
     48.0,
     title="Short RSI Maximum",
     minval=20.0,
     maxval=60.0,
     step=1.0,
     group="Momentum"
)


// ============================================================================
// SECTION 8: RISK
// ============================================================================

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

minimumStopPoints = input.float(
     3.0,
     title="Minimum Stop Points",
     minval=0.5,
     step=0.5,
     group="Risk Management"
)

maximumStopPoints = input.float(
     6.0,
     title="Maximum Allowed Stop Points",
     minval=1.0,
     step=0.5,
     group="Risk Management"
)

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


// ============================================================================
// SECTION 9: SESSION
// ============================================================================

strategyTimezone = input.string(
     "Asia/Ho_Chi_Minh",
     title="Strategy 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"
)

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

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


// ============================================================================
// SECTION 10: ALERTS
// ============================================================================

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


// ============================================================================
// SECTION 11: DISPLAY
// ============================================================================

showTrend = input.bool(
     false,
     title="Show EMA / VWAP?",
     group="Display"
)

showStructure = input.bool(
     false,
     title="Show Swing / BOS Levels?",
     group="Display"
)

showSetupLevels = input.bool(
     false,
     title="Show Active Setup Level?",
     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 12: TIME ENGINE
// ============================================================================

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 sessionEndTotal = sessionEndHour * 60 + sessionEndMinute

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

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

bool entrySession = morningWindow or afternoonWindow


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


// ============================================================================
// SECTION 14: CORE INDICATORS
// ============================================================================

float atrValue = ta.atr(14)

float emaFast = ta.ema(close, fastEMALength)

float emaSlow = ta.ema(close, slowEMALength)

float vwapValue = ta.vwap(hlc3)

float rsiValue = ta.rsi(close, rsiLength)

float averageVolume = ta.sma(volume, volumeLookback)


// ============================================================================
// SECTION 15: TREND REGIME
// ============================================================================

bool emaFastRising = emaFast > emaFast[emaSlopeLookback]

bool emaFastFalling = emaFast < emaFast[emaSlopeLookback]

bool vwapLongOK = not useVWAPFilter or close > vwapValue

bool vwapShortOK = not useVWAPFilter or close < vwapValue

bool bullishRegime = emaFast > emaSlow and emaFastRising and vwapLongOK

bool bearishRegime = emaFast < emaSlow and emaFastFalling and vwapShortOK


// ============================================================================
// SECTION 16: STRUCTURE
// ============================================================================

float pivotHigh = ta.pivothigh(high, swingLength, swingLength)

float pivotLow = ta.pivotlow(low, swingLength, swingLength)

var float lastSwingHigh = na

var float lastSwingLow = na

if not na(pivotHigh)
    lastSwingHigh := pivotHigh

if not na(pivotLow)
    lastSwingLow := pivotLow


// ============================================================================
// SECTION 17: CANDLE QUALITY
// ============================================================================

float candleRange = high - low

float candleBody = math.abs(close - open)

float bodyRatio = candleRange > 0 ? candleBody / candleRange : 0.0

bool bullishCandle = close > open

bool bearishCandle = close < open

bool confirmationBodyOK = bodyRatio >= minimumConfirmationBodyRatio


// ============================================================================
// SECTION 18: BOS QUALITY
// ============================================================================

bool bosBodyOK = bodyRatio >= minimumBOSBodyRatio

bool bosRangeOK = atrValue > 0 and candleRange >= atrValue * minimumBOSRangeATR

bool bosVolumeOK = not useVolumeExpansion or volume >= averageVolume * volumeMultiplier


// ============================================================================
// SECTION 19: RAW BOS EVENT
// ============================================================================

float bullishBreakDistance = atrValue * minimumBOSDistanceATR

float bearishBreakDistance = atrValue * minimumBOSDistanceATR

bool bullishBOS = (
     barstate.isconfirmed and
     bullishRegime and
     bullishCandle and
     bosBodyOK and
     bosRangeOK and
     bosVolumeOK and
     not na(lastSwingHigh) and
     close > lastSwingHigh + bullishBreakDistance and
     close[1] <= lastSwingHigh
)

bool bearishBOS = (
     barstate.isconfirmed and
     bearishRegime and
     bearishCandle and
     bosBodyOK and
     bosRangeOK and
     bosVolumeOK and
     not na(lastSwingLow) and
     close < lastSwingLow - bearishBreakDistance and
     close[1] >= lastSwingLow
)


// ============================================================================
// SECTION 20: SETUP STATE
// ============================================================================

var bool longSetupArmed = false

var bool shortSetupArmed = false

var int longBOSBar = na

var int shortBOSBar = na

var float longBOSLevel = na

var float shortBOSLevel = na

var bool longRetestSeen = false

var bool shortRetestSeen = false

var float longRetestLow = na

var float shortRetestHigh = na


// ============================================================================
// SECTION 21: ARM LONG BOS
// ============================================================================

if bullishBOS and strategy.position_size == 0 and entrySession

    longSetupArmed := true
    longBOSBar := bar_index
    longBOSLevel := lastSwingHigh

    longRetestSeen := false
    longRetestLow := na

    shortSetupArmed := false
    shortRetestSeen := false
    shortRetestHigh := na


// ============================================================================
// SECTION 22: ARM SHORT BOS
// ============================================================================

if bearishBOS and strategy.position_size == 0 and entrySession

    shortSetupArmed := true
    shortBOSBar := bar_index
    shortBOSLevel := lastSwingLow

    shortRetestSeen := false
    shortRetestHigh := na

    longSetupArmed := false
    longRetestSeen := false
    longRetestLow := na


// ============================================================================
// SECTION 23: SETUP EXPIRATION
// ============================================================================

bool longExpired = longSetupArmed and not na(longBOSBar) and bar_index - longBOSBar > setupLifetimeBars

bool shortExpired = shortSetupArmed and not na(shortBOSBar) and bar_index - shortBOSBar > setupLifetimeBars

if longExpired

    longSetupArmed := false
    longRetestSeen := false
    longRetestLow := na


if shortExpired

    shortSetupArmed := false
    shortRetestSeen := false
    shortRetestHigh := na


// ============================================================================
// SECTION 24: REGIME INVALIDATION
// ============================================================================

if longSetupArmed and not bullishRegime

    longSetupArmed := false
    longRetestSeen := false
    longRetestLow := na


if shortSetupArmed and not bearishRegime

    shortSetupArmed := false
    shortRetestSeen := false
    shortRetestHigh := na


// ============================================================================
// SECTION 25: BOS FAILURE INVALIDATION
// ============================================================================

float longFailureLevel = na

if longSetupArmed and not na(longBOSLevel)
    longFailureLevel := longBOSLevel - atrValue * maximumBOSFailureATR


float shortFailureLevel = na

if shortSetupArmed and not na(shortBOSLevel)
    shortFailureLevel := shortBOSLevel + atrValue * maximumBOSFailureATR


if longSetupArmed and not na(longFailureLevel) and close < longFailureLevel

    longSetupArmed := false
    longRetestSeen := false
    longRetestLow := na


if shortSetupArmed and not na(shortFailureLevel) and close > shortFailureLevel

    shortSetupArmed := false
    shortRetestSeen := false
    shortRetestHigh := na


// ============================================================================
// SECTION 26: LONG RETEST
// ============================================================================

float longRetestUpper = na

if longSetupArmed and not na(longBOSLevel)
    longRetestUpper := longBOSLevel + atrValue * retestToleranceATR


bool longTouchesRetest = (
     longSetupArmed and
     not na(longBOSBar) and
     bar_index > longBOSBar and
     not na(longRetestUpper) and
     low <= longRetestUpper
)

if longTouchesRetest

    longRetestSeen := true

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


// ============================================================================
// SECTION 27: SHORT RETEST
// ============================================================================

float shortRetestLower = na

if shortSetupArmed and not na(shortBOSLevel)
    shortRetestLower := shortBOSLevel - atrValue * retestToleranceATR


bool shortTouchesRetest = (
     shortSetupArmed and
     not na(shortBOSBar) and
     bar_index > shortBOSBar and
     not na(shortRetestLower) and
     high >= shortRetestLower
)

if shortTouchesRetest

    shortRetestSeen := true

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


// ============================================================================
// SECTION 28: MOMENTUM CONFIRMATION
// ============================================================================

bool longRSIOK = not useRSI or rsiValue >= rsiLongMinimum

bool shortRSIOK = not useRSI or rsiValue <= rsiShortMaximum


// ============================================================================
// SECTION 29: PRICE CONFIRMATION
// ============================================================================

bool longPreviousBreakOK = not requirePreviousHighLowBreak or close > high[1]

bool shortPreviousBreakOK = not requirePreviousHighLowBreak or close < low[1]

bool longFastEMAOK = not requireFastEMA or close > emaFast

bool shortFastEMAOK = not requireFastEMA or close < emaFast


// ============================================================================
// SECTION 30: RAW LONG CONTINUATION
// ============================================================================

bool rawLongSignal = (
     longSetupArmed and
     longRetestSeen and
     bullishRegime and
     bullishCandle and
     confirmationBodyOK and
     not na(longBOSLevel) and
     close > longBOSLevel and
     longPreviousBreakOK and
     longFastEMAOK and
     longRSIOK and
     barstate.isconfirmed
)


// ============================================================================
// SECTION 31: RAW SHORT CONTINUATION
// ============================================================================

bool rawShortSignal = (
     shortSetupArmed and
     shortRetestSeen and
     bearishRegime and
     bearishCandle and
     confirmationBodyOK and
     not na(shortBOSLevel) and
     close < shortBOSLevel and
     shortPreviousBreakOK and
     shortFastEMAOK and
     shortRSIOK and
     barstate.isconfirmed
)


// ============================================================================
// SECTION 32: PROSPECTIVE STOPS
// ============================================================================

float prospectiveLongStop = na

if rawLongSignal and not na(longRetestLow)
    prospectiveLongStop := longRetestLow - structureStopBuffer


float prospectiveShortStop = na

if rawShortSignal and not na(shortRetestHigh)
    prospectiveShortStop := shortRetestHigh + structureStopBuffer


float prospectiveLongRisk = na

if not na(prospectiveLongStop)
    prospectiveLongRisk := close - prospectiveLongStop


float prospectiveShortRisk = na

if not na(prospectiveShortStop)
    prospectiveShortRisk := prospectiveShortStop - close


// ============================================================================
// SECTION 33: RISK QUALITY FILTER
// ============================================================================

bool longRiskValid = (
     not na(prospectiveLongRisk) and
     prospectiveLongRisk > 0 and
     prospectiveLongRisk <= maximumStopPoints
)

bool shortRiskValid = (
     not na(prospectiveShortRisk) and
     prospectiveShortRisk > 0 and
     prospectiveShortRisk <= maximumStopPoints
)


// ============================================================================
// SECTION 34: 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 35: ENTRY CONTROL
// ============================================================================

var int lastEntryBar = na

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

bool tradeLimitOK = tradesToday < maximumTradesPerDay

bool commonEntryApproval = (
     flat and
     entrySession and
     currentCloseMinute < sessionEndTotal and
     cooldownOK and
     tradeLimitOK and
     barstate.isconfirmed
)

bool longCandidate = (
     commonEntryApproval and
     allowLong and
     rawLongSignal and
     longRiskValid
)

bool shortCandidate = (
     commonEntryApproval and
     allowShort and
     rawShortSignal and
     shortRiskValid
)

bool ambiguousSignal = longCandidate and shortCandidate

bool finalLongSignal = longCandidate and not ambiguousSignal

bool finalShortSignal = shortCandidate and not ambiguousSignal


// ============================================================================
// SECTION 36: PENDING STRUCTURE STOPS
// ============================================================================

var float pendingLongStop = na

var float pendingShortStop = na


// ============================================================================
// SECTION 37: LONG ENTRY
// ============================================================================

if finalLongSignal

    pendingLongStop := prospectiveLongStop

    strategy.entry(
         "Long",
         strategy.long,
         qty=3,
         comment="Bastion V4 Long",
         alert_message="ENTRY_LONG_CNPS22_V4"
    )

    lastEntryBar := bar_index
    tradesToday += 1

    longSetupArmed := false
    longRetestSeen := false


// ============================================================================
// SECTION 38: SHORT ENTRY
// ============================================================================

if finalShortSignal

    pendingShortStop := prospectiveShortStop

    strategy.entry(
         "Short",
         strategy.short,
         qty=3,
         comment="Bastion V4 Short",
         alert_message="ENTRY_SHORT_CNPS22_V4"
    )

    lastEntryBar := bar_index
    tradesToday += 1

    shortSetupArmed := false
    shortRetestSeen := false


// ============================================================================
// SECTION 39: ACTIVE TRADE STATE
// ============================================================================

var float activeEntryPrice = na

var float activeInitialRisk = na

var float activeLongSL = na

var float activeLongTP = na

var float activeShortSL = na

var float activeShortTP = na


// ============================================================================
// SECTION 40: INITIALIZE LONG
// ============================================================================

if newLongPosition

    activeEntryPrice := strategy.position_avg_price

    float rawRisk = activeEntryPrice - pendingLongStop

    if na(rawRisk) or rawRisk <= 0
        rawRisk := minimumStopPoints

    activeInitialRisk := math.max(rawRisk, minimumStopPoints)

    activeLongSL := activeEntryPrice - activeInitialRisk

    activeLongTP := activeEntryPrice + activeInitialRisk * rewardRisk

    activeShortSL := na
    activeShortTP := na

    pendingLongStop := na


// ============================================================================
// SECTION 41: INITIALIZE SHORT
// ============================================================================

if newShortPosition

    activeEntryPrice := strategy.position_avg_price

    float rawRisk = pendingShortStop - activeEntryPrice

    if na(rawRisk) or rawRisk <= 0
        rawRisk := minimumStopPoints

    activeInitialRisk := math.max(rawRisk, minimumStopPoints)

    activeShortSL := activeEntryPrice + activeInitialRisk

    activeShortTP := activeEntryPrice - activeInitialRisk * rewardRisk

    activeLongSL := na
    activeLongTP := na

    pendingShortStop := na


// ============================================================================
// SECTION 42: LONG EXIT
// ============================================================================

if (
     inLong and
     not na(activeLongSL) and
     not na(activeLongTP)
)

    strategy.exit(
         "Long Exit",
         from_entry="Long",
         stop=activeLongSL,
         limit=activeLongTP,
         alert_profit="EXIT_LONG_TP_CNPS22_V4",
         alert_loss="EXIT_LONG_SL_CNPS22_V4",
         disable_alert=disableSLTPAlerts
    )


// ============================================================================
// SECTION 43: SHORT EXIT
// ============================================================================

if (
     inShort and
     not na(activeShortSL) and
     not na(activeShortTP)
)

    strategy.exit(
         "Short Exit",
         from_entry="Short",
         stop=activeShortSL,
         limit=activeShortTP,
         alert_profit="EXIT_SHORT_TP_CNPS22_V4",
         alert_loss="EXIT_SHORT_SL_CNPS22_V4",
         disable_alert=disableSLTPAlerts
    )


// ============================================================================
// SECTION 44: POSITION CLEANUP
// ============================================================================

if positionJustClosed

    activeEntryPrice := na
    activeInitialRisk := na

    activeLongSL := na
    activeLongTP := na

    activeShortSL := na
    activeShortTP := na


// ============================================================================
// SECTION 45: SESSION END
// ============================================================================

bool finalSessionBar = currentMinute < sessionEndTotal and currentCloseMinute >= sessionEndTotal

if finalSessionBar

    longSetupArmed := false
    shortSetupArmed := false

    longRetestSeen := false
    shortRetestSeen := false

    if strategy.position_size != 0

        strategy.close_all(
             immediately=true,
             comment="Session End",
             alert_message="SESSION_END_CNPS22_V4"
        )


// ============================================================================
// SECTION 46: TREND DISPLAY
// ============================================================================

plot(
     showTrend ? emaFast : na,
     title="EMA Fast",
     color=color.orange,
     linewidth=1
)

plot(
     showTrend ? emaSlow : na,
     title="EMA Slow",
     color=color.gray,
     linewidth=1
)

plot(
     showTrend ? vwapValue : na,
     title="VWAP",
     color=color.teal,
     linewidth=1
)


// ============================================================================
// SECTION 47: STRUCTURE DISPLAY
// ============================================================================

plot(
     showStructure ? lastSwingHigh : na,
     title="Swing High",
     color=color.new(color.green, 60),
     linewidth=1,
     style=plot.style_linebr
)

plot(
     showStructure ? lastSwingLow : na,
     title="Swing Low",
     color=color.new(color.red, 60),
     linewidth=1,
     style=plot.style_linebr
)


// ============================================================================
// SECTION 48: ACTIVE BOS LEVEL
// ============================================================================

plot(
     showSetupLevels and longSetupArmed ? longBOSLevel : na,
     title="Long BOS",
     color=color.new(color.green, 25),
     linewidth=2,
     style=plot.style_linebr
)

plot(
     showSetupLevels and shortSetupArmed ? shortBOSLevel : na,
     title="Short BOS",
     color=color.new(color.red, 25),
     linewidth=2,
     style=plot.style_linebr
)


// ============================================================================
// SECTION 49: ACTIVE SL / TP
// ============================================================================

plot(
     showActiveSLTP and inLong ? activeLongSL : na,
     title="Long SL",
     color=color.red,
     linewidth=2,
     style=plot.style_linebr
)

plot(
     showActiveSLTP and inLong ? activeLongTP : na,
     title="Long TP",
     color=color.green,
     linewidth=2,
     style=plot.style_linebr
)

plot(
     showActiveSLTP and inShort ? activeShortSL : na,
     title="Short SL",
     color=color.red,
     linewidth=2,
     style=plot.style_linebr
)

plot(
     showActiveSLTP and inShort ? activeShortTP : na,
     title="Short TP",
     color=color.green,
     linewidth=2,
     style=plot.style_linebr
)


// ============================================================================
// SECTION 50: SIGNAL MARKERS
// ============================================================================

plotshape(
     showSignals and bullishBOS,
     title="Bullish BOS",
     style=shape.circle,
     location=location.belowbar,
     color=color.orange,
     size=size.tiny,
     text="B"
)

plotshape(
     showSignals and bearishBOS,
     title="Bearish BOS",
     style=shape.circle,
     location=location.abovebar,
     color=color.orange,
     size=size.tiny,
     text="B"
)

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

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


// ============================================================================
// SECTION 51: DIAGNOSTIC COUNTERS
// ============================================================================

var int bullishBOSCount = 0

var int bearishBOSCount = 0

var int longRetestCount = 0

var int shortRetestCount = 0

var int longEntryCount = 0

var int shortEntryCount = 0


if bullishBOS
    bullishBOSCount += 1

if bearishBOS
    bearishBOSCount += 1

if longTouchesRetest and not longRetestSeen[1]
    longRetestCount += 1

if shortTouchesRetest and not shortRetestSeen[1]
    shortRetestCount += 1

if finalLongSignal
    longEntryCount += 1

if finalShortSignal
    shortEntryCount += 1


// ============================================================================
// SECTION 52: DIAGNOSTICS
// ============================================================================

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

if showDiagnostics and barstate.islast

    string trendText = "NONE"

    if bullishRegime
        trendText := "BULL"

    else if bearishRegime
        trendText := "BEAR"


    string setupText = "NONE"

    if longSetupArmed
        setupText := "LONG"

    else if shortSetupArmed
        setupText := "SHORT"


    string positionText = "FLAT"

    if inLong
        positionText := "LONG"

    else if inShort
        positionText := "SHORT"


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

    table.cell(diagnostics, 0, 1, "Regime")
    table.cell(diagnostics, 1, 1, trendText)

    table.cell(diagnostics, 0, 2, "Setup")
    table.cell(diagnostics, 1, 2, setupText)

    table.cell(diagnostics, 0, 3, "EMA Fast")
    table.cell(diagnostics, 1, 3, str.tostring(emaFast, format.mintick))

    table.cell(diagnostics, 0, 4, "VWAP")
    table.cell(diagnostics, 1, 4, str.tostring(vwapValue, format.mintick))

    table.cell(diagnostics, 0, 5, "RSI")
    table.cell(diagnostics, 1, 5, str.tostring(rsiValue, "0.0"))

    table.cell(diagnostics, 0, 6, "Bull BOS")
    table.cell(diagnostics, 1, 6, str.tostring(bullishBOSCount))

    table.cell(diagnostics, 0, 7, "Bear BOS")
    table.cell(diagnostics, 1, 7, str.tostring(bearishBOSCount))

    table.cell(diagnostics, 0, 8, "Retest L")
    table.cell(diagnostics, 1, 8, str.tostring(longRetestCount))

    table.cell(diagnostics, 0, 9, "Retest S")
    table.cell(diagnostics, 1, 9, str.tostring(shortRetestCount))

    table.cell(diagnostics, 0, 10, "Entry L")
    table.cell(diagnostics, 1, 10, str.tostring(longEntryCount))

    table.cell(diagnostics, 0, 11, "Entry S")
    table.cell(diagnostics, 1, 11, str.tostring(shortEntryCount))

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

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

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