Strategy · Pine Script v6 · On the chart

MTF Second-Flip Continuation

by rosijay17-cmd

A strategy that trades continuation after a second directional flip, intended for a 15-minute chart.

View on GitHub

GitHub stars1on the repository
Pine versionv6current
Lines of code1,399
Settings40inputs
LicenceGPL-3.0
Last update7 days ago22 Oct 2026

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new strategy, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart. The trades and results appear in the Strategy Tester tab; adjust the settings with the gear icon.

Source Code

1,399 lines of Pine Script v6 from MTF_Second_Flip_Continuation_v1_2.pine, published under GPL-3.0. Keep the author's credit and licence lines when you reuse it.

MTF_Second_Flip_Continuation_v1_2.pine
//@version=6
strategy("MTF Second-Flip Continuation v1.2 - Signal Volatility", shorttitle="MTF2FLIP v1.2", overlay=true, pyramiding=0, initial_capital=100000, default_qty_type=strategy.fixed, default_qty_value=1, commission_type=strategy.commission.cash_per_contract, commission_value=2.50, slippage=1, process_orders_on_close=true, calc_on_order_fills=true, calc_on_every_tick=false, max_bars_back=5000, max_labels_count=100)

//=============================================================================
// MTF SECOND-FLIP CONTINUATION v1.2
//=============================================================================
// Intended chart: 15-minute
// Bias: confirmed 1H structure AND confirmed 4H structure
// Trigger: reconstruct lower-timeframe states relative to the current 15m open
// Entry: directional flip #2 -> freeze setup -> later fresh prior-bar break
//
// v1.2 RESEARCH INTEGRATION
// 1. Separates raw signal volatility from genuine, excursion-qualified flips.
// 2. Freezes the breakout and structural-stop levels when flip #2 arms.
// 3. Invalidates an armed setup after a genuine opposing flip.
// 4. Expires setups that do not break within a limited number of intrabars.
// 5. Rejects excessive genuine transitions before entry.
// 6. Optionally filters high signal churn and low trend quality.
// 7. Uses compact visuals and alert() calls to reduce plot consumption.
// 8. Adds an independent engulfing + third-candle continuation entry: an
//    engulfing candle in the direction of the confirmed HTF bias, followed
//    by a third candle whose open is rejected back into the trend. Fires
//    alongside the second-flip engine, sharing its HTF bias filter and
//    daily/session/risk controls (see section 19B).
//
// IMPORTANT
// request.security_lower_tf() reconstructs chronology only at the selected
// lower timeframe. Tick order inside a one-minute OHLC candle is unknowable.
// "Intrabar Close" remains the preferred non-ambiguous default.
//=============================================================================


//=============================================================================
// 01. INPUT GROUPS
//=============================================================================

string G_TF      = "01 - Timeframes"
string G_BIAS    = "02 - HTF Structure"
string G_FLIP    = "03 - Flip and Signal Volatility"
string G_BREAK   = "04 - Breakout"
string G_SESSION = "05 - Session"
string G_FILTER  = "06 - Optional Filters"
string G_RISK    = "07 - Risk Management"
string G_DAILY   = "08 - Daily Controls"
string G_VIS     = "09 - Visuals"
string G_ENGULF  = "10 - Engulfing Continuation"


//=============================================================================
// 02. TIMEFRAMES
//=============================================================================

string biasTF1 = input.timeframe("60", "Bias TF 1", group=G_TF)
string biasTF2 = input.timeframe("240", "Bias TF 2", group=G_TF)
string intrabarTF = input.timeframe("1", "Intrabar timeframe", group=G_TF)


//=============================================================================
// 03. HTF BIAS
//=============================================================================

int pivotLength = input.int(3, "HTF pivot length", minval=1, maxval=20, group=G_BIAS)


//=============================================================================
// 04. FLIP AND SIGNAL-VOLATILITY ENGINE
//=============================================================================

string flipDetectionMode = input.string(
     "Intrabar Close",
     "Flip detection mode",
     options=["Intrabar Close", "Intrabar High/Low Touch"],
     group=G_FLIP,
     tooltip="Intrabar Close is preferred. High/Low Touch is approximate when one minute trades on both sides of the 15m open."
)

bool requireExactlySecondFlip = input.bool(
     true,
     "Require exactly second directional flip",
     group=G_FLIP
)

float minFlipExcursionATR = input.float(
     0.05,
     "Minimum excursion between genuine flips, ATR",
     minval=0.0,
     maxval=1.0,
     step=0.01,
     group=G_FLIP,
     tooltip="A candidate state change must move this fraction of the confirmed 15m ATR from the last accepted state price. Zero disables the excursion test."
)

int maxGenuineTransitions = input.int(
     4,
     "Maximum genuine transitions before entry",
     minval=2,
     maxval=20,
     group=G_FLIP,
     tooltip="A second directional flip normally requires three or four total transitions, depending on the first state observed."
)

bool invalidateOnOpposingFlip = input.bool(
     true,
     "Invalidate armed setup on opposing flip",
     group=G_FLIP
)

int maxIntrabarsAfterSecondFlip = input.int(
     8,
     "Breakout deadline after flip #2, intrabars",
     minval=1,
     maxval=14,
     group=G_FLIP
)

bool useSignalVolatilityFilter = input.bool(
     true,
     "Use raw signal-volatility filter",
     group=G_FLIP
)

int minimumSignalSamples = input.int(
     6,
     "Minimum directional samples before churn filter",
     minval=2,
     maxval=15,
     group=G_FLIP
)

float maximumSignalVolatility = input.float(
     0.60,
     "Maximum raw signal volatility",
     minval=0.05,
     maxval=1.0,
     step=0.05,
     group=G_FLIP,
     tooltip="Raw directional state changes divided by possible non-neutral transitions. The filter waits for the minimum sample count before enforcing this threshold."
)

bool useTrendQualityFilter = input.bool(false, "Use market-volatility / signal-churn quality filter", group=G_FLIP)

float minimumTrendQuality = input.float(
     0.10,
     "Minimum trend-quality score",
     minval=0.0,
     maxval=5.0,
     step=0.05,
     group=G_FLIP,
     tooltip="Trend Quality = current 15m running range / confirmed ATR x (1 - raw signal volatility). Leave the filter off until calibrated on your instrument."
)


//=============================================================================
// 05. BREAKOUT
//=============================================================================

string breakMode = input.string(
     "Prior Candle High/Low",
     "Break level",
     options=["Prior Candle High/Low", "Prior 2-Bar Extreme"],
     group=G_BREAK
)

string breakConfirmationMode = input.string(
     "Intrabar Close",
     "Break confirmation",
     options=["Intrabar Close", "Intrabar High/Low"],
     group=G_BREAK
)


//=============================================================================
// 06. SESSION
//=============================================================================

string executionSession = input.session("0930-1600", "Execution session", group=G_SESSION)
string sessionTimezone = input.string("America/New_York", "Session timezone", group=G_SESSION)

int openingDelayMinutes = input.int(
     5,
     "Opening delay, minutes",
     minval=0,
     maxval=120,
     group=G_SESSION
)

bool flattenAtSessionEnd = input.bool(
     true,
     "Flatten at session end",
     group=G_SESSION
)


//=============================================================================
// 07. OPTIONAL FILTERS
//=============================================================================

bool useVWAPFilter = input.bool(false, "Use RTH VWAP filter", group=G_FILTER)
bool useEMAFilter = input.bool(false, "Use EMA filter", group=G_FILTER)
int emaLength = input.int(50, "EMA length", minval=2, group=G_FILTER)

bool useATRRegimeFilter = input.bool(false, "Use ATR regime filter", group=G_FILTER)
int atrLength = input.int(14, "ATR length", minval=2, group=G_FILTER)
int atrBaselineLength = input.int(100, "ATR baseline", minval=20, group=G_FILTER)

float minATRRatio = input.float(
     0.70,
     "Minimum ATR ratio",
     minval=0.10,
     maxval=5.0,
     step=0.05,
     group=G_FILTER
)

float maxATRRatio = input.float(
     2.00,
     "Maximum ATR ratio",
     minval=0.20,
     maxval=10.0,
     step=0.05,
     group=G_FILTER
)

bool useEfficiencyFilter = input.bool(false, "Use Efficiency Ratio filter", group=G_FILTER)
int efficiencyLength = input.int(20, "Efficiency Ratio length", minval=2, maxval=200, group=G_FILTER)

float minimumEfficiency = input.float(
     0.18,
     "Minimum Efficiency Ratio",
     minval=0.0,
     maxval=1.0,
     step=0.01,
     group=G_FILTER
)


//=============================================================================
// 08. RISK MANAGEMENT
//=============================================================================

int contracts = input.int(1, "Contracts", minval=1, maxval=100, group=G_RISK)

string stopMode = input.string(
     "Wider",
     "Initial stop model",
     options=["ATR", "Prior Candle", "Wider", "Tighter"],
     group=G_RISK
)

float atrStopMultiple = input.float(
     1.25,
     "ATR stop multiple",
     minval=0.10,
     maxval=10.0,
     step=0.05,
     group=G_RISK
)

float rewardRisk = input.float(
     2.00,
     "Reward / Risk",
     minval=0.25,
     maxval=10.0,
     step=0.25,
     group=G_RISK
)

bool useBreakEven = input.bool(true, "Use break-even", group=G_RISK)

float breakEvenAtR = input.float(
     1.00,
     "Break-even trigger, R",
     minval=0.25,
     maxval=5.0,
     step=0.25,
     group=G_RISK
)

bool useTrailingStop = input.bool(false, "Use ATR trailing stop", group=G_RISK)

float trailingATR = input.float(
     2.00,
     "Trailing ATR",
     minval=0.25,
     maxval=10.0,
     step=0.25,
     group=G_RISK
)

bool useTimeExit = input.bool(true, "Use time exit", group=G_RISK)

int maximumBarsInTrade = input.int(
     16,
     "Maximum bars in trade",
     minval=1,
     maxval=200,
     group=G_RISK
)


//=============================================================================
// 09. DAILY CONTROLS
//=============================================================================

int maxTradesPerDay = input.int(2, "Maximum trades per RTH session", minval=1, maxval=20, group=G_DAILY)
int cooldownBars = input.int(0, "Cooldown bars", minval=0, maxval=100, group=G_DAILY)

float dailyLossLimit = input.float(
     0.0,
     "Daily loss limit $, 0 = disabled",
     minval=0.0,
     step=50.0,
     group=G_DAILY
)


//=============================================================================
// 09B. ENGULFING CONTINUATION INPUTS
//=============================================================================

bool useEngulfingSignal = input.bool(
     true,
     "Enable engulfing + 3rd-candle entry",
     group=G_ENGULF,
     tooltip="Independent entry: an engulfing candle in the direction of the confirmed HTF bias, followed by a third candle whose open is rejected back into the trend."
)

float minEngulfBodyATR = input.float(
     0.0,
     "Minimum engulfing body, x ATR (0 = off)",
     minval=0.0,
     maxval=3.0,
     step=0.05,
     group=G_ENGULF,
     tooltip="Requires the engulfing candle's body to be at least this fraction of the confirmed 15m ATR. Zero disables the filter."
)

int engulfExpiryBars = input.int(
     6,
     "Setup expiry after engulfing candle, bars",
     minval=1,
     maxval=50,
     group=G_ENGULF,
     tooltip="If price has not both retested and broken back through the engulfing candle's level within this many bars, the setup is discarded."
)

bool showEngulfMarkers = input.bool(true, "Show engulfing setup markers", group=G_ENGULF)


//=============================================================================
// 10. VISUALS
//=============================================================================

bool showDebugMarkers = input.bool(false, "Show debug markers", group=G_VIS)
bool showDashboard = input.bool(true, "Show compact dashboard", group=G_VIS)
bool showBiasBackground = input.bool(true, "Show HTF bias background", group=G_VIS)


//=============================================================================
// 11. HELPERS
//=============================================================================

f_biasName(int biasValue) =>
    switch biasValue
        1 => "BULL"
        -1 => "BEAR"
        => "NEUTRAL"

f_stateName(int stateValue) =>
    switch stateValue
        1 => "BULLISH"
        -1 => "BEARISH"
        => "NEUTRAL"

f_yesNo(bool value) =>
    value ? "YES" : "NO"

f_price(float value) =>
    na(value) ? "-" : str.tostring(value, format.mintick)

f_percent(float value) =>
    na(value) ? "-" : str.tostring(value * 100.0, "#.0") + "%"

f_number(float value) =>
    na(value) ? "-" : str.tostring(value, "#.00")


// Confirmed pivot structure. A pivot is available only after its right-side
// confirmation period. The two latest confirmed highs and lows are compared.
f_structureBias(int len) =>
    float ph = ta.pivothigh(high, len, len)
    float pl = ta.pivotlow(low, len, len)

    float latestHigh = ta.valuewhen(not na(ph), ph, 0)
    float previousHigh = ta.valuewhen(not na(ph), ph, 1)
    float latestLow = ta.valuewhen(not na(pl), pl, 0)
    float previousLow = ta.valuewhen(not na(pl), pl, 1)

    bool bullishStructure = not na(latestHigh) and not na(previousHigh) and not na(latestLow) and not na(previousLow) and latestHigh > previousHigh and latestLow > previousLow
    bool bearishStructure = not na(latestHigh) and not na(previousHigh) and not na(latestLow) and not na(previousLow) and latestHigh < previousHigh and latestLow < previousLow

    bullishStructure ? 1 : bearishStructure ? -1 : 0


// RTH VWAP calculated in the lower-timeframe request context.
f_rthVWAP() =>
    bool localInSession = not na(time(timeframe.period, executionSession, sessionTimezone))
    bool localSessionStart = localInSession and not localInSession[1]

    var float cumPV = 0.0
    var float cumVolume = 0.0

    if localSessionStart
        cumPV := hlc3 * volume
        cumVolume := volume
    else if localInSession
        cumPV += hlc3 * volume
        cumVolume += volume

    float localVWAP = na
    if localInSession and cumVolume > 0
        localVWAP := cumPV / cumVolume

    localVWAP


// Intrabar state relative to the current 15m open.
f_intrabarState(float execBarOpen, float intrabarHigh, float intrabarLow, float intrabarClose, int previousState, string mode) =>
    int state = 0

    if mode == "Intrabar Close"
        if intrabarClose > execBarOpen
            state := 1
        else if intrabarClose < execBarOpen
            state := -1
    else
        bool above = intrabarHigh > execBarOpen
        bool below = intrabarLow < execBarOpen

        if above and not below
            state := 1
        else if below and not above
            state := -1
        else if above and below
            if intrabarClose > execBarOpen
                state := 1
            else if intrabarClose < execBarOpen
                state := -1
            else
                state := previousState

    state


f_longStopCandidate(float signalReference, float atrValue, float structureLow) =>
    float atrStop = signalReference - atrValue * atrStopMultiple
    float structuralStop = structureLow - syminfo.mintick * 2.0
    float result = atrStop

    if stopMode == "Prior Candle"
        result := structuralStop
    else if stopMode == "Wider"
        result := math.min(atrStop, structuralStop)
    else if stopMode == "Tighter"
        result := math.max(atrStop, structuralStop)

    result


f_shortStopCandidate(float signalReference, float atrValue, float structureHigh) =>
    float atrStop = signalReference + atrValue * atrStopMultiple
    float structuralStop = structureHigh + syminfo.mintick * 2.0
    float result = atrStop

    if stopMode == "Prior Candle"
        result := structuralStop
    else if stopMode == "Wider"
        result := math.max(atrStop, structuralStop)
    else if stopMode == "Tighter"
        result := math.min(atrStop, structuralStop)

    result


//=============================================================================
// 12. TIMEFRAME VALIDATION
//=============================================================================

bool chartIs15m = timeframe.isminutes and timeframe.multiplier == 15
bool lowerTFValid = timeframe.in_seconds(intrabarTF) < timeframe.in_seconds(timeframe.period)

if barstate.isfirst and not chartIs15m
    runtime.error("MTF Second-Flip strategy must be used on a 15-minute chart.")

if barstate.isfirst and not lowerTFValid
    runtime.error("Intrabar timeframe must be lower than the chart timeframe.")


//=============================================================================
// 13. CONFIRMED HTF BIAS
//=============================================================================
// [1] is evaluated in the requested HTF context. lookahead_on exposes only
// that already-completed value during the next HTF candle.

int bias1H = request.security(syminfo.tickerid, biasTF1, f_structureBias(pivotLength)[1], gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_on)
int bias4H = request.security(syminfo.tickerid, biasTF2, f_structureBias(pivotLength)[1], gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_on)

int finalBias = 0
if bias1H == 1 and bias4H == 1
    finalBias := 1
else if bias1H == -1 and bias4H == -1
    finalBias := -1


//=============================================================================
// 14. EXECUTION-TIMEFRAME FILTER DATA
//=============================================================================

float ema50 = ta.ema(close, emaLength)
float atr = ta.atr(atrLength)
float atrBaseline = ta.sma(atr, atrBaselineLength)

float atrRatio = na
if not na(atrBaseline) and atrBaseline > 0
    atrRatio := atr / atrBaseline

float pricePath = math.sum(math.abs(ta.change(close)), efficiencyLength)
float efficiencyRatio = na
if not na(pricePath) and pricePath > 0 and bar_index >= efficiencyLength
    efficiencyRatio := math.abs(close - close[efficiencyLength]) / pricePath

float confirmedEMA = ema50[1]
bool confirmedEMARising = ema50[1] > ema50[2]
bool confirmedEMAFalling = ema50[1] < ema50[2]
float confirmedATR = atr[1]
float confirmedATRRatio = atrRatio[1]
float confirmedEfficiency = efficiencyRatio[1]


//=============================================================================
// 15. PRIOR 15-MINUTE LEVELS
//=============================================================================

float execOpen = open
float priorHigh = high[1]
float priorLow = low[1]

float breakHigh = breakMode == "Prior 2-Bar Extreme" ? math.max(high[1], high[2]) : priorHigh
float breakLow = breakMode == "Prior 2-Bar Extreme" ? math.min(low[1], low[2]) : priorLow


//=============================================================================
// 16. SESSION STATE
//=============================================================================

bool inExecutionSession = not na(time(timeframe.period, executionSession, sessionTimezone))
bool executionSessionStart = inExecutionSession and not inExecutionSession[1]

int sessionStartHour = int(str.tonumber(str.substring(executionSession, 0, 2)))
int sessionStartMinute = int(str.tonumber(str.substring(executionSession, 2, 4)))
int sessionEndHour = int(str.tonumber(str.substring(executionSession, 5, 7)))
int sessionEndMinute = int(str.tonumber(str.substring(executionSession, 7, 9)))

int sessionStartMOD = sessionStartHour * 60 + sessionStartMinute
int sessionEndMOD = sessionEndHour * 60 + sessionEndMinute

int chartHour = hour(time, sessionTimezone)
int chartMinute = minute(time, sessionTimezone)
int chartMOD = chartHour * 60 + chartMinute
int finalExecutionBarStartMOD = sessionEndMOD - 15

bool isFinalSessionBar = inExecutionSession and chartMOD >= finalExecutionBarStartMOD and chartMOD < sessionEndMOD


//=============================================================================
// 17. DAILY CONTROLS
//=============================================================================

var int tradesToday = 0
var float dayStartNetProfit = 0.0
var bool dayHalted = false
var int lastExitBar = na
var int lastSignalBar = na
var int lastSessionResetBar = na

// Guard against repeated resets caused by calc_on_order_fills recalculations.
if executionSessionStart and (na(lastSessionResetBar) or lastSessionResetBar != bar_index)
    tradesToday := 0
    dayStartNetProfit := strategy.netprofit
    dayHalted := false
    lastSessionResetBar := bar_index

float dayPnL = strategy.netprofit + strategy.openprofit - dayStartNetProfit

if dailyLossLimit > 0 and dayPnL <= -dailyLossLimit
    dayHalted := true

bool cooldownPass = na(lastExitBar) or bar_index - lastExitBar > cooldownBars
bool tradeCountPass = tradesToday < maxTradesPerDay
bool positionPass = strategy.position_size == 0
bool signalBarPass = na(lastSignalBar) or lastSignalBar != bar_index


//=============================================================================
// 18. LOWER-TIMEFRAME DATA
//=============================================================================

[ltfTime, ltfOpen, ltfHigh, ltfLow, ltfClose, ltfVWAP] = request.security_lower_tf(
     syminfo.tickerid,
     intrabarTF,
     [time, open, high, low, close, f_rthVWAP()]
)


//=============================================================================
// 19. INTRABAR RECONSTRUCTION AND SIGNAL-VOLATILITY CONTROL
//=============================================================================

int bullFlipCount = 0
int bearFlipCount = 0
int rawDirectionalTransitions = 0
int genuineDirectionalTransitions = 0
int rejectedMicroTransitions = 0
int directionalSamples = 0

bool secondBullFlipSeen = false
bool secondBearFlipSeen = false
bool longBreakArmed = false
bool shortBreakArmed = false

bool bullFlip1Event = false
bool bullFlip2Event = false
bool bearFlip1Event = false
bool bearFlip2Event = false
bool longArmEvent = false
bool shortArmEvent = false
bool longInvalidationEvent = false
bool shortInvalidationEvent = false

bool longEntrySignal = false
bool shortEntrySignal = false

int currentExecState = 0
int acceptedNonNeutralState = 0
int rawPreviousNonNeutralState = 0

int longArmIntrabarIndex = na
int shortArmIntrabarIndex = na
int longArmIntrabarTime = na
int shortArmIntrabarTime = na

int longSignalIntrabarTime = na
int shortSignalIntrabarTime = na

float lastAcceptedStatePrice = na
float longFrozenBreakLevel = na
float shortFrozenBreakLevel = na
float longFrozenStructureLow = na
float shortFrozenStructureHigh = na

float longSignalReferencePrice = na
float shortSignalReferencePrice = na
float longSignalStructureLow = na
float shortSignalStructureHigh = na
float signalRTHVWAP = na

float runningIntrabarHigh = na
float runningIntrabarLow = na
float rawSignalVolatility = 0.0
float genuineSignalVolatility = 0.0
float marketVolatilityRatio = 0.0
float trendQuality = 0.0

bool previousLongBreakConfirmed = false
bool previousShortBreakConfirmed = false

int invalidationCode = 0
// 0 = none, 1 = opposing flip, 2 = expired, 3 = transition budget

float minimumFlipExcursion = minFlipExcursionATR <= 0.0 ? 0.0 : not na(confirmedATR) ? confirmedATR * minFlipExcursionATR : syminfo.mintick * 2.0

int intrabarCount = array.size(ltfClose)

if intrabarCount > 0
    for i = 0 to intrabarCount - 1
        int intrabarTime = array.get(ltfTime, i)
        float intrabarHigh = array.get(ltfHigh, i)
        float intrabarLow = array.get(ltfLow, i)
        float intrabarClose = array.get(ltfClose, i)
        float intrabarVWAP = array.get(ltfVWAP, i)

        runningIntrabarHigh := na(runningIntrabarHigh) ? intrabarHigh : math.max(runningIntrabarHigh, intrabarHigh)
        runningIntrabarLow := na(runningIntrabarLow) ? intrabarLow : math.min(runningIntrabarLow, intrabarLow)

        int intrabarHour = hour(intrabarTime, sessionTimezone)
        int intrabarMinute = minute(intrabarTime, sessionTimezone)
        int intrabarMOD = intrabarHour * 60 + intrabarMinute

        bool intrabarInSession = intrabarMOD >= sessionStartMOD and intrabarMOD < sessionEndMOD
        bool openingDelayComplete = intrabarMOD >= sessionStartMOD + openingDelayMinutes
        bool finalSessionEntryBlocked = flattenAtSessionEnd and intrabarMOD >= finalExecutionBarStartMOD
        bool intrabarSessionAllowed = intrabarInSession and openingDelayComplete and not finalSessionEntryBlocked

        //---------------------------------------------------------------------
        // DETERMINE AND PROCESS THIS MINUTE'S STATE FIRST
        //---------------------------------------------------------------------
        // Entry is tested later and requires i > arm index. This preserves the
        // no-same-minute arm/break rule while allowing a new opposing flip to
        // invalidate the setup before an entry can be accepted.

        int currentState = f_intrabarState(
             execOpen,
             intrabarHigh,
             intrabarLow,
             intrabarClose,
             acceptedNonNeutralState,
             flipDetectionMode
        )

        bool genuineTransitionThisMinute = false
        bool bullTransitionThisMinute = false
        bool bearTransitionThisMinute = false
        bool longArmedThisMinute = false
        bool shortArmedThisMinute = false

        if currentState != 0
            directionalSamples += 1

            if rawPreviousNonNeutralState != 0 and currentState != rawPreviousNonNeutralState
                rawDirectionalTransitions += 1

            rawPreviousNonNeutralState := currentState

            if acceptedNonNeutralState == 0
                acceptedNonNeutralState := currentState
                lastAcceptedStatePrice := intrabarClose
            else if currentState != acceptedNonNeutralState
                bool excursionPass = minimumFlipExcursion <= 0.0 or na(lastAcceptedStatePrice) or math.abs(intrabarClose - lastAcceptedStatePrice) >= minimumFlipExcursion

                if excursionPass
                    int stateBeforeTransition = acceptedNonNeutralState

                    acceptedNonNeutralState := currentState
                    lastAcceptedStatePrice := intrabarClose
                    genuineDirectionalTransitions += 1
                    genuineTransitionThisMinute := true

                    bullTransitionThisMinute := stateBeforeTransition == -1 and currentState == 1
                    bearTransitionThisMinute := stateBeforeTransition == 1 and currentState == -1

                    if bullTransitionThisMinute
                        bullFlipCount += 1

                        if bullFlipCount == 1
                            bullFlip1Event := true

                    if bearTransitionThisMinute
                        bearFlipCount += 1

                        if bearFlipCount == 1
                            bearFlip1Event := true
                else
                    rejectedMicroTransitions += 1

        currentExecState := acceptedNonNeutralState

        //---------------------------------------------------------------------
        // SIGNAL VOLATILITY AND MARKET VOLATILITY ARE DISTINCT
        //---------------------------------------------------------------------

        int possibleTransitions = math.max(directionalSamples - 1, 1)
        rawSignalVolatility := float(rawDirectionalTransitions) / float(possibleTransitions)
        genuineSignalVolatility := float(genuineDirectionalTransitions) / float(possibleTransitions)

        float runningRange = not na(runningIntrabarHigh) and not na(runningIntrabarLow) ? runningIntrabarHigh - runningIntrabarLow : 0.0
        marketVolatilityRatio := not na(confirmedATR) and confirmedATR > 0 ? runningRange / confirmedATR : 0.0
        trendQuality := marketVolatilityRatio * math.max(0.0, 1.0 - rawSignalVolatility)

        //---------------------------------------------------------------------
        // INVALIDATE AN EXISTING SETUP BEFORE CONSIDERING A NEW ARM
        //---------------------------------------------------------------------

        if genuineTransitionThisMinute and invalidateOnOpposingFlip
            if longBreakArmed and bearTransitionThisMinute
                longBreakArmed := false
                longInvalidationEvent := true
                invalidationCode := 1

            if shortBreakArmed and bullTransitionThisMinute
                shortBreakArmed := false
                shortInvalidationEvent := true
                invalidationCode := 1

        if genuineDirectionalTransitions > maxGenuineTransitions
            if longBreakArmed
                longBreakArmed := false
                longInvalidationEvent := true

            if shortBreakArmed
                shortBreakArmed := false
                shortInvalidationEvent := true

            invalidationCode := 3

        //---------------------------------------------------------------------
        // IDENTIFY DIRECTIONAL FLIP #2 AND FREEZE THE SETUP
        //---------------------------------------------------------------------

        if bullTransitionThisMinute
            bool bullishSecondEvent = requireExactlySecondFlip ? bullFlipCount == 2 : bullFlipCount >= 2 and not secondBullFlipSeen

            if bullishSecondEvent
                bullFlip2Event := true
                secondBullFlipSeen := true

                if finalBias == 1 and genuineDirectionalTransitions <= maxGenuineTransitions
                    longBreakArmed := true
                    longArmEvent := true
                    longArmedThisMinute := true
                    longArmIntrabarIndex := i
                    longArmIntrabarTime := intrabarTime
                    longFrozenBreakLevel := breakHigh
                    longFrozenStructureLow := priorLow

        if bearTransitionThisMinute
            bool bearishSecondEvent = requireExactlySecondFlip ? bearFlipCount == 2 : bearFlipCount >= 2 and not secondBearFlipSeen

            if bearishSecondEvent
                bearFlip2Event := true
                secondBearFlipSeen := true

                if finalBias == -1 and genuineDirectionalTransitions <= maxGenuineTransitions
                    shortBreakArmed := true
                    shortArmEvent := true
                    shortArmedThisMinute := true
                    shortArmIntrabarIndex := i
                    shortArmIntrabarTime := intrabarTime
                    shortFrozenBreakLevel := breakLow
                    shortFrozenStructureHigh := priorHigh

        //---------------------------------------------------------------------
        // SETUP EXPIRATION
        //---------------------------------------------------------------------

        if longBreakArmed and not na(longArmIntrabarIndex) and i - longArmIntrabarIndex > maxIntrabarsAfterSecondFlip
            longBreakArmed := false
            longInvalidationEvent := true
            invalidationCode := 2

        if shortBreakArmed and not na(shortArmIntrabarIndex) and i - shortArmIntrabarIndex > maxIntrabarsAfterSecondFlip
            shortBreakArmed := false
            shortInvalidationEvent := true
            invalidationCode := 2

        //---------------------------------------------------------------------
        // BREAKOUT STATUS USES THE LEVEL FROZEN AT ARMING
        //---------------------------------------------------------------------

        float activeLongBreakLevel = longBreakArmed and not na(longFrozenBreakLevel) ? longFrozenBreakLevel : breakHigh
        float activeShortBreakLevel = shortBreakArmed and not na(shortFrozenBreakLevel) ? shortFrozenBreakLevel : breakLow

        bool currentLongBreakConfirmed = breakConfirmationMode == "Intrabar Close" ? intrabarClose > activeLongBreakLevel : intrabarHigh > activeLongBreakLevel
        bool currentShortBreakConfirmed = breakConfirmationMode == "Intrabar Close" ? intrabarClose < activeShortBreakLevel : intrabarLow < activeShortBreakLevel

        bool freshPostArmLongBreak = longBreakArmed and not longArmedThisMinute and not na(longArmIntrabarIndex) and i > longArmIntrabarIndex and currentLongBreakConfirmed and not previousLongBreakConfirmed
        bool freshPostArmShortBreak = shortBreakArmed and not shortArmedThisMinute and not na(shortArmIntrabarIndex) and i > shortArmIntrabarIndex and currentShortBreakConfirmed and not previousShortBreakConfirmed

        //---------------------------------------------------------------------
        // OPTIONAL FILTERS
        //---------------------------------------------------------------------

        bool longVWAPPass = not useVWAPFilter or (not na(intrabarVWAP) and intrabarClose > intrabarVWAP)
        bool shortVWAPPass = not useVWAPFilter or (not na(intrabarVWAP) and intrabarClose < intrabarVWAP)

        bool longEMAPass = not useEMAFilter or (not na(confirmedEMA) and intrabarClose > confirmedEMA and confirmedEMARising)
        bool shortEMAPass = not useEMAFilter or (not na(confirmedEMA) and intrabarClose < confirmedEMA and confirmedEMAFalling)

        bool atrRegimePass = not useATRRegimeFilter or (not na(confirmedATRRatio) and confirmedATRRatio >= minATRRatio and confirmedATRRatio <= maxATRRatio)
        bool efficiencyPass = not useEfficiencyFilter or (not na(confirmedEfficiency) and confirmedEfficiency >= minimumEfficiency)

        bool enoughChurnSamples = directionalSamples >= minimumSignalSamples
        bool signalVolatilityPass = not useSignalVolatilityFilter or not enoughChurnSamples or rawSignalVolatility <= maximumSignalVolatility
        bool trendQualityPass = not useTrendQualityFilter or trendQuality >= minimumTrendQuality

        bool commonEntryPass = intrabarSessionAllowed and not dayHalted and tradeCountPass and cooldownPass and positionPass and signalBarPass

        bool canLongNow = finalBias == 1 and freshPostArmLongBreak and commonEntryPass and longVWAPPass and longEMAPass and atrRegimePass and efficiencyPass and signalVolatilityPass and trendQualityPass
        bool canShortNow = finalBias == -1 and freshPostArmShortBreak and commonEntryPass and shortVWAPPass and shortEMAPass and atrRegimePass and efficiencyPass and signalVolatilityPass and trendQualityPass

        if not longEntrySignal and not shortEntrySignal
            if canLongNow
                longEntrySignal := true
                longSignalIntrabarTime := intrabarTime
                longSignalReferencePrice := intrabarClose
                longSignalStructureLow := longFrozenStructureLow
                signalRTHVWAP := intrabarVWAP
                longBreakArmed := false
            else if canShortNow
                shortEntrySignal := true
                shortSignalIntrabarTime := intrabarTime
                shortSignalReferencePrice := intrabarClose
                shortSignalStructureHigh := shortFrozenStructureHigh
                signalRTHVWAP := intrabarVWAP
                shortBreakArmed := false

        previousLongBreakConfirmed := currentLongBreakConfirmed
        previousShortBreakConfirmed := currentShortBreakConfirmed


//=============================================================================
// 19B. ENGULFING + THIRD-CANDLE RETEST-AND-BREAK CONTINUATION SIGNAL
//=============================================================================
// Independent entry trigger for 15m trend continuation, separate from the
// second-flip breakout engine above. A break-return-cross state machine,
// structurally identical to the ORB break-return-cross pattern used
// elsewhere in this repo, rooted at an engulfing candle's range instead of
// an opening-range box:
//   1. ARM  -- candle[1] is an engulfing candle in the direction of the
//      confirmed HTF bias: its body fully engulfs the prior, counter-trend
//      candle[2] body. Its high/low freeze as the armed level and stop
//      reference.
//   2. RETURN -- a later candle retests back into the engulfing candle's
//      range (trades through its midpoint) and closes back in the trend
//      direction ("the third candle retests and flips toward the trade").
//   3. CROSS -- entry fires on the first subsequent bar that freshly breaks
//      back out beyond the engulfing candle's high (long) / low (short).
// Shares the confirmed HTF bias filter and the same daily/session/risk
// controls as the flip engine, plus the same optional EMA / ATR-regime /
// Efficiency filters, so both entry paths obey one rulebook. VWAP filtering
// is intrabar-only and does not apply here.

float engulfBodyATR  = na(confirmedATR) or confirmedATR <= 0 ? na : math.abs(close[1] - open[1]) / confirmedATR
bool  engulfBodyPass = minEngulfBodyATR <= 0.0 or (not na(engulfBodyATR) and engulfBodyATR >= minEngulfBodyATR)

bool bullEngulfPrior = close[1] > open[1] and open[2] > close[2] and close[1] >= open[2] and open[1] <= close[2] and engulfBodyPass
bool bearEngulfPrior = close[1] < open[1] and close[2] > open[2] and close[1] <= open[2] and open[1] >= close[2] and engulfBodyPass

var float engulfLongLevel               = na
var float engulfLongStopRef             = na
var float engulfLongMid                 = na
var bool  engulfLongArmed               = false
var bool  engulfLongReturned            = false
var int   engulfLongArmBar              = na
var bool  engulfPrevLongBreakConfirmed  = false

var float engulfShortLevel              = na
var float engulfShortStopRef            = na
var float engulfShortMid                = na
var bool  engulfShortArmed              = false
var bool  engulfShortReturned           = false
var int   engulfShortArmBar             = na
var bool  engulfPrevShortBreakConfirmed = false

// --- ARM: a fresh engulfing candle freezes the level, stop, and midpoint ---
if finalBias == 1 and bullEngulfPrior
    engulfLongArmed := true
    engulfLongLevel := high[1]
    engulfLongStopRef := low[1]
    engulfLongMid := (high[1] + low[1]) / 2.0
    engulfLongArmBar := bar_index
    engulfLongReturned := false
    engulfPrevLongBreakConfirmed := false

if finalBias == -1 and bearEngulfPrior
    engulfShortArmed := true
    engulfShortLevel := low[1]
    engulfShortStopRef := high[1]
    engulfShortMid := (high[1] + low[1]) / 2.0
    engulfShortArmBar := bar_index
    engulfShortReturned := false
    engulfPrevShortBreakConfirmed := false

// --- expire a stale, unresolved arm ---
if engulfLongArmed and not na(engulfLongArmBar) and bar_index - engulfLongArmBar > engulfExpiryBars
    engulfLongArmed := false

if engulfShortArmed and not na(engulfShortArmBar) and bar_index - engulfShortArmBar > engulfExpiryBars
    engulfShortArmed := false

// --- RETURN: the third candle (or later) retests into the engulfing range
//     and closes back in the trend direction ---
if engulfLongArmed and not engulfLongReturned and bar_index > engulfLongArmBar
    if low <= engulfLongMid and close > open
        engulfLongReturned := true

if engulfShortArmed and not engulfShortReturned and bar_index > engulfShortArmBar
    if high >= engulfShortMid and close < open
        engulfShortReturned := true

// --- CROSS: a fresh break of the engulfing candle's high/low confirms entry ---
bool longBreakConfirmedNow  = engulfLongArmed  and (breakConfirmationMode == "Intrabar Close" ? close > engulfLongLevel  : high > engulfLongLevel)
bool shortBreakConfirmedNow = engulfShortArmed and (breakConfirmationMode == "Intrabar Close" ? close < engulfShortLevel : low  < engulfShortLevel)

bool engulfEMAPassLong    = not useEMAFilter or (not na(confirmedEMA) and close > confirmedEMA and confirmedEMARising)
bool engulfEMAPassShort   = not useEMAFilter or (not na(confirmedEMA) and close < confirmedEMA and confirmedEMAFalling)
bool engulfATRPass        = not useATRRegimeFilter or (not na(confirmedATRRatio) and confirmedATRRatio >= minATRRatio and confirmedATRRatio <= maxATRRatio)
bool engulfEfficiencyPass = not useEfficiencyFilter or (not na(confirmedEfficiency) and confirmedEfficiency >= minimumEfficiency)

bool openingDelayPassClose = chartMOD >= sessionStartMOD + openingDelayMinutes
bool commonEntryPassClose  = inExecutionSession and openingDelayPassClose and not (flattenAtSessionEnd and isFinalSessionBar) and not dayHalted and tradeCountPass and cooldownPass and positionPass and signalBarPass

bool engulfContinuationLong  = engulfLongArmed  and engulfLongReturned  and longBreakConfirmedNow  and not engulfPrevLongBreakConfirmed  and bar_index > engulfLongArmBar  and engulfEMAPassLong  and engulfATRPass and engulfEfficiencyPass
bool engulfContinuationShort = engulfShortArmed and engulfShortReturned and shortBreakConfirmedNow and not engulfPrevShortBreakConfirmed and bar_index > engulfShortArmBar and engulfEMAPassShort and engulfATRPass and engulfEfficiencyPass

bool engulfLongSignal  = useEngulfingSignal and engulfContinuationLong  and commonEntryPassClose and not longEntrySignal and not shortEntrySignal
bool engulfShortSignal = useEngulfingSignal and engulfContinuationShort and commonEntryPassClose and not longEntrySignal and not shortEntrySignal

// disarm once fired, and roll the break-confirmation trackers forward
if engulfLongSignal
    engulfLongArmed := false

if engulfShortSignal
    engulfShortArmed := false

engulfPrevLongBreakConfirmed := longBreakConfirmedNow
engulfPrevShortBreakConfirmed := shortBreakConfirmedNow


//=============================================================================
// 20. MUTUALLY EXCLUSIVE ENTRY SIGNAL
//=============================================================================

bool validLongSignal = longEntrySignal and not shortEntrySignal
bool validShortSignal = shortEntrySignal and not longEntrySignal

var float pendingStop = na
var int pendingDirection = 0

if validLongSignal
    pendingStop := f_longStopCandidate(longSignalReferencePrice, confirmedATR, longSignalStructureLow)
    pendingDirection := 1
    lastSignalBar := bar_index
    tradesToday += 1

    strategy.entry(
         "Long",
         strategy.long,
         qty=contracts,
         comment="Second Flip Long",
         alert_message="MTF2FLIP v1.2 LONG entry"
    )

if engulfLongSignal
    pendingStop := f_longStopCandidate(close, confirmedATR, engulfLongStopRef)
    pendingDirection := 1
    lastSignalBar := bar_index
    tradesToday += 1

    strategy.entry(
         "Long",
         strategy.long,
         qty=contracts,
         comment="Engulf Continuation Long",
         alert_message="MTF2FLIP v1.2 ENGULF LONG entry"
    )

if validShortSignal
    pendingStop := f_shortStopCandidate(shortSignalReferencePrice, confirmedATR, shortSignalStructureHigh)
    pendingDirection := -1
    lastSignalBar := bar_index
    tradesToday += 1

    strategy.entry(
         "Short",
         strategy.short,
         qty=contracts,
         comment="Second Flip Short",
         alert_message="MTF2FLIP v1.2 SHORT entry"
    )

if engulfShortSignal
    pendingStop := f_shortStopCandidate(close, confirmedATR, engulfShortStopRef)
    pendingDirection := -1
    lastSignalBar := bar_index
    tradesToday += 1

    strategy.entry(
         "Short",
         strategy.short,
         qty=contracts,
         comment="Engulf Continuation Short",
         alert_message="MTF2FLIP v1.2 ENGULF SHORT entry"
    )


//=============================================================================
// 21. FILLED POSITION INITIALIZATION
//=============================================================================

var bool tradeInitialized = false
var float actualEntry = na
var float initialRisk = na
var float activeStop = na
var float activeTarget = na
var int entryBarIndex = na

if strategy.position_size == 0
    tradeInitialized := false

if strategy.position_size > 0 and not tradeInitialized
    actualEntry := strategy.position_avg_price
    activeStop := pendingStop
    initialRisk := actualEntry - activeStop

    if not na(initialRisk) and initialRisk > syminfo.mintick
        activeTarget := actualEntry + initialRisk * rewardRisk
        entryBarIndex := bar_index
        tradeInitialized := true
    else
        strategy.close("Long", comment="Invalid Long Risk")

if strategy.position_size < 0 and not tradeInitialized
    actualEntry := strategy.position_avg_price
    activeStop := pendingStop
    initialRisk := activeStop - actualEntry

    if not na(initialRisk) and initialRisk > syminfo.mintick
        activeTarget := actualEntry - initialRisk * rewardRisk
        entryBarIndex := bar_index
        tradeInitialized := true
    else
        strategy.close("Short", comment="Invalid Short Risk")


//=============================================================================
// 22. BREAK-EVEN AND ATR TRAILING STOP
//=============================================================================

bool afterEntryBar = tradeInitialized and not na(entryBarIndex) and bar_index > entryBarIndex

if strategy.position_size > 0 and useBreakEven and afterEntryBar
    if high >= actualEntry + initialRisk * breakEvenAtR
        activeStop := math.max(activeStop, actualEntry)

if strategy.position_size < 0 and useBreakEven and afterEntryBar
    if low <= actualEntry - initialRisk * breakEvenAtR
        activeStop := math.min(activeStop, actualEntry)

if strategy.position_size > 0 and useTrailingStop and afterEntryBar
    float longTrail = close - atr * trailingATR
    activeStop := math.max(activeStop, longTrail)

if strategy.position_size < 0 and useTrailingStop and afterEntryBar
    float shortTrail = close + atr * trailingATR
    activeStop := math.min(activeStop, shortTrail)


//=============================================================================
// 23. STOP / TARGET ORDERS
//=============================================================================

if strategy.position_size > 0 and tradeInitialized
    strategy.exit(
         "Long Exit",
         from_entry="Long",
         stop=activeStop,
         limit=activeTarget,
         alert_message="MTF2FLIP v1.2 LONG exit"
    )

if strategy.position_size < 0 and tradeInitialized
    strategy.exit(
         "Short Exit",
         from_entry="Short",
         stop=activeStop,
         limit=activeTarget,
         alert_message="MTF2FLIP v1.2 SHORT exit"
    )


//=============================================================================
// 24. TIME, DAILY-LOSS, AND SESSION EXITS
//=============================================================================

int barsInTrade = tradeInitialized and not na(entryBarIndex) ? bar_index - entryBarIndex : 0
bool timeExitReady = useTimeExit and strategy.position_size != 0 and afterEntryBar and barsInTrade >= maximumBarsInTrade

if timeExitReady
    strategy.close_all(comment="Time Exit")

if dayHalted and strategy.position_size != 0
    strategy.close_all(comment="Daily Loss Limit")

if flattenAtSessionEnd and isFinalSessionBar and strategy.position_size != 0
    strategy.close_all(comment="Session Close")


//=============================================================================
// 25. POSITION CLOSED STATE
//=============================================================================

bool positionClosed = strategy.position_size == 0 and strategy.position_size[1] != 0

if positionClosed
    lastExitBar := bar_index
    pendingStop := na
    pendingDirection := 0
    actualEntry := na
    initialRisk := na
    activeStop := na
    activeTarget := na
    entryBarIndex := na
    tradeInitialized := false


//=============================================================================
// 26. COMPACT VISUALS
//=============================================================================

plot(execOpen, "15m Open", color=color.new(color.blue, 20), linewidth=1)
plot(breakHigh, "Active Long Break Level", color=color.new(color.green, 35), linewidth=1, style=plot.style_stepline)
plot(breakLow, "Active Short Break Level", color=color.new(color.red, 35), linewidth=1, style=plot.style_stepline)
plot(strategy.position_size != 0 ? activeStop : na, "Active Stop", color=color.red, linewidth=2, style=plot.style_linebr)
plot(strategy.position_size != 0 ? activeTarget : na, "Active Target", color=color.lime, linewidth=2, style=plot.style_linebr)

plotshape(validLongSignal, title="Long Entry", style=shape.triangleup, location=location.belowbar, color=color.lime, size=size.small, text="L")
plotshape(validShortSignal, title="Short Entry", style=shape.triangledown, location=location.abovebar, color=color.red, size=size.small, text="S")

plotshape(showEngulfMarkers and (finalBias == 1 and bullEngulfPrior), title="Bullish Engulfing Candle", style=shape.circle, location=location.belowbar, color=color.new(color.teal, 40), size=size.tiny, text="E", offset=-1)
plotshape(showEngulfMarkers and (finalBias == -1 and bearEngulfPrior), title="Bearish Engulfing Candle", style=shape.circle, location=location.abovebar, color=color.new(color.purple, 40), size=size.tiny, text="E", offset=-1)

plotshape(showEngulfMarkers and engulfLongSignal, title="Engulf Continuation Long", style=shape.triangleup, location=location.belowbar, color=color.teal, size=size.small, text="EL")
plotshape(showEngulfMarkers and engulfShortSignal, title="Engulf Continuation Short", style=shape.triangledown, location=location.abovebar, color=color.purple, size=size.small, text="ES")

plotshape(showDebugMarkers and bullFlip2Event, title="Bull Flip 2", style=shape.diamond, location=location.belowbar, color=color.lime, size=size.tiny, text="B2")
plotshape(showDebugMarkers and bearFlip2Event, title="Bear Flip 2", style=shape.diamond, location=location.abovebar, color=color.red, size=size.tiny, text="S2")
plotshape(showDebugMarkers and longInvalidationEvent, title="Long Setup Invalidated", style=shape.xcross, location=location.belowbar, color=color.orange, size=size.tiny, text="X")
plotshape(showDebugMarkers and shortInvalidationEvent, title="Short Setup Invalidated", style=shape.xcross, location=location.abovebar, color=color.orange, size=size.tiny, text="X")

color biasBackground = na
if showBiasBackground
    biasBackground := finalBias == 1 ? color.new(color.green, 95) : finalBias == -1 ? color.new(color.red, 95) : na

bgcolor(biasBackground, title="Confirmed HTF Bias")


//=============================================================================
// 27. COMPACT DASHBOARD
//=============================================================================

var table dash = table.new(position.top_right, 2, 10, border_width=1)

f_cell(int row, string leftText, string rightText, color valueColor) =>
    table.cell(dash, 0, row, leftText, text_color=color.white, bgcolor=color.new(color.gray, 72), text_size=size.small, text_halign=text.align_left)
    table.cell(dash, 1, row, rightText, text_color=color.white, bgcolor=color.new(valueColor, 55), text_size=size.small, text_halign=text.align_right)

color bias1Color = bias1H == 1 ? color.green : bias1H == -1 ? color.red : color.gray
color bias4Color = bias4H == 1 ? color.green : bias4H == -1 ? color.red : color.gray
color finalBiasColor = finalBias == 1 ? color.green : finalBias == -1 ? color.red : color.gray
color stateColor = currentExecState == 1 ? color.green : currentExecState == -1 ? color.red : color.gray

string sessionStatus = "CLOSED"
color sessionColor = color.gray

if dayHalted
    sessionStatus := "HALTED"
    sessionColor := color.red
else if isFinalSessionBar
    sessionStatus := "FINAL BAR"
    sessionColor := color.orange
else if inExecutionSession
    if chartMOD < sessionStartMOD + openingDelayMinutes
        sessionStatus := "OPEN DELAY"
        sessionColor := color.orange
    else
        sessionStatus := "OPEN"
        sessionColor := color.green

string setupStatus = "WAIT"
color setupColor = color.gray

if validLongSignal
    setupStatus := "LONG SIGNAL"
    setupColor := color.lime
else if validShortSignal
    setupStatus := "SHORT SIGNAL"
    setupColor := color.red
else if engulfLongSignal
    setupStatus := "ENGULF LONG"
    setupColor := color.teal
else if engulfShortSignal
    setupStatus := "ENGULF SHORT"
    setupColor := color.purple
else if engulfLongArmed and engulfLongReturned
    setupStatus := "ENGULF LONG RETEST"
    setupColor := color.new(color.teal, 15)
else if engulfShortArmed and engulfShortReturned
    setupStatus := "ENGULF SHORT RETEST"
    setupColor := color.new(color.purple, 15)
else if engulfLongArmed
    setupStatus := "ENGULF LONG ARMED"
    setupColor := color.new(color.teal, 40)
else if engulfShortArmed
    setupStatus := "ENGULF SHORT ARMED"
    setupColor := color.new(color.purple, 40)
else if longBreakArmed
    setupStatus := "LONG ARMED"
    setupColor := color.green
else if shortBreakArmed
    setupStatus := "SHORT ARMED"
    setupColor := color.red
else if longInvalidationEvent or shortInvalidationEvent
    setupStatus := invalidationCode == 1 ? "OPPOSING FLIP" : invalidationCode == 2 ? "EXPIRED" : "TOO MANY FLIPS"
    setupColor := color.orange

string positionText = strategy.position_size > 0 ? "LONG" : strategy.position_size < 0 ? "SHORT" : "FLAT"
color positionColor = strategy.position_size > 0 ? color.green : strategy.position_size < 0 ? color.red : color.gray

bool churnPassNow = directionalSamples < minimumSignalSamples or rawSignalVolatility <= maximumSignalVolatility
color churnColor = churnPassNow ? color.green : color.orange
color qualityColor = not useTrendQualityFilter or trendQuality >= minimumTrendQuality ? color.green : color.orange

if barstate.islast
    table.clear(dash, 0, 0, 1, 9)

    if showDashboard
        f_cell(0, "1H / 4H", f_biasName(bias1H) + " / " + f_biasName(bias4H), bias1H == bias4H ? finalBiasColor : color.orange)
        f_cell(1, "Final Bias", f_biasName(finalBias), finalBiasColor)
        f_cell(2, "15m State", f_stateName(currentExecState), stateColor)
        f_cell(3, "Bull / Bear", str.tostring(bullFlipCount) + " / " + str.tostring(bearFlipCount), color.blue)
        f_cell(4, "Raw / Genuine", str.tostring(rawDirectionalTransitions) + " / " + str.tostring(genuineDirectionalTransitions), color.blue)
        f_cell(5, "Signal Vol", f_percent(rawSignalVolatility), churnColor)
        f_cell(6, "Trend Quality", f_number(trendQuality), qualityColor)
        f_cell(7, "Setup", setupStatus, setupColor)
        f_cell(8, "Session / Trades", sessionStatus + "  " + str.tostring(tradesToday) + "/" + str.tostring(maxTradesPerDay), sessionColor)
        f_cell(9, "Position", positionText, positionColor)


//=============================================================================
// 28. ALERTS
//=============================================================================
// alertcondition() calls were removed because they consume plot counts and do
// not create usable alert triggers in strategies. Select "Any alert() function
// call" for event alerts, or use strategy order-fill alerts.

if bullFlip1Event
    alert("MTF2FLIP: Bull flip #1 detected.", alert.freq_all)

if bullFlip2Event
    alert("MTF2FLIP: Bull flip #2 detected.", alert.freq_all)

if bearFlip1Event
    alert("MTF2FLIP: Bear flip #1 detected.", alert.freq_all)

if bearFlip2Event
    alert("MTF2FLIP: Bear flip #2 detected.", alert.freq_all)

if longArmEvent
    alert("MTF2FLIP: Long setup armed; breakout level frozen.", alert.freq_all)

if shortArmEvent
    alert("MTF2FLIP: Short setup armed; breakout level frozen.", alert.freq_all)

if longInvalidationEvent
    alert("MTF2FLIP: Long setup invalidated.", alert.freq_all)

if shortInvalidationEvent
    alert("MTF2FLIP: Short setup invalidated.", alert.freq_all)

if validLongSignal
    alert("MTF2FLIP: Long entry signal.", alert.freq_all)

if validShortSignal
    alert("MTF2FLIP: Short entry signal.", alert.freq_all)

if engulfLongSignal
    alert("MTF2FLIP: Engulfing continuation long entry.", alert.freq_all)

if engulfShortSignal
    alert("MTF2FLIP: Engulfing continuation short entry.", alert.freq_all)

if positionClosed
    alert("MTF2FLIP: Position closed.", alert.freq_all)


//=============================================================================
// 29. REDUCED DATA-WINDOW DIAGNOSTICS
//=============================================================================

plot(float(bullFlipCount), "Bull Flip Count", display=display.data_window)
plot(float(bearFlipCount), "Bear Flip Count", display=display.data_window)
plot(float(rawDirectionalTransitions), "Raw Directional Transitions", display=display.data_window)
plot(float(genuineDirectionalTransitions), "Genuine Directional Transitions", display=display.data_window)
plot(float(rejectedMicroTransitions), "Rejected Micro Transitions", display=display.data_window)
plot(rawSignalVolatility, "Raw Signal Volatility", display=display.data_window)
plot(genuineSignalVolatility, "Genuine Signal Volatility", display=display.data_window)
plot(marketVolatilityRatio, "Market Volatility Ratio", display=display.data_window)
plot(trendQuality, "Trend Quality", display=display.data_window)
plot(float(finalBias), "Final HTF Bias", display=display.data_window)
plot(float(invalidationCode), "Invalidation Code", display=display.data_window)
plot(engulfBodyATR, "Engulf Body, x ATR", display=display.data_window)
plot(bullEngulfPrior ? 1.0 : 0.0, "Bull Engulf Prior Bar", display=display.data_window)
plot(bearEngulfPrior ? 1.0 : 0.0, "Bear Engulf Prior Bar", display=display.data_window)
plot(engulfLongSignal ? 1.0 : 0.0, "Engulf Continuation Long Signal", display=display.data_window)
plot(engulfShortSignal ? 1.0 : 0.0, "Engulf Continuation Short Signal", display=display.data_window)
plot(engulfLongArmed ? 1.0 : 0.0, "Engulf Long Armed", display=display.data_window)
plot(engulfShortArmed ? 1.0 : 0.0, "Engulf Short Armed", display=display.data_window)
plot(engulfLongReturned ? 1.0 : 0.0, "Engulf Long Retested", display=display.data_window)
plot(engulfShortReturned ? 1.0 : 0.0, "Engulf Short Retested", display=display.data_window)

Overview

Bias must agree between confirmed one-hour and four-hour structure. Lower-timeframe states are rebuilt relative to the bar open, the setup is frozen once the second genuine flip arms, and entry needs a fresh break of the prior candle high or low before a deadline. A signal-volatility filter, optional VWAP, EMA, ATR and efficiency filters, and an engulfing continuation entry are included. It restricts trading to a session and can flatten at its end.

Details

Published by

rosijay17-cmd on GitHub

Type

Strategy, Pine Script v6

Draws

On the price chart

Category

Trend, Sessions & time

Markets

Indices

Timeframes

M15-H1

Risk Notes

No repainting, lookahead or stacked entries were found in its code or README. That says nothing about how well it works: test it first.

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
Bias TF 1timeframe60biasTF1
Bias TF 2timeframe240biasTF2
Intrabar timeframetimeframe1intrabarTF
HTF pivot lengthint3pivotLength
Flip detection modestringIntrabar CloseflipDetectionMode
Require exactly second directional flipbooltruerequireExactlySecondFlip
Minimum excursion between genuine flips, ATRfloat0.05minFlipExcursionATR
Maximum genuine transitions before entryint4maxGenuineTransitions
Invalidate armed setup on opposing flipbooltrueinvalidateOnOpposingFlip
Breakout deadline after flip #2, intrabarsint8maxIntrabarsAfterSecondFlip
Use raw signal-volatility filterbooltrueuseSignalVolatilityFilter
Minimum directional samples before churn filterint6minimumSignalSamples
Show all 40 settings
SettingTypeDefaultVariable
Maximum raw signal volatilityfloat0.60maximumSignalVolatility
Use market-volatility / signal-churn quality filterboolfalseuseTrendQualityFilter
Minimum trend-quality scorefloat0.10minimumTrendQuality
Break levelstringPrior Candle High/LowbreakMode
Break confirmationstringIntrabar ClosebreakConfirmationMode
Execution sessionsession0930-1600executionSession
Session timezonestringAmerica/New_YorksessionTimezone
Opening delay, minutesint5openingDelayMinutes
Flatten at session endbooltrueflattenAtSessionEnd
Use RTH VWAP filterboolfalseuseVWAPFilter
Use EMA filterboolfalseuseEMAFilter
EMA lengthint50emaLength
Use ATR regime filterboolfalseuseATRRegimeFilter
ATR lengthint14atrLength
ATR baselineint100atrBaselineLength
Minimum ATR ratiofloat0.70minATRRatio
Maximum ATR ratiofloat2.00maxATRRatio
Use Efficiency Ratio filterboolfalseuseEfficiencyFilter
Efficiency Ratio lengthint20efficiencyLength
Minimum Efficiency Ratiofloat0.18minimumEfficiency
Contractsint1contracts
Initial stop modelstringWiderstopMode
ATR stop multiplefloat1.25atrStopMultiple
Reward / Riskfloat2.00rewardRisk
Use break-evenbooltrueuseBreakEven
Break-even trigger, Rfloat1.00breakEvenAtR
Use ATR trailing stopboolfalseuseTrailingStop
Trailing ATRfloat2.00trailingATR
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

257 stars

Donald - Test Logic Futures is an overlay strategy designed for intraday futures trading. It combines kernel regression functions with exponential moving averages to evaluate price retracements and entry timing. Trades are filtered by session execution windows

Strategy v5 TrendMoving averages Repaints
Updated 6 weeks ago · None stated GitHub repository of Donald - Test Logic Futures
106 stars

This strategy trades the three-candle strike pattern filtered by trend direction and time sessions. It calculates market regime using linear regression and exponential or smoothed moving averages to distinguish between ranging and trending conditions. Risk man

Strategy v5 Candlestick patternsTrend
Updated 2 years ago · GPL-3.0 GitHub repository of 3 Candle Strike Strategy
53 stars

This strategy enters long or short on the price chart when price is on the correct side of a 200-period EMA, MACD agrees, RSI is not at the opposing extreme, ADX exceeds a threshold, volume beats a 20-bar average, and close breaks the recent high or low. A tim

Strategy v6 TrendOscillators & momentum
Updated 2 months ago · MPL-2.0 GitHub repository of BTCUSD 5M Optimized Strategy

ATOM 1m scalp HK

by dearvn
43 stars

This overlay strategy targets one-minute ATOM scalps. It tracks pivot highs and lows as resistance and support, compares a three-period EMA with a multi-stage smoothed close, and flags long or short when price crosses that curve in the matching direction. Dail

Strategy v5 ScalpingSupport & resistance Repaints
Updated 2 years ago · None stated GitHub repository of ATOM 1m scalp HK
23 stars

This strategy evaluates breakout and trend opportunities on the price chart using a weighted composite score of volume, trend, and candlestick structure. It tracks volume spikes, fast and slow EMAs, higher-timeframe EMA trend alignment, opening ranges, and ses

Strategy v6 TrendVolume Repaints
Updated 3 weeks ago · Custom licence GitHub repository of TradingAlpha Point-Clip Strategy

More strategys

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers