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.
SMC is an MQL5 custom indicator that reads an OHLC series and produces, for every bar, a comprehensive snapshot of Smart Money Concepts state:
Signal() and Trend() helpers work unchangedInternally 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.
cSMC instance consumes exactly one symbol/timeframe pair. Multi-timeframe analysis is done by instantiating one SMC per timeframe (see §9).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:
SMC1(shift, buffer, lsr, mask) functioncSMC::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.
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.
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.
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:
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.
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.
LiquidityClassificationABH × ABH (default 7.0 × ABH). Represents significant higher-timeframe swing highs/lows — real institutional liquidity targets.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.
A pool is swept when price crosses its pivot extreme:
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.
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:
Likewise for low pivots:
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.
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.
A BOS is a bar whose close breaks a recent structure point:
ChoCHLookback/ChoCHOppositeThreshold in play for ChoCh promotion)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.
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.
A ChoCh is a BOS that changes the trend direction. The rules for promotion (all must pass):
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.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.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.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 onlychochInvalidationPrice — the most recent opposite-side structure price, written to Buffer 11 on the ChoCh bar onlyand enters a transition state that persists until either:
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).
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):
CHOCH_FIB_MAX_BARS in SMCFibSet.mqh) → fib freezes regardlessWhile 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).
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:
Triggering rules (all must pass):
MinTFibStrength (default 70).(impulse_close − previous_same_side_pivot_close) / ABH ≥ TFibExpansionABH (default 1.0).(impulse_close − anchor_wick) / ABH ≥ TFibImpulseABH (default 2.0).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:
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.).
For every bar, SMC classifies a conviction tier and direction into Buffer 16 (ConvictionData). Three tiers + hysteresis:
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.
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 |
SMC.ex5 has been compiled (it lives at Indicators\NTL\SMC\SMC.mq5).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.
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.
SMCInputs.mqh documents 9 inputs as "display-only — never passed to IndicatorCreate(), only used when the indicator is dragged onto a chart":
InpSMC_HideWeakChoChInpSMC_DrawTFibsInpSMC_OnlyShowVStrongTFibsInpSMC_TFibShowBoxInpSMC_ShowStructureLabelsInpSMC_ShowBOSLabelsInpSMC_ShowSMCLabelsInpSMC_ChartLabelStyleInpSMC_FontSizeWhen 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.
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.
| 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. |
| 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). |
| 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. |
| 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). |
| 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. |
| 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. |
| 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. |
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. |
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.
On each bar i in the [_startPos, _endPos] window, SMC runs:
UpdateExisting(i, _bsl, …) and UpdateExisting(i, _ssl, …) — walk each side's chain, propagate sweptAge/external flags, detect new sweeps, write relay data.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.AddNew(...) writes a new chain link to the buffer.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.UpdateActiveTFib(i, ...) — walk pending TFibs and promote the most recent qualifying trigger to the active TFib slot.CalculateStructureTrend(...) → store the final state code into Buffer 14.retracePct ≥ RetracementMinPct, flip the trend code to 17 or 18.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:
LastPivotPos across consecutive bars and produce an SMC on every bar.MinBarStrength check. Failed pivots retry with the next-older candidate.MinSwingInABH. Measures max_excursion / ABH and rejects small swings. Failed pivots retry.PrePivotSwingInABH. 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).
Structure classification runs inside UpdateStructureTracking() and uses the same pivot stream. Every new high pivot is compared to _highState.LastStructurePrice:
pivot_high > prev_high → HHpivot_high ≤ prev_high → LHLow pivots are classified HL / LL symmetrically. Each classification:
LastStructure* state_strengthValues[] / _strengthDirs[] for the strength ratioEvery 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:
Buffer 8 primary slot (bits 0–15): bosType, brokenLevel (bars back), formed=1. The broken structure's price goes to Buffer 9._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 ChoChBuffer 15 bits 0–1 (forward-filled on every bar).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:
DetectBOS(). Written to the primary slot (bits 0–15).ExportEvents() both read the secondary slot too.The secondary-BOS slot is the reason Buffer 8 has the seemingly odd 15 / 31 formed bits — one for each slot.
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:
CHOCH_FIB_MAX_BARS = 200 in SMCFibSet.mqh) — fixed, not user-configurableActive fibs extend to the right via OBJPROP_RAY_RIGHT; terminated fibs freeze at the termination bar's time.
DetectTFibTrigger() in SMCTFibDetect.mqh is the shared trigger function used on every structure classification:
MinTFibStrength.TFibExpansionABH × ABH.(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()):
Terminations that resolve at or before the trigger confirmation bar skip rendering entirely — a setup that was invalidated before it was even drawn.
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:
bullishPct, bearishPct.chochDistanceABH; if retracePct ≥ RetracementMinPct, return RETRACEMENT (11 or 16).bullishPct ≥ threshold or bearishPct ≥ threshold: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.
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:
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.
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 |
There are two kinds of bars in every chain:
formed=1). All bit fields are valid on formation bars for drawing; a subset is safe for EA reads.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 linksweptAge — monotonically increasing counterswept — sticky "has been swept" bitexternal — 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 safeNOT real-time safe (draw-layer internal — uses future bar data to assign):
size — pattern width in bars, determined retroactively when the pattern terminatespivotShift — bars from current to pivot, only finalised when the chain relaysterminated — only known with certainty after lookforwardexternal 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 slotsWhen consuming from an EA, stick to backlink/sweptAge/swept/external-on-relay/formed/type and your reads will never repaint.
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
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
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
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
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.
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)
Plain double price. The level of the structure that was broken on this bar (non-zero when BOSData.formed=1).
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.
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.
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
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.
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.
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.
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
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–17lsr — "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.
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.
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
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
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
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.
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 |
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:
maxEvents / 3 (Both) or maxEvents (Liquidity-only). First fills active BSL (up to budget / 2), then active SSL.(maxEvents × 2) / 3 (Both) or maxEvents (Liquidity-only). Emits events with status: "swept" or status: "expired" and includes an is_external metadata flag.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_agossl_swept_external_bars_agoso the AI always has O(1) access to the most recent external sweep ages.
The schemas are documented as constants in Globals.mqh:
INDICATOR_SMC_GROUNDEDEVENTSSCHEMA_LIQUIDITY — fields for BSL/SSL eventsINDICATOR_SMC_GROUNDEDEVENTSSCHEMA_STRUCTURE — fields for structure/BOS eventsINDICATOR_SMC_GROUNDEDEVENTSSCHEMA — combined schema used when ExportType = BothEach 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.
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:
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.
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.
Defaults ship tuned for mid-timeframe index / major-pair trading (M15–H1). The knobs below cover the most common adjustments.
Symptom: noisy liquidity pools, shallow sweeps every few bars, no signal-to-noise.
Symptom: too many ChoCh markers, many of them barely breaking the prior structure.
chochDistanceAbh field in Buffer 14 directly — SMC1(0, 14, 31, 1023) / 10.0 >= 3.0 is the EA-visible equivalent of the HideWeakChoCh check.Symptom: running SMC on M1/M5; sweeps and ChoCh fibs look bloated.
Symptom: running on H4/D1; signals are sparse and trends are obvious.
"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:
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.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.
cABH) provides a volatility-normalised distance unit; most SMC thresholds are expressed as "N × ABH" so they auto-adapt to regime.LiquidityClassificationABH threshold; represents micro liquidity inside consolidations.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.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.formed=0). Carries forwarded state like sweptAge and the OR-propagated external flag; safe for real-time reads.formed=1). Holds the original classification data; a subset of fields are real-time safe.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.SMC1 section..set preset files that instantiate SMC via creation strings.Rev: 2026-04-15 (TOC anchor links fixed)