SMC User Guide

Complete guide to the NTL Smart Money Concepts (SMC) indicator — the pivot-based liquidity, structure, Break-of-Structure, Change-of-Character, conviction and trend-fib engine that powers SMC-style decision making in EAsiTrader. The same 18 bit-packed buffers documented here are what the EAsiScript SMC1(...) function reads, what the GroundedEvents pipeline exports to the AI providers, and what renders on the chart when you drag the indicator on yourself.

For how EAsiTrader strategies use the indicator, see the EAsiTrader User Guide. For scripting language details, see the EAsiScript User Guide. For the AI architecture that consumes the grounded events, see the AI Architecture Guide.

Table of Contents

  1. Introduction
  2. Core Concepts
  3. Installing the Indicator
  4. Input Parameters
  5. How Detection Logic Works
  6. Indicator Buffers
  7. Using SMC from EAsiScript
  8. AI GroundedEvents Integration
  9. Multi-Instance and Multi-Timeframe Usage
  10. Tuning Guide
  11. Troubleshooting
  12. Glossary
  13. Related Guides

1. Introduction

What SMC Is

SMC is an MQL5 custom indicator that reads an OHLC series and produces, for every bar, a comprehensive snapshot of Smart Money Concepts state:

  • Active and swept liquidity pools (Buy Side and Sell Side) — rolling chains of pivots with internal/external classification
  • Classified market structure points (HH / LH / HL / LL) with bar-strength and a 24-entry rolling sequence history
  • Break of Structure and Change of Character events — including a same-bar secondary-BOS slot
  • Active ChoCh fib ladders (38.2 / 50 / 61.8 / 78.6) drawn from the ChoCh bar's wick back to the violated structure point
  • Active trend-fib ladders ("TFibs") drawn on confirmed HL→HH (bullish) or LH→LL (bearish) impulse legs
  • A 19-state structure trend machine (ranging, bullish/bearish, strong/v.strong, four transition tiers per side, ChoCh mitigation, TFib retracement, contested)
  • A three-tier conviction score (Low / Medium / High) with bull/bear direction and a hysteresis stable flag
  • A standard INDICATORSIGNALDATA signal flags value so the EA's Signal() and Trend() helpers work unchanged

Internally SMC orchestrates two sub-indicators (cABH for bar-height normalisation and cPivotPoints for the raw pivot stream) and writes 18 output buffers. Every downstream consumer — chart drawing, EAsiScript, GroundedEvents for AI, CSV export — reads from the same buffer set. There is no "display-only" path that bypasses them; the on-screen graphics are simply a rendering of the buffer data.

What SMC Is Not

  • It is not a strategy. SMC is a data producer; entries, exits and risk live in EAsiTrader preset files and EAsiScript rules that consume SMC output.
  • It does not look into the future. All real-time fields (backlink, sweptAge, external on relay bars, BOSData, StructureData formed/strength, ConvictionData, SignalFlags) are safe to read for EA decisions. A small subset of internal fields (pivotShift, terminated, size, external on formation bars) use future bars and are flagged in §6 as unsafe for EAs — these are only used by the draw layer.
  • It is not a multi-provider feed. Each cSMC instance consumes exactly one symbol/timeframe pair. Multi-timeframe analysis is done by instantiating one SMC per timeframe (see §9).

Relationship to EAsiScript and EAsiTrader

When EAsiTrader loads a preset that includes an SMC indicator line, it calls IndicatorCreate() with the creation string, which in turn runs SMC.mq5's OnInit() in "EA-instantiated" mode. In that mode:

  • All logic-affecting inputs (§4.1–4.7) are respected
  • All display-only inputs (§4.8) are silently ignored — the EA cannot see a chart, so there is nothing to draw or label
  • The 18 buffers are filled on every tick and read back by EAsiScript via the SMC1(shift, buffer, lsr, mask) function
  • When the AI mode is active, the same buffers are walked by cSMC::ExportEvents() to produce grounded-event JSON (§8)

The existing EAsiScript guide has a short SMC1 section covering the most common buffer reads; this document is the long-form companion that explains the full feature set, the bit layouts of every buffer, and how all the pieces fit together.


2. Core Concepts

Pivots and Bar Strength

Every SMC decision starts with a pivot point — a bar whose high (for BSL / HH / LH structure) or low (for SSL / HL / LL structure) is more extreme than its neighbours. The sub-indicator cPivotPoints produces pivots at multiple "orders"; SMC uses MinPivotOrder = 1 as its starting order and retries with weaker pivots if a candidate fails downstream filters.

Bar strength is a count of how many neighbouring bars on each side the pivot dominates. A strength-7 high pivot means the pivot's high is higher than the 7 bars to its left and the 7 bars to its right. Larger strength = cleaner pivot, fewer repaints, fewer false positives.

High-pivot, bar strength = 5 pivot (5 lower highs on each side) left 5 right 5

The MinBarStrength input (default 7) sets the minimum bar strength required for any liquidity pool pivot. A separate ChoCHMinBarStrength input (default 150) lives in SMC's state, not in the StructureData buffer's Strength field, and is used during ChoCh qualification. See §5.

Liquidity Pools: BSL and SSL

A liquidity pool is a horizontal line anchored at a recent pivot extreme where stop-loss orders and breakout buy-orders are assumed to cluster. Two kinds exist:

  • BSL (Buy Side Liquidity) — anchored at a high pivot. Stops from short positions and buy-stops from breakout traders sit above this price.
  • SSL (Sell Side Liquidity) — anchored at a low pivot. Stops from long positions and sell-stops sit below this price.

Buffers 0 and 1 hold the bit-packed SMC chain data for each side; buffers 2 and 3 hold the chain's latest active pivot price.

BSL above, SSL below BSL SSL

Internal vs External Liquidity

Not every pivot matters equally. SMC classifies each liquidity pool as internal or external based on how far price moved away from the pivot after it formed, measured in ABH (Average Bar Height) multiples.

  • External: excursion from pivot ≥ LiquidityClassificationABH × ABH (default 7.0 × ABH). Represents significant higher-timeframe swing highs/lows — real institutional liquidity targets.
  • Internal: excursion below the threshold. Represents micro liquidity inside consolidations.

Classification is a one-way latch: once a pattern is promoted to external, it stays external. Pending patterns can be promoted from internal to external as price continues moving away. For relay (non-formation) bars, the external bit is OR-propagated from the active chain, so a single read of SMC1(0, 0, 48, 1) gives you an O(1) "any active BSL in the chain is external" answer — no traversal needed.

Set LiquidityClassificationABH to 0 to disable classification entirely; all patterns will then be treated as internal and the external flag will always be 0.

Liquidity Sweeps

A pool is swept when price crosses its pivot extreme:

  • BSL is swept when subsequent high > pivot high
  • SSL is swept when subsequent low < pivot low

On the sweep bar the sweptAge counter starts at 1 and then increments every subsequent bar up to a hard cap of 255. This is what lets SMC1(0, 0, 12, 255) answer "has BSL been swept in the last N bars?" without walking the chain.

A parallel counter — bslSweptExternalAge / sslSweptExternalAge in Buffer 14 — tracks bars since the most recent external sweep. It is independent of sweptAge: subsequent internal sweeps do not reset the external counter, so the counter always points at the most recent external sweep. This is crucial for strategies that only care about the big HTF liquidity targets.

BSL sweep: high breaks pivot BSL sweep bar — sweptAge becomes 1

Market Structure: HH, LH, HL, LL

SMC runs a second, independent structure engine on the same pivot stream. Every time a new high pivot is confirmed, it is compared to the previously classified high pivot's price:

  • HH (Higher High) — new high > previous high
  • LH (Lower High) — new high ≤ previous high

Likewise for low pivots:

  • HL (Higher Low) — new low > previous low
  • LL (Lower Low) — new low ≤ previous low

These four codes (1=HH, 2=LH, 3=HL, 4=LL) are written to Buffer 6 (StructureData) on the classification bar — which is the bar where the pivot is confirmed, not the pivot bar itself. The pivot's bar strength is packed into bits 36–43 of the same buffer slot.

Bullish structure: HL → HH → HL → HH LL HH HL HH HL HH HL

The last 24 structure codes are kept in a rolling 50-bit sequence buffer (Buffer 15 — SequenceData) as 24 × 2-bit pairs. Bit layout is designed so that index 0 (bits 3:2) is the newest pivot and index 23 (bits 49:48) is the oldest; bits 0–1 hold the most recent BOS direction. This is what powers the EAsiScript StructureFlow(indicator, pattern) and StructureQuality(indicator) functions, which simply decode the latest N pairs.

Break of Structure (BOS)

A BOS is a bar whose close breaks a recent structure point:

  • Bullish BOS — close exceeds the most-recent HH (or LH, with ChoCHLookback/ChoCHOppositeThreshold in play for ChoCh promotion)
  • Bearish BOS — close falls below the most-recent HL (or LL)

On a BOS bar, the engine writes the BOS type + bars-back-to-broken-level to Buffer 8 (BOSData) and the broken structure's price to Buffer 9 (BOSPrice). A Wyckoff cycle name — "Markup" / "Markdown" / "Accumulation" / "Distribution" — is derived for chart labels and grounded events.

Bullish BOS: close breaks last HH last HH BOS: close above last HH

A special case: a BOS whose direction opposes the preceding trend is upgraded to a ChoCh (Change of Character). This is where the interesting data lives.

Change of Character (ChoCh)

A ChoCh is a BOS that changes the trend direction. The rules for promotion (all must pass):

  1. Sequence condition: the latest two structure points must indicate a reversal — for a bullish ChoCh, s[0]==LL, s[1]!=HH (i.e. the market was making LLs and we're breaking out upward). For a bearish ChoCh, s[0]==HH, s[1]!=LL.
  2. ChoCh opposite filter: if ChoCHOppositeThreshold > 0, the percentage of opposite-side structures in the last ChoCHLookback pivots must be below the threshold. When both inputs are zero (the defaults), this filter is skipped.
  3. Bar-strength filter: the pivot bar strength behind the break must be ≥ ChoCHMinBarStrength (default 150). Note: this is a different, much larger strength threshold than the one used for liquidity-pool detection, because ChoChs are rare and we want only the cleanest pivots to promote.
  4. Weak ChoCh filter (display-only, governed by InpSMC_HideWeakChoCh): if the impulse distance from broken level to BOS-bar wick is less than 3.0 × ABH, the ChoCh label is hidden. The underlying state machine still treats it as a ChoCh — only the chart label and grounded event are suppressed.

On a ChoCh bar, SMC captures a fib anchor pair:

  • chochImpulsePrice — the bar's high (for bullish) or low (for bearish) wick, written to Buffer 10 on the ChoCh bar only
  • chochInvalidationPrice — the most recent opposite-side structure price, written to Buffer 11 on the ChoCh bar only

and enters a transition state that persists until either:

  • The opposite-side structure confirms (bullish ChoCh → waiting for a HL to form; bearish ChoCh → waiting for a LH), at which point the transition resolves to a confirmed trend, or
  • Price closes beyond the invalidation level, at which point the transition is cancelled

Structure Trend and the 19-State Machine

The structure trend direction is a 5-bit code (Buffer 14, bits 16–20) classifying the current market regime. There are 19 states (0–18). The Name column is the source-level enum; the Chart label column is what renders on the chart:

Code Name Chart label Meaning
0 RANGING RANGING No dominant trend direction in the lookback window
1 BULLISH BULLISH Confirmed bullish trend (≥80% HH/HL over lookback, default)
2 BEARISH BEARISH Confirmed bearish trend
3 STRONG_BULLISH STRONG BULLISH Bullish with confirming Bull BOS and strength ratio boost
4 STRONG_BEARISH STRONG BEARISH Bearish with confirming Bear BOS and strength ratio boost
5 VSTRONG_BULLISH V.STRONG BULLISH Bullish + BOS agreement + very high strength ratio
6 VSTRONG_BEARISH V.STRONG BEARISH Bearish + BOS agreement + very high strength ratio
7 WEAK_BULLISH_TRANSITION WEAK BULLISH TRANSITION Bull ChoCh seen, impulse distance < 3 ABH
8 MODERATE_BULLISH_TRANSITION MODERATE BULLISH TRANSITION Bull ChoCh, impulse distance 3–5 ABH
9 STRONG_BULLISH_TRANSITION STRONG BULLISH TRANSITION Bull ChoCh, impulse distance 5–8 ABH
10 VSTRONG_BULLISH_TRANSITION V.STRONG BULLISH TRANSITION Bull ChoCh, impulse distance ≥8 ABH
11 BULLISH_RETRACEMENT [tier] BULLISH MITIGATION Bull ChoCh active, price retraced ≥ RetracementMinPct into fib
12 WEAK_BEARISH_TRANSITION WEAK BEARISH TRANSITION Mirror of 7 for bearish
13 MODERATE_BEARISH_TRANSITION MODERATE BEARISH TRANSITION Mirror of 8
14 STRONG_BEARISH_TRANSITION STRONG BEARISH TRANSITION Mirror of 9
15 VSTRONG_BEARISH_TRANSITION V.STRONG BEARISH TRANSITION Mirror of 10
16 BEARISH_RETRACEMENT [tier] BEARISH MITIGATION Mirror of 11
17 BULLISH_TFIB_RETRACEMENT [tier] BULLISH RETRACEMENT Confirmed bullish trend + active TFib retraced by ≥RetracementMinPct
18 BEARISH_TFIB_RETRACEMENT [tier] BEARISH RETRACEMENT Mirror of 17

The transition tier (WEAK / MODERATE / STRONG / VSTRONG) is decided by scaled ABH distance thresholds: ≥80 → VSTRONG, ≥50 → STRONG, ≥30 → MODERATE, else WEAK. The distance is held in Buffer 14 bits 31–40 as an integer scaled ×10, so a value of 65 means "6.5 × ABH".

⚠ Mitigation vs. Retracement — the important naming distinction. The source enum calls states 11/16 BULLISH_RETRACEMENT / BEARISH_RETRACEMENT, but those states render on the chart as "MITIGATION". States 17/18 (BULLISH_TFIB_RETRACEMENT / BEARISH_TFIB_RETRACEMENT) render as "RETRACEMENT". The two words mean different things in NTL SMC usage:

  • Mitigation (states 11/16) — during a ChoCh transition (not yet fully confirmed), price has pulled back into the ChoCh fib ladder. The market is returning to the ChoCh source to mitigate unfilled orders that were left at the broken structure. Entry here is counter-impulse: you're fading into the ChoCh leg, betting the new trend will resume.
  • Retracement (states 17/18) — a confirmed trend has printed and an active TFib has been retraced into by ≥ RetracementMinPct. This is the canonical "pullback in a trend" setup — entering with the established trend off a TFib level.

When reading your chart: MITIGATION = early, pre-confirmation; RETRACEMENT = confirmed, post-TFib. When reading SMC1 buffer values in EAsiScript: use the enum codes (11/16 for mitigation, 17/18 for TFib retracement).

An auxiliary isContested flag (Buffer 14 bit 58) is set when the trend classifier observes conflicting evidence — most commonly a BOS that disagrees with the sequence-count direction. Contested states are capped at the base BULLISH/BEARISH tier and prevent HIGH conviction (see §5).

ChoCh Fib Ladders

On each active ChoCh, SMC draws a four-level retracement — 38.2%, 50%, 61.8%, 78.6% — anchored at the ChoCh bar's wick (ChochFibImpulse, 0%) and the violated structure point (ChochFibInvalidation, 100%). The ladder rays right until the fib terminates.

Termination rules (first to fire wins):

  • Body close beyond the invalidation level → fib freezes at that bar
  • Same-direction BOS → fib freezes at that bar (the trend is now confirmed, no longer a transition)
  • 200-bar hard cap (CHOCH_FIB_MAX_BARS in SMCFibSet.mqh) → fib freezes regardless

While active, the fib ray-extends to the right; once terminated, it freezes at the termination bar. The underlying state in SMC uses the same logic to compute the ChoCH retracement percent (Buffer 14, bits 41–47) and to emit retracement-state trend codes (11/16).

Bullish ChoCh fib ladder 0% 38.2% 50% 61.8% 78.6% 100% (invalid) ChoCh bar

Trend Fibs (TFibs)

A TFib is a second kind of fib ladder that only appears after a trend is already confirmed. It measures the immediate pullback potential of the most recent impulse leg:

  • In a bullish trend, TFib is anchored at the last confirmed HL (100%) and the following HH (0%).
  • In a bearish trend, TFib is anchored at the last confirmed LH (100%) and the following LL (0%).

Triggering rules (all must pass):

  1. The current structure must be HH (for bullish TFib) or LL (for bearish TFib) with pivot bar strength ≥ MinTFibStrength (default 70).
  2. The immediately-preceding formed structure must be the opposite type (HL for bull, LH for bear).
  3. Expansion guard: (impulse_close − previous_same_side_pivot_close) / ABH ≥ TFibExpansionABH (default 1.0).
  4. Impulse size guard: (impulse_close − anchor_wick) / ABH ≥ TFibImpulseABH (default 2.0).
  5. Body confirm (optional, default on): the pivot bar's close must have traded beyond the previous same-side pivot. Disabled by setting InpSMC_TFibRequireBodyConfirm = false.

On trigger, the engine writes anchor and impulse prices to Buffer 12 and Buffer 13 on that bar only. The direction is inferred by the draw layer from the sign of impulse − anchor (positive = bullish, negative = bearish).

TFibs terminate on:

  • Opposite-direction BOS — the trend has broken
  • Body close beyond the anchor (HL for bull, LH for bear) — invalidation
  • Wick sweep of either endpoint — a shakeout that reaches the anchor wick or blows through the impulse

Once a confirmed trend coincides with an active TFib and price has retraced into it by ≥ RetracementMinPct, the structure trend state flips to BULLISH_TFIB_RETRACEMENT / BEARISH_TFIB_RETRACEMENT (states 17 / 18) and a synthesised strength distance is written to chochDistanceAbh so the render path picks up the correct tier prefix ("STRONG BULLISH RETRACEMENT" etc.).

Conviction Tiers

For every bar, SMC classifies a conviction tier and direction into Buffer 16 (ConvictionData). Three tiers + hysteresis:

  • HIGH — set only after a clean handoff from a same-direction transition or retracement state to a confirmed same-direction trend. Never set in contested states.
  • MEDIUM — confirmed non-contested trend, or an uncontested transition/retracement
  • LOW — ranging, weak, or contested

A stable bit (Buffer 16 bit 4) is set when the (tier, direction) pair has held for CONVICTION_HYSTERESIS_BARS prior bars (compile-time default 2 — not user-facing). Before the bar count reaches the dwell window, stable=0.

When InpSMC_Timeframe == SeriesTimeframe_Current and the indicator is the first SMC dragged on the chart (label slot 0), a top-right conviction dashboard is drawn: one "Conviction" title row + three dots (Low / Medium / High) that glow in the direction colour when active (bright when stable, dim until stable). Only label slot 0 renders the dashboard to prevent stacked duplicates when multiple SMC instances are on the same chart. The dashboard is behind a compile-time flag (INDICATOR_SMC_DEFAULTSHOWCONVICTIONDASHBOARD = false) — there is no user input for it.

Wyckoff Cycle Names

For BOS labels and grounded-event metadata, the BOS type maps to a Wyckoff cycle phase:

BOS Type Wyckoff Cycle
Bullish BOS Markup
Bearish BOS Markdown
Bullish ChoCh Accumulation
Bearish ChoCh Distribution

3. Installing the Indicator

Dragging SMC onto a Chart

  1. Make sure SMC.ex5 has been compiled (it lives at Indicators\NTL\SMC\SMC.mq5).
  2. In MetaTrader 5, open the target chart.
  3. From the Navigator panel, drag Indicators → NTL → SMC → SMC onto the chart.
  4. The Inputs dialog appears. Configure the inputs as described in §4 and click OK.

If you have multiple SMC instances on the same chart (e.g. one for M5, one for H1), they are automatically assigned label slots — the first dragged is slot 0, the second is slot 1, etc. This keeps their chart object names unique and offsets their debug stacks so labels don't collide. Only label slot 0 draws the conviction dashboard.

Using SMC from an EA

EAsiTrader instantiates SMC via IndicatorCreate() using a creation string of the form:

NTL\SMC(1,0,800,7,400,100,7.0,2.0,0.0,11,80,1,30,0,0,150,1.0,2.0,1,70,2).ex5,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17

The first 1 is the hidden InpEAIC flag ("created by an EA"). Parameters 1–20 correspond to the logic-affecting inputs in the order shown in §4.1–4.7. The trailing comma-separated list after the .ex5 is the read buffer list — EAsiTrader keeps buffer 0 through buffer 17 available for SMC1(...) reads. If you omit a buffer from that list, SMC1() calls targeting it will return 0.

Do not pass display-only inputs in the creation string. See the next subsection for why.

Display-Only Inputs: A Critical Warning

SMCInputs.mqh documents 9 inputs as "display-only — never passed to IndicatorCreate(), only used when the indicator is dragged onto a chart":

  • InpSMC_HideWeakChoCh
  • InpSMC_DrawTFibs
  • InpSMC_OnlyShowVStrongTFibs
  • InpSMC_TFibShowBox
  • InpSMC_ShowStructureLabels
  • InpSMC_ShowBOSLabels
  • InpSMC_ShowSMCLabels
  • InpSMC_ChartLabelStyle
  • InpSMC_FontSize

When EAsiTrader creates an SMC instance, it cannot see a chart and these inputs are silently ignored. If you add them to an EA creation string, they'll be eaten as extra parameters, shift your other indices, and break the indicator's configuration. Equally, do not reference them from an EA's preset when thinking about strategy logic — their on/off state has no bearing on what appears in the buffers.

The single exception is that the underlying state behind some of these flags is still computed internally (e.g. "is this ChoCh weak?" is still tracked even when HideWeakChoCh only affects drawing). Your EA can read that state from the buffers directly — §6 tells you where.


4. Input Parameters

All defaults in this section come from Include\NTL\SMC\Globals.mqh. The "Creation index" column matches the positional slot in an EAsiTrader creation string, starting at index 0 for InpEAIC. Display-only inputs (§4.8) are not part of the creation string.

4.1 Pivot Detection

Input Default Creation idx Description
InpEAIC false 0 Hidden flag the EA sets to true to tell SMC it was instantiated programmatically. Do not set this manually when dragging the indicator.
InpSMC_Timeframe Current 1 Timeframe the instance analyses. Current (0) uses the chart timeframe and the plot buffer path; any explicit timeframe uses the IndicatorManager refresh path and cannot render the dashboard.
InpSMC_MaxLookbackInBars 800 2 Maximum bars to search for a valid pivot when forming a new liquidity pool. Larger values find older pivots but cost more CPU on the first load.
InpSMC_MinBarStrength 7 3 Minimum pivot bar strength for liquidity pool detection. A pivot with strength below this is rejected even if it would otherwise pass swing-size filters.
InpSMC_MaxWidthInBars 400 4 Maximum width (in bars) a liquidity pool line can reach before it's auto-terminated. 0 disables the cap.
InpSMC_ABHPeriod 100 5 Bar count for the sub-indicator cABH (Average Bar Height). ABH normalises all "distance in bars/ABH" measurements — swing sizes, excursion thresholds, ChoCh impulse distances, TFib expansion — so they auto-adapt to volatility regimes.

4.2 Liquidity Classification

Input Default Creation idx Description
InpSMC_LiquidityClassificationABH 7.0 6 ABH multiplier for internal/external classification. A pool is promoted to external when max_excursion / ABH ≥ this value. Set to 0 to disable classification (all pools internal, external bit always 0).

4.3 Swing Size Filters

Input Default Creation idx Description
InpSMC_MinSwingInABH 2.0 7 Minimum post-pivot swing size in ABH units. Rejects small, insignificant pivots before they become liquidity pools. Set to 0 to disable. (Note: shown as "Post Pivot Swing Size (ABH)" in the input dialog.)
InpSMC_PrePivotSwingInABH 0.0 8 Minimum swing distance in ABH from the previous opposite-side pivot. Rejects pivots that lack a significant preceding move. 0 disables.

4.4 Structure Trend

Input Default Creation idx Description
InpSMC_StructureTrendLookback 11 9 Number of recent structure points to examine when computing the trend direction. Hard-capped at 24 (sequence buffer depth).
InpSMC_StructureTrendThreshold 80 10 Percentage of bullish or bearish pivots required to declare a confirmed trend (0–100). With the default 80, you need at least 9 of the last 11 structures to be bullish (HH/HL) to get a BULLISH state.
InpSMC_StructureReset true 11 When a new ChoCh starts or an existing transition invalidates, clamp the effective lookback to the number of structures formed since that event. Prevents pre-ChoCh pivots from polluting the trend vote.
InpSMC_RetracementMinPct 30 12 Minimum retracement percent into a ChoCh fib or TFib to flip the trend state into a retracement code (11, 16, 17, 18).

4.5 ChoCh Qualification

Input Default Creation idx Description
InpSMC_ChoCHLookback 0 13 Extra structures to scan when testing a BOS for ChoCh promotion. 0 disables the opposite-side filter.
InpSMC_ChoCHOppositeThreshold 0 14 Max percent of opposite-side structures allowed within ChoCHLookback when promoting a BOS to ChoCh. 0 with ChoCHLookback=0 = filter off.
InpSMC_ChoCHMinBarStrength 150 15 Minimum pivot bar strength required for a BOS to be promoted to a ChoCh. Note this is much higher than the liquidity-pool MinBarStrength (default 7) — ChoChs should only be promoted off unusually clean pivots.

4.6 Trend Fib (TFib) Detection

Input Default Creation idx Description
InpSMC_TFibExpansionABH 1.0 16 Minimum HL→HH (bullish) or LH→LL (bearish) expansion in ABH multiples. Measured close-to-close between the impulse pivot and the prior same-side pivot.
InpSMC_TFibImpulseABH 2.0 17 Minimum impulse leg in ABH multiples — anchor-wick to impulse-close.
InpSMC_TFibRequireBodyConfirm true 18 When true, the pivot bar's close must have traded beyond the previous same-side pivot before the TFib is accepted. Disabling this produces noisier triggers.
InpSMC_MinTFibStrength 70 19 Minimum TFib pivot strength (0–100). Below this, the setup is skipped.

4.7 Grounded Events Export

Input Default Creation idx Description
InpSMC_ExportType Both (2) 20 Which event types the AI export writes. 0 = Liquidity only, 1 = Structure only, 2 = Both. See §8 for the budgeting behaviour of each mode.

4.8 Display-Only Inputs (chart-drag only)

These never appear in an EA creation string. Setting them in one is a configuration bug.

Input Default Description
InpSMC_HideWeakChoCh true Hides ChoCh chart labels when the impulse distance is < 3.0 × ABH. The underlying state machine is unaffected — a hidden ChoCh is still counted as a ChoCh internally.
InpSMC_DrawTFibs true Master toggle for TFib drawing.
InpSMC_OnlyShowVStrongTFibs false When on, only TFibs whose trigger coincides with a V.Strong trend tier are drawn.
InpSMC_TFibShowBox true Draws a filled rectangle between the 38.2% and 78.6% levels.
InpSMC_ShowStructureLabels true Draws HH/LH/HL/LL labels at each structure point.
InpSMC_ShowBOSLabels true Draws BOS / ChoCh labels with Wyckoff cycle text.
InpSMC_ShowSMCLabels true Draws the BSL / SSL origin labels.
InpSMC_ChartLabelStyle LabelOnly Label rendering style (LabelOnly, LabelWithBox, etc. — see eChartLabelStyle).
InpSMC_FontSize 7 Font size for every SMC chart label.

5. How Detection Logic Works

This section walks through the per-bar calculation pipeline. It is a summary of cSMC::Calculate() in SMC.mqh — if anything here contradicts the source, the source is right.

The Per-Bar Calculation Pipeline

On each bar i in the [_startPos, _endPos] window, SMC runs:

  1. UpdateExisting(i, _bsl, …) and UpdateExisting(i, _ssl, …) — walk each side's chain, propagate sweptAge/external flags, detect new sweeps, write relay data.
  2. IsSMC(i, ...) on SSL then BSL — search for a qualifying pivot in the lookback window. Pivots that fail filters are retried with the next-older pivot.
  3. If an SMC is detected, AddNew(...) writes a new chain link to the buffer.
  4. UpdateStructureTracking(i, ...) — classify any newly-confirmed pivots as HH/LH/HL/LL, push to the sequence buffer, and check for BOS / ChoCh on both the structure data and the prior-bar's state.
  5. Transition confirmation — if we're currently in a ChoCh transition, check whether the required opposite-side pivot has formed.
  6. Transition invalidation — check whether the current bar's close has broken beyond the ChoCh invalidation level.
  7. UpdateActiveTFib(i, ...) — walk pending TFibs and promote the most recent qualifying trigger to the active TFib slot.
  8. Compute retracement percent (ChoCh fib first, then TFib fallback).
  9. CalculateStructureTrend(...) → store the final state code into Buffer 14.
  10. TFib retracement override — if a confirmed trend coincides with an active TFib and retracePct ≥ RetracementMinPct, flip the trend code to 17 or 18.
  11. Write Buffer 14 (all diagnostic fields), Buffer 15 (sequence bits), Buffer 16 (conviction), Buffer 17 (signal flags).

Pivot → Liquidity Pool

IsSMC() only looks backward. From the current bar it searches for a qualifying pivot with MinPivotOrder = 1 (a compile-time constant, not an input) within MaxLookbackInBars. Each candidate pivot is tested in order:

  • Same-pivot guard — if the pivot matches the side's last-used pivot, the search stops (no retry). Without this guard, two nearby pivots could alternate as LastPivotPos across consecutive bars and produce an SMC on every bar.
  • Bar strength filterMinBarStrength check. Failed pivots retry with the next-older candidate.
  • Post-pivot swing filterMinSwingInABH. Measures max_excursion / ABH and rejects small swings. Failed pivots retry.
  • Pre-pivot swing filterPrePivotSwingInABH. Measures the distance in ABH units from the previous opposite-side pivot. Failed pivots retry.

On success, AddNew() packs the chain link: backlink, sweptAge, type (01=BSL / 10=SSL), formed=1, swept (on if price has already broken the pivot between pivotPos and pos), size, pivotShift, terminated (true if swept or size >= SMC_MAXSIZE), and external (from the excursion-vs-ABH test).

Pivot → Structure Classification

Structure classification runs inside UpdateStructureTracking() and uses the same pivot stream. Every new high pivot is compared to _highState.LastStructurePrice:

  • pivot_high > prev_high → HH
  • pivot_high ≤ prev_high → LH

Low pivots are classified HL / LL symmetrically. Each classification:

  1. Writes the struct code + pivot bar strength to Buffer 6 on the current bar
  2. Writes the pivot price to Buffer 7
  3. Pushes the struct code into the rolling sequence (Buffer 15)
  4. Updates the side's LastStructure* state
  5. Accumulates the bar-strength value into _strengthValues[] / _strengthDirs[] for the strength ratio

Structure → BOS → ChoCh Promotion

Every bar's close is checked against the most-recent HH/HL (for a bullish BOS) and the most-recent LL/LH (for a bearish BOS). If a break is detected:

  1. The raw BOS type is computed (bullish / bearish).
  2. The BOS is tested for ChoCh promotion (§2 rules).
  3. The final type is written to Buffer 8 primary slot (bits 0–15): bosType, brokenLevel (bars back), formed=1. The broken structure's price goes to Buffer 9.
  4. If this is a ChoCh:
    • _chochImpulsePrice = BOS bar's wick (high for bullish, low for bearish) → Buffer 10
    • _chochInvalidationPrice = most recent opposite-side structure price → Buffer 11
    • _chochDistanceABH = impulse distance scaled ×10
    • _inTransition = true, _transitionBOSType = bullish/bearish ChoCh
  5. The 2-bit BOS direction code is also written to Buffer 15 bits 0–1 (forward-filled on every bar).

Retroactive and Same-Bar BOS Paths

There are three paths into the BOS slot, and the source file labels them with comments for a reason — it is easy to miss that a single bar can carry two BOS events:

  • PATH 1 — Immediate: A BOS detected during the primary structure check, inside DetectBOS(). Written to the primary slot (bits 0–15).
  • PATH 2 — Post-classification: A structure classified earlier in the same bar can retroactively trigger a BOS on that same bar. This second BOS is written to the secondary slot (bits 16–31) so the primary slot is preserved. Downstream consumers reading the primary-only low 16 bits will miss the secondary event — but the chart draw layer and ExportEvents() both read the secondary slot too.
  • RETROACTIVE ChoCh upgrade: A BOS previously written as a regular BOS can be upgraded to a ChoCh after a later structure classification reveals the reversal. This rewrites the original BOS slot in place.

The secondary-BOS slot is the reason Buffer 8 has the seemingly odd 15 / 31 formed bits — one for each slot.

ChoCh Fib Termination Rules

cSMCFibSet::Draw() (in SMCFibSet.mqh) is called on every chart refresh when the indicator is on a chart. It walks the visible bar range, finds every bar with a non-zero ChochFibImpulseBuffer value, reconstructs the fib ladder from (impulsePrice, invalidationPrice), and then scans forward from the ChoCh bar to find the termination point.

Termination hits the first of:

  1. Body close beyond the invalidation level → frozen at that bar
  2. Same-direction BOS (bullish BOS after bullish ChoCh etc.) → frozen, marked "confirmed"
  3. 200-bar hard cap (CHOCH_FIB_MAX_BARS = 200 in SMCFibSet.mqh) — fixed, not user-configurable

Active fibs extend to the right via OBJPROP_RAY_RIGHT; terminated fibs freeze at the termination bar's time.

TFib Triggering and Termination

DetectTFibTrigger() in SMCTFibDetect.mqh is the shared trigger function used on every structure classification:

  1. Current structure must be HH (bullish) or LL (bearish) with pivot bar strength ≥ MinTFibStrength.
  2. The immediately-preceding formed structure must be HL (for bullish) or LH (for bearish). An intervening structure that isn't of the right type kills the candidate.
  3. Expansion guard: impulse close — previous same-side pivot close ≥ TFibExpansionABH × ABH.
  4. Body confirm (if enabled): the pivot bar's close must have traded beyond the previous same-side pivot.
  5. Impulse size guard: (impulse_close − anchor_wick) / ABH ≥ TFibImpulseABH.

Triggered candidates go into _tfibPending* arrays and are promoted to the active slot by UpdateActiveTFib() on the next bar where a qualifying trend state exists. Termination (inside cSMCTFibSet::Draw()):

  1. Same-direction BOS (Bull BOS on a bullish TFib, Bear BOS on a bearish TFib) → freeze marked "BOS"
  2. Body close beyond the anchor (close < HL for bullish, close > LH for bearish) → invalidation
  3. Wick sweep of either the anchor or the impulse pivot → freeze

Terminations that resolve at or before the trigger confirmation bar skip rendering entirely — a setup that was invalidated before it was even drawn.

Trend State Derivation

CalculateStructureTrend() (in SMCTypes.mqh) is the decision function that takes the sequence bits, BOS state, ChoCh distance, strength ratio and retracement percent and returns one of the 19 state codes. Skeleton:

  • Count bullish (HH/HL) and bearish (LH/LL) in the effective lookback window.
  • Compute bullishPct, bearishPct.
  • If we're in a ChoCh transition, return a WEAK/MODERATE/STRONG/VSTRONG transition code (7–10 or 12–15) based on chochDistanceABH; if retracePct ≥ RetracementMinPct, return RETRACEMENT (11 or 16).
  • Else if bullishPct ≥ threshold or bearishPct ≥ threshold:
    • With BOS agreement (same-direction Bull/Bear BOS on record) and strength ratio boost, return STRONG or VSTRONG (multiplier 4.0x / 5.0x for confirmed VSTRONG).
    • Without BOS agreement or with contested evidence, return base BULLISH or BEARISH.
  • Else return RANGING.

The isContested flag is set when BOS direction disagrees with the percent vote or breaks a tie. Contested trends are capped at base BULLISH/BEARISH and prevent HIGH conviction.

Conviction Classification

ClassifyConviction() (in SMCTypes.mqh) takes the current bar's structure trend, the previous bar's structure trend, and the contested flag, and emits a tier + direction:

  • HIGH only when:
    • Previous bar was a same-direction transition or retracement (e.g. STRONG_BULLISH_TRANSITION or BULLISH_RETRACEMENT)
    • Current bar is a confirmed same-direction trend (BULLISH / STRONG / VSTRONG)
    • Not contested
  • MEDIUM:
    • Confirmed, non-contested trends that don't qualify for HIGH
    • Any transition or retracement state
  • LOW:
    • RANGING
    • Any contested state

Hysteresis is applied by the calculate loop: walk back ConvictionHysteresisBars prior bars (compile-time default 2) and set the stable bit only if every intermediate bar had the same (tier, direction) pair. For the first few bars after indicator load, stable=0 until the dwell window fills.


6. Indicator Buffers

6.1 Buffer Overview

There are 18 buffers, all written every bar, all readable from EAsiScript via SMC1(shift, buffer, lsr, mask). Bit layouts are sourced from SMCMacros.mqh and the authoritative descriptions in Globals.mqh (INDICATOR_SMC_DESCRIPTIONBUFFERS).

Idx Name Kind Purpose
0 BSLData uint64 bitpacked Buy-side liquidity chain link (or relay)
1 SSLData uint64 bitpacked Sell-side liquidity chain link (or relay)
2 BSLPrice double price BSL latest active pivot price
3 SSLPrice double price SSL latest active pivot price
4 BSLSweptPrice double price BSL most-recent-sweep pivot price
5 SSLSweptPrice double price SSL most-recent-sweep pivot price
6 StructureData uint64 bitpacked HH/LH/HL/LL classification + strength
7 StructurePrice double price Pivot price at structure point
8 BOSData uint64 bitpacked Primary + secondary BOS slot
9 BOSPrice double price Price of broken structure level
10 ChochFibImpulse double price ChoCh fib 0% anchor (formation bar only)
11 ChochFibInvalidation double price ChoCh fib 100% anchor (formation bar only)
12 TFibAnchor double price TFib HL/LH anchor (trigger bar only)
13 TFibImpulse double price TFib HH/LL impulse (trigger bar only)
14 SweptExternalAge uint64 bitpacked Swept ext ages + trend/ChoCh diagnostics
15 SequenceData uint64 rolling 24-entry swing history + BOS direction
16 ConvictionData uint64 bitpacked Tier + direction + stable flag
17 SignalFlags uint64 INDICATORSIGNALDATA Standard sweep signal format

6.2 Formation Bars, Relay Bars and Real-Time Safety

There are two kinds of bars in every chain:

  • Formation bars — the bar where a new pivot was confirmed (formed=1). All bit fields are valid on formation bars for drawing; a subset is safe for EA reads.
  • Relay bars — every other bar in the chain (formed=0). Only a subset of fields are meaningful. The fields that are meaningful on relay bars are real-time safe — they never reflect future data.

Real-time safe on all bars (formation + relay):

  • backlink — always bars to the previous chain link
  • sweptAge — monotonically increasing counter
  • swept — sticky "has been swept" bit
  • external — on relay bars, this is OR-propagated from the active chain (any active pattern external → relay bit set). Gives you O(1) "is there external liquidity active?" without traversal.
  • type — constant per buffer (01 for BSL, 10 for SSL)
  • formed — trivially safe

NOT real-time safe (draw-layer internal — uses future bar data to assign):

  • size — pattern width in bars, determined retroactively when the pattern terminates
  • pivotShift — bars from current to pivot, only finalised when the chain relays
  • terminated — only known with certainty after lookforward
  • external on formation bars — the formation-bar external bit is finalised later when CalculateMaxExcursion() has all the subsequent bars; do not read external specifically on formed=1 slots

When consuming from an EA, stick to backlink/sweptAge/swept/external-on-relay/formed/type and your reads will never repaint.

6.3 Buffer 0 / 1 — BSL Data / SSL Data

49-bit bitpacked liquidity chain data.

Field Bits Range Description
backlink 0–11 0–4095 Bars to previous chain link (0 = chain end)
sweptAge 12–19 0–255 Bars since sweep (0 = not swept, 1+ = bars elapsed)
type 20–21 1 or 2 01 = BSL (Buffer 0), 10 = SSL (Buffer 1)
formed 22 0 or 1 1 at pattern origin bar
swept 23 0 or 1 1 when price broke the pivot
size 24–35 0–4095 Pattern width in bars — internal use only
pivotShift 36–45 0–1023 Bars from current to pivot — internal use only
terminated 47 0 or 1 1 when pattern is no longer active — internal use only
external 48 0 or 1 1 if external; safe on relay bars, internal only on formation bars

EAsiScript reads:

BSLSweptAge = SMC1(0, 0, 12, 255)     // bars since BSL sweep
SSLSweptAge = SMC1(0, 1, 12, 255)     // bars since SSL sweep
BSLFormed   = SMC1(0, 0, 22, 1)       // 1 if new BSL formed this bar
SSLFormed   = SMC1(0, 1, 22, 1)       // 1 if new SSL formed this bar
AnyExternal = SMC1(0, 0, 48, 1)       // 1 if active BSL chain is external

6.4 Buffer 2 / 3 — BSL Price / SSL Price

Plain double prices. On a formation bar, this is the newly-added pivot's price. On relay bars, it holds the newest active pattern's pivot price (or 0 if no active patterns remain on this side). A direct sweep check is as simple as:

BSLPrice = SMC1(0, 2)
// True when current bar's high >= BSLPrice

6.5 Buffer 4 / 5 — BSL Swept Price / SSL Swept Price

Plain double prices. Written on the sweep bar (sweptAge=1) and forward-propagated while sweptAge > 0 (up to 255 bars), then reset to 0. Combined with the SweptExternalAge buffer to distinguish internal from external sweeps:

BSLSweptPrice = SMC1(0, 4)
BSLExtAge     = SMC1(0, 14, 0, 255)
// BSLSweptPrice represents an external sweep when BSLExtAge > 0

6.6 Buffer 6 — StructureData

44-bit bitpacked structure classification.

Field Bits Range Description
backlink 0–11 0–4095 Bars to previous structure point (0 = chain end)
sweptAge 12–19 0 Reserved — currently unused, always 0
structType 20–22 0–4 0=None, 1=HH, 2=LH, 3=HL, 4=LL
formed 23 0 or 1 1 at classification bar
swept 24 0 Reserved — currently unused, always 0
pivotShift 25–34 0–1023 Bars from current to pivot — internal use only
terminated 35 0 or 1 1 when structure point no longer active — internal use only
strength 36–43 0–255 Bar strength of the pivot (safe on formation bars and relay bars)

Real-time safe fields: backlink, structType, formed, strength.

StructType     = SMC1(0, 6, 20, 7)    // newly-formed this bar (0 if none)
StructStrength = SMC1(0, 6, 36, 255)  // pivot bar strength

6.7 Buffer 7 — StructurePrice

Plain double price. Holds the pivot price of the most recent structure classification. Non-zero on every relay bar for as long as at least one structure exists on either side.

6.8 Buffer 8 — BOSData

32-bit bitpacked primary + secondary BOS slots.

Primary BOS (bits 0–15):

Field Bits Range Description
bosType 0–2 0–4 0=None, 1=Bullish BOS, 2=Bearish BOS, 3=Bullish ChoCh, 4=Bearish ChoCh
brokenLevel 3–14 0–4095 Bars back to the broken structure point
formed 15 0 or 1 1 on the bar where the break was confirmed

Secondary BOS (bits 16–31): same layout, shifted up by 16. Used on bars where a same-bar retroactive BOS needs to be stored alongside a primary BOS (PATH 2 in §5).

BOSType      = SMC1(0, 8, 0, 7)       // primary BOS type (0/1/2/3/4)
BOSBrokenBar = SMC1(0, 8, 3, 4095)    // bars back to broken structure
BOS2Type     = SMC1(0, 8, 16, 7)      // secondary slot type (usually 0)

6.9 Buffer 9 — BOSPrice

Plain double price. The level of the structure that was broken on this bar (non-zero when BOSData.formed=1).

6.10 Buffer 10 / 11 — ChochFibImpulse / ChochFibInvalidation

Plain double prices. Written only at the ChoCh bar. Zero on every other bar — do not forward-fill these into indicator state without walking back to the most recent ChoCh bar yourself.

  • Bull ChoCh: impulse = BOS bar's high, invalidation = most recent HL/LL price
  • Bear ChoCh: impulse = BOS bar's low, invalidation = most recent HL/LH price

Reconstruct the fib ladder:

impulse       = SMC1(N, 10)   // find an N where this is > 0
invalidation  = SMC1(N, 11)
level_382     = impulse + (invalidation - impulse) * 0.382
level_500     = impulse + (invalidation - impulse) * 0.500
level_618     = impulse + (invalidation - impulse) * 0.618
level_786     = impulse + (invalidation - impulse) * 0.786

Do not assume the ChoCh is still active just because the buffer slot is non-zero — it was frozen at that bar and you need to check the retracement percent in Buffer 14 or the current _inTransition state indirectly via the structure trend code in Buffer 14 bits 16–20.

6.11 Buffer 12 / 13 — TFibAnchor / TFibImpulse

Plain double prices. Written only at the TFib trigger bar. Direction is inferred by the draw layer from sign(impulse − anchor) — positive = bullish (HL→HH), negative = bearish (LH→LL).

anchor  = SMC1(N, 12)
impulse = SMC1(N, 13)
// isBullish = impulse > anchor

6.12 Buffer 14 — SweptExternalAge (diagnostics)

60-bit bitpacked. This is the most important buffer for EAs — it carries the structure trend state, ChoCh diagnostics, swept external ages, and contested flags in a single slot that's updated on every bar.

Field Bits Range Description
bslSweptExternalAge 0–7 0–255 Bars since most recent external BSL sweep (0 = none)
sslSweptExternalAge 8–15 0–255 Bars since most recent external SSL sweep
structureTrendDirection 16–20 0–18 19-state structure trend code (see §2 table)
structureTrendStrength 21–30 0–1023 max/min swing ABH ratio × 100; always ≥100 when non-zero
chochDistanceAbh 31–40 0–1023 ChoCh impulse distance in ABH × 10 (max 102.3 ABH)
chochRetracePct 41–47 0–100 ChoCh (or TFib, when in retracement override) retracement percent
structDebugLookback 48–52 0–31 Effective lookback used this bar (internal diagnostic)
structDebugBullish 53–57 0–31 Bullish-side count in the lookback (internal diagnostic)
isContested 58 0 or 1 BOS disagrees with sequence direction
abhBullish 59 0 or 1 Cumulative bullish ABH > bearish ABH

EAsiScript examples:

StructureTrendDir  = SMC1(0, 14, 16, 31)    // 0..18
TrendStrength      = SMC1(0, 14, 21, 1023)
ChochDistAbhX10    = SMC1(0, 14, 31, 1023)
ChochRetracePct    = SMC1(0, 14, 41, 127)   // 0..100
IsContested        = SMC1(0, 14, 58, 1)
BSLExtAge          = SMC1(0, 14, 0, 255)
SSLExtAge          = SMC1(0, 14, 8, 255)

Note that bslSweptExternalAge operates independently from the side's own sweptAge. Subsequent internal sweeps do not reset this counter — it always points at the most recent external sweep on the given side.

6.13 Buffer 15 — SequenceData

50-bit rolling history.

Field Bits Description
bosDirection 0–1 0=none, 1=bullish, 2=bearish — most recent BOS direction, forward-filled every bar
swing[0] 2–3 newest structure (0=HH, 1=HL, 2=LH, 3=LL)
swing[1] 4–5 second newest
swing[23] 48–49 oldest structure in the history

Reads:

BOSDirection       = SMC1(0, 15, 0, 3)
LatestSwingType    = SMC1(0, 15, 2, 3)
PreviousSwingType  = SMC1(0, 15, 4, 3)

The StructureFlow(indicator, pattern, shift) EAsiScript function decodes the latest 3 swings and compares against a 3-digit pattern (1=HH, 2=HL, 3=LH, 4=LL, 0=wildcard). The StructureQuality(indicator, shift) function returns +2 / +1 / -1 / -2 / 0 based on well-known quality patterns — see the EAsiScript User Guide.

6.14 Buffer 16 — ConvictionData

5-bit bitpacked.

Field Bits Range Description
tier 0–1 0–3 0=None, 1=Low, 2=Medium, 3=High
direction 2–3 0–2 0=None, 1=Bull, 2=Bear
stable 4 0/1 1 when the (tier, direction) pair has held for ConvictionHysteresisBars prior bars
ConvTier   = SMC1(0, 16, 0, 3)    // 0..3
ConvDir    = SMC1(0, 16, 2, 3)    // 0..2
ConvStable = SMC1(0, 16, 4, 1)

A high-conviction long entry rule might read ConvTier == 3 and ConvDir == 1 and ConvStable == 1.

6.15 Buffer 17 — SignalFlags (INDICATORSIGNALDATA)

The standard INDICATORSIGNALDATA uint64 bit-packed format (see Indicator/INDICATORSIGNALDATA_Reference.md for the complete schema). For SMC, only bits 2 (bearish sweep signal — SSL swept) and 3 (bullish sweep signal — BSL swept) of the flags byte are ever set. The signal data's identifier is 0x15 (SMC).

EAsiScript does not read this buffer directly — instead the standard Signal('SMC1'), Trend('SMC1') helpers decode it for you. Example:

isBullSweep = Signal('SMC1') > 0      // BSL sweep signal in this buffer's bar offset
isBearSweep = Signal('SMC1') < 0
trendDir    = Trend('SMC1')           // last trend bar offset

7. Using SMC from EAsiScript

7.1 The SMC1(...) Read API

Every SMC buffer read goes through the SMC1(shift, buffer, lsr, mask) function:

  • shift — bar offset (0 = current bar, 1 = one bar back, …)
  • buffer — buffer index 0–17
  • lsr — "least significant bit" — the bit offset to shift right by before applying the mask (defaults to 0)
  • mask — bit mask applied after the shift (defaults to 0, which means "read the whole double value")

For plain-double buffers (2, 3, 4, 5, 7, 9, 10, 11, 12, 13), you pass only shift and buffer:

BSLPrice = SMC1(0, 2)

For bit-packed buffers (0, 1, 6, 8, 14, 15, 16, 17), you must supply both lsr and mask to extract a single field:

StructType = SMC1(0, 6, 20, 7)    // 3 bits at offset 20 (structType)

The mask is the maximum value the field can take, not the raw bitmask. So a 3-bit field uses mask 7, an 8-bit field uses 255, a 10-bit field uses 1023, etc. EAsiScript does (value >> lsr) & mask internally.

7.2 Signal, Trend, StructureFlow, StructureQuality

Four general indicator-consumer functions work with SMC:

Function What it returns
Signal('SMC1') Decodes SignalFlags bit 2/3 into +offset for bull sweep, −offset for bear sweep
Trend('SMC1') Decodes SignalFlags trend bits into the bar offset of the most recent trend change
StructureFlow(indicator, pattern, shift=1) Returns 1 if the last 3 swings match the 3-digit pattern (oldest→newest), 0 otherwise
StructureQuality(indicator, shift=1) Returns +2/+1/-1/-2/0 based on swing quality (strong/acceptable bullish, neutral, bearish)

StructureFlow pattern codes: 1=HH, 2=HL, 3=LH, 4=LL, 0=wildcard. Example: StructureFlow('SMC1', 212) = true when the last 3 swings are HL → HH → HL. StructureFlow('SMC1', 102) = true when the last 3 are HH → anything → HL.

7.3 Worked Example 1 — BSL Sweep Reversal

Setup: after an external BSL sweep, we want to enter short when the next SSL forms. SL above the sweep wick, TP at the most recent HL.

# === BSL sweep reversal, enter on new SSL, SL above wick, TP at last HL ===

# Freshness test: BSL external sweep within the last 20 bars, and an SSL just formed
BSLExtAge        = SMC1(0, 14, 0, 255)
SSLFormed        = SMC1(0, 1, 22, 1)
BSLSweptPrice    = SMC1(0, 4)
SSLPrice         = SMC1(0, 3)

isFreshBSLExtSweep = BSLExtAge > 0 and BSLExtAge <= 20
shortTrigger       = isFreshBSLExtSweep and SSLFormed == 1

# Most recent HL as TP target (structureType 3)
StructType       = SMC1(0, 6, 20, 7)
StructPrice      = SMC1(0, 7)

Preset snippet (entry / SL / TP / exit blocks, abbreviated):

; Entry: short when a fresh BSL external sweep is followed by an SSL formation
EntryRules_Short=isFreshBSLExtSweep and SSLFormed == 1

; SL: above the swept BSL wick plus a small buffer in pips
StopLossFormula_Short=BSLSweptPrice + ToPrice(5)

; TP: target the most recent HL (structureType = 3 means HL)
TakeProfitFormula_Short=IF(StructType == 3, StructPrice, LastOpenPrice - ToPrice(50))

; Exit: flip if a Bullish BOS appears (BOSType 1)
ExitRules_Short=SMC1(0, 8, 0, 7) == 1

7.4 Worked Example 2 — ChoCh Retracement Entry

Setup: wait for a bullish ChoCh in its mitigation state (StructureTrendDirection == 11 == BULLISH_RETRACEMENT, rendered on the chart as "BULLISH MITIGATION"), enter long when price revisits the 61.8% fib level. SL at 100%, TP at the 0% impulse.

# === Bullish ChoCh 61.8% retracement long ===

StructureTrendDir = SMC1(0, 14, 16, 31)
isBullRetrace     = StructureTrendDir == 11

# Walk back to find the most recent ChoCh bar (where ChochFibImpulse > 0)
# The indicator guarantees this bar has both Buffer 10 and 11 populated.
# Use MaxBarsBack() to cap the scan:
chochBar          = FindLast(SMC1(@,10) > 0, 200)    # pseudocode; your script's find helper
chochImpulse      = SMC1(chochBar, 10)
chochInvalidation = SMC1(chochBar, 11)

# Compute the 61.8% level (bullish: impulse is high, invalidation is below)
level_618 = chochImpulse + (chochInvalidation - chochImpulse) * 0.618

closeNow          = Close(0)
# Entry: in a bullish retracement state, close is at or below the 61.8% level
longTrigger       = isBullRetrace and closeNow <= level_618
EntryRules_Long=isBullRetrace and Close(0) <= level_618
StopLossFormula_Long=chochInvalidation - ToPrice(3)
TakeProfitFormula_Long=chochImpulse
ExitRules_Long=SMC1(0, 14, 16, 31) == 0   ; regime changed away from BULLISH_RETRACEMENT

7.5 Worked Example 3 — TFib Pullback with High Conviction

Setup: only take long entries when conviction is HIGH, the trend is confirmed bullish (state 1/3/5), and an active TFib has been retraced by at least 50%.

# === High-conviction TFib retracement long ===

ConvTier          = SMC1(0, 16, 0, 3)
ConvDir           = SMC1(0, 16, 2, 3)
ConvStable        = SMC1(0, 16, 4, 1)

highConvBull      = ConvTier == 3 and ConvDir == 1 and ConvStable == 1

StructureTrendDir = SMC1(0, 14, 16, 31)
inBullTfibRetrace = StructureTrendDir == 17    # BULLISH_TFIB_RETRACEMENT

retracePct        = SMC1(0, 14, 41, 127)
deepRetrace       = retracePct >= 50

# Walk back to find the most recent TFib trigger bar
tfibBar           = FindLast(SMC1(@,12) > 0, 200)
anchor            = SMC1(tfibBar, 12)
impulse           = SMC1(tfibBar, 13)

longTrigger       = highConvBull and inBullTfibRetrace and deepRetrace
EntryRules_Long=highConvBull and inBullTfibRetrace and deepRetrace
StopLossFormula_Long=anchor - ToPrice(3)              ; below the HL anchor
TakeProfitFormula_Long=impulse + (impulse - anchor)   ; 1R projection above the HH impulse
ExitRules_Long=SMC1(0, 14, 16, 31) != 17              ; state left TFib retracement

8. AI GroundedEvents Integration

When EAsiTrader runs in an AI mode, SMC's buffers are walked by cSMC::ExportEvents() (in SMCExport.mqh) to produce a JSON array of grounded events that the AI consumes as witness evidence. The GroundedEvents architecture exists to prevent hallucinations — the AI can only reference events that SMC actually saw.

ExportType Modes

InpSMC_ExportType selects which event families are emitted:

Mode Value Contents
Liquidity 0 BSL/SSL patterns only — active, swept, expired
Structure 1 Structure points (HH/LH/HL/LL) + BOS events only
Both 2 Everything, with budget split as described below

Three-Phase Budgeting

ExportEvents() runs three phases to fit within a caller-supplied maxEvents budget. When mode is Both, the budget is split so no single family crowds out the others:

  • Phase 1 — active liquidity. Budget = maxEvents / 3 (Both) or maxEvents (Liquidity-only). First fills active BSL (up to budget / 2), then active SSL.
  • Phase 2 — swept/expired liquidity. Budget = (maxEvents × 2) / 3 (Both) or maxEvents (Liquidity-only). Emits events with status: "swept" or status: "expired" and includes an is_external metadata flag.
  • Phase 3 — structure + BOS. Emitted as SM_HH, SM_LH, SM_HL, SM_LL (with bar_strength) and BOS_<type>_<barsAgo> + BOS2_<type>_<barsAgo> for secondary slots (with broken_price and the mapped Wyckoff cycle name).

At the end of the liquidity export, two summary fields are appended regardless of phase budgeting:

  • bsl_swept_external_bars_ago
  • ssl_swept_external_bars_ago

so the AI always has O(1) access to the most recent external sweep ages.

Event Schemas

The schemas are documented as constants in Globals.mqh:

  • INDICATOR_SMC_GROUNDEDEVENTSSCHEMA_LIQUIDITY — fields for BSL/SSL events
  • INDICATOR_SMC_GROUNDEDEVENTSSCHEMA_STRUCTURE — fields for structure/BOS events
  • INDICATOR_SMC_GROUNDEDEVENTSSCHEMA — combined schema used when ExportType = Both

Each event has a unique id (the naming convention <TypeCode>_<barsAgo> makes them stable across refreshes for the same bar), a human-readable name, price, status, and a timeframe tag. Events are filtered by activeOnly when emitted during the active-phase walk.


9. Multi-Instance and Multi-Timeframe Usage

You can place multiple SMC indicators on the same chart — for example, one on M5 and one on H1 — and they will not conflict. Two things to know:

  1. Label slots. DetectSMCChartSlot() in SMC.mq5 walks ChartIndicatorsTotal() and counts prior SMC indicators, returning a slot index (0, 1, 2, …). The slot offsets chart object names and debug-stack label positions so multiple instances don't overwrite each other. It also gates the conviction dashboard: only slot 0 renders the top-right panel. If you want the dashboard on a specific instance, make it the first one you drag onto the chart.

  2. Plot buffer vs series buffer paths. If InpSMC_Timeframe == SeriesTimeframe_Current, the instance uses the chart's own price series and writes to the 18 plot buffers directly on every OnCalculate(). If you pick a different timeframe, the instance uses a cSeriesManager-managed HTF series and is refreshed via cIndicatorManager on every tick. The second path does not expose the buffers to the chart's indicator window — use it for EA reads via SMC1(shift, buffer, ...), not for visual analysis.

For the EA-instantiated case, typical EAsiTrader presets create multiple SMC instances with explicit timeframes and read them with SMC1 / SMC2 / SMC3 (the numbered variants mirror the SMC creation order in the preset). The indicator manager handles caching so each timeframe's buffers are only recomputed when that timeframe actually ticks.


10. Tuning Guide

Defaults ship tuned for mid-timeframe index / major-pair trading (M15–H1). The knobs below cover the most common adjustments.

Recipe: "I see too many sweeps on every swing"

Symptom: noisy liquidity pools, shallow sweeps every few bars, no signal-to-noise.

  • Raise MinBarStrength (7 → 10 or 12). Fewer, cleaner pivots.
  • Raise MinSwingInABH (2.0 → 3.0 or 4.0). Forces bigger post-pivot excursion before a pool can form.
  • Raise LiquidityClassificationABH (7.0 → 10.0 or higher) so fewer pools ever get promoted to external. Combined with a strategy that only reacts to external sweeps, this gives you much better signal quality.

Recipe: "ChoCh labels are cluttering the chart"

Symptom: too many ChoCh markers, many of them barely breaking the prior structure.

  • Raise ChoCHMinBarStrength (150 → 200 or 250). Only the cleanest pivots will promote.
  • Leave HideWeakChoCh enabled (display-only; hides ChoChs with impulse < 3 ABH from the chart).
  • For EA decisions that should also ignore weak ChoChs, test the chochDistanceAbh field in Buffer 14 directly — SMC1(0, 14, 31, 1023) / 10.0 >= 3.0 is the EA-visible equivalent of the HideWeakChoCh check.

Recipe: "Scalping on low timeframes"

Symptom: running SMC on M1/M5; sweeps and ChoCh fibs look bloated.

  • Lower ABHPeriod (100 → 30 or 50). ABH will adapt to smaller bars faster.
  • Lower MaxLookbackInBars (800 → 200 or 300). No point looking back half a session for a pivot.
  • Lower StructureTrendLookback (11 → 5 or 7). Faster regime shifts.
  • Consider TFibExpansionABH at 0.5, TFibImpulseABH at 1.0 — scalping doesn't need H1-size impulses to be meaningful.

Recipe: "Position trading on H4/D1"

Symptom: running on H4/D1; signals are sparse and trends are obvious.

  • Raise MaxLookbackInBars (800 → 1500 or 2000). More room to find the real swings.
  • Raise StructureTrendLookback (11 → 15 or 20). Slower, more patient regime calls.
  • Raise StructureTrendThreshold (80 → 85 or 90). Only the cleanest trends pass.
  • Raise MinTFibStrength (70 → 90). TFibs will be rare but high-quality.

11. Troubleshooting

"SMC1 reads always return 0" — You probably forgot to include the buffer in the creation string's read-list. The part after .ex5, is a comma-separated list of buffer indices made available to SMC1(); any index not listed there returns 0. The standard list is 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17.

"My display-only inputs are not doing anything when I run in an EA" — Correct. They are silently ignored during IndicatorCreate(). See §3 and §4.8. The indicator only consults them when it sees a chart (i.e. when dragged on manually).

"ChoCh fib never appears even though I see the ChoCh label" — The HideWeakChoCh display flag is independent of the fib drawing. Check that the ChoCh is not weak (impulse distance ≥ 3 ABH) and that you haven't exceeded the 200-bar CHOCH_FIB_MAX_BARS cap. Also confirm the ChoCh hasn't been terminated by a same-direction BOS or body close beyond invalidation.

"The TFib draws for a few bars then disappears" — Normal when the setup is invalidated (same-direction BOS or close beyond the anchor). Check the termination-bar debug label; the draw layer skips TFibs whose termination resolves at or before the trigger confirmation bar.

"Conviction dashboard doesn't appear" — Three things to check:

  1. INDICATOR_SMC_DEFAULTSHOWCONVICTIONDASHBOARD is false by default in Globals.mqh — this is a compile-time flag and must be set at build time. There is no user input for it.
  2. The dashboard only renders on label slot 0 (the first SMC dragged on the chart).
  3. The dashboard requires InpSMC_Timeframe = Current (the plot-buffer path).

"StructureTrendDirection reads 17 or 18 — I didn't know those existed" — Those are the TFib retracement overrides added on top of the base 17-state machine. They fire only when a confirmed trend coincides with an active TFib retraced by ≥ RetracementMinPct. On the chart they render as "BULLISH/BEARISH RETRACEMENT" — distinct from states 11/16 which render as "MITIGATION". See §2 and §5.

"Why does the ChoCh bar's invalidation sometimes disagree with my eye?" — The fib invalidation is the most recent opposite-side structure price at the moment the ChoCh was detected — specifically the most recent HL/LL for a bullish ChoCh, or the most recent HL/LH for a bearish one. It is not the structure point that was broken (that's Buffer 9, BOSPrice). The two will often differ.

"How do I know if an EA read is real-time safe?" — See §6.2. Stick to backlink, sweptAge, swept, external (on relay bars), formed, type, structType, strength, all of Buffer 14 diagnostics, Buffer 15 sequence reads, Buffer 16 conviction, Buffer 17 signal flags. Avoid size, pivotShift, terminated, and formation-bar external.


12. Glossary

  • ABH — Average Bar Height. The sub-indicator (cABH) provides a volatility-normalised distance unit; most SMC thresholds are expressed as "N × ABH" so they auto-adapt to regime.
  • BSL — Buy Side Liquidity. A pool of orders (stops from shorts, buy-stops from breakout traders) sitting above a swing high pivot.
  • SSL — Sell Side Liquidity. The mirror, sitting below a swing low pivot.
  • Sweep — Price crossing a liquidity pool's pivot extreme, absorbing the resting orders.
  • Internal liquidity — A liquidity pool whose post-pivot excursion is below the LiquidityClassificationABH threshold; represents micro liquidity inside consolidations.
  • External liquidity — A liquidity pool whose excursion meets or exceeds the threshold; represents higher-timeframe swing highs/lows that institutional flows target.
  • HH / LH / HL / LL — Higher High / Lower High / Higher Low / Lower Low structure classifications. SMC writes these as codes 1 / 2 / 3 / 4 in Buffer 6 bits 20–22.
  • BOS — Break of Structure. A bar whose close breaks a recent structure point in the trend direction.
  • ChoCh — Change of Character. A BOS that opposes the prior trend direction, promoted by bar-strength, opposite-threshold, and sequence-reversal rules.
  • ChoCh fib — The 38.2 / 50 / 61.8 / 78.6 retracement ladder anchored at the ChoCh bar's wick (0%) and the violated structure price (100%). 200-bar hard-cap.
  • TFib — Trend Fib. A post-confirmation fib ladder drawn on the last impulse leg (HL→HH for bullish, LH→LL for bearish).
  • Transition state — A ChoCh has fired but the opposite-side structure hasn't confirmed yet; the 19-state machine emits codes 7–10 / 12–15 during this window.
  • Mitigation state — Chart-display name for state codes 11 / 16 (enum BULLISH_RETRACEMENT / BEARISH_RETRACEMENT). Active when a ChoCh transition has seen price retrace ≥ RetracementMinPct into the ChoCh fib — the market is "mitigating" unfilled orders at the ChoCh source before the new trend resumes. Pre-confirmation.
  • Retracement state — In chart labels, refers specifically to state codes 17 / 18 (enum BULLISH_TFIB_RETRACEMENT / BEARISH_TFIB_RETRACEMENT): a confirmed trend with an active TFib retraced by ≥ RetracementMinPct. Post-confirmation, with-trend pullback. Distinct from the ChoCh mitigation states — see §2 for the naming rationale.
  • Contested — The trend classifier saw mixed evidence (BOS direction disagrees with sequence vote, or BOS breaks a tie). Contested trends are capped at base BULLISH/BEARISH and block HIGH conviction.
  • Label slot — Multi-instance disambiguator. The first SMC on a chart is slot 0; subsequent adds are 1, 2, … Slot 0 owns the conviction dashboard.
  • Wyckoff cycle — Shorthand trend-phase labels attached to BOS events: Bull BOS=Markup, Bear BOS=Markdown, Bull ChoCh=Accumulation, Bear ChoCh=Distribution.
  • Relay bar — Any bar in a chain that is not a formation bar (formed=0). Carries forwarded state like sweptAge and the OR-propagated external flag; safe for real-time reads.
  • Formation bar — The bar where a pivot is confirmed (formed=1). Holds the original classification data; a subset of fields are real-time safe.
  • Grounded event — A JSON event emitted by cSMC::ExportEvents() for AI consumption. Every event has a unique id, price, status and timeframe, letting the AI reference verifiable witness data rather than hallucinate.

13. Related Guides


Rev: 2026-04-15 (TOC anchor links fixed)