//@version=6
strategy(
     title="CNPS38 V2 RF-HA PULLBACK",
     shorttitle="CNPS38 V2 RFHA",
     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,
     calc_on_every_tick=false,
     max_labels_count=100,
     max_lines_count=100,
     max_bars_back=5000
)


// ============================================================================
// CNPS38 V2 RF-HA PULLBACK
//
// WHY V2:
//
// V1 entered immediately when:
//
// Range Filter
// + Smoothed HA
// + HTF HA
//
// first became aligned.
//
// On VN30 M1 this can be late.
//
// V2:
//
// AGREEMENT TRANSITION
//        ↓
// ARM SETUP
//        ↓
// WAIT FOR PULLBACK TO RANGE FILTER
//        ↓
// AGREEMENT STILL VALID
// + SMI NOT EXTENDED
// + CONFIRMATION CANDLE
//        ↓
// ENTRY
//
// DEFAULT:
// - HTF confirmation OFF
// - HTF available as optional 15m filter
// - 2.5 to 7 point structural risk
// - TP 2R
// - No RF flip exit
// - Max 2 trades/day
// - Cooldown 8 bars
// - 3 contracts
// ============================================================================


// ============================================================================
// SECTION 1: RANGE FILTER
// ============================================================================

rangeSamplingPeriod = input.int(
     50,
     title="Sampling Period",
     minval=2,
     group="Range Filter"
)

rangeMultiplier = input.float(
     2.0,
     title="Range Multiplier",
     minval=0.5,
     step=0.25,
     group="Range Filter"
)


// ============================================================================
// SECTION 2: SMOOTHED HEIKIN-ASHI
// ============================================================================

preSmoothLength = input.int(
     6,
     title="Pre-Smooth Length",
     minval=1,
     group="Smoothed Heikin-Ashi"
)

postSmoothLength = input.int(
     8,
     title="Post-Smooth Length",
     minval=1,
     group="Smoothed Heikin-Ashi"
)


// ============================================================================
// SECTION 3: OPTIONAL HIGHER TIMEFRAME
// ============================================================================

useHTFAgreement = input.bool(
     false,
     title="Require Closed HTF HA Agreement?",
     group="Higher Timeframe"
)

higherTimeframe = input.timeframe(
     "15",
     title="HTF",
     group="Higher Timeframe"
)


// ============================================================================
// SECTION 4: SMI ANTI-CHASE
// ============================================================================

useSMIFilter = input.bool(
     true,
     title="Use SMI Anti-Chase?",
     group="SMI"
)

smiLength = input.int(
     8,
     title="SMI Length",
     minval=2,
     group="SMI"
)

smiSmooth1 = input.int(
     3,
     title="SMI Smooth 1",
     minval=1,
     group="SMI"
)

smiSmooth2 = input.int(
     3,
     title="SMI Smooth 2",
     minval=1,
     group="SMI"
)

smiMaximum = input.float(
     55.0,
     title="Maximum Long SMI / Minimum Short SMI",
     minval=10.0,
     maxval=100.0,
     step=5.0,
     group="SMI"
)


// ============================================================================
// SECTION 5: ARM / PULLBACK
// ============================================================================

armLifeBars = input.int(
     12,
     title="Maximum Bars To Wait For Pullback",
     minval=1,
     maxval=100,
     group="Pullback"
)

pullbackToleranceATR = input.float(
     0.20,
     title="Range Filter Touch Tolerance x ATR",
     minval=0.0,
     maxval=2.0,
     step=0.05,
     group="Pullback"
)

requireStateAgreementAtEntry = input.bool(
     true,
     title="Require RF + HA Agreement At Entry?",
     group="Pullback"
)

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

minimumBodyRatio = input.float(
     0.40,
     title="Minimum Body / Candle Range",
     minval=0.0,
     maxval=1.0,
     step=0.05,
     group="Pullback"
)

requirePreviousBreak = input.bool(
     false,
     title="Require Close Beyond Previous High / Low?",
     group="Pullback"
)


// ============================================================================
// SECTION 6: OPTIONAL ADX
// ============================================================================

useADXFilter = input.bool(
     false,
     title="Use ADX Filter?",
     group="Optional Filters"
)

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

adxThreshold = input.float(
     18.0,
     title="Minimum ADX",
     minval=1.0,
     step=1.0,
     group="Optional Filters"
)

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


// ============================================================================
// SECTION 7: DIRECTION
// ============================================================================

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

bool allowLong = tradeDirection != "Short Only"

bool allowShort = tradeDirection != "Long Only"


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

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

stopLookback = input.int(
     5,
     title="Structure Stop Lookback",
     minval=2,
     maxval=30,
     group="Risk Management"
)

stopBufferATR = input.float(
     0.10,
     title="Stop Buffer x ATR",
     minval=0.0,
     maxval=2.0,
     step=0.05,
     group="Risk Management"
)

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

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

rewardRisk = input.float(
     2.0,
     title="Take Profit R",
     minval=1.0,
     maxval=5.0,
     step=0.25,
     group="Risk Management"
)


// ============================================================================
// SECTION 9: OPTIONAL RANGE FILTER EXIT
// ============================================================================
//
// Source uses this.
// V2 defaults it OFF because M1 Range Filter can flip too easily.
//
// ============================================================================

exitOnRangeFilterFlip = input.bool(
     false,
     title="Exit On Range Filter Flip?",
     group="Risk Management"
)


// ============================================================================
// SECTION 10: OPTIONAL 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 Points",
     minval=0.0,
     step=0.5,
     group="Break Even"
)


// ============================================================================
// SECTION 11: TIME EXIT
// ============================================================================

maximumBarsInTrade = input.int(
     90,
     title="Maximum Bars In Trade (0 = Off)",
     minval=0,
     maxval=500,
     group="Risk Management"
)


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

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


// ============================================================================
// SECTION 15: DISPLAY
// ============================================================================

showRangeFilter = input.bool(
     true,
     title="Show Range Filter?",
     group="Display"
)

showRangeBands = input.bool(
     false,
     title="Show Range Bands?",
     group="Display"
)

showArmMarkers = input.bool(
     false,
     title="Show Arm Markers?",
     group="Display"
)

showEntrySignals = 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 16: SMOOTHED HA FUNCTION
// ============================================================================

f_smoothedHA(int length1, int length2) =>

    float smoothOpen = ta.ema(open, length1)

    float smoothHigh = ta.ema(high, length1)

    float smoothLow = ta.ema(low, length1)

    float smoothClose = ta.ema(close, length1)

    float haClose = (smoothOpen + smoothHigh + smoothLow + smoothClose) / 4.0

    var float haOpen = na


    if na(haOpen[1])

        haOpen := (smoothOpen + smoothClose) / 2.0


    else

        haOpen := (haOpen[1] + haClose[1]) / 2.0


    float haHigh = math.max(smoothHigh, math.max(haOpen, haClose))

    float haLow = math.min(smoothLow, math.min(haOpen, haClose))

    float finalOpen = ta.ema(haOpen, length2)

    float finalClose = ta.ema(haClose, length2)

    float finalHigh = ta.ema(haHigh, length2)

    float finalLow = ta.ema(haLow, length2)


    [
         finalOpen,
         finalHigh,
         finalLow,
         finalClose
    ]


// ============================================================================
// SECTION 17: SMI FUNCTION
// ============================================================================

f_smi(int length, int smooth1, int smooth2) =>

    float lowestValue = ta.lowest(low, length)

    float highestValue = ta.highest(high, length)

    float rangeValue = highestValue - lowestValue

    float relativeValue = close - (highestValue + lowestValue) / 2.0

    float averageRelative = ta.ema(ta.ema(relativeValue, smooth1), smooth2)

    float averageRange = ta.ema(ta.ema(rangeValue, smooth1), smooth2)

    float result = 0.0


    if averageRange != 0

        result := averageRelative / (averageRange / 2.0) * 100.0


    result


// ============================================================================
// SECTION 18: CLOSED HTF HA
// ============================================================================

f_closedHTFHADirection() =>

    [
         htfOpen,
         htfHigh,
         htfLow,
         htfClose
    ] = f_smoothedHA(
         preSmoothLength,
         postSmoothLength
    )


    int direction = htfClose >= htfOpen ? 1 : -1

    direction[1]


// ============================================================================
// SECTION 19: ATR
// ============================================================================

float atrValue = ta.atr(
     atrLength
)


// ============================================================================
// SECTION 20: RANGE FILTER
// ============================================================================

float rawRange = math.abs(close - close[1])

float averageRange = ta.ema(
     rawRange,
     rangeSamplingPeriod
)

int doubleSamplingPeriod = rangeSamplingPeriod * 2 - 1

float smoothedRange = ta.ema(
     averageRange,
     doubleSamplingPeriod
) * rangeMultiplier


var float rangeFilter = na

float previousRangeFilter = nz(
     rangeFilter[1],
     close
)


if na(smoothedRange)

    rangeFilter := close


else if close > previousRangeFilter

    rangeFilter := math.max(
         previousRangeFilter,
         close - smoothedRange
    )


else

    rangeFilter := math.min(
         previousRangeFilter,
         close + smoothedRange
    )


// ============================================================================
// SECTION 21: RANGE BANDS
// ============================================================================

float upperRangeBand = rangeFilter + nz(smoothedRange)

float lowerRangeBand = rangeFilter - nz(smoothedRange)


// ============================================================================
// SECTION 22: RANGE FILTER DIRECTION
// ============================================================================

var int rangeFilterDirection = 0


if rangeFilter > previousRangeFilter

    rangeFilterDirection := 1


else if rangeFilter < previousRangeFilter

    rangeFilterDirection := -1


// ============================================================================
// SECTION 23: LOCAL SMOOTHED HA
// ============================================================================

[
     localHAOpen,
     localHAHigh,
     localHALow,
     localHAClose
] = f_smoothedHA(
     preSmoothLength,
     postSmoothLength
)


int localHADirection = localHAClose >= localHAOpen ? 1 : -1


// ============================================================================
// SECTION 24: OPTIONAL HTF DIRECTION
// ============================================================================

int higherTimeframeDirection = nz(
     request.security(
         syminfo.tickerid,
         higherTimeframe,
         f_closedHTFHADirection(),
         lookahead=barmerge.lookahead_on
     ),
     0
)


// ============================================================================
// SECTION 25: SMI
// ============================================================================

float smiValue = f_smi(
     smiLength,
     smiSmooth1,
     smiSmooth2
)


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

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


// ============================================================================
// SECTION 27: LOCAL AGREEMENT
// ============================================================================

bool localBullAgreement = (
     rangeFilterDirection == 1 and
     localHADirection == 1
)


bool localBearAgreement = (
     rangeFilterDirection == -1 and
     localHADirection == -1
)


// ============================================================================
// SECTION 28: FULL AGREEMENT
// ============================================================================

bool bullAgreement = (
     localBullAgreement and
     (
         not useHTFAgreement or
         higherTimeframeDirection == 1
     )
)


bool bearAgreement = (
     localBearAgreement and
     (
         not useHTFAgreement or
         higherTimeframeDirection == -1
     )
)


// ============================================================================
// SECTION 29: AGREEMENT STATE
// ============================================================================

int trendState = 0


if bullAgreement

    trendState := 1


else if bearAgreement

    trendState := -1


// ============================================================================
// SECTION 30: TRANSITIONS
// ============================================================================

bool bullTransition = (
     trendState == 1 and
     trendState[1] != 1
)


bool bearTransition = (
     trendState == -1 and
     trendState[1] != -1
)


// ============================================================================
// SECTION 31: ARM STATE
// ============================================================================

var bool longArmed = false

var bool shortArmed = false

var int longArmBar = na

var int shortArmBar = na


if bullTransition

    longArmed := true

    shortArmed := false

    longArmBar := bar_index

    shortArmBar := na


if bearTransition

    shortArmed := true

    longArmed := false

    shortArmBar := bar_index

    longArmBar := na


// ============================================================================
// SECTION 32: ARM EXPIRATION
// ============================================================================

if (
     longArmed and
     not na(longArmBar) and
     bar_index - longArmBar > armLifeBars
)

    longArmed := false

    longArmBar := na


if (
     shortArmed and
     not na(shortArmBar) and
     bar_index - shortArmBar > armLifeBars
)

    shortArmed := false

    shortArmBar := na


// ============================================================================
// SECTION 33: INVALIDATE ARM
// ============================================================================
//
// If both core engines actually reverse, invalidate.
//
// A temporary neutral state does not immediately kill the setup.
//
// ============================================================================

if (
     longArmed and
     rangeFilterDirection == -1 and
     localHADirection == -1
)

    longArmed := false

    longArmBar := na


if (
     shortArmed and
     rangeFilterDirection == 1 and
     localHADirection == 1
)

    shortArmed := false

    shortArmBar := na


// ============================================================================
// SECTION 34: PULLBACK TOUCH
// ============================================================================

float pullbackTolerance = atrValue * pullbackToleranceATR


bool longPullbackTouch = (
     low <= rangeFilter + pullbackTolerance
)


bool shortPullbackTouch = (
     high >= rangeFilter - pullbackTolerance
)


// ============================================================================
// SECTION 35: RANGE FILTER RECLAIM
// ============================================================================

bool longReclaim = (
     close > rangeFilter
)


bool shortReclaim = (
     close < rangeFilter
)


// ============================================================================
// SECTION 36: CANDLE QUALITY
// ============================================================================

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

float candleBody = math.abs(
     close - open
)

float candleBodyRatio = candleBody / candleRange


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


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


// ============================================================================
// SECTION 37: STATE CONFIRMATION AT ENTRY
// ============================================================================

bool longStateOK = (
     not requireStateAgreementAtEntry or
     bullAgreement
)


bool shortStateOK = (
     not requireStateAgreementAtEntry or
     bearAgreement
)


// ============================================================================
// SECTION 38: SMI CONFIRMATION
// ============================================================================

bool longSMIOK = (
     not useSMIFilter or
     smiValue < smiMaximum
)


bool shortSMIOK = (
     not useSMIFilter or
     smiValue > -smiMaximum
)


// ============================================================================
// SECTION 39: ADX CONFIRMATION
// ============================================================================

bool longADXOK = true

bool shortADXOK = true


if useADXFilter

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


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


// ============================================================================
// SECTION 40: RAW PULLBACK SETUPS
// ============================================================================

bool rawLongSetup = (
     longArmed and
     not na(longArmBar) and
     bar_index > longArmBar and
     longPullbackTouch and
     longReclaim and
     longStateOK and
     longSMIOK and
     longADXOK and
     longCandleOK and
     barstate.isconfirmed
)


bool rawShortSetup = (
     shortArmed and
     not na(shortArmBar) and
     bar_index > shortArmBar and
     shortPullbackTouch and
     shortReclaim and
     shortStateOK and
     shortSMIOK and
     shortADXOK and
     shortCandleOK and
     barstate.isconfirmed
)


// ============================================================================
// SECTION 41: STRUCTURAL STOPS
// ============================================================================

float recentLow = ta.lowest(
     low,
     stopLookback
)

float recentHigh = ta.highest(
     high,
     stopLookback
)


float prospectiveLongStop = recentLow - atrValue * stopBufferATR

float prospectiveShortStop = recentHigh + atrValue * stopBufferATR


float prospectiveLongRisk = close - prospectiveLongStop

float prospectiveShortRisk = prospectiveShortStop - close


bool longRiskOK = (
     prospectiveLongRisk >= minimumRiskPoints and
     prospectiveLongRisk <= maximumRiskPoints
)


bool shortRiskOK = (
     prospectiveShortRisk >= minimumRiskPoints and
     prospectiveShortRisk <= maximumRiskPoints
)


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

    longArmed := false

    shortArmed := false

    longArmBar := na

    shortArmBar := na


// ============================================================================
// SECTION 44: 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 45: COOLDOWN
// ============================================================================

var int lastExitBar = na


if positionJustClosed

    lastExitBar := bar_index


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


// ============================================================================
// SECTION 46: TRADE LIMIT
// ============================================================================

bool tradeLimitOK = (
     tradesToday < maximumTradesPerDay
)


// ============================================================================
// SECTION 47: ENTRY APPROVAL
// ============================================================================

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


bool finalLongSignal = (
     commonEntryOK and
     allowLong and
     rawLongSetup and
     longRiskOK
)


bool finalShortSignal = (
     commonEntryOK and
     allowShort and
     rawShortSetup and
     shortRiskOK and
     not finalLongSignal
)


// ============================================================================
// SECTION 48: PENDING RISK
// ============================================================================

var float pendingStop = na


// ============================================================================
// SECTION 49: LONG ENTRY
// ============================================================================

if finalLongSignal

    pendingStop := prospectiveLongStop


    strategy.entry(
         "Long",
         strategy.long,
         qty=3,
         comment="RFHA PB L",
         alert_message="ENTRY_LONG_CNPS38_V2"
    )


    tradesToday += 1

    longArmed := false

    longArmBar := na


// ============================================================================
// SECTION 50: SHORT ENTRY
// ============================================================================

if finalShortSignal

    pendingStop := prospectiveShortStop


    strategy.entry(
         "Short",
         strategy.short,
         qty=3,
         comment="RFHA PB S",
         alert_message="ENTRY_SHORT_CNPS38_V2"
    )


    tradesToday += 1

    shortArmed := false

    shortArmBar := na


// ============================================================================
// SECTION 51: 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 52: INITIALIZE LONG
// ============================================================================

if newLongPosition

    activeEntryPrice := strategy.position_avg_price

    activeEntryBar := bar_index

    activeStop := pendingStop

    breakEvenActivated := false


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

        activeStop := activeEntryPrice - minimumRiskPoints


    activeInitialRisk := activeEntryPrice - activeStop

    activeTarget := activeEntryPrice + activeInitialRisk * rewardRisk

    pendingStop := na


// ============================================================================
// SECTION 53: INITIALIZE SHORT
// ============================================================================

if newShortPosition

    activeEntryPrice := strategy.position_avg_price

    activeEntryBar := bar_index

    activeStop := pendingStop

    breakEvenActivated := false


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

        activeStop := activeEntryPrice + minimumRiskPoints


    activeInitialRisk := activeStop - activeEntryPrice

    activeTarget := activeEntryPrice - activeInitialRisk * rewardRisk

    pendingStop := na


// ============================================================================
// SECTION 54: BREAK EVEN LONG
// ============================================================================

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

    float triggerPrice = activeEntryPrice + activeInitialRisk * breakEvenTriggerR


    if high >= triggerPrice

        float candidateStop = activeEntryPrice + breakEvenOffsetPoints

        activeStop := math.max(
             activeStop,
             candidateStop
        )

        breakEvenActivated := true


// ============================================================================
// SECTION 55: BREAK EVEN SHORT
// ============================================================================

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

    float triggerPrice = activeEntryPrice - activeInitialRisk * breakEvenTriggerR


    if low <= triggerPrice

        float candidateStop = activeEntryPrice - breakEvenOffsetPoints

        activeStop := math.min(
             activeStop,
             candidateStop
        )

        breakEvenActivated := true


// ============================================================================
// SECTION 56: LONG PROTECTIVE EXIT
// ============================================================================

if (
     inLong and
     not na(activeStop) and
     not na(activeTarget)
)

    strategy.exit(
         "Long Exit",
         from_entry="Long",
         stop=activeStop,
         limit=activeTarget,
         alert_profit="EXIT_LONG_TP_CNPS38_V2",
         alert_loss=breakEvenActivated ? "EXIT_LONG_BE_CNPS38_V2" : "EXIT_LONG_SL_CNPS38_V2",
         disable_alert=disableSLTPAlerts
    )


// ============================================================================
// SECTION 57: SHORT PROTECTIVE EXIT
// ============================================================================

if (
     inShort and
     not na(activeStop) and
     not na(activeTarget)
)

    strategy.exit(
         "Short Exit",
         from_entry="Short",
         stop=activeStop,
         limit=activeTarget,
         alert_profit="EXIT_SHORT_TP_CNPS38_V2",
         alert_loss=breakEvenActivated ? "EXIT_SHORT_BE_CNPS38_V2" : "EXIT_SHORT_SL_CNPS38_V2",
         disable_alert=disableSLTPAlerts
    )


// ============================================================================
// SECTION 58: OPTIONAL RF FLIP EXIT
// ============================================================================

bool longRFFlipExit = (
     inLong and
     exitOnRangeFilterFlip and
     rangeFilterDirection == -1 and
     not na(activeEntryBar) and
     bar_index > activeEntryBar and
     barstate.isconfirmed
)


bool shortRFFlipExit = (
     inShort and
     exitOnRangeFilterFlip and
     rangeFilterDirection == 1 and
     not na(activeEntryBar) and
     bar_index > activeEntryBar and
     barstate.isconfirmed
)


if longRFFlipExit

    strategy.close(
         "Long",
         comment="RF Flip",
         alert_message="RF_FLIP_EXIT_LONG_CNPS38_V2"
    )


if shortRFFlipExit

    strategy.close(
         "Short",
         comment="RF Flip",
         alert_message="RF_FLIP_EXIT_SHORT_CNPS38_V2"
    )


// ============================================================================
// SECTION 59: TIME EXIT
// ============================================================================

if (
     maximumBarsInTrade > 0 and
     strategy.position_size != 0 and
     not na(activeEntryBar)
)

    int barsInTrade = bar_index - activeEntryBar


    if barsInTrade >= maximumBarsInTrade

        strategy.close_all(
             comment="Time Exit",
             alert_message="TIME_EXIT_CNPS38_V2"
        )


// ============================================================================
// SECTION 60: CLEANUP
// ============================================================================

if positionJustClosed

    activeEntryPrice := na

    activeInitialRisk := na

    activeStop := na

    activeTarget := na

    activeEntryBar := na

    breakEvenActivated := false


// ============================================================================
// SECTION 61: FORCE CLOSE
// ============================================================================

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


if forceCloseBar

    longArmed := false

    shortArmed := false

    longArmBar := na

    shortArmBar := na


    if strategy.position_size != 0

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


// ============================================================================
// SECTION 62: RANGE FILTER COLOR
// ============================================================================

color rangeFilterColor = color.gray


if rangeFilterDirection == 1
    rangeFilterColor := color.green

else if rangeFilterDirection == -1
    rangeFilterColor := color.red


// ============================================================================
// SECTION 63: RANGE FILTER
// ============================================================================

plot(
     showRangeFilter ? rangeFilter : na,
     title="Range Filter",
     color=rangeFilterColor,
     linewidth=2
)


// ============================================================================
// SECTION 64: RANGE BANDS
// ============================================================================

upperRangePlot = plot(
     showRangeBands ? upperRangeBand : na,
     title="Upper Band",
     color=color.new(rangeFilterColor, 70),
     linewidth=1
)


lowerRangePlot = plot(
     showRangeBands ? lowerRangeBand : na,
     title="Lower Band",
     color=color.new(rangeFilterColor, 70),
     linewidth=1
)


fill(
     upperRangePlot,
     lowerRangePlot,
     color=showRangeBands ? color.new(rangeFilterColor, 93) : na
)


// ============================================================================
// SECTION 65: 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 66: ARM MARKERS
// ============================================================================

plotshape(
     showArmMarkers and bullTransition,
     title="Long Arm",
     style=shape.circle,
     location=location.belowbar,
     color=color.green,
     size=size.tiny,
     text="A"
)


plotshape(
     showArmMarkers and bearTransition,
     title="Short Arm",
     style=shape.circle,
     location=location.abovebar,
     color=color.red,
     size=size.tiny,
     text="A"
)


// ============================================================================
// SECTION 67: ENTRY MARKERS
// ============================================================================

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


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


// ============================================================================
// SECTION 68: COUNTERS
// ============================================================================

var int bullArmCount = 0

var int bearArmCount = 0

var int rawLongCount = 0

var int rawShortCount = 0

var int longEntryCount = 0

var int shortEntryCount = 0


if bullTransition
    bullArmCount += 1


if bearTransition
    bearArmCount += 1


if rawLongSetup
    rawLongCount += 1


if rawShortSetup
    rawShortCount += 1


if finalLongSignal
    longEntryCount += 1


if finalShortSignal
    shortEntryCount += 1


// ============================================================================
// SECTION 69: DIAGNOSTICS
// ============================================================================

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


if showDiagnostics and barstate.islast

    string rfText = "FLAT"


    if rangeFilterDirection == 1
        rfText := "BULL"

    else if rangeFilterDirection == -1
        rfText := "BEAR"


    string haText = localHADirection == 1 ? "BULL" : "BEAR"


    string htfText = "OFF"


    if useHTFAgreement

        if higherTimeframeDirection == 1
            htfText := "BULL"

        else if higherTimeframeDirection == -1
            htfText := "BEAR"

        else
            htfText := "NA"


    string armText = "NONE"


    if longArmed
        armText := "LONG"

    else if shortArmed
        armText := "SHORT"


    string positionText = "FLAT"


    if inLong
        positionText := "LONG"

    else if inShort
        positionText := "SHORT"


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


    table.cell(diagnostics, 0, 1, "Range Filter")
    table.cell(diagnostics, 1, 1, rfText)


    table.cell(diagnostics, 0, 2, "Smoothed HA")
    table.cell(diagnostics, 1, 2, haText)


    table.cell(diagnostics, 0, 3, "HTF HA")
    table.cell(diagnostics, 1, 3, htfText)


    table.cell(diagnostics, 0, 4, "Armed")
    table.cell(diagnostics, 1, 4, armText)


    table.cell(diagnostics, 0, 5, "SMI")
    table.cell(diagnostics, 1, 5, str.tostring(smiValue, "0.0"))


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


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


    table.cell(diagnostics, 0, 8, "Body Ratio")
    table.cell(diagnostics, 1, 8, str.tostring(candleBodyRatio, "0.00"))


    table.cell(diagnostics, 0, 9, "Long Risk")
    table.cell(diagnostics, 1, 9, str.tostring(prospectiveLongRisk, "0.00"))


    table.cell(diagnostics, 0, 10, "Short Risk")
    table.cell(diagnostics, 1, 10, str.tostring(prospectiveShortRisk, "0.00"))


    table.cell(diagnostics, 0, 11, "Arms L / S")
    table.cell(diagnostics, 1, 11, str.tostring(bullArmCount) + " / " + str.tostring(bearArmCount))


    table.cell(diagnostics, 0, 12, "Setups L / S")
    table.cell(diagnostics, 1, 12, str.tostring(rawLongCount) + " / " + str.tostring(rawShortCount))


    table.cell(diagnostics, 0, 13, "Entries L / S")
    table.cell(diagnostics, 1, 13, str.tostring(longEntryCount) + " / " + str.tostring(shortEntryCount))


    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, "RF Flip Exit")
    table.cell(diagnostics, 1, 16, exitOnRangeFilterFlip ? "ON" : "OFF")


    table.cell(diagnostics, 0, 17, "HTF Filter")
    table.cell(diagnostics, 1, 17, useHTFAgreement ? higherTimeframe : "OFF")


    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)