Indicator · Pine Script v6 · On the chart

AlphaX Structure Flow Pro

by majixai· code credited to AlphaX

A smart money concepts indicator that marks market structure swings, momentum-filtered structure breaks, order blocks with a point of control and a high probability zone score, fair value gaps, liquidity sweeps and equal highs or lows.

View on GitHub

GitHub stars1on the repository
Pine versionv6current
Lines of code904
Settings40inputs
LicenceNone statedfile: MPL-2.0
Last updateyesterday28 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 indicator, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.

Source Code

904 lines of Pine Script v6 from tradingview_integration/pine_script/test010.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

test010.pine
// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © AlphaX

//@version=6
indicator("[ A L P H A X ] Structure Flow Pro", shorttitle="AlphaX SFP",
  overlay=true, max_boxes_count=500, max_lines_count=500, max_labels_count=500, max_bars_back=500)

// ═══════════════════════════════════════════════════════════════════════════════
// ═══════════════════════════ INPUTS ═══════════════════════════════════════════
// ═══════════════════════════════════════════════════════════════════════════════

// ── Market Structure ──────────────────────────────────────────────────────────
pivotLen       = input.int(7,      "Pivot Lookback",            minval=3,   maxval=21,   group="Market Structure")
msbZScore      = input.float(0.5,  "MSB Momentum Z-Score",      minval=0.1, maxval=3.0,  step=0.1, group="Market Structure")
showMSB        = input.bool(true,  "Show MSB Lines & Labels",                            group="Market Structure")
showStructure  = input.bool(true,  "Show Structure Swings",                              group="Market Structure")
bmsEnabled     = input.bool(true,  "Detect Break of Market Structure (BMS)",             group="Market Structure")

// ── Order Blocks ──────────────────────────────────────────────────────────────
obCount        = input.int(10,     "Max Active OBs",            minval=1,   maxval=100,  group="Order Blocks")
obLookback     = input.int(10,     "OB Lookback (candles)",     minval=3,   maxval=20,   group="Order Blocks")
extendBroken   = input.bool(false, "Extend Broken OBs",                                  group="Order Blocks")
hideOverlaps   = input.bool(false, "Hide Overlapping OBs",                               group="Order Blocks")
obMode         = input.string("Historical", "Display Mode",
  options=["Historical","Present"],                                                        group="Order Blocks")
hpzThresh      = input.float(75.0, "HPZ Threshold Score (%)",   minval=50,  maxval=95,   step=5, group="Order Blocks")
showPOC        = input.bool(true,  "Show OB Point of Control",                           group="Order Blocks")

// ── Liquidity & Imbalance ─────────────────────────────────────────────────────
showFVG        = input.bool(true,  "Show Fair Value Gaps (FVG)",                         group="Liquidity & Imbalance")
fvgMinSize     = input.float(0.05, "Min FVG Size (ATR × factor)", minval=0.01, maxval=1.0, step=0.01, group="Liquidity & Imbalance")
showLiqSweep   = input.bool(true,  "Show Liquidity Sweeps",                              group="Liquidity & Imbalance")
sweepLookback  = input.int(20,     "Sweep Lookback (bars)",     minval=5,   maxval=50,   group="Liquidity & Imbalance")
showEQL        = input.bool(true,  "Show Equal Highs / Lows",                            group="Liquidity & Imbalance")
eqlTolerance   = input.float(0.1,  "EQL Tolerance (ATR × factor)", minval=0.01, maxval=0.5, step=0.01, group="Liquidity & Imbalance")

// ── Smart Money Confluence (SMC) ─────────────────────────────────────────────
smcEnabled     = input.bool(true,  "Enable SMC Confluence Engine",                       group="Smart Money Confluence")
minSMCScore    = input.int(30,     "Min SMC Score to Signal",   minval=10,  maxval=80,   group="Smart Money Confluence")
cooldownBars   = input.int(3,      "Signal Cooldown (bars)",    minval=1,   maxval=20,   group="Smart Money Confluence")
useVolFilter   = input.bool(false, "Require Volume Confirmation",                        group="Smart Money Confluence")
volMinRatio    = input.float(0.7,  "Min Volume Ratio",          minval=0.3, maxval=2.0,  step=0.1, group="Smart Money Confluence")

// ── Sessions ──────────────────────────────────────────────────────────────────
showSessions   = input.bool(true,  "Show Session Ranges",                                group="Sessions")
showLondon     = input.bool(false, "London Session",                                     group="Sessions")
londonSess     = input.session("0800-1700:1234567", "London Time",                       group="Sessions")
londonCol      = input.color(#3b82f6, "London Color",                                   group="Sessions", inline="London")
showNY         = input.bool(false, "New York Session",                                   group="Sessions")
nySess         = input.session("1300-2200:1234567", "New York Time",                     group="Sessions")
nyCol          = input.color(#f97316, "New York Color",                                  group="Sessions", inline="NY")
showTokyo      = input.bool(false, "Tokyo Session",                                      group="Sessions")
tokyoSess      = input.session("0000-0900:1234567", "Tokyo Time",                        group="Sessions")
tokyoCol       = input.color(#f43f5e, "Tokyo Color",                                    group="Sessions", inline="Tokyo")
showSydney     = input.bool(false, "Sydney Session",                                     group="Sessions")
sydneySess     = input.session("2100-0600:1234567", "Sydney Time",                       group="Sessions")
sydneyCol      = input.color(#eab308, "Sydney Color",                                   group="Sessions", inline="Sydney")

// ── Dashboard ─────────────────────────────────────────────────────────────────
showDash       = input.bool(true,  "Show Dashboard",                                     group="Dashboard")
dashPos        = input.string("Top Right", "Position",
  options=["Top Left","Top Right","Bottom Left","Bottom Right"],                          group="Dashboard")
dashSizeInp    = input.string("Small", "Size",
  options=["Tiny","Small","Normal","Large"],                                              group="Dashboard")

// ── Appearance ────────────────────────────────────────────────────────────────
labelSizeInp   = input.string("Small", "Label Size",
  options=["Tiny","Small","Normal"],                                                       group="Appearance")

// ── Colors ────────────────────────────────────────────────────────────────────
colBullPrimary  = input.color(#c8e624, "Bull Primary",        group="Colors")
colBullBright   = input.color(#d4f03a, "Bull Bright",         group="Colors")
colBullDim      = input.color(#9ab81c, "Bull Dim",            group="Colors")
colBearPrimary  = input.color(#ff1744, "Bear Primary",        group="Colors")
colBearBright   = input.color(#ff5252, "Bear Bright",         group="Colors")
colBearDim      = input.color(#d50032, "Bear Dim",            group="Colors")
colNeutral      = input.color(#555555, "Neutral",             group="Colors")
colNeutralLight = input.color(#888888, "Neutral Light",       group="Colors")
colTextLight    = input.color(#cccccc, "Text Light",          group="Colors")
colDashBg       = input.color(#1a1a1a, "Dashboard Background",group="Colors")
colFVGBull      = input.color(#c8e624, "FVG Bull Color",      group="Colors")
colFVGBear      = input.color(#ff1744, "FVG Bear Color",      group="Colors")
colSweep        = input.color(color.rgb(40, 41, 40, 19), "Liquidity Sweep",     group="Colors")
colEQL          = input.color(#9c27b0, "Equal Hi/Lo",         group="Colors")

// ═══════════════════════════════════════════════════════════════════════════════
// ═══════════════════════════ TYPE DEFINITIONS ══════════════════════════════════
// ═══════════════════════════════════════════════════════════════════════════════

type OrderBlock
    box        boxId
    label      labelId
    line       pocLineId
    float      qualityScore
    float      top
    float      bottom
    bool       isBull
    bool       isHPZ
    bool       mitigated
    int        mitigationBar
    int        birthBar

type FVG
    box        boxId
    float      top
    float      bottom
    bool       isBull
    bool       filled
    int        birthBar

// ═══════════════════════════════════════════════════════════════════════════════
// ═══════════════════════════ HELPERS ══════════════════════════════════════════
// ═══════════════════════════════════════════════════════════════════════════════

getLabelSize(sz) =>
    switch sz
        "Tiny"   => size.tiny
        "Small"  => size.small
        "Normal" => size.normal
        => size.small

getDashSize(sz) =>
    switch sz
        "Tiny"   => size.tiny
        "Small"  => size.small
        "Normal" => size.normal
        "Large"  => size.large
        => size.small

getDashPos(p) =>
    switch p
        "Top Left"     => position.top_left
        "Top Right"    => position.top_right
        "Bottom Left"  => position.bottom_left
        "Bottom Right" => position.bottom_right
        => position.top_right

labelSize    = getLabelSize(labelSizeInp)
dashTextSize = getDashSize(dashSizeInp)

// ═══════════════════════════════════════════════════════════════════════════════
// ═══════════════════════════ CORE CALCULATIONS ════════════════════════════════
// ═══════════════════════════════════════════════════════════════════════════════

atrVal       = ta.atr(14)
float priceChange = ta.change(close)
float avgChange   = ta.sma(priceChange, 50)
float stdChange   = ta.stdev(priceChange, 50)
float momentumZ   = stdChange > 0 ? (priceChange - avgChange) / stdChange : 0.0
float volPercent  = ta.percentrank(volume, 100)
volSMA            = ta.sma(volume, 20)
volRatio          = volSMA > 0 ? volume / volSMA : 1.0
volOK             = not useVolFilter or volRatio >= volMinRatio

// Candle metrics
candleBody  = math.abs(close - open)
candleRange = high - low
bodyRatio   = candleRange > 0 ? candleBody / candleRange : 0.0

// ═══════════════════════════════════════════════════════════════════════════════
// ═══════════════════════════ PIVOT DETECTION ══════════════════════════════════
// ═══════════════════════════════════════════════════════════════════════════════

float ph = ta.pivothigh(high, pivotLen, pivotLen)
float pl = ta.pivotlow(low, pivotLen, pivotLen)

var float lastPh    = na
var float lastPl    = na
var int   lastPhIdx = na
var int   lastPlIdx = na

// Swing tracking for structure drawing
var float prevSwingHigh    = na
var float prevSwingLow     = na
var int   prevSwingHighIdx = na
var int   prevSwingLowIdx  = na
var float currSwingHigh    = na
var float currSwingLow     = na
var int   currSwingHighIdx = na
var int   currSwingLowIdx  = na

if not na(ph)
    prevSwingHigh    := currSwingHigh
    prevSwingHighIdx := currSwingHighIdx
    currSwingHigh    := ph
    currSwingHighIdx := bar_index - pivotLen
    lastPh    := ph
    lastPhIdx := bar_index - pivotLen

if not na(pl)
    prevSwingLow    := currSwingLow
    prevSwingLowIdx := currSwingLowIdx
    currSwingLow    := pl
    currSwingLowIdx := bar_index - pivotLen
    lastPl    := pl
    lastPlIdx := bar_index - pivotLen

// ── Structure Swing Lines ────────────────────────────────────────────────────
if showStructure and not na(ph) and not na(prevSwingHigh) and not na(prevSwingHighIdx)
    line.new(prevSwingHighIdx, prevSwingHigh, bar_index - pivotLen, ph,
      color=color.new(colBearDim, 60), style=line.style_dotted, width=1)

if showStructure and not na(pl) and not na(prevSwingLow) and not na(prevSwingLowIdx)
    line.new(prevSwingLowIdx, prevSwingLow, bar_index - pivotLen, pl,
      color=color.new(colBullDim, 60), style=line.style_dotted, width=1)

// ── Swing High/Low Labels ────────────────────────────────────────────────────
if showStructure and not na(ph)
    label.new(bar_index - pivotLen, ph, "H",
      style=label.style_label_down, color=color.new(colBearDim, 80),
      textcolor=colBearDim, size=size.tiny, yloc=yloc.price)

if showStructure and not na(pl)
    label.new(bar_index - pivotLen, pl, "L",
      style=label.style_label_up, color=color.new(colBullDim, 80),
      textcolor=colBullDim, size=size.tiny, yloc=yloc.price)

// ═══════════════════════════════════════════════════════════════════════════════
// ═══════════════════════════ MARKET STRUCTURE BREAK ═══════════════════════════
// ═══════════════════════════════════════════════════════════════════════════════

bool isMSBBull = not na(lastPh) and close > lastPh and close[1] <= lastPh and momentumZ > msbZScore
bool isMSBBear = not na(lastPl) and close < lastPl and close[1] >= lastPl and momentumZ < -msbZScore

// Break of Market Structure (confirmed with body close)
bool isBMSBull = bmsEnabled and not na(lastPh) and close > lastPh and close[1] <= lastPh and candleBody / candleRange > 0.5
bool isBMSBear = bmsEnabled and not na(lastPl) and close < lastPl and close[1] >= lastPl and candleBody / candleRange > 0.5

if showMSB and isMSBBull and not na(lastPhIdx)
    int mid = math.round(math.avg(lastPhIdx, bar_index))
    line.new(lastPhIdx, lastPh, bar_index, lastPh,
      color=color.new(colBullPrimary, 0), width=2)
    label.new(mid, lastPh, "MSB ▲",
      color=color.new(colBullPrimary, 100),
      textcolor=colBullPrimary,
      style=label.style_label_down, size=labelSize)

if showMSB and isMSBBear and not na(lastPlIdx)
    int mid = math.round(math.avg(lastPlIdx, bar_index))
    line.new(lastPlIdx, lastPl, bar_index, lastPl,
      color=color.new(colBearPrimary, 0), width=2)
    label.new(mid, lastPl, "MSB ▼",
      color=color.new(colBearPrimary, 100),
      textcolor=colBearPrimary,
      style=label.style_label_up, size=labelSize)

// BMS is a stronger label
if showMSB and isBMSBull and not isMSBBull and not na(lastPhIdx)
    label.new(bar_index, lastPh, "BMS",
      color=color.new(colBullBright, 85),
      textcolor=colBullBright,
      style=label.style_label_down, size=size.tiny)

if showMSB and isBMSBear and not isMSBBear and not na(lastPlIdx)
    label.new(bar_index, lastPl, "BMS",
      color=color.new(colBearBright, 85),
      textcolor=colBearBright,
      style=label.style_label_up, size=size.tiny)

// ═══════════════════════════════════════════════════════════════════════════════
// ═══════════════════════════ ORDER BLOCK ENGINE ════════════════════════════════
// ═══════════════════════════════════════════════════════════════════════════════

var obArray = array.new<OrderBlock>()

// Mitigation stats
var float totalMitigated = 0.0
var float totalOBs       = 0.0

if isMSBBull or isMSBBear
    int obIdx = 0
    for i = 1 to obLookback
        if (isMSBBull and close[i] < open[i]) or (isMSBBear and close[i] > open[i])
            obIdx := i
            break

    if obIdx > 0
        float obTop    = high[obIdx]
        float obBottom = low[obIdx]
        float obPOC    = math.avg(obTop, obBottom)
        float obSize   = obTop - obBottom

        // Enhanced Quality Score (0-100)
        // Factors: momentum strength, volume, body ratio, ob size relative to ATR, z-score magnitude
        float baseScore = math.min(100.0,
          (math.abs(momentumZ) * 15.0) +
          (volPercent * 0.4) +
          (bodyRatio * 20.0) +
          (obSize / atrVal * 10.0))
        float score = math.max(0.0, math.min(100.0, baseScore))
        bool isHPZ  = score >= hpzThresh

        color baseColor = isMSBBull ? colBullPrimary : colBearPrimary
        color hpzColor  = isMSBBull ? colBullBright  : colBearBright

        // Overlap check
        bool overlapping = false
        if hideOverlaps and obArray.size() > 0
            for i = 0 to obArray.size() - 1
                OrderBlock o = obArray.get(i)
                if not o.mitigated
                    if (obTop <= o.top and obTop >= o.bottom) or (obBottom <= o.top and obBottom >= o.bottom)
                        overlapping := true
                        break

        if not overlapping
            box   obBox   = na
            label obLabel = na
            line  obPoc   = na

            if isHPZ
                obBox  := box.new(bar_index[obIdx], obTop, bar_index + 20, obBottom,
                  border_color=color.new(hpzColor, 30),
                  bgcolor=color.new(hpzColor, 82),
                  border_width=1, border_style=line.style_dashed)
                if showPOC
                    obPoc := line.new(bar_index[obIdx], obPOC, bar_index + 20, obPOC,
                      color=color.new(hpzColor, 35), width=1, style=line.style_dashed)
            else
                obBox  := box.new(bar_index[obIdx], obTop, bar_index + 20, obBottom,
                  border_color=color.new(baseColor, 55),
                  bgcolor=color.new(baseColor, 88),
                  border_style=line.style_dashed, border_width=1)
                if showPOC
                    obPoc := line.new(bar_index[obIdx], obPOC, bar_index + 20, obPOC,
                      color=color.new(baseColor, 65), style=line.style_dashed, width=1)

            string scoreLabel = str.tostring(score, "#") + "%" + (isHPZ ? " ⬡HPZ" : "")
            obLabel := label.new(bar_index + 22, obPOC, scoreLabel,
              color=color.new(baseColor, 100),
              textcolor=isHPZ ? hpzColor : baseColor,
              style=label.style_none, size=size.tiny, textalign=text.align_left)

            OrderBlock newOB = OrderBlock.new(
              obBox, obLabel, obPoc,
              score, obTop, obBottom,
              isMSBBull, isHPZ, false, 0, bar_index[obIdx])
            obArray.push(newOB)
            totalOBs += 1.0

    if isMSBBull
        lastPh := na
    else
        lastPl := na

// ── OB Dynamic Management ────────────────────────────────────────────────────
if obArray.size() > 0
    for i = obArray.size() - 1 to 0
        OrderBlock ob = obArray.get(i)
        if not ob.mitigated
            ob.boxId.set_right(bar_index + 20)
            ob.labelId.set_x(bar_index + 22)
            if showPOC and not na(ob.pocLineId)
                ob.pocLineId.set_x2(bar_index + 20)

            bool isMitigated = ob.isBull ? low < ob.bottom : high > ob.top
            if isMitigated
                ob.mitigated      := true
                ob.mitigationBar  := bar_index
                totalMitigated    += 1.0
                if obMode == "Present"
                    ob.boxId.delete()
                    ob.labelId.delete()
                    if not na(ob.pocLineId)
                        ob.pocLineId.delete()
                else
                    ob.boxId.set_bgcolor(color.new(colNeutral, 90))
                    ob.boxId.set_border_color(color.new(colNeutral, 70))
                    ob.labelId.set_textcolor(color.new(colNeutral, 50))
                    if not na(ob.pocLineId)
                        ob.pocLineId.set_color(color.new(colNeutral, 80))
                    ob.boxId.set_right(bar_index)
                    ob.labelId.set_x(bar_index)
                    if not na(ob.pocLineId)
                        ob.pocLineId.set_x2(bar_index)
        else
            bool shouldExtend = extendBroken and (bar_index - ob.mitigationBar <= 50)
            if obMode == "Present"
                ob.boxId.delete()
                ob.labelId.delete()
                if not na(ob.pocLineId)
                    ob.pocLineId.delete()
            else if shouldExtend
                ob.boxId.set_right(bar_index + 20)
                ob.labelId.set_x(bar_index + 22)
                if not na(ob.pocLineId)
                    ob.pocLineId.set_x2(bar_index + 20)

    if obArray.size() > obCount
        OrderBlock oldOB = obArray.shift()
        oldOB.boxId.delete()
        oldOB.labelId.delete()
        if not na(oldOB.pocLineId)
            oldOB.pocLineId.delete()

// ═══════════════════════════════════════════════════════════════════════════════
// ═══════════════════════════ FAIR VALUE GAPS ══════════════════════════════════
// ═══════════════════════════════════════════════════════════════════════════════

var fvgArray = array.new<FVG>()

// Detect FVG on bar_index - 1 (middle candle)
float fvgMinSz = atrVal * fvgMinSize

// Bullish FVG: gap between high[2] and low[0] — price ran up fast
bool bullFVG = low > high[2] and (low - high[2]) >= fvgMinSz
// Bearish FVG: gap between low[2] and high[0]
bool bearFVG = high < low[2] and (low[2] - high) >= fvgMinSz

if showFVG and bullFVG
    float top    = low
    float bottom = high[2]
    box b = box.new(bar_index - 2, top, bar_index + 30, bottom,
      border_color=color.new(colFVGBull, 50),
      bgcolor=color.new(colFVGBull, 91),
      border_width=1, border_style=line.style_dotted)
    fvgArray.push(FVG.new(b, top, bottom, true, false, bar_index))

if showFVG and bearFVG
    float top    = low[2]
    float bottom = high
    box b = box.new(bar_index - 2, top, bar_index + 30, bottom,
      border_color=color.new(colFVGBear, 50),
      bgcolor=color.new(colFVGBear, 91),
      border_width=1, border_style=line.style_dotted)
    fvgArray.push(FVG.new(b, top, bottom, false, false, bar_index))

// Extend and fill FVGs
if showFVG and fvgArray.size() > 0
    for i = fvgArray.size() - 1 to 0
        FVG fvg = fvgArray.get(i)
        if not fvg.filled
            fvg.boxId.set_right(bar_index + 30)
            // Filled when price trades into the gap
            bool filled = fvg.isBull ? low <= fvg.top : high >= fvg.bottom
            if filled
                fvg.filled := true
                fvg.boxId.set_bgcolor(color.new(colNeutral, 93))
                fvg.boxId.set_border_color(color.new(colNeutral, 75))
                fvg.boxId.set_right(bar_index)
        if fvgArray.size() > 50
            FVG old = fvgArray.shift()
            old.boxId.delete()

// ═══════════════════════════════════════════════════════════════════════════════
// ═══════════════════════════ LIQUIDITY SWEEPS ═════════════════════════════════
// ═══════════════════════════════════════════════════════════════════════════════

// A liquidity sweep: wick breaks previous high/low pivot then price reverses inside
var float   sweepHigh     = ta.highest(high, sweepLookback)
var float   sweepLow      = ta.lowest(low,   sweepLookback)

// Bullish sweep: wick below recent low + close back above it
bool bullSweep = showLiqSweep and
  low < ta.lowest(low[1], sweepLookback) and
  close > ta.lowest(low[1], sweepLookback) and
  close > open

// Bearish sweep: wick above recent high + close back below
bool bearSweep = showLiqSweep and
  high > ta.highest(high[1], sweepLookback) and
  close < ta.highest(high[1], sweepLookback) and
  close < open

if bullSweep
    label.new(bar_index, low,
      text="⚡",
      style=label.style_label_up,
      color=color.new(colSweep, 0),
      textcolor=colSweep,
      size=labelSize,
      yloc=yloc.belowbar)

if bearSweep
    label.new(bar_index, high,
      text="⚡",
      style=label.style_label_down,
      color=color.new(colSweep, 0),
      textcolor=colSweep,
      size=labelSize,
      yloc=yloc.abovebar)

// ═══════════════════════════════════════════════════════════════════════════════
// ═══════════════════════════ EQUAL HIGHS / LOWS ═══════════════════════════════
// ═══════════════════════════════════════════════════════════════════════════════

eqlTol = atrVal * eqlTolerance

if showEQL and not na(ph) and not na(prevSwingHigh)
    if math.abs(ph - prevSwingHigh) <= eqlTol
        line.new(prevSwingHighIdx, prevSwingHigh, bar_index - pivotLen, ph,
          color=color.new(colEQL, 30), style=line.style_dotted, width=2)
        label.new(math.round(math.avg(prevSwingHighIdx, bar_index - pivotLen)), ph,
          "EQH", color=color.new(colEQL, 85), textcolor=colEQL,
          style=label.style_label_down, size=size.tiny)

if showEQL and not na(pl) and not na(prevSwingLow)
    if math.abs(pl - prevSwingLow) <= eqlTol
        line.new(prevSwingLowIdx, prevSwingLow, bar_index - pivotLen, pl,
          color=color.new(colEQL, 30), style=line.style_dotted, width=2)
        label.new(math.round(math.avg(prevSwingLowIdx, bar_index - pivotLen)), pl,
          "EQL", color=color.new(colEQL, 85), textcolor=colEQL,
          style=label.style_label_up, size=size.tiny)

// ═══════════════════════════════════════════════════════════════════════════════
// ═══════════════════════════ SMC CONFLUENCE SCORING ══════════════════════════
// ═══════════════════════════════════════════════════════════════════════════════

// Count active HPZ OBs nearby (within 2x ATR)
int nearBullHPZ = 0
int nearBearHPZ = 0
if obArray.size() > 0
    for i = 0 to obArray.size() - 1
        OrderBlock ob = obArray.get(i)
        if not ob.mitigated and ob.isHPZ
            if ob.isBull and close >= ob.bottom - atrVal * 2 and close <= ob.top + atrVal * 2
                nearBullHPZ += 1
            if not ob.isBull and close >= ob.bottom - atrVal * 2 and close <= ob.top + atrVal * 2
                nearBearHPZ += 1

// Count unfilled FVGs below (bull) / above (bear) as draw-on-liquidity
int fvgBullBelow = 0
int fvgBearAbove = 0
if fvgArray.size() > 0
    for i = 0 to fvgArray.size() - 1
        FVG fvg = fvgArray.get(i)
        if not fvg.filled
            if fvg.isBull and fvg.top < close
                fvgBullBelow += 1
            if not fvg.isBull and fvg.bottom > close
                fvgBearAbove += 1

calcBullSMC() =>
    float s = 0.0
    // MSB or BMS signal (core)
    s += isMSBBull ? 30.0 : isBMSBull ? 20.0 : 0.0
    // Liquidity sweep at lows (smart money accumulation)
    s += bullSweep ? 20.0 : 0.0
    // Near bullish HPZ OB
    s += nearBullHPZ >= 2 ? 18.0 : nearBullHPZ == 1 ? 10.0 : 0.0
    // FVG imbalance above as magnet
    s += fvgBearAbove >= 2 ? 10.0 : fvgBearAbove == 1 ? 5.0 : 0.0
    // Momentum Z-score confirmation
    s += momentumZ > 2.0 ? 12.0 : momentumZ > 1.0 ? 7.0 : momentumZ > 0.5 ? 3.0 : 0.0
    // Volume confirmation
    s += volRatio > 2.0 ? 8.0 : volRatio > 1.3 ? 5.0 : volRatio > 1.0 ? 2.0 : 0.0
    // Candle body
    s += close > open and bodyRatio > 0.6 ? 6.0 : close > open ? 3.0 : 0.0
    // Penalties
    s -= momentumZ < -1.0 ? 10.0 : 0.0
    s -= nearBearHPZ >= 2 ? 8.0 : 0.0
    math.max(0.0, math.min(100.0, s))

calcBearSMC() =>
    float s = 0.0
    s += isMSBBear ? 30.0 : isBMSBear ? 20.0 : 0.0
    s += bearSweep ? 20.0 : 0.0
    s += nearBearHPZ >= 2 ? 18.0 : nearBearHPZ == 1 ? 10.0 : 0.0
    s += fvgBullBelow >= 2 ? 10.0 : fvgBullBelow == 1 ? 5.0 : 0.0
    s += momentumZ < -2.0 ? 12.0 : momentumZ < -1.0 ? 7.0 : momentumZ < -0.5 ? 3.0 : 0.0
    s += volRatio > 2.0 ? 8.0 : volRatio > 1.3 ? 5.0 : volRatio > 1.0 ? 2.0 : 0.0
    s += close < open and bodyRatio > 0.6 ? 6.0 : close < open ? 3.0 : 0.0
    s -= momentumZ > 1.0 ? 10.0 : 0.0
    s -= nearBullHPZ >= 2 ? 8.0 : 0.0
    math.max(0.0, math.min(100.0, s))

bullSMC = smcEnabled ? calcBullSMC() : 0.0
bearSMC = smcEnabled ? calcBearSMC() : 0.0

// ═══════════════════════════════════════════════════════════════════════════════
// ═══════════════════════════ SMC SIGNAL GENERATION ════════════════════════════
// ═══════════════════════════════════════════════════════════════════════════════

bullSignalRaw = smcEnabled and bullSMC >= minSMCScore and volOK and
  (isMSBBull or bullSweep)

bearSignalRaw = smcEnabled and bearSMC >= minSMCScore and volOK and
  (isMSBBear or bearSweep)

var int lastBullBar = -999
var int lastBearBar = -999
bullCoolOK = bar_index - lastBullBar >= cooldownBars
bearCoolOK = bar_index - lastBearBar >= cooldownBars

bullSignal = bullSignalRaw and bullCoolOK
bearSignal = bearSignalRaw and bearCoolOK

if bullSignal
    lastBullBar := bar_index
if bearSignal
    lastBearBar := bar_index

// Signal tier
bullTier = bullSMC >= 70 ? "S" : bullSMC >= 55 ? "A" : "B"
bearTier = bearSMC >= 70 ? "S" : bearSMC >= 55 ? "A" : "B"

// ── Entry Labels ──────────────────────────────────────────────────────────────
if bullSignal
    string confText = str.tostring(bullSMC, "#")
    string entryTxt = "▲ " + bullTier + "-SMC  (" + confText + ")"
    color  bgClr    = bullSMC >= 70 ? color.new(colBullBright, 5) :
      bullSMC >= 55 ? color.new(colBullPrimary, 10) :
      color.new(colBullDim, 15)
    label.new(bar_index, low,
      text=entryTxt, yloc=yloc.belowbar,
      color=bgClr, textcolor=colDashBg,
      size=labelSize, style=label.style_label_up)

if bearSignal
    string confText = str.tostring(bearSMC, "#")
    string entryTxt = "▼ " + bearTier + "-SMC  (" + confText + ")"
    color  bgClr    = bearSMC >= 70 ? color.new(colBearBright, 5) :
      bearSMC >= 55 ? color.new(colBearPrimary, 10) :
      color.new(colBearDim, 15)
    label.new(bar_index, high,
      text=entryTxt, yloc=yloc.abovebar,
      color=bgClr, textcolor=#ffffff,
      size=labelSize, style=label.style_label_down)

// ═══════════════════════════════════════════════════════════════════════════════
// ═══════════════════════════ SESSIONS ════════════════════════════════════════
// ═══════════════════════════════════════════════════════════════════════════════

drawSession(bool show, string sessStr, color col, string name) =>
    var box   sBox   = na
    var label sLabel = na
    var float sHigh  = na
    var float sLow   = na
    inSession = not na(time(timeframe.period, sessStr))
    isStart   = inSession and not inSession[1]
    if show and inSession
        if isStart
            sHigh  := high
            sLow   := low
            sBox   := box.new(bar_index, sHigh, bar_index, sLow,
              border_color=col, border_style=line.style_dotted,
              bgcolor=color.new(col, 92))
            sLabel := label.new(bar_index, sHigh, name,
              style=label.style_none, textcolor=col, size=size.small, textalign=text.align_center)
        else
            sHigh := math.max(sHigh, high)
            sLow  := math.min(sLow, low)
            sBox.set_top(sHigh)
            sBox.set_bottom(sLow)
            sBox.set_right(bar_index)
            sLabel.set_xy(math.round(math.avg(sBox.get_left(), bar_index)), sHigh)
    float(na)

if showSessions
    drawSession(showLondon, londonSess, londonCol, "London")
    drawSession(showNY,     nySess,     nyCol,     "New York")
    drawSession(showTokyo,  tokyoSess,  tokyoCol,  "Tokyo")
    drawSession(showSydney, sydneySess, sydneyCol, "Sydney")

// ═══════════════════════════════════════════════════════════════════════════════
// ═══════════════════════════ DASHBOARD ════════════════════════════════════════
// ═══════════════════════════════════════════════════════════════════════════════

// Count active HPZs
int activeHPZ = 0
int activeOBs = 0
if obArray.size() > 0
    for i = 0 to obArray.size() - 1
        OrderBlock ob = obArray.get(i)
        if not ob.mitigated
            activeOBs += 1
            if ob.isHPZ
                activeHPZ += 1

float reliability = totalOBs > 0 ? (totalMitigated / totalOBs) * 100.0 : 0.0

// Unfilled FVG count
int activeFVGs = 0
if fvgArray.size() > 0
    for i = 0 to fvgArray.size() - 1
        if not fvgArray.get(i).filled
            activeFVGs += 1

var table dash = na
if showDash
    dash := table.new(getDashPos(dashPos), 2, 30,
      bgcolor=color.new(colDashBg, 0),
      border_width=1,
      border_color=color.new(colNeutral, 70),
      frame_width=2,
      frame_color=color.new(colBullPrimary, 60))

if showDash and barstate.islast
    color secBg = color.new(#222222, 0)

    // ── HEADER ────────────────────────────────────────────────────────────────
    table.cell(dash, 0, 0, "AlphaX  S T R U C T U R E  F L O W",
      text_color=colBullPrimary, text_size=dashTextSize,
      bgcolor=color.new(#111111, 0), text_halign=text.align_center)
    table.cell(dash, 1, 0, "PRO",
      text_color=color.new(colBullDim, 0), text_size=size.tiny,
      bgcolor=color.new(#111111, 0), text_halign=text.align_center)

    // ── SECTION: MARKET STRUCTURE ─────────────────────────────────────────────
    table.cell(dash, 0, 1, "─── MARKET STRUCTURE ───",
      text_color=color.new(colNeutral, 20), text_size=size.tiny,
      bgcolor=secBg, text_halign=text.align_center)
    table.cell(dash, 1, 1, "", bgcolor=secBg)

    // MSB status
    msbTxt = isMSBBull ? "▲ BULL MSB" : isMSBBear ? "▼ BEAR MSB" : "— NONE"
    msbClr = isMSBBull ? colBullPrimary : isMSBBear ? colBearPrimary : colNeutral
    msbBg  = isMSBBull ? color.new(colBullPrimary, 88) : isMSBBear ? color.new(colBearPrimary, 88) : color.new(colDashBg, 0)
    table.cell(dash, 0, 2, "MSB STATUS", text_color=colTextLight, text_size=dashTextSize, bgcolor=msbBg)
    table.cell(dash, 1, 2, msbTxt, text_color=msbClr, text_size=dashTextSize, bgcolor=msbBg)

    // BMS
    bmsTxt = isBMSBull ? "▲ BULL BREAK" : isBMSBear ? "▼ BEAR BREAK" : "— NONE"
    bmsClr = isBMSBull ? colBullBright : isBMSBear ? colBearBright : colNeutral
    table.cell(dash, 0, 3, "BMS (BODY)", text_color=colTextLight, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))
    table.cell(dash, 1, 3, bmsTxt, text_color=bmsClr, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))

    // Momentum Z
    zTxt = momentumZ > 2.0 ? "▲▲ STRONG" : momentumZ > 1.0 ? "▲ MODERATE" :
      momentumZ > 0.3 ? "▲ MILD" : momentumZ < -2.0 ? "▼▼ STRONG" :
      momentumZ < -1.0 ? "▼ MODERATE" : momentumZ < -0.3 ? "▼ MILD" : "— FLAT"
    zClr = momentumZ > 1.0 ? colBullBright : momentumZ > 0.3 ? colBullPrimary :
      momentumZ < -1.0 ? colBearBright : momentumZ < -0.3 ? colBearPrimary : colNeutralLight
    table.cell(dash, 0, 4, "MOMENTUM Z", text_color=colTextLight, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))
    table.cell(dash, 1, 4, zTxt + "  (" + str.tostring(momentumZ, "#.##") + ")", text_color=zClr, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))

    // ── SECTION: ORDER BLOCKS ─────────────────────────────────────────────────
    table.cell(dash, 0, 5, "─── ORDER BLOCKS ───",
      text_color=color.new(colNeutral, 20), text_size=size.tiny,
      bgcolor=secBg, text_halign=text.align_center)
    table.cell(dash, 1, 5, "", bgcolor=secBg)

    table.cell(dash, 0, 6, "ACTIVE OBs", text_color=colTextLight, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))
    table.cell(dash, 1, 6, str.tostring(activeOBs), text_color=colNeutralLight, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))

    hpzTxtDash = activeHPZ > 0 ? str.tostring(activeHPZ) + " ⬡ ACTIVE" : "— NONE"
    hpzClrDash = activeHPZ > 0 ? colBullBright : colNeutral
    hpzBgDash  = activeHPZ > 0 ? color.new(colBullPrimary, 92) : color.new(colDashBg, 0)
    table.cell(dash, 0, 7, "HPZ OBs", text_color=colTextLight, text_size=dashTextSize, bgcolor=hpzBgDash)
    table.cell(dash, 1, 7, hpzTxtDash, text_color=hpzClrDash, text_size=dashTextSize, bgcolor=hpzBgDash)

    relClr = reliability > 65 ? colBullPrimary : reliability > 40 ? colBullDim : colBearDim
    table.cell(dash, 0, 8, "OB RELIABILITY", text_color=colTextLight, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))
    table.cell(dash, 1, 8, str.tostring(reliability, "#.#") + "%", text_color=relClr, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))

    // ── SECTION: LIQUIDITY & IMBALANCE ────────────────────────────────────────
    table.cell(dash, 0, 9, "─── LIQUIDITY & IMBALANCE ───",
      text_color=color.new(colNeutral, 20), text_size=size.tiny,
      bgcolor=secBg, text_halign=text.align_center)
    table.cell(dash, 1, 9, "", bgcolor=secBg)

    // Sweep status
    sweepTxt = bullSweep ? "⚡ BULL SWEEP" : bearSweep ? "⚡ BEAR SWEEP" : "— NONE"
    sweepClr = bullSweep or bearSweep ? colSweep : colNeutral
    sweepBg  = bullSweep or bearSweep ? color.new(colSweep, 88) : color.new(colDashBg, 0)
    table.cell(dash, 0, 10, "LIQ SWEEP", text_color=colTextLight, text_size=dashTextSize, bgcolor=sweepBg)
    table.cell(dash, 1, 10, sweepTxt, text_color=sweepClr, text_size=dashTextSize, bgcolor=sweepBg)

    // FVG
    fvgClr = activeFVGs > 3 ? colBullBright : activeFVGs > 0 ? colBullPrimary : colNeutral
    table.cell(dash, 0, 11, "OPEN FVGs", text_color=colTextLight, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))
    table.cell(dash, 1, 11, str.tostring(activeFVGs) + " unfilled", text_color=fvgClr, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))

    // FVG draw on liquidity
    drawOnLiqTxt = fvgBearAbove > 0 and fvgBullBelow > 0 ? "↕ BOTH SIDES" :
      fvgBearAbove > 0 ? "▲ DRAW UP (" + str.tostring(fvgBearAbove) + ")" :
      fvgBullBelow > 0 ? "▼ DRAW DOWN (" + str.tostring(fvgBullBelow) + ")" : "— NONE"
    drawClr = fvgBearAbove > 0 and fvgBullBelow == 0 ? colBullDim :
      fvgBullBelow > 0 and fvgBearAbove == 0 ? colBearDim : colNeutralLight
    table.cell(dash, 0, 12, "DRAW ON LIQ", text_color=colTextLight, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))
    table.cell(dash, 1, 12, drawOnLiqTxt, text_color=drawClr, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))

    // ── SECTION: SMC CONFLUENCE ───────────────────────────────────────────────
    table.cell(dash, 0, 13, "─── SMC CONFLUENCE ───",
      text_color=color.new(colNeutral, 20), text_size=size.tiny,
      bgcolor=secBg, text_halign=text.align_center)
    table.cell(dash, 1, 13, "", bgcolor=secBg)

    // Bull SMC score
    bTier = bullSMC >= 70 ? "S-TIER" : bullSMC >= 55 ? "A-TIER" : bullSMC >= minSMCScore ? "B-TIER" : "LOW"
    bClr  = bullSMC >= 70 ? colBullBright : bullSMC >= 55 ? colBullPrimary : bullSMC >= minSMCScore ? colBullDim : colNeutral
    table.cell(dash, 0, 14, "BULL SMC SCORE", text_color=colTextLight, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))
    table.cell(dash, 1, 14, str.tostring(bullSMC, "#") + "%  [" + bTier + "]", text_color=bClr, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))

    // Bear SMC score
    sTier = bearSMC >= 70 ? "S-TIER" : bearSMC >= 55 ? "A-TIER" : bearSMC >= minSMCScore ? "B-TIER" : "LOW"
    sClr  = bearSMC >= 70 ? colBearBright : bearSMC >= 55 ? colBearPrimary : bearSMC >= minSMCScore ? colBearDim : colNeutral
    table.cell(dash, 0, 15, "BEAR SMC SCORE", text_color=colTextLight, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))
    table.cell(dash, 1, 15, str.tostring(bearSMC, "#") + "%  [" + sTier + "]", text_color=sClr, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))

    // Near HPZ confluence
    hpzConfTxt = nearBullHPZ > 0 and nearBearHPZ > 0 ? "⬡ BOTH SIDES" :
      nearBullHPZ > 0 ? "⬡ BULL ZONE (" + str.tostring(nearBullHPZ) + ")" :
      nearBearHPZ > 0 ? "⬡ BEAR ZONE (" + str.tostring(nearBearHPZ) + ")" : "— NONE"
    hpzConfClr = nearBullHPZ > nearBearHPZ ? colBullPrimary :
      nearBearHPZ > nearBullHPZ ? colBearPrimary : colNeutralLight
    table.cell(dash, 0, 16, "HPZ PROXIMITY", text_color=colTextLight, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))
    table.cell(dash, 1, 16, hpzConfTxt, text_color=hpzConfClr, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))

    // ── SECTION: MARKET CONDITIONS ────────────────────────────────────────────
    table.cell(dash, 0, 17, "─── MARKET CONDITIONS ───",
      text_color=color.new(colNeutral, 20), text_size=size.tiny,
      bgcolor=secBg, text_halign=text.align_center)
    table.cell(dash, 1, 17, "", bgcolor=secBg)

    // Volume
    volTxt = volRatio > 2.0 ? "SPIKE  ×" + str.tostring(volRatio, "#.##") :
      volRatio > 1.3 ? "HIGH  ×" + str.tostring(volRatio, "#.##") :
      volRatio > 0.7 ? "NORMAL  ×" + str.tostring(volRatio, "#.##") : "DRY  ×" + str.tostring(volRatio, "#.##")
    volClr = volRatio > 2.0 ? colBullBright : volRatio > 1.3 ? colBullPrimary :
      volRatio > 0.7 ? colNeutralLight : colNeutral
    table.cell(dash, 0, 18, "VOLUME", text_color=colTextLight, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))
    table.cell(dash, 1, 18, volTxt, text_color=volClr, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))

    // ATR
    table.cell(dash, 0, 19, "ATR (14)", text_color=colTextLight, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))
    table.cell(dash, 1, 19, str.tostring(atrVal, format.mintick), text_color=colNeutralLight, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))

    // Body Ratio
    bodyTxt = bodyRatio > 0.7 ? "STRONG" : bodyRatio > 0.4 ? "MODERATE" : "WEAK / INDECISION"
    bodyClr = bodyRatio > 0.7 ? colBullPrimary : bodyRatio > 0.4 ? colNeutralLight : colBearDim
    table.cell(dash, 0, 20, "CANDLE BODY", text_color=colTextLight, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))
    table.cell(dash, 1, 20, bodyTxt + "  (" + str.tostring(bodyRatio * 100, "#") + "%)", text_color=bodyClr, text_size=dashTextSize, bgcolor=color.new(colDashBg, 0))

    // ── SECTION: SIGNAL ───────────────────────────────────────────────────────
    table.cell(dash, 0, 21, "─── ACTIVE SIGNAL ───",
      text_color=color.new(colNeutral, 20), text_size=size.tiny,
      bgcolor=secBg, text_halign=text.align_center)
    table.cell(dash, 1, 21, "", bgcolor=secBg)

    sigTxt = bullSignal ? "▲ LONG  (" + bullTier + ")" : bearSignal ? "▼ SHORT  (" + bearTier + ")" : "— NONE"
    sigClr = bullSignal ? colBullPrimary : bearSignal ? colBearPrimary : colNeutral
    sigBg  = bullSignal ? color.new(colBullPrimary, 85) : bearSignal ? color.new(colBearPrimary, 85) : color.new(colDashBg, 0)
    table.cell(dash, 0, 22, "SIGNAL", text_color=colTextLight, text_size=dashTextSize, bgcolor=sigBg)
    table.cell(dash, 1, 22, sigTxt, text_color=sigClr, text_size=dashTextSize, bgcolor=sigBg)

// ═══════════════════════════════════════════════════════════════════════════════
// ═══════════════════════════ ALERTS ═══════════════════════════════════════════
// ═══════════════════════════════════════════════════════════════════════════════

// MSB Alerts
alertcondition(isMSBBull,
  title="Bull Market Structure Break",
  message="[AlphaX SFP] BULL MSB — {{ticker}} {{interval}} @ {{close}}")
alertcondition(isMSBBear,
  title="Bear Market Structure Break",
  message="[AlphaX SFP] BEAR MSB — {{ticker}} {{interval}} @ {{close}}")

// BMS Alerts
alertcondition(isBMSBull,
  title="Bull Break of Market Structure (Body Close)",
  message="[AlphaX SFP] BULL BMS (body close) — {{ticker}} {{interval}} @ {{close}}")
alertcondition(isBMSBear,
  title="Bear Break of Market Structure (Body Close)",
  message="[AlphaX SFP] BEAR BMS (body close) — {{ticker}} {{interval}} @ {{close}}")

// SMC Confluence Signals
alertcondition(bullSignal and bullSMC >= 70,
  title="S-Tier Bull SMC Signal",
  message="[AlphaX SFP] S-TIER BULL SMC — {{ticker}} {{interval}} @ {{close}}")
alertcondition(bullSignal and bullSMC >= 55 and bullSMC < 70,
  title="A-Tier Bull SMC Signal",
  message="[AlphaX SFP] A-TIER BULL SMC — {{ticker}} {{interval}} @ {{close}}")
alertcondition(bullSignal and bullSMC < 55,
  title="B-Tier Bull SMC Signal",
  message="[AlphaX SFP] B-TIER BULL SMC — {{ticker}} {{interval}} @ {{close}}")

alertcondition(bearSignal and bearSMC >= 70,
  title="S-Tier Bear SMC Signal",
  message="[AlphaX SFP] S-TIER BEAR SMC — {{ticker}} {{interval}} @ {{close}}")
alertcondition(bearSignal and bearSMC >= 55 and bearSMC < 70,
  title="A-Tier Bear SMC Signal",
  message="[AlphaX SFP] A-TIER BEAR SMC — {{ticker}} {{interval}} @ {{close}}")
alertcondition(bearSignal and bearSMC < 55,
  title="B-Tier Bear SMC Signal",
  message="[AlphaX SFP] B-TIER BEAR SMC — {{ticker}} {{interval}} @ {{close}}")

alertcondition(bullSignal or bearSignal,
  title="Any SMC Signal",
  message="[AlphaX SFP] SMC signal — {{ticker}} {{interval}} @ {{close}}")

// Liquidity Sweep Alerts
alertcondition(bullSweep,
  title="Bull Liquidity Sweep",
  message="[AlphaX SFP] BULL liquidity sweep — {{ticker}} {{interval}} @ {{close}}")
alertcondition(bearSweep,
  title="Bear Liquidity Sweep",
  message="[AlphaX SFP] BEAR liquidity sweep — {{ticker}} {{interval}} @ {{close}}")

// FVG Alerts
alertcondition(bullFVG,
  title="Bullish Fair Value Gap",
  message="[AlphaX SFP] Bull FVG formed — {{ticker}} {{interval}} @ {{close}}")
alertcondition(bearFVG,
  title="Bearish Fair Value Gap",
  message="[AlphaX SFP] Bear FVG formed — {{ticker}} {{interval}} @ {{close}}")

// HPZ Alert
alertcondition(isMSBBull and nearBullHPZ > 0,
  title="Bull MSB + HPZ Confluence",
  message="[AlphaX SFP] Bull MSB at HPZ zone — {{ticker}} {{interval}} @ {{close}}")
alertcondition(isMSBBear and nearBearHPZ > 0,
  title="Bear MSB + HPZ Confluence",
  message="[AlphaX SFP] Bear MSB at HPZ zone — {{ticker}} {{interval}} @ {{close}}")

Overview

An optional confluence engine scores setups and shows signals above a minimum score with a cooldown and optional volume filter. London and New York session ranges can be shown. Many boxes, lines and labels are drawn on the price chart.

Details

Published by

majixai on GitHub

Code credit

AlphaX, as written in the script's header

Draws

On the price chart

Markets

Any symbol

Timeframes

Any timeframe

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
Pivot Lookbackint7pivotLen
MSB Momentum Z-Scorefloat0.5msbZScore
Show MSB Lines & LabelsbooltrueshowMSB
Show Structure SwingsbooltrueshowStructure
Detect Break of Market Structure (BMS)booltruebmsEnabled
Max Active OBsint10obCount
OB Lookback (candles)int10obLookback
Extend Broken OBsboolfalseextendBroken
Hide Overlapping OBsboolfalsehideOverlaps
Display ModestringHistoricalobMode
HPZ Threshold Score (%)float75.0hpzThresh
Show OB Point of ControlbooltrueshowPOC
Show all 40 settings
SettingTypeDefaultVariable
Show Fair Value Gaps (FVG)booltrueshowFVG
Min FVG Size (ATR × factor)float0.05fvgMinSize
Show Liquidity SweepsbooltrueshowLiqSweep
Sweep Lookback (bars)int20sweepLookback
Show Equal Highs / LowsbooltrueshowEQL
EQL Tolerance (ATR × factor)float0.1eqlTolerance
Enable SMC Confluence EnginebooltruesmcEnabled
Min SMC Score to Signalint30minSMCScore
Signal Cooldown (bars)int3cooldownBars
Require Volume ConfirmationboolfalseuseVolFilter
Min Volume Ratiofloat0.7volMinRatio
Show Session RangesbooltrueshowSessions
London SessionboolfalseshowLondon
London Timesession0800-1700:1234567londonSess
London Colorcolor#3b82f6londonCol
New York SessionboolfalseshowNY
New York Timesession1300-2200:1234567nySess
New York Colorcolor#f97316nyCol
Tokyo SessionboolfalseshowTokyo
Tokyo Timesession0000-0900:1234567tokyoSess
Tokyo Colorcolor#f43f5etokyoCol
Sydney SessionboolfalseshowSydney
Sydney Timesession2100-0600:1234567sydneySess
Sydney Colorcolor#eab308sydneyCol
Show DashboardbooltrueshowDash
PositionstringTop RightdashPos
SizestringSmalldashSizeInp
Label SizestringSmalllabelSizeInp
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

Golden Bullet Strategy

by TradersPost
228 stars

The Golden Bullet Strategy is an overlay signal indicator based on Smart Money Concepts and time-based liquidity models. It identifies liquidity sweeps of a designated reference session range during a specified hunt window. It then confirms Market Structure Sh

Indicator v6 SMC / ICT / price actionSessions & time
Updated 3 months ago · None stated GitHub repository of Golden Bullet Strategy

This indicator detects and draws the first Fair Value Gap (FVG) that forms in each hourly session based on a selected lower timeframe, with specialized handling for Regular Trading Hours (RTH) confluence windows. It draws FVG range boxes, midpoint lines, hourl

Indicator v5 SMC / ICT / price actionSupport & resistance Repaints

This indicator combines six trading tools into a single chart overlay to optimize indicator limits. It calculates Bollinger Bands, projects weekend price boundaries, plots market structure elements like order blocks, breaker blocks, and MSB lines, marks New Yo

Indicator v6 Support & resistanceSMC / ICT / price action Repaints
19 stars

This indicator highlights Inner Circle Trader (ICT) killzones and key session pivot points directly on the price chart. It projects shaded boxes for specific trading sessions, including Asia, London, NY AM, NY Lunch, and NY PM, to help traders identify high-li

Indicator v5 SMC / ICT / price actionSessions & time
Updated 3 years ago · MIT GitHub repository of ICT Killzones & Pivots

Bandz Session Ranges + ORG + Opening Prices plots trading session ranges, killzones, opening RTH gaps, standard deviation projections, and key opening prices directly on the price chart. It calculates gap ranges based on prior regular trading hours close sourc

Indicator v6 Sessions & timeSupport & resistance
Updated 2 weeks ago · MPL-2.0 GitHub repository of Bandz Session Ranges + ORG + Opening Prices

More SMC / ICT scripts

Related on ForexR

Trade Your Charts With a Regulated Broker

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

Compare brokers