//@version=6

strategy(
     title="CNPS45 TREND PULLBACK RE-ENTRY V3 ACTIVE",
     shorttitle="CNPS45 TPRE V3",
     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=false,
     calc_on_order_fills=true,
     max_labels_count=300,
     max_lines_count=300,
     max_bars_back=5000
)


// ============================================================================
// CNPS45 TREND PULLBACK RE-ENTRY V3 ACTIVE
//
// Trend
// -> Pullback inside current trend
// -> Arm setup
// -> Momentum recovery
// -> Previous High/Low break
// -> Fast EMA reclaim
// -> Entry
//
// V3 = higher-frequency version of V2.
// ============================================================================


// ============================================================================
// 1. TREND REGIME
// ============================================================================

fastEMALength = input.int(
     34,
     "Fast EMA Length",
     minval=2,
     maxval=200,
     group="Trend Regime"
)

slowEMALength = input.int(
     89,
     "Slow EMA Length",
     minval=5,
     maxval=600,
     group="Trend Regime"
)

slopeLookback = input.int(
     5,
     "Slow EMA Slope Lookback",
     minval=1,
     maxval=100,
     group="Trend Regime"
)

minimumEMASeparationATR = input.float(
     0.08,
     "Minimum EMA Separation x ATR",
     minval=0.0,
     maxval=5.0,
     step=0.01,
     group="Trend Regime"
)

useADXFilter = input.bool(
     true,
     "Use ADX Filter?",
     group="Trend Regime"
)

minimumADX = input.float(
     15.0,
     "Minimum ADX",
     minval=0.0,
     maxval=100.0,
     step=1.0,
     group="Trend Regime"
)


// ============================================================================
// 2. PULLBACK
// ============================================================================

pullbackRSILength = input.int(
     7,
     "Pullback RSI Length",
     minval=2,
     maxval=30,
     group="Pullback"
)

longPullbackRSI = input.float(
     40.0,
     "Long Pullback RSI",
     minval=1.0,
     maxval=49.0,
     step=1.0,
     group="Pullback"
)

shortPullbackRSI = input.float(
     60.0,
     "Short Pullback RSI",
     minval=51.0,
     maxval=99.0,
     step=1.0,
     group="Pullback"
)

setupLifetimeBars = input.int(
     6,
     "Setup Lifetime Bars",
     minval=1,
     maxval=30,
     group="Pullback"
)

pullbackTouchATR = input.float(
     0.35,
     "Fast EMA Touch Tolerance x ATR",
     minval=0.0,
     maxval=3.0,
     step=0.05,
     group="Pullback"
)


// ============================================================================
// 3. TRIGGER QUALITY
// ============================================================================

minimumBodyRatio = input.float(
     0.25,
     "Minimum Trigger Body / Range",
     minval=0.0,
     maxval=1.0,
     step=0.05,
     group="Trigger"
)

requireFastEMAReclaim = input.bool(
     true,
     "Require Fast EMA Reclaim?",
     group="Trigger"
)

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


// ============================================================================
// 4. TRADE DIRECTION
// ============================================================================

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

bool allowLong = tradeDirection == "Long" or tradeDirection == "Both"
bool allowShort = tradeDirection == "Short" or tradeDirection == "Both"


// ============================================================================
// 5. POSITION SIZE
// ============================================================================

positionContracts = input.int(
     3,
     "Position Size",
     minval=1,
     maxval=20,
     group="Trade Control"
)


// ============================================================================
// 6. FREQUENCY CONTROL
// ============================================================================

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

cooldownBarsAfterExit = input.int(
     3,
     "Cooldown Bars After Exit",
     minval=0,
     maxval=100,
     group="Trade Control"
)


// ============================================================================
// 7. RISK
// ============================================================================

atrLength = input.int(
     14,
     "ATR Length",
     minval=1,
     group="Risk Management"
)

stopBufferATR = input.float(
     0.20,
     "Structural Stop Buffer x ATR",
     minval=0.0,
     maxval=3.0,
     step=0.05,
     group="Risk Management"
)

minimumRiskPoints = input.float(
     2.0,
     "Minimum Accepted Risk Points",
     minval=0.5,
     step=0.5,
     group="Risk Management"
)

maximumRiskPoints = input.float(
     8.0,
     "Maximum Accepted Risk Points",
     minval=1.0,
     step=0.5,
     group="Risk Management"
)


// ============================================================================
// 8. TAKE PROFIT
// ============================================================================

useTakeProfit = input.bool(
     true,
     "Use Take Profit?",
     group="Risk Management"
)

takeProfitR = input.float(
     1.80,
     "Take Profit R Multiple",
     minval=0.5,
     maxval=10.0,
     step=0.10,
     group="Risk Management"
)


// ============================================================================
// 9. BREAK EVEN
// ============================================================================

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

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

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


// ============================================================================
// 10. PROFIT TRAIL
// ============================================================================

useProfitTrail = input.bool(
     true,
     "Use Profit Trailing?",
     group="Trailing Stop"
)

trailStartR = input.float(
     1.50,
     "Start Trailing At R",
     minval=0.25,
     maxval=10.0,
     step=0.25,
     group="Trailing Stop"
)

trailATRMultiplier = input.float(
     2.5,
     "Trailing ATR Multiplier",
     minval=0.5,
     maxval=10.0,
     step=0.25,
     group="Trailing Stop"
)


// ============================================================================
// 11. SESSION
// ============================================================================

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

morningSession = input.session(
     "0900-1130",
     "Morning Entry Session",
     group="Session"
)

afternoonSession = input.session(
     "1300-1430",
     "Afternoon Entry Session",
     group="Session"
)

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

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


// ============================================================================
// 12. ALERTS
// ============================================================================

disableExitAlerts = input.bool(
     false,
     "Disable Exit Alerts?",
     group="Alerts"
)


// ============================================================================
// 13. DISPLAY
// ============================================================================

showEMAs = input.bool(
     true,
     "Show Trend EMAs?",
     group="Display"
)

showPullbackMarkers = input.bool(
     false,
     "Show Pullback Armed?",
     group="Display"
)

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

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

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


// ============================================================================
// 14. CORE INDICATORS
// ============================================================================

float fastEMA = ta.ema(close, fastEMALength)
float slowEMA = ta.ema(close, slowEMALength)
float pullbackRSI = ta.rsi(close, pullbackRSILength)
float atrValue = ta.atr(atrLength)

[positiveDI, negativeDI, adxValue] = ta.dmi(14, 14)


// ============================================================================
// 15. TREND QUALITY
// ============================================================================

float emaSeparation = math.abs(fastEMA - slowEMA)

float emaSeparationATR = (
     emaSeparation /
     math.max(atrValue, syminfo.mintick)
)

bool emaSeparationOK = (
     emaSeparationATR >=
     minimumEMASeparationATR
)

bool adxOK = (
     not useADXFilter or
     adxValue >= minimumADX
)

bool slowSlopeBullish = (
     slowEMA >
     slowEMA[slopeLookback]
)

bool slowSlopeBearish = (
     slowEMA <
     slowEMA[slopeLookback]
)


// ============================================================================
// 16. REGIME
// ============================================================================

bool uptrend = (
     fastEMA >
     slowEMA and
     slowSlopeBullish and
     emaSeparationOK and
     adxOK
)

bool downtrend = (
     fastEMA <
     slowEMA and
     slowSlopeBearish and
     emaSeparationOK and
     adxOK
)


// ============================================================================
// 17. CANDLE QUALITY
// ============================================================================

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

float candleBody = math.abs(
     close - open
)

float bodyRatio = (
     candleBody /
     candleRange
)

bool bullishTriggerCandle = (
     close > open and
     bodyRatio >= minimumBodyRatio
)

bool bearishTriggerCandle = (
     close < open and
     bodyRatio >= minimumBodyRatio
)


// ============================================================================
// 18. SESSION
// ============================================================================

bool inMorningSession = not na(
     time(
         timeframe.period,
         morningSession,
         strategyTimezone
     )
)

bool inAfternoonSession = not na(
     time(
         timeframe.period,
         afternoonSession,
         strategyTimezone
     )
)

bool entrySession = (
     inMorningSession or
     inAfternoonSession
)


// ============================================================================
// 19. TIME
// ============================================================================

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

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

int forceCloseTotal = (
     forceCloseHour * 60 +
     forceCloseMinute
)


// ============================================================================
// 20. 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


// ============================================================================
// 21. 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
)


// ============================================================================
// 22. COOLDOWN
// ============================================================================

var int lastExitBar = na

if positionJustClosed
    lastExitBar := bar_index

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


// ============================================================================
// 23. DAILY LIMIT
// ============================================================================

bool tradeLimitOK = (
     tradesToday <
     maximumTradesPerDay
)


// ============================================================================
// 24. SETUP STATE
// ============================================================================

var int longSetupBar = na
var float longSetupLow = na

var int shortSetupBar = na
var float shortSetupHigh = na


if newTradingDay

    longSetupBar := na
    longSetupLow := na

    shortSetupBar := na
    shortSetupHigh := na


// ============================================================================
// 25. EMA TOUCH
// ============================================================================

bool longEMATouch = (
     low <=
     fastEMA +
     atrValue *
     pullbackTouchATR
)

bool shortEMATouch = (
     high >=
     fastEMA -
     atrValue *
     pullbackTouchATR
)


// ============================================================================
// 26. VALID LONG PULLBACK
// ============================================================================

bool validLongPullback = (
     flat and
     entrySession and
     uptrend and
     pullbackRSI <= longPullbackRSI and
     longEMATouch and
     close > slowEMA
)


// ============================================================================
// 27. VALID SHORT PULLBACK
// ============================================================================

bool validShortPullback = (
     flat and
     entrySession and
     downtrend and
     pullbackRSI >= shortPullbackRSI and
     shortEMATouch and
     close < slowEMA
)


// ============================================================================
// 28. ARM LONG SETUP
// ============================================================================

if validLongPullback

    if na(longSetupBar)

        longSetupBar := bar_index
        longSetupLow := low

    else

        longSetupLow := math.min(
             longSetupLow,
             low
        )


// ============================================================================
// 29. ARM SHORT SETUP
// ============================================================================

if validShortPullback

    if na(shortSetupBar)

        shortSetupBar := bar_index
        shortSetupHigh := high

    else

        shortSetupHigh := math.max(
             shortSetupHigh,
             high
        )


// ============================================================================
// 30. ACTIVE SETUPS
// ============================================================================

bool longSetupActive = (
     not na(longSetupBar) and
     bar_index > longSetupBar and
     bar_index - longSetupBar <= setupLifetimeBars and
     uptrend
)

bool shortSetupActive = (
     not na(shortSetupBar) and
     bar_index > shortSetupBar and
     bar_index - shortSetupBar <= setupLifetimeBars and
     downtrend
)


// ============================================================================
// 31. UPDATE SETUP EXTREMES
// ============================================================================

if longSetupActive

    longSetupLow := math.min(
         longSetupLow,
         low
    )


if shortSetupActive

    shortSetupHigh := math.max(
         shortSetupHigh,
         high
    )


// ============================================================================
// 32. EXPIRE LONG
// ============================================================================

bool longSetupExpired = (
     not na(longSetupBar) and
     (
         bar_index - longSetupBar > setupLifetimeBars or
         not uptrend or
         not entrySession
     )
)

if longSetupExpired

    longSetupBar := na
    longSetupLow := na


// ============================================================================
// 33. EXPIRE SHORT
// ============================================================================

bool shortSetupExpired = (
     not na(shortSetupBar) and
     (
         bar_index - shortSetupBar > setupLifetimeBars or
         not downtrend or
         not entrySession
     )
)

if shortSetupExpired

    shortSetupBar := na
    shortSetupHigh := na


// ============================================================================
// 34. LONG TRIGGER
// ============================================================================

bool longHighBreakOK = (
     not requirePreviousHighLowBreak or
     close > high[1]
)

bool longReclaimOK = (
     not requireFastEMAReclaim or
     close > fastEMA
)

bool rawLongTrigger = (
     flat and
     allowLong and
     longSetupActive and
     bullishTriggerCandle and
     longHighBreakOK and
     longReclaimOK and
     barstate.isconfirmed
)


// ============================================================================
// 35. SHORT TRIGGER
// ============================================================================

bool shortLowBreakOK = (
     not requirePreviousHighLowBreak or
     close < low[1]
)

bool shortReclaimOK = (
     not requireFastEMAReclaim or
     close < fastEMA
)

bool rawShortTrigger = (
     flat and
     allowShort and
     shortSetupActive and
     bearishTriggerCandle and
     shortLowBreakOK and
     shortReclaimOK and
     barstate.isconfirmed
)


// ============================================================================
// 36. STRUCTURAL STOPS
// ============================================================================

float candidateLongStop = na
float candidateShortStop = na

if not na(longSetupLow)

    candidateLongStop := (
         longSetupLow -
         atrValue *
         stopBufferATR
    )

if not na(shortSetupHigh)

    candidateShortStop := (
         shortSetupHigh +
         atrValue *
         stopBufferATR
    )


// ============================================================================
// 37. SIGNAL RISK
// ============================================================================

float candidateLongRisk = na
float candidateShortRisk = na

if not na(candidateLongStop)
    candidateLongRisk := close - candidateLongStop

if not na(candidateShortStop)
    candidateShortRisk := candidateShortStop - close


// ============================================================================
// 38. RISK FILTER
// ============================================================================

bool longRiskOK = (
     not na(candidateLongRisk) and
     candidateLongRisk >= minimumRiskPoints and
     candidateLongRisk <= maximumRiskPoints
)

bool shortRiskOK = (
     not na(candidateShortRisk) and
     candidateShortRisk >= minimumRiskPoints and
     candidateShortRisk <= maximumRiskPoints
)


// ============================================================================
// 39. COMMON ENTRY
// ============================================================================

bool commonEntryOK = (
     flat and
     entrySession and
     cooldownOK and
     tradeLimitOK
)


// ============================================================================
// 40. FINAL LONG
// ============================================================================

bool finalLongSignal = (
     rawLongTrigger and
     commonEntryOK and
     longRiskOK
)


// ============================================================================
// 41. FINAL SHORT
// ============================================================================

bool finalShortSignal = (
     rawShortTrigger and
     commonEntryOK and
     shortRiskOK and
     not finalLongSignal
)


// ============================================================================
// 42. PENDING STOP
// ============================================================================

var float pendingStructuralStop = na


// ============================================================================
// 43. LONG ENTRY
// ============================================================================

if finalLongSignal

    pendingStructuralStop := candidateLongStop

    strategy.entry(
         "Long",
         strategy.long,
         qty=positionContracts,
         comment="Trend Pullback Long",
         alert_message="ENTRY_LONG_CNPS45_V3"
    )

    tradesToday += 1

    longSetupBar := na
    longSetupLow := na

    shortSetupBar := na
    shortSetupHigh := na


// ============================================================================
// 44. SHORT ENTRY
// ============================================================================

if finalShortSignal

    pendingStructuralStop := candidateShortStop

    strategy.entry(
         "Short",
         strategy.short,
         qty=positionContracts,
         comment="Trend Pullback Short",
         alert_message="ENTRY_SHORT_CNPS45_V3"
    )

    tradesToday += 1

    shortSetupBar := na
    shortSetupHigh := na

    longSetupBar := na
    longSetupLow := na


// ============================================================================
// 45. ACTIVE TRADE STATE
// ============================================================================

var float activeEntryPrice = na
var float activeInitialRisk = na
var float activeStopPrice = na
var float activeTargetPrice = na

var float maximumProfitR = 0.0

var bool breakEvenActivated = false
var bool trailingActivated = false


// ============================================================================
// 46. INITIALIZE LONG
// ============================================================================

if newLongPosition

    activeEntryPrice := strategy.position_avg_price

    activeStopPrice := pendingStructuralStop

    activeInitialRisk := (
         activeEntryPrice -
         activeStopPrice
    )

    if useTakeProfit

        activeTargetPrice := (
             activeEntryPrice +
             activeInitialRisk *
             takeProfitR
        )

    else

        activeTargetPrice := na

    maximumProfitR := 0.0
    breakEvenActivated := false
    trailingActivated := false

    pendingStructuralStop := na


// ============================================================================
// 47. INITIALIZE SHORT
// ============================================================================

if newShortPosition

    activeEntryPrice := strategy.position_avg_price

    activeStopPrice := pendingStructuralStop

    activeInitialRisk := (
         activeStopPrice -
         activeEntryPrice
    )

    if useTakeProfit

        activeTargetPrice := (
             activeEntryPrice -
             activeInitialRisk *
             takeProfitR
        )

    else

        activeTargetPrice := na

    maximumProfitR := 0.0
    breakEvenActivated := false
    trailingActivated := false

    pendingStructuralStop := na


// ============================================================================
// 48. PROFIT R
// ============================================================================

float longProfitR = na
float shortProfitR = na

if (
     inLong and
     not na(activeEntryPrice) and
     not na(activeInitialRisk) and
     activeInitialRisk > 0
)

    longProfitR := (
         high -
         activeEntryPrice
    ) / activeInitialRisk


if (
     inShort and
     not na(activeEntryPrice) and
     not na(activeInitialRisk) and
     activeInitialRisk > 0
)

    shortProfitR := (
         activeEntryPrice -
         low
    ) / activeInitialRisk


// ============================================================================
// 49. MAXIMUM PROFIT R
// ============================================================================

if inLong and not na(longProfitR)

    maximumProfitR := math.max(
         maximumProfitR,
         longProfitR
    )


if inShort and not na(shortProfitR)

    maximumProfitR := math.max(
         maximumProfitR,
         shortProfitR
    )


// ============================================================================
// 50. LONG BREAK EVEN
// ============================================================================

if (
     inLong and
     useBreakEven and
     not breakEvenActivated and
     maximumProfitR >= breakEvenTriggerR
)

    float longBreakEvenStop = (
         activeEntryPrice +
         breakEvenOffsetPoints
    )

    activeStopPrice := math.max(
         activeStopPrice,
         longBreakEvenStop
    )

    breakEvenActivated := true


// ============================================================================
// 51. SHORT BREAK EVEN
// ============================================================================

if (
     inShort and
     useBreakEven and
     not breakEvenActivated and
     maximumProfitR >= breakEvenTriggerR
)

    float shortBreakEvenStop = (
         activeEntryPrice -
         breakEvenOffsetPoints
    )

    activeStopPrice := math.min(
         activeStopPrice,
         shortBreakEvenStop
    )

    breakEvenActivated := true


// ============================================================================
// 52. LONG PROFIT TRAIL
// ============================================================================

if (
     inLong and
     useProfitTrail and
     maximumProfitR >= trailStartR
)

    trailingActivated := true

    float longTrailCandidate = (
         close -
         atrValue *
         trailATRMultiplier
    )

    activeStopPrice := math.max(
         activeStopPrice,
         longTrailCandidate
    )


// ============================================================================
// 53. SHORT PROFIT TRAIL
// ============================================================================

if (
     inShort and
     useProfitTrail and
     maximumProfitR >= trailStartR
)

    trailingActivated := true

    float shortTrailCandidate = (
         close +
         atrValue *
         trailATRMultiplier
    )

    activeStopPrice := math.min(
         activeStopPrice,
         shortTrailCandidate
    )


// ============================================================================
// 54. LONG EXIT
// ============================================================================

if (
     inLong and
     not na(activeStopPrice)
)

    strategy.exit(
         "Long Exit",
         from_entry="Long",
         stop=activeStopPrice,
         limit=useTakeProfit ? activeTargetPrice : na,
         alert_profit="EXIT_LONG_TP_CNPS45_V3",
         alert_loss=breakEvenActivated ? "EXIT_LONG_BE_OR_TRAIL_CNPS45_V3" : "EXIT_LONG_SL_CNPS45_V3",
         disable_alert=disableExitAlerts
    )


// ============================================================================
// 55. SHORT EXIT
// ============================================================================

if (
     inShort and
     not na(activeStopPrice)
)

    strategy.exit(
         "Short Exit",
         from_entry="Short",
         stop=activeStopPrice,
         limit=useTakeProfit ? activeTargetPrice : na,
         alert_profit="EXIT_SHORT_TP_CNPS45_V3",
         alert_loss=breakEvenActivated ? "EXIT_SHORT_BE_OR_TRAIL_CNPS45_V3" : "EXIT_SHORT_SL_CNPS45_V3",
         disable_alert=disableExitAlerts
    )


// ============================================================================
// 56. FORCE CLOSE
// ============================================================================

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

if (
     forceCloseBar and
     strategy.position_size != 0
)

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


// ============================================================================
// 57. CLEANUP
// ============================================================================

if positionJustClosed

    activeEntryPrice := na
    activeInitialRisk := na
    activeStopPrice := na
    activeTargetPrice := na

    maximumProfitR := 0.0

    breakEvenActivated := false
    trailingActivated := false


// ============================================================================
// 58. REGIME COLOR
// ============================================================================

color regimeColor = color.gray

if uptrend
    regimeColor := color.green

else if downtrend
    regimeColor := color.red


// ============================================================================
// 59. EMA PLOTS
// ============================================================================

plot(
     showEMAs ? fastEMA : na,
     "Fast EMA",
     color=color.new(regimeColor, 35),
     linewidth=1
)

plot(
     showEMAs ? slowEMA : na,
     "Slow EMA",
     color=regimeColor,
     linewidth=2
)


// ============================================================================
// 60. STOP / TARGET
// ============================================================================

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

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


// ============================================================================
// 61. PULLBACK MARKERS
// ============================================================================

plotshape(
     showPullbackMarkers and
     not na(longSetupBar),
     title="Long Pullback Armed",
     style=shape.circle,
     location=location.belowbar,
     color=color.orange,
     size=size.tiny,
     text="PB"
)

plotshape(
     showPullbackMarkers and
     not na(shortSetupBar),
     title="Short Pullback Armed",
     style=shape.circle,
     location=location.abovebar,
     color=color.orange,
     size=size.tiny,
     text="PB"
)


// ============================================================================
// 62. ENTRY MARKERS
// ============================================================================

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

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


// ============================================================================
// 63. DATA WINDOW
// ============================================================================

plot(
     pullbackRSI,
     "Pullback RSI",
     display=display.data_window
)

plot(
     adxValue,
     "ADX",
     display=display.data_window
)

plot(
     emaSeparationATR,
     "EMA Separation ATR",
     display=display.data_window
)

plot(
     bodyRatio,
     "Trigger Body Ratio",
     display=display.data_window
)

plot(
     activeInitialRisk,
     "Initial Risk",
     display=display.data_window
)

plot(
     maximumProfitR,
     "Maximum Profit R",
     display=display.data_window
)


// ============================================================================
// 64. DIAGNOSTICS
// ============================================================================

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

if (
     showDiagnostics and
     barstate.islast
)

    string regimeText = "NO TREND"

    if uptrend
        regimeText := "UPTREND"

    else if downtrend
        regimeText := "DOWNTREND"


    string setupText = "NONE"

    if not na(longSetupBar)
        setupText := "LONG ARMED"

    else if not na(shortSetupBar)
        setupText := "SHORT ARMED"


    string positionText = "FLAT"

    if inLong
        positionText := "LONG"

    else if inShort
        positionText := "SHORT"


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

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

    table.cell(diagnostics, 0, 2, "EMA Sep ATR")
    table.cell(diagnostics, 1, 2, str.tostring(emaSeparationATR, "0.00"))

    table.cell(diagnostics, 0, 3, "ADX")
    table.cell(diagnostics, 1, 3, str.tostring(adxValue, "0.00"))

    table.cell(diagnostics, 0, 4, "Pullback RSI")
    table.cell(diagnostics, 1, 4, str.tostring(pullbackRSI, "0.00"))

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

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

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

    table.cell(diagnostics, 0, 8, "Cooldown")
    table.cell(diagnostics, 1, 8, cooldownOK ? "READY" : "WAIT")

    table.cell(diagnostics, 0, 9, "Initial Risk")
    table.cell(diagnostics, 1, 9, str.tostring(activeInitialRisk, "0.00"))

    table.cell(diagnostics, 0, 10, "Max Profit R")
    table.cell(diagnostics, 1, 10, str.tostring(maximumProfitR, "0.00"))

    table.cell(diagnostics, 0, 11, "Break Even")
    table.cell(diagnostics, 1, 11, breakEvenActivated ? "ON" : "OFF")

    table.cell(diagnostics, 0, 12, "Trailing")
    table.cell(diagnostics, 1, 12, trailingActivated ? "ON" : "OFF")

    table.cell(diagnostics, 0, 13, "Stop")
    table.cell(diagnostics, 1, 13, str.tostring(activeStopPrice, format.mintick))

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