// This derivative work retains attribution to the original source.
// Original: "Uptrick: Adaptive Trend Matrix"
// © Uptrick
// Licensed under CC BY-SA 4.0:
// https://creativecommons.org/licenses/by-sa/4.0/

//@version=6

strategy(
     title="CNPS46 ADAPTIVE TREND MATRIX",
     shorttitle="CNPS46 ATM",
     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
)


// ============================================================================
// CNPS46 ADAPTIVE TREND MATRIX
//
// SOURCE CORE:
//
// TREND BASIS:
//      EMA(EMA(HLC3, 16), 3)
//
// VOLATILITY:
//      EMA(ATR(10), 3)
//
// RATCHETING TRAILS:
//      Bull Candidate = Basis - ATR x 1.8
//      Bear Candidate = Basis + ATR x 1.8
//
// MATRIX:
//      10 directional confirmations:
//
//      1. EMA 12 / 34
//      2. RSI 14
//      3. MACD 12 / 26 / 9
//      4. Stochastic 14 / 3
//      5. CCI 20
//      6. DMI / ADX 14
//      7. ROC 10
//      8. MFI 14
//      9. OBV vs EMA 14
//     10. VWAP
//
// DEFAULT REQUIRED SCORE:
//      6 / 10
//
// BUY:
//
//      Previous trend = bearish
//      + Close > Bear Trail
//      + Close > Trend Basis
//      + Bull Score >= Required Confirmations
//
// SELL:
//
//      Previous trend = bullish
//      + Close < Bull Trail
//      + Close < Trend Basis
//      + Bear Score >= Required Confirmations
//
//
// CNPS EXECUTION:
//
//      Current chart timeframe
//      Recommended = M1
//
//      Capital    = 100M VND
//      Position   = 3 contracts
//      Commission = 10,000 VND / contract
//      Slippage   = 3 ticks
//
//      Entry      = 09:00 -> 14:30
//      Force Exit = 14:45
//
// Source signals are confirmed at candle close.
// Therefore process_orders_on_close=true is used.
//
// ============================================================================


// ============================================================================
// SECTION 1: TREND ENGINE
// ============================================================================

basisLength = input.int(
     16,
     title="Trend Length",
     minval=2,
     group="Trend Engine"
)

smoothLength = input.int(
     3,
     title="Trend / ATR Smoothing",
     minval=1,
     group="Trend Engine"
)

atrLength = input.int(
     10,
     title="ATR Length",
     minval=2,
     group="Trend Engine"
)

atrMultiplier = input.float(
     1.8,
     title="Trail Distance ATR Multiplier",
     minval=0.5,
     step=0.1,
     group="Trend Engine"
)


// ============================================================================
// SECTION 2: MATRIX SIGNAL
// ============================================================================

requiredConfirmations = input.int(
     6,
     title="Required Confirmations",
     minval=4,
     maxval=10,
     group="Matrix Signal"
)

adxMinimum = input.float(
     15.0,
     title="Minimum ADX",
     minval=0.0,
     step=1.0,
     group="Matrix Signal"
)


// ============================================================================
// SECTION 3: TRADE DIRECTION
// ============================================================================

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

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

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


// ============================================================================
// SECTION 4: POSITION SIZE
// ============================================================================

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


// ============================================================================
// SECTION 5: OPPOSITE SIGNAL BEHAVIOR
// ============================================================================

reverseOnOppositeSignal = input.bool(
     true,
     title="Reverse On Opposite Signal?",
     group="Trade Control"
)

exitOnOppositeSignal = input.bool(
     true,
     title="Exit On Opposite Signal?",
     group="Trade Control"
)


// ============================================================================
// SECTION 6: TRADE FREQUENCY
// ============================================================================

maximumTradesPerDay = input.int(
     0,
     title="Maximum Trades Per Day (0 = Unlimited)",
     minval=0,
     maxval=50,
     group="Trade Control"
)

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


// ============================================================================
// SECTION 7: FIXED STOP
// ============================================================================

stopLossPoints = input.float(
     5.0,
     title="Stop Loss Points",
     minval=0.5,
     step=0.5,
     group="Risk Management"
)


// ============================================================================
// SECTION 8: TAKE PROFIT MODE
// ============================================================================
//
// Source TP values are informational targets.
//
// They are converted here into selectable actual strategy exits.
//
// ============================================================================

takeProfitMode = input.string(
     "Fixed Points",
     title="Take Profit Mode",
     options=[
         "Fixed Points",
         "Source TP1",
         "Source TP2",
         "Source TP3",
         "Off"
     ],
     group="Risk Management"
)

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

tp1ATR = input.float(
     1.0,
     title="Source TP1 ATR",
     minval=0.1,
     step=0.1,
     group="Risk Management"
)

tp2ATR = input.float(
     2.0,
     title="Source TP2 ATR",
     minval=0.1,
     step=0.1,
     group="Risk Management"
)

tp3ATR = input.float(
     3.25,
     title="Source TP3 ATR",
     minval=0.1,
     step=0.05,
     group="Risk Management"
)


// ============================================================================
// SECTION 9: OPTIONAL MATRIX TRAIL STOP
// ============================================================================
//
// OFF by default.
//
// When enabled:
//
// LONG:
// Fixed Stop can only tighten toward Bull Trail.
//
// SHORT:
// Fixed Stop can only tighten toward Bear Trail.
//
// ============================================================================

useMatrixTrailStop = input.bool(
     false,
     title="Use Matrix Trail As Dynamic Stop?",
     group="Risk Management"
)


// ============================================================================
// SECTION 10: BREAK EVEN
// ============================================================================

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

breakEvenTriggerPoints = input.float(
     7.5,
     title="Break Even Trigger Points",
     minval=0.5,
     step=0.5,
     group="Break Even"
)

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


// ============================================================================
// SECTION 11: SESSION
// ============================================================================

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

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

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

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

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

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

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


// ============================================================================
// SECTION 12: ALERTS
// ============================================================================

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


// ============================================================================
// SECTION 13: DISPLAY
// ============================================================================

showBasis = input.bool(
     false,
     title="Show Trend Basis?",
     group="Display"
)

showMatrixTrail = input.bool(
     true,
     title="Show Adaptive Trail?",
     group="Display"
)

showEMA = input.bool(
     false,
     title="Show Matrix EMA 12 / 34?",
     group="Display"
)

showSignalMarkers = 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 14: BASIS
// ============================================================================

float basisRaw = ta.ema(
     hlc3,
     basisLength
)

float basis = ta.ema(
     basisRaw,
     smoothLength
)


// ============================================================================
// SECTION 15: SMOOTHED ATR
// ============================================================================

float atrRaw = ta.atr(
     atrLength
)

float atrValue = ta.ema(
     atrRaw,
     smoothLength
)


// ============================================================================
// SECTION 16: RATCHETING TRAIL CANDIDATES
// ============================================================================

float longCandidate = (
     basis -
     atrValue *
     atrMultiplier
)

float shortCandidate = (
     basis +
     atrValue *
     atrMultiplier
)


// ============================================================================
// SECTION 17: RATCHETING TRAILS
// ============================================================================

var float bullTrail = na
var float bearTrail = na


bullTrail := (
     na(bullTrail[1])
     ? longCandidate
     : math.max(
         longCandidate,
         bullTrail[1]
     )
)


bearTrail := (
     na(bearTrail[1])
     ? shortCandidate
     : math.min(
         shortCandidate,
         bearTrail[1]
     )
)


if (
     not na(bullTrail[1]) and
     close <
     bullTrail[1]
)

    bullTrail := longCandidate


if (
     not na(bearTrail[1]) and
     close >
     bearTrail[1]
)

    bearTrail := shortCandidate


// ============================================================================
// SECTION 18: EMA CONFIRMATION
// ============================================================================

float emaFast = ta.ema(
     close,
     12
)

float emaSlow = ta.ema(
     close,
     34
)

bool emaBull = (
     emaFast >
     emaSlow
)

bool emaBear = (
     emaFast <
     emaSlow
)


// ============================================================================
// SECTION 19: RSI CONFIRMATION
// ============================================================================

float rsiValue = ta.rsi(
     close,
     14
)

bool rsiBull = (
     rsiValue >
     52
)

bool rsiBear = (
     rsiValue <
     48
)


// ============================================================================
// SECTION 20: MACD CONFIRMATION
// ============================================================================

[macdLine, macdSignal, macdHistogram] = ta.macd(
     close,
     12,
     26,
     9
)

bool macdBull = (
     macdLine >
     macdSignal and
     macdHistogram >
     0
)

bool macdBear = (
     macdLine <
     macdSignal and
     macdHistogram <
     0
)


// ============================================================================
// SECTION 21: STOCHASTIC CONFIRMATION
// ============================================================================

float stochasticK = ta.stoch(
     close,
     high,
     low,
     14
)

float stochasticD = ta.sma(
     stochasticK,
     3
)

bool stochasticBull = (
     stochasticK >
     stochasticD and
     stochasticK >
     45
)

bool stochasticBear = (
     stochasticK <
     stochasticD and
     stochasticK <
     55
)


// ============================================================================
// SECTION 22: CCI CONFIRMATION
// ============================================================================

float cciValue = ta.cci(
     hlc3,
     20
)

bool cciBull = (
     cciValue >
     0
)

bool cciBear = (
     cciValue <
     0
)


// ============================================================================
// SECTION 23: DMI / ADX CONFIRMATION
// ============================================================================

[plusDI, minusDI, adxValue] = ta.dmi(
     14,
     14
)

bool dmiBull = (
     plusDI >
     minusDI and
     adxValue >=
     adxMinimum
)

bool dmiBear = (
     minusDI >
     plusDI and
     adxValue >=
     adxMinimum
)


// ============================================================================
// SECTION 24: ROC CONFIRMATION
// ============================================================================

float rocValue = ta.roc(
     close,
     10
)

bool rocBull = (
     rocValue >
     0
)

bool rocBear = (
     rocValue <
     0
)


// ============================================================================
// SECTION 25: MFI CONFIRMATION
// ============================================================================

float mfiValue = ta.mfi(
     hlc3,
     14
)

bool mfiBull = (
     mfiValue >
     52
)

bool mfiBear = (
     mfiValue <
     48
)


// ============================================================================
// SECTION 26: OBV CONFIRMATION
// ============================================================================

float obvValue = ta.cum(
     close > close[1]
     ? volume
     : close < close[1]
     ? -volume
     : 0
)

float obvEMA = ta.ema(
     obvValue,
     14
)

bool obvBull = (
     obvValue >
     obvEMA
)

bool obvBear = (
     obvValue <
     obvEMA
)


// ============================================================================
// SECTION 27: VWAP CONFIRMATION
// ============================================================================

float vwapValue = ta.vwap(
     hlc3
)

bool vwapBull = (
     close >
     vwapValue
)

bool vwapBear = (
     close <
     vwapValue
)


// ============================================================================
// SECTION 28: ADAPTIVE TREND MATRIX SCORE
// ============================================================================

int bullScore = (
     (emaBull ? 1 : 0) +
     (rsiBull ? 1 : 0) +
     (macdBull ? 1 : 0) +
     (stochasticBull ? 1 : 0) +
     (cciBull ? 1 : 0) +
     (dmiBull ? 1 : 0) +
     (rocBull ? 1 : 0) +
     (mfiBull ? 1 : 0) +
     (obvBull ? 1 : 0) +
     (vwapBull ? 1 : 0)
)

int bearScore = (
     (emaBear ? 1 : 0) +
     (rsiBear ? 1 : 0) +
     (macdBear ? 1 : 0) +
     (stochasticBear ? 1 : 0) +
     (cciBear ? 1 : 0) +
     (dmiBear ? 1 : 0) +
     (rocBear ? 1 : 0) +
     (mfiBear ? 1 : 0) +
     (obvBear ? 1 : 0) +
     (vwapBear ? 1 : 0)
)


// ============================================================================
// SECTION 29: SOURCE TREND STATE
// ============================================================================

var int sourceTrend = 0


if sourceTrend == 0

    sourceTrend := (
         close >= basis
         ? 1
         : -1
    )


// ============================================================================
// SECTION 30: BREAK CONDITIONS
// ============================================================================

bool bullBreak = (
     close >
     bearTrail and
     close >
     basis
)

bool bearBreak = (
     close <
     bullTrail and
     close <
     basis
)


// ============================================================================
// SECTION 31: RAW SOURCE SIGNALS
// ============================================================================

bool rawBuy = (
     barstate.isconfirmed and
     sourceTrend == -1 and
     bullBreak and
     bullScore >=
     requiredConfirmations
)

bool rawSell = (
     barstate.isconfirmed and
     sourceTrend == 1 and
     bearBreak and
     bearScore >=
     requiredConfirmations
)


// ============================================================================
// SECTION 32: SOURCE SIGNAL DIRECTION
// ============================================================================
//
// Prevent repeated same-direction signals.
//
// ============================================================================

var int signalDirection = 0


bool sourceBuySignal = (
     rawBuy and
     signalDirection !=
     1
)

bool sourceSellSignal = (
     rawSell and
     signalDirection !=
     -1
)


// ============================================================================
// SECTION 33: UPDATE SOURCE STATE
// ============================================================================

if sourceBuySignal

    sourceTrend := 1

    signalDirection := 1


else if sourceSellSignal

    sourceTrend := -1

    signalDirection := -1


// ============================================================================
// SECTION 34: ACTIVE MATRIX TRAIL
// ============================================================================

float activeMatrixTrail = (
     sourceTrend == 1
     ? bullTrail
     : bearTrail
)

float displayMatrixTrail = na


if sourceTrend == 1

    displayMatrixTrail := math.min(
         activeMatrixTrail,
         close - syminfo.mintick
    )


else

    displayMatrixTrail := math.max(
         activeMatrixTrail,
         close + syminfo.mintick
    )


// ============================================================================
// SECTION 35: SESSION ENGINE
// ============================================================================

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

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

int entryStartTotal = (
     entryStartHour *
     60 +
     entryStartMinute
)

int lastEntryTotal = (
     lastEntryHour *
     60 +
     lastEntryMinute
)

int forceCloseTotal = (
     forceCloseHour *
     60 +
     forceCloseMinute
)

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


// ============================================================================
// SECTION 36: DAILY STATE
// ============================================================================

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

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

var int tradesToday = 0

if newTradingDay
    tradesToday := 0


// ============================================================================
// SECTION 37: 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 38: COOLDOWN
// ============================================================================

var int lastExitBar = na


if positionJustClosed

    lastExitBar := bar_index


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


// ============================================================================
// SECTION 39: DAILY TRADE LIMIT
// ============================================================================

bool tradeLimitOK = (
     maximumTradesPerDay == 0 or
     tradesToday <
     maximumTradesPerDay
)


// ============================================================================
// SECTION 40: OPPOSITE SIGNAL EXITS
// ============================================================================

bool closeLongOnSell = (
     inLong and
     sourceSellSignal and
     exitOnOppositeSignal and
     not reverseOnOppositeSignal
)

bool closeShortOnBuy = (
     inShort and
     sourceBuySignal and
     exitOnOppositeSignal and
     not reverseOnOppositeSignal
)


if closeLongOnSell

    strategy.close(
         "Long",
         comment="Opposite Matrix Signal",
         alert_message="OPPOSITE_EXIT_LONG_CNPS46"
    )


if closeShortOnBuy

    strategy.close(
         "Short",
         comment="Opposite Matrix Signal",
         alert_message="OPPOSITE_EXIT_SHORT_CNPS46"
    )


// ============================================================================
// SECTION 41: FINAL LONG SIGNAL
// ============================================================================

bool finalLongSignal = (
     sourceBuySignal and
     allowLong and
     entrySession and
     cooldownOK and
     tradeLimitOK and
     (
         flat or
         (
             inShort and
             reverseOnOppositeSignal
         )
     )
)


// ============================================================================
// SECTION 42: FINAL SHORT SIGNAL
// ============================================================================

bool finalShortSignal = (
     sourceSellSignal and
     allowShort and
     entrySession and
     cooldownOK and
     tradeLimitOK and
     (
         flat or
         (
             inLong and
             reverseOnOppositeSignal
         )
     ) and
     not finalLongSignal
)


// ============================================================================
// SECTION 43: LONG ENTRY
// ============================================================================

if finalLongSignal

    strategy.entry(
         "Long",
         strategy.long,
         qty=positionContracts,
         comment="Matrix Long",
         alert_message="ENTRY_LONG_CNPS46"
    )

    tradesToday += 1


// ============================================================================
// SECTION 44: SHORT ENTRY
// ============================================================================

if finalShortSignal

    strategy.entry(
         "Short",
         strategy.short,
         qty=positionContracts,
         comment="Matrix Short",
         alert_message="ENTRY_SHORT_CNPS46"
    )

    tradesToday += 1


// ============================================================================
// SECTION 45: ACTIVE TRADE STATE
// ============================================================================

var float activeEntryPrice = na

var float activeEntryATR = na

var float activeStopPrice = na

var float activeTargetPrice = na

var bool breakEvenActivated = false


// ============================================================================
// SECTION 46: TARGET DISTANCE HELPER
// ============================================================================

f_targetDistance(float entryATRValue) =>

    float distance = na

    if takeProfitMode == "Fixed Points"

        distance := fixedTakeProfitPoints


    else if takeProfitMode == "Source TP1"

        distance := (
             entryATRValue *
             tp1ATR
        )


    else if takeProfitMode == "Source TP2"

        distance := (
             entryATRValue *
             tp2ATR
        )


    else if takeProfitMode == "Source TP3"

        distance := (
             entryATRValue *
             tp3ATR
        )


    distance


// ============================================================================
// SECTION 47: INITIALIZE LONG
// ============================================================================

if newLongPosition

    activeEntryPrice := (
         strategy.position_avg_price
    )

    activeEntryATR := atrValue

    activeStopPrice := (
         activeEntryPrice -
         stopLossPoints
    )

    float targetDistance = (
         f_targetDistance(
             activeEntryATR
         )
    )

    if takeProfitMode == "Off"

        activeTargetPrice := na


    else

        activeTargetPrice := (
             activeEntryPrice +
             targetDistance
        )


    breakEvenActivated := false


// ============================================================================
// SECTION 48: INITIALIZE SHORT
// ============================================================================

if newShortPosition

    activeEntryPrice := (
         strategy.position_avg_price
    )

    activeEntryATR := atrValue

    activeStopPrice := (
         activeEntryPrice +
         stopLossPoints
    )

    float targetDistance = (
         f_targetDistance(
             activeEntryATR
         )
    )

    if takeProfitMode == "Off"

        activeTargetPrice := na


    else

        activeTargetPrice := (
             activeEntryPrice -
             targetDistance
        )


    breakEvenActivated := false


// ============================================================================
// SECTION 49: MATRIX TRAIL STOP - LONG
// ============================================================================

if (
     inLong and
     useMatrixTrailStop and
     not na(bullTrail) and
     not na(activeStopPrice)
)

    float matrixLongStop = math.min(
         bullTrail,
         close - syminfo.mintick
    )

    activeStopPrice := math.max(
         activeStopPrice,
         matrixLongStop
    )


// ============================================================================
// SECTION 50: MATRIX TRAIL STOP - SHORT
// ============================================================================

if (
     inShort and
     useMatrixTrailStop and
     not na(bearTrail) and
     not na(activeStopPrice)
)

    float matrixShortStop = math.max(
         bearTrail,
         close + syminfo.mintick
    )

    activeStopPrice := math.min(
         activeStopPrice,
         matrixShortStop
    )


// ============================================================================
// SECTION 51: LONG BREAK EVEN
// ============================================================================

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

    float longBETrigger = (
         activeEntryPrice +
         breakEvenTriggerPoints
    )

    if high >= longBETrigger

        float longBEStop = (
             activeEntryPrice +
             breakEvenOffsetPoints
        )

        activeStopPrice := math.max(
             activeStopPrice,
             longBEStop
        )

        breakEvenActivated := true


// ============================================================================
// SECTION 52: SHORT BREAK EVEN
// ============================================================================

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

    float shortBETrigger = (
         activeEntryPrice -
         breakEvenTriggerPoints
    )

    if low <= shortBETrigger

        float shortBEStop = (
             activeEntryPrice -
             breakEvenOffsetPoints
        )

        activeStopPrice := math.min(
             activeStopPrice,
             shortBEStop
        )

        breakEvenActivated := true


// ============================================================================
// SECTION 53: LONG PROTECTIVE EXIT
// ============================================================================

if (
     inLong and
     not na(activeStopPrice)
)

    strategy.exit(
         "Long Exit",
         from_entry="Long",
         stop=activeStopPrice,
         limit=takeProfitMode != "Off" ? activeTargetPrice : na,
         alert_profit="EXIT_LONG_TP_CNPS46",
         alert_loss=breakEvenActivated ? "EXIT_LONG_BE_CNPS46" : "EXIT_LONG_SL_CNPS46",
         disable_alert=disableExitAlerts
    )


// ============================================================================
// SECTION 54: SHORT PROTECTIVE EXIT
// ============================================================================

if (
     inShort and
     not na(activeStopPrice)
)

    strategy.exit(
         "Short Exit",
         from_entry="Short",
         stop=activeStopPrice,
         limit=takeProfitMode != "Off" ? activeTargetPrice : na,
         alert_profit="EXIT_SHORT_TP_CNPS46",
         alert_loss=breakEvenActivated ? "EXIT_SHORT_BE_CNPS46" : "EXIT_SHORT_SL_CNPS46",
         disable_alert=disableExitAlerts
    )


// ============================================================================
// SECTION 55: 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_CNPS46"
    )


// ============================================================================
// SECTION 56: CLEANUP
// ============================================================================

if positionJustClosed

    activeEntryPrice := na

    activeEntryATR := na

    activeStopPrice := na

    activeTargetPrice := na

    breakEvenActivated := false


// ============================================================================
// SECTION 57: TREND COLOR
// ============================================================================

color trendColor = (
     sourceTrend == 1
     ? color.green
     : color.red
)


// ============================================================================
// SECTION 58: TREND BASIS
// ============================================================================

plot(
     showBasis
     ? basis
     : na,
     title="Trend Basis",
     color=color.gray,
     linewidth=1
)


// ============================================================================
// SECTION 59: MATRIX TRAIL
// ============================================================================

plot(
     showMatrixTrail
     ? displayMatrixTrail
     : na,
     title="Adaptive Trend Matrix Trail",
     color=trendColor,
     linewidth=2,
     style=plot.style_linebr
)


// ============================================================================
// SECTION 60: MATRIX EMA FAST
// ============================================================================

plot(
     showEMA
     ? emaFast
     : na,
     title="Matrix EMA Fast",
     color=color.blue,
     linewidth=1
)


// ============================================================================
// SECTION 61: MATRIX EMA SLOW
// ============================================================================

plot(
     showEMA
     ? emaSlow
     : na,
     title="Matrix EMA Slow",
     color=color.orange,
     linewidth=1
)


// ============================================================================
// SECTION 62: ACTIVE STOP
// ============================================================================

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


// ============================================================================
// SECTION 63: ACTIVE TARGET
// ============================================================================

plot(
     showActiveSLTP and
     strategy.position_size != 0 and
     takeProfitMode != "Off"
     ? activeTargetPrice
     : na,
     title="Active Take Profit",
     color=color.green,
     linewidth=2,
     style=plot.style_linebr
)


// ============================================================================
// SECTION 64: SIGNAL MARKERS
// ============================================================================

plotshape(
     showSignalMarkers and
     sourceBuySignal,
     title="Matrix Buy Signal",
     style=shape.triangleup,
     location=location.belowbar,
     color=color.green,
     size=size.small,
     text="BUY"
)

plotshape(
     showSignalMarkers and
     sourceSellSignal,
     title="Matrix Sell Signal",
     style=shape.triangledown,
     location=location.abovebar,
     color=color.red,
     size=size.small,
     text="SELL"
)


// ============================================================================
// SECTION 65: SOURCE TP REFERENCE LEVELS
// ============================================================================

float referenceTP1 = na
float referenceTP2 = na
float referenceTP3 = na


if (
     strategy.position_size != 0 and
     not na(activeEntryPrice) and
     not na(activeEntryATR)
)

    if inLong

        referenceTP1 := (
             activeEntryPrice +
             activeEntryATR *
             tp1ATR
        )

        referenceTP2 := (
             activeEntryPrice +
             activeEntryATR *
             tp2ATR
        )

        referenceTP3 := (
             activeEntryPrice +
             activeEntryATR *
             tp3ATR
        )


    else if inShort

        referenceTP1 := (
             activeEntryPrice -
             activeEntryATR *
             tp1ATR
        )

        referenceTP2 := (
             activeEntryPrice -
             activeEntryATR *
             tp2ATR
        )

        referenceTP3 := (
             activeEntryPrice -
             activeEntryATR *
             tp3ATR
        )


// ============================================================================
// SECTION 66: DATA WINDOW
// ============================================================================

plot(
     bullScore,
     title="Bull Confirmations",
     display=display.data_window
)

plot(
     bearScore,
     title="Bear Confirmations",
     display=display.data_window
)

plot(
     rsiValue,
     title="RSI",
     display=display.data_window
)

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

plot(
     mfiValue,
     title="MFI",
     display=display.data_window
)

plot(
     referenceTP1,
     title="Source TP1 Reference",
     display=display.data_window
)

plot(
     referenceTP2,
     title="Source TP2 Reference",
     display=display.data_window
)

plot(
     referenceTP3,
     title="Source TP3 Reference",
     display=display.data_window
)


// ============================================================================
// SECTION 67: SIGNAL COUNTERS
// ============================================================================

var int buySignalCount = 0
var int sellSignalCount = 0
var int executedLongCount = 0
var int executedShortCount = 0


if sourceBuySignal
    buySignalCount += 1


if sourceSellSignal
    sellSignalCount += 1


if finalLongSignal
    executedLongCount += 1


if finalShortSignal
    executedShortCount += 1


// ============================================================================
// SECTION 68: DIAGNOSTICS
// ============================================================================

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


if (
     showDiagnostics and
     barstate.islast
)

    string trendText = (
         sourceTrend == 1
         ? "BULL"
         : "BEAR"
    )


    string positionText = "FLAT"


    if inLong
        positionText := "LONG"


    else if inShort
        positionText := "SHORT"


    table.cell(
         diagnostics,
         0,
         0,
         "CNPS46"
    )

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


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

    table.cell(
         diagnostics,
         1,
         1,
         trendText
    )


    table.cell(
         diagnostics,
         0,
         2,
         "Bull Score"
    )

    table.cell(
         diagnostics,
         1,
         2,
         str.tostring(bullScore) +
         "/10"
    )


    table.cell(
         diagnostics,
         0,
         3,
         "Bear Score"
    )

    table.cell(
         diagnostics,
         1,
         3,
         str.tostring(bearScore) +
         "/10"
    )


    table.cell(
         diagnostics,
         0,
         4,
         "Required"
    )

    table.cell(
         diagnostics,
         1,
         4,
         str.tostring(requiredConfirmations)
    )


    table.cell(
         diagnostics,
         0,
         5,
         "Basis"
    )

    table.cell(
         diagnostics,
         1,
         5,
         str.tostring(
             basis,
             format.mintick
         )
    )


    table.cell(
         diagnostics,
         0,
         6,
         "Bull Trail"
    )

    table.cell(
         diagnostics,
         1,
         6,
         str.tostring(
             bullTrail,
             format.mintick
         )
    )


    table.cell(
         diagnostics,
         0,
         7,
         "Bear Trail"
    )

    table.cell(
         diagnostics,
         1,
         7,
         str.tostring(
             bearTrail,
             format.mintick
         )
    )


    table.cell(
         diagnostics,
         0,
         8,
         "RSI"
    )

    table.cell(
         diagnostics,
         1,
         8,
         str.tostring(
             rsiValue,
             "0.0"
         )
    )


    table.cell(
         diagnostics,
         0,
         9,
         "ADX"
    )

    table.cell(
         diagnostics,
         1,
         9,
         str.tostring(
             adxValue,
             "0.0"
         )
    )


    table.cell(
         diagnostics,
         0,
         10,
         "MFI"
    )

    table.cell(
         diagnostics,
         1,
         10,
         str.tostring(
             mfiValue,
             "0.0"
         )
    )


    table.cell(
         diagnostics,
         0,
         11,
         "Source B / S"
    )

    table.cell(
         diagnostics,
         1,
         11,
         str.tostring(
             buySignalCount
         ) +
         " / " +
         str.tostring(
             sellSignalCount
         )
    )


    table.cell(
         diagnostics,
         0,
         12,
         "Entry L / S"
    )

    table.cell(
         diagnostics,
         1,
         12,
         str.tostring(
             executedLongCount
         ) +
         " / " +
         str.tostring(
             executedShortCount
         )
    )


    table.cell(
         diagnostics,
         0,
         13,
         "Trades Today"
    )

    table.cell(
         diagnostics,
         1,
         13,
         maximumTradesPerDay == 0
         ? str.tostring(tradesToday) + " / INF"
         : str.tostring(tradesToday) + " / " + str.tostring(maximumTradesPerDay)
    )


    table.cell(
         diagnostics,
         0,
         14,
         "TP Mode"
    )

    table.cell(
         diagnostics,
         1,
         14,
         takeProfitMode
    )


    table.cell(
         diagnostics,
         0,
         15,
         "Entry ATR"
    )

    table.cell(
         diagnostics,
         1,
         15,
         str.tostring(
             activeEntryATR,
             "0.00"
         )
    )


    table.cell(
         diagnostics,
         0,
         16,
         "Active Stop"
    )

    table.cell(
         diagnostics,
         1,
         16,
         str.tostring(
             activeStopPrice,
             format.mintick
         )
    )


    table.cell(
         diagnostics,
         0,
         17,
         "Position"
    )

    table.cell(
         diagnostics,
         1,
         17,
         positionText
    )