//@version=6
strategy(
     title="CNPS26 SUPERTREND + 40-BAR BREAKOUT ADD",
     shorttitle="CNPS26 ST BREAKOUT",
     overlay=true,
     initial_capital=100000000,
     default_qty_type=strategy.fixed,
     default_qty_value=3,
     pyramiding=2,
     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
)


// ============================================================================
// CNPS26 SUPERTREND + 40-BAR BREAKOUT ADD
//
// SOURCE-DERIVED LOGIC:
//
// 1. Supertrend flips Bull
//      -> BASE LONG
//
// 2. Already Long
//      + no previous add-on
//      + Close > highest CLOSE of previous N bars
//      -> ADD POSITION ONCE
//
// 3. Supertrend flips Bear
//      -> CLOSE ENTIRE POSITION
//
// CNPS ADAPTATION:
//
// - Current chart timeframe
// - Long only
// - Base = 2 contracts
// - Add  = 1 contract
// - Maximum exposure = 3 contracts
// - 100M VND
// - Commission = 10,000 VND / contract
// - Slippage = 3 ticks
// - Entry window = 09:00 -> 14:30
// - Force close = 14:45
//
// SOURCE EXIT:
// - Bearish Supertrend flip
//
// OPTIONAL:
// - Hard stop
// - Fixed take profit
// ============================================================================


// ============================================================================
// SECTION 1: POSITION SIZE
// ============================================================================

baseContracts = input.int(
     2,
     title="Base Entry Contracts",
     minval=1,
     maxval=10,
     group="Position Size"
)

addContracts = input.int(
     1,
     title="Breakout Add Contracts",
     minval=1,
     maxval=10,
     group="Position Size"
)


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

atrLength = input.int(
     10,
     title="Supertrend ATR Length",
     minval=1,
     group="Supertrend"
)

supertrendMultiplier = input.float(
     4.0,
     title="Supertrend Multiplier",
     minval=0.1,
     step=0.1,
     group="Supertrend"
)


// ============================================================================
// SECTION 3: BREAKOUT ADD
// ============================================================================

breakoutLength = input.int(
     40,
     title="Breakout Lookback",
     minval=2,
     group="Breakout Add"
)

requireBullishSupertrendForAdd = input.bool(
     true,
     title="Require Supertrend Still Bullish?",
     group="Breakout Add"
)

requireBullishBreakoutCandle = input.bool(
     false,
     title="Require Bullish Breakout Candle?",
     group="Breakout Add"
)


// ============================================================================
// SECTION 4: OPTIONAL PROTECTIVE RISK
// ============================================================================
//
// Default OFF because the source strategy exits on the opposite Supertrend
// flip rather than using a fixed SL / TP.
//
// ============================================================================

useHardStop = input.bool(
     false,
     title="Use Hard Stop?",
     group="Risk Management"
)

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

useFixedTakeProfit = input.bool(
     false,
     title="Use Fixed Take Profit?",
     group="Risk Management"
)

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


// ============================================================================
// SECTION 5: OPTIONAL 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 6: SESSION
// ============================================================================

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

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

sessionStartMinute = 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"
)

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

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


// ============================================================================
// SECTION 7: DAILY TRADE CONTROL
// ============================================================================

maximumBaseEntriesPerDay = input.int(
     4,
     title="Maximum Base Entries Per Day",
     minval=1,
     maxval=20,
     group="Trade Control"
)

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


// ============================================================================
// SECTION 8: ALERTS
// ============================================================================

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


// ============================================================================
// SECTION 9: DISPLAY
// ============================================================================

showSupertrend = input.bool(
     true,
     title="Show Supertrend?",
     group="Display"
)

showBreakoutLevel = input.bool(
     false,
     title="Show Previous N-Bar Highest Close?",
     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 10: TIME ENGINE
// ============================================================================

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

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

int sessionStartTotal = sessionStartHour * 60 + sessionStartMinute

int lastEntryTotal = lastEntryHour * 60 + lastEntryMinute

int sessionEndTotal = sessionEndHour * 60 + sessionEndMinute

bool entrySession = currentMinute >= sessionStartTotal and currentMinute <= lastEntryTotal


// ============================================================================
// SECTION 11: DAILY COUNTER
// ============================================================================

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

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

var int baseEntriesToday = 0

if newTradingDay
    baseEntriesToday := 0


// ============================================================================
// SECTION 12: SUPERTREND
// ============================================================================

[supertrendValue, supertrendDirection] = ta.supertrend(
     supertrendMultiplier,
     atrLength
)


// TradingView convention:
//
// direction < 0 = bullish
// direction > 0 = bearish

bool bullishSupertrend = supertrendDirection < 0

bool bearishSupertrend = supertrendDirection > 0


bool bullishFlip = (
     bullishSupertrend and
     supertrendDirection[1] > 0
)


bool bearishFlip = (
     bearishSupertrend and
     supertrendDirection[1] < 0
)


// ============================================================================
// SECTION 13: BREAKOUT LEVEL
// ============================================================================
//
// Current candle deliberately excluded.
//
// ============================================================================

float highestPreviousClose = ta.highest(
     close,
     breakoutLength
)[1]


bool breakoutSignal = (
     not na(highestPreviousClose) and
     close >
     highestPreviousClose
)


// ============================================================================
// SECTION 14: POSITION STATE
// ============================================================================

bool flat = strategy.position_size == 0

bool inLong = strategy.position_size > 0

bool newLongPosition = (
     inLong and
     strategy.position_size[1] <= 0
)

bool positionJustClosed = (
     flat and
     strategy.position_size[1] != 0
)


// ============================================================================
// SECTION 15: TRADE STATE
// ============================================================================

var bool addedPosition = false

var int baseEntryBar = na

var int lastExitBar = na


if positionJustClosed

    addedPosition := false

    baseEntryBar := na

    lastExitBar := bar_index


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


bool baseTradeLimitOK = (
     baseEntriesToday <
     maximumBaseEntriesPerDay
)


// ============================================================================
// SECTION 16: BASE LONG SIGNAL
// ============================================================================

bool baseLongSignal = (
     flat and
     entrySession and
     currentCloseMinute <
     sessionEndTotal and
     bullishFlip and
     cooldownOK and
     baseTradeLimitOK and
     barstate.isconfirmed
)


// ============================================================================
// SECTION 17: BASE LONG ENTRY
// ============================================================================

if baseLongSignal

    strategy.entry(
         "Base Long",
         strategy.long,
         qty=baseContracts,
         comment="ST Base Long",
         alert_message="ENTRY_BASE_LONG_CNPS26"
    )

    baseEntryBar := bar_index

    addedPosition := false

    baseEntriesToday += 1


// ============================================================================
// SECTION 18: ADD-ON FILTERS
// ============================================================================

bool addTrendOK = (
     not requireBullishSupertrendForAdd or
     bullishSupertrend
)


bool addCandleOK = (
     not requireBullishBreakoutCandle or
     close >
     open
)


bool addAfterBaseBar = (
     not na(baseEntryBar) and
     bar_index >
     baseEntryBar
)


// ============================================================================
// SECTION 19: ADD-ON SIGNAL
// ============================================================================

bool addLongSignal = (
     inLong and
     entrySession and
     currentCloseMinute <
     sessionEndTotal and
     not addedPosition and
     addAfterBaseBar and
     breakoutSignal and
     addTrendOK and
     addCandleOK and
     barstate.isconfirmed
)


// ============================================================================
// SECTION 20: ADD-ON ENTRY
// ============================================================================

if addLongSignal

    strategy.entry(
         "Breakout Add",
         strategy.long,
         qty=addContracts,
         comment="40-Bar Add",
         alert_message="ENTRY_ADD_LONG_CNPS26"
    )

    addedPosition := true


// ============================================================================
// SECTION 21: ACTIVE TRADE STATE
// ============================================================================

var float activeAveragePrice = na

var float activeStop = na

var float activeTakeProfit = na

var bool breakEvenActivated = false


// ============================================================================
// SECTION 22: UPDATE AVERAGE ENTRY
// ============================================================================
//
// Recalculate whenever:
// - the Base fills
// - the Add fills
//
// This lets an optional fixed protective exit follow the true average price.
//
// ============================================================================

bool positionSizeIncreased = (
     inLong and
     strategy.position_size >
     strategy.position_size[1]
)


if newLongPosition or positionSizeIncreased

    activeAveragePrice := strategy.position_avg_price


    if useHardStop

        activeStop := activeAveragePrice - hardStopPoints

    else

        activeStop := na


    if useFixedTakeProfit

        activeTakeProfit := activeAveragePrice + fixedTakeProfitPoints

    else

        activeTakeProfit := na


    breakEvenActivated := false


// ============================================================================
// SECTION 23: BREAK EVEN
// ============================================================================

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

    float breakEvenTriggerPrice = activeAveragePrice + breakEvenTriggerPoints


    if high >= breakEvenTriggerPrice

        float newStop = activeAveragePrice + breakEvenOffsetPoints


        if na(activeStop) or newStop > activeStop
            activeStop := newStop


        breakEvenActivated := true


// ============================================================================
// SECTION 24: PROTECTIVE EXIT - BASE POSITION
// ============================================================================

if (
     inLong and
     (
         not na(activeStop) or
         not na(activeTakeProfit)
     )
)

    strategy.exit(
         "Base Protective Exit",
         from_entry="Base Long",
         stop=activeStop,
         limit=activeTakeProfit,
         alert_profit="EXIT_BASE_TP_CNPS26",
         alert_loss=breakEvenActivated ? "EXIT_BASE_BE_CNPS26" : "EXIT_BASE_SL_CNPS26",
         disable_alert=disableSLTPAlerts
    )


// ============================================================================
// SECTION 25: PROTECTIVE EXIT - ADD POSITION
// ============================================================================

if (
     inLong and
     addedPosition and
     (
         not na(activeStop) or
         not na(activeTakeProfit)
     )
)

    strategy.exit(
         "Add Protective Exit",
         from_entry="Breakout Add",
         stop=activeStop,
         limit=activeTakeProfit,
         alert_profit="EXIT_ADD_TP_CNPS26",
         alert_loss=breakEvenActivated ? "EXIT_ADD_BE_CNPS26" : "EXIT_ADD_SL_CNPS26",
         disable_alert=disableSLTPAlerts
    )


// ============================================================================
// SECTION 26: SUPERTREND EXIT
// ============================================================================
//
// Core source exit.
//
// Close ALL Base + Add contracts when Supertrend flips bearish.
//
// ============================================================================

bool supertrendExitSignal = (
     inLong and
     bearishFlip and
     barstate.isconfirmed
)


if supertrendExitSignal

    strategy.close_all(
         comment="Bearish Supertrend",
         alert_message="SUPERTREND_EXIT_LONG_CNPS26"
    )


// ============================================================================
// SECTION 27: POSITION CLEANUP
// ============================================================================

if positionJustClosed

    activeAveragePrice := na

    activeStop := na

    activeTakeProfit := na

    breakEvenActivated := false


// ============================================================================
// SECTION 28: SESSION END
// ============================================================================

bool finalSessionBar = (
     currentMinute <
     sessionEndTotal and
     currentCloseMinute >=
     sessionEndTotal
)


if finalSessionBar

    if strategy.position_size != 0

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


// ============================================================================
// SECTION 29: SUPERTREND DISPLAY
// ============================================================================

plot(
     showSupertrend and bullishSupertrend
     ? supertrendValue
     : na,
     title="Bullish Supertrend",
     color=color.green,
     linewidth=2,
     style=plot.style_linebr
)


plot(
     showSupertrend and bearishSupertrend
     ? supertrendValue
     : na,
     title="Bearish Supertrend",
     color=color.red,
     linewidth=2,
     style=plot.style_linebr
)


// ============================================================================
// SECTION 30: BREAKOUT LEVEL DISPLAY
// ============================================================================

plot(
     showBreakoutLevel
     ? highestPreviousClose
     : na,
     title="Previous N-Bar Highest Close",
     color=color.orange,
     linewidth=1
)


// ============================================================================
// SECTION 31: ACTIVE SL / TP
// ============================================================================

plot(
     showActiveSLTP and inLong
     ? activeStop
     : na,
     title="Active Stop",
     color=color.red,
     linewidth=2,
     style=plot.style_linebr
)


plot(
     showActiveSLTP and inLong
     ? activeTakeProfit
     : na,
     title="Active Take Profit",
     color=color.green,
     linewidth=2,
     style=plot.style_linebr
)


// ============================================================================
// SECTION 32: SIGNAL MARKERS
// ============================================================================

plotshape(
     showSignalMarkers and baseLongSignal,
     title="Base Long",
     location=location.belowbar,
     style=shape.triangleup,
     color=color.green,
     size=size.small,
     text="BASE",
     textcolor=color.white
)


plotshape(
     showSignalMarkers and addLongSignal,
     title="Breakout Add",
     location=location.belowbar,
     style=shape.circle,
     color=color.blue,
     size=size.small,
     text="+1",
     textcolor=color.white
)


plotshape(
     showSignalMarkers and supertrendExitSignal,
     title="Supertrend Exit",
     location=location.abovebar,
     style=shape.triangledown,
     color=color.red,
     size=size.small,
     text="EXIT",
     textcolor=color.white
)


// ============================================================================
// SECTION 33: DIAGNOSTIC COUNTERS
// ============================================================================

var int bullishFlipCount = 0

var int bearishFlipCount = 0

var int breakoutCount = 0

var int baseEntryCount = 0

var int addEntryCount = 0

var int supertrendExitCount = 0


if bullishFlip
    bullishFlipCount += 1


if bearishFlip
    bearishFlipCount += 1


if breakoutSignal
    breakoutCount += 1


if baseLongSignal
    baseEntryCount += 1


if addLongSignal
    addEntryCount += 1


if supertrendExitSignal
    supertrendExitCount += 1


// ============================================================================
// SECTION 34: DIAGNOSTICS
// ============================================================================

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


if showDiagnostics and barstate.islast

    string trendText = bullishSupertrend ? "BULL" : bearishSupertrend ? "BEAR" : "NONE"

    string positionText = flat ? "FLAT" : "LONG"


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


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


    table.cell(diagnostics, 0, 2, "ST Value")
    table.cell(diagnostics, 1, 2, str.tostring(supertrendValue, format.mintick))


    table.cell(diagnostics, 0, 3, "40-Bar High")
    table.cell(diagnostics, 1, 3, str.tostring(highestPreviousClose, format.mintick))


    table.cell(diagnostics, 0, 4, "Breakout")
    table.cell(diagnostics, 1, 4, breakoutSignal ? "YES" : "NO")


    table.cell(diagnostics, 0, 5, "Added")
    table.cell(diagnostics, 1, 5, addedPosition ? "YES" : "NO")


    table.cell(diagnostics, 0, 6, "Position Qty")
    table.cell(diagnostics, 1, 6, str.tostring(strategy.position_size))


    table.cell(diagnostics, 0, 7, "Avg Entry")
    table.cell(diagnostics, 1, 7, str.tostring(strategy.position_avg_price, format.mintick))


    table.cell(diagnostics, 0, 8, "Bull Flips")
    table.cell(diagnostics, 1, 8, str.tostring(bullishFlipCount))


    table.cell(diagnostics, 0, 9, "Bear Flips")
    table.cell(diagnostics, 1, 9, str.tostring(bearishFlipCount))


    table.cell(diagnostics, 0, 10, "Breakouts")
    table.cell(diagnostics, 1, 10, str.tostring(breakoutCount))


    table.cell(diagnostics, 0, 11, "Base Entries")
    table.cell(diagnostics, 1, 11, str.tostring(baseEntryCount))


    table.cell(diagnostics, 0, 12, "Add Entries")
    table.cell(diagnostics, 1, 12, str.tostring(addEntryCount))


    table.cell(diagnostics, 0, 13, "ST Exits")
    table.cell(diagnostics, 1, 13, str.tostring(supertrendExitCount))


    table.cell(diagnostics, 0, 14, "Base Today")
    table.cell(diagnostics, 1, 14, str.tostring(baseEntriesToday) + " / " + str.tostring(maximumBaseEntriesPerDay))


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