In brief
- Confirmed structure and current price behavior answer different questions. The last confirmed BOS or ChoCh can remain bearish while newer candles are already breaching the opposite swing boundary.
- smartmoneyconcepts remains the only authority allowed to label BOS and ChoCh. LiquidMind adds deterministic intact, wick-breach, close-breach and sustained-breach states without inventing a second structure engine.
- The open candle may reveal a live wick or current price, but it can never confirm a close breach. Sustained breach requires two consecutive closed candles beyond the confirmed boundary.
- Every confirmed event now carries age in candles and elapsed minutes, so an agent can distinguish a one-candle-old signal from an event the market has spent days moving away from.
- Legacy fields remain for compatibility, but the agent-facing contract and prompts explicitly reserve confirmed BOS/ChoCh language for confirmed.last_event.
At 12:00 UTC, our BTCUSDT 1H market-structure tool returned this:
trend = Bearish (ChoCh Confirmed)
last_swing_high = 63,879.1
last_closed_price = 64,209.8The bearish ChoCh was real. It had been confirmed by the SMC library 21 closed candles earlier.
The price above the swing high was also real. In fact, the last three hourly candles had closed above it.
Nothing in the calculation was technically false. The output was still dangerous.
One field sounded like a current market verdict while another quietly showed that price had already moved through the opposite structural boundary. A language model could read the first line, anchor on bearish confirmed, and treat the newer price action as noise.
The bug was not in Smart Money Concepts. It was in the contract around them.
One response was describing two different moments
The previous tool mixed three questions:
- What was the latest structural event confirmed by the SMC engine?
- What is price doing now relative to the latest confirmed swings?
- Does that current behavior suggest a transition that the SMC engine has not confirmed yet?
Those questions operate on different clocks.
confirmed event current market
│ │
├── 21 closed candles ───────┤
│ │
bearish ChoCh 3 closes above swing highCollapsing both ends into trend made the older fact look newer than it was. The stronger the label sounded — BOS Continuation, ChoCh Confirmed — the easier it was for the model to ignore the transition happening at the right edge of the chart.
This is a recurring problem in agent systems: a value can be correct and still be badly named. When the consumer is a model rather than a human scanning a chart, semantic precision is part of correctness.
We did not replace the SMC engine
The tempting response would be to write a second BOS/ChoCh detector that reacts faster.
We explicitly did not do that.
LiquidMind already uses smartmoneyconcepts to calculate confirmed swing highs and lows, BOS and ChoCh. That library remains the single source of truth for those labels. Its parameters remain unchanged:
swings = smc.swing_highs_lows(
closed_candles,
swing_length=5,
)
events = smc.bos_choch(
closed_candles,
swings,
close_break=True,
)The newest open candle is removed before either calculation. A swing needs the full confirmation window of closed candles on both sides. Synthetic edge swings inserted by the package are filtered out, as described in our previous post about confirmed dealing ranges and multi-timeframe price location.
That delay is intentional. A confirmed structural event should not repaint just because the current candle moved.
But confirmation delay does not justify hiding what the market has done since the event. The correct architecture is not a faster competing SMC engine. It is a live descriptive layer above the confirmed one.
Three layers, one hierarchy of truth
The new response follows an explicit sequence:
smartmoneyconcepts
↓
confirmed event and swings
↓
closed candles + current open candle
↓
live boundary state
↓
provisional interpretation
↓
agent reasoningEach layer is allowed to say less than the layer above it, never more.
Confirmed: what SMC has actually classified
confirmed contains the last SMC event, confirmed swing boundaries, swing geometry and freshness:
{
"confirmed": {
"bias": "bearish",
"bias_basis": "last_confirmed_event",
"swing_geometry": "lower high, higher low",
"last_event": {
"type": "ChoCh",
"direction": "bearish",
"level": 63804.1,
"source_swing_timestamp": "2026-08-11T06:00:00",
"broken_timestamp": "2026-08-11T14:00:00"
},
"last_swing_high": {
"level": 63879.1,
"timestamp": "2026-08-12T02:00:00"
},
"last_swing_low": {
"level": 63570.1,
"timestamp": "2026-08-12T06:00:00"
},
"age": {
"candles_since_event": 21,
"minutes_since_event": 1260
},
"source": "smartmoneyconcepts",
"status": "confirmed"
}
}Only confirmed.last_event may be called BOS or ChoCh.
The word bias is deliberately qualified by bias_basis. In this example it means direction of the latest confirmed event. If no event exists, the fallback comes from confirmed swing geometry and says so explicitly. It is not a timeless market truth.
Live: what price is doing at the boundary
live compares two kinds of observations with the latest confirmed swing high and low:
- the latest closed candle and the consecutive closed candles before it;
- the current price and high/low of the newest open candle.
The distinction stays visible in the response:
{
"live": {
"last_closed_price": 64209.8,
"last_closed_candle_time": "2026-08-12T11:00:00",
"current_price": 64083.5,
"current_candle_time": "2026-08-12T12:00:00",
"current_candle_is_closed": false,
"current_price_source": "open_candle_close",
"sustained_breach_closes": 2,
"swing_high_relation": {
"level": 63879.1,
"state": "sustained_breach",
"distance_pct": 0.319979,
"consecutive_closes_beyond": 3
},
"swing_low_relation": {
"level": 63570.1,
"state": "intact",
"consecutive_closes_beyond": 0
}
}
}The open candle is useful for current_price, observed_extreme and wick detection. It is never counted as a close.
That one invariant prevents a fast intrabar move from becoming fake structural confirmation.
Four boundary states, no homemade BOS
For a confirmed swing high, the states are:
| State | Deterministic rule |
|---|---|
intact |
Price and observed highs remain below the boundary |
wick_breach |
A high trades above the boundary, but the latest closed candle does not close above it |
close_breach |
The latest closed candle is above the boundary, with only one consecutive close beyond it |
sustained_breach |
At least two consecutive closed candles are above the boundary |
For a swing low, every comparison is mirrored below the boundary.
These states describe evidence. They do not classify market structure.
wick above swing high ≠ bullish BOS
one close above swing high ≠ bullish ChoCh
two closes above swing high ≠ confirmed anything from SMCThe first two may be important. The third may be extremely important. None gives our code permission to reuse a label owned by the confirmed engine.
That naming boundary matters because two engines can otherwise produce an impossible conversation:
our code: bullish BOS
SMC: bearish BOSThe new output can express the same situation without contradiction:
SMC confirmed: bearish ChoCh
live: 3 closes above confirmed swing high
provisional: bullish sustained breach, confirmation pendingBoth observations survive. Their authority is different.
Provisional is a transition, not a prediction
The final deterministic layer converts boundary states into a concise transition state:
{
"provisional": {
"state": "bullish_sustained_breach",
"direction": "bullish",
"confirmation": "pending",
"reason": "At least two consecutive closed candles are above the latest confirmed swing high."
}
}The possible states include:
confirmed_structure_intact;bullish_boundary_test/bearish_boundary_test;bullish_structure_shift_candidate/bearish_structure_shift_candidate;bullish_sustained_breach/bearish_sustained_breach;two_sided_boundary_test;insufficient_confirmed_boundaries;no_pending_shiftafter the matching SMC event catches up.
Candidate does not mean forecast. Pending does not mean inevitable.
A wick can close back inside. One close can be reclaimed by the next candle. Even a sustained breach can persist for several candles before the library classifies a new structural event — or fail to become the event a trader expected.
The provisional layer exists so the agent must reason about the transition instead of pretending it is not there.
Freshness changes the meaning of the same label
Consider two identical strings:
Bearish BOS
Bearish BOSOne happened on the previous candle. The other happened 42 daily candles ago.
Without age, they look equal in a JSON response. In a trading decision, they are not remotely equal.
We now measure event freshness from the candle where the SMC break was confirmed to the latest closed candle:
{
"candles_since_event": 42,
"minutes_since_event": 60480,
"measured_from": "confirmed_event_broken_candle"
}candles_since_event is the stable cross-timeframe measure. minutes_since_event gives the model an intuitive elapsed duration when timestamps are available.
Age does not invalidate an event. It prevents an old event from impersonating a new one.
The live audit found the mirror image on 4H
We ran the production tool path against real Bybit BTCUSDT data for 1H, 4H and 1D. The result was more useful than a clean synthetic demo:
| Timeframe | Confirmed SMC state | Age | Live boundary state | Provisional |
|---|---|---|---|---|
| 1H | bearish ChoCh | 21 candles | 3 closes above swing high | bullish sustained breach, pending |
| 4H | bullish BOS | 30 candles | 12 closes below swing low | bearish sustained breach, pending |
| 1D | bearish BOS | 42 candles | both boundaries intact | confirmed structure intact |
The 4H result was the exact mirror of 1H. The tool still had a bullish confirmed BOS while price had accumulated twelve closes below the latest confirmed swing low.
Under the old schema, a model could flatten the three timeframes into:
1H bearish
4H bullish
1D bearishThat loses the most important information in the entire observation: both intraday structures were in transition, in opposite directions to their last confirmed events, while the daily boundary remained intact.
The new contract does not resolve that tension for the model. It makes the tension impossible to miss.
Backward compatibility without semantic endorsement
We could have deleted trend, structure, last_swing_high, last_swing_low and last_choch_bos_event immediately.
We kept them.
Internal ranking, persistence, historical traces and unknown external consumers may still rely on the legacy shape. Removing fields during a structural rewrite would combine two risks: changing meaning and breaking transport.
The response therefore carries both contracts for now:
new code: confirmed / live / provisional
legacy code: trend / structure / last_swing_* / last_choch_bos_eventCompatibility is not an endorsement of the old semantics. The tool description explicitly calls those fields temporary aliases, and every affected system prompt tells the model to use the layered contract.
At the model boundary, a serializer removes internal snapshot hashes, row positions, event IDs and swing IDs. The model receives levels, timestamps, age and state — the evidence needed for reasoning, not implementation provenance that consumes context without changing a decision.
The prompt rule is intentionally short
A better schema should carry most of the semantic load. For a trading-critical distinction, we still did not want correctness to depend on the model noticing one paragraph in a tool description.
Entry, bias, position-management, personal-chat, terminal and judge prompts now share the same compact rule:
Treat confirmed as the authoritative confirmed SMC structure.
Only confirmed.last_event may be called BOS or ChoCh.
Live describes current behavior around confirmed swing boundaries.
Provisional describes a potential shift awaiting SMC confirmation.
When confirmed and provisional differ, reason about the transition.This is one of the rare places where a small amount of duplication is useful. The consequence of misreading the distinction is not awkward prose. It can change an entry score, an invalidation claim or a position-management recommendation.
The tests target semantic failures, not just arithmetic
The unit suite covers the obvious progression in both directions:
intact
→ wick breach
→ one closed breach
→ two closed breaches
→ matching event confirmed by SMCIt also asserts negative behavior:
- a wick breach contains no BOS/ChoCh claim;
- an open candle cannot increment consecutive closed breaches;
- serializer output contains no internal event or swing IDs;
- tool descriptions reserve BOS/ChoCh for
confirmed.last_event; - every live agent prompt contains the hierarchy rule;
- legacy fields remain available.
The focused suite finished with 57 passing tests. The standalone audit uses the same ToolWrapper.get_market_structure path as production rather than duplicating calculations in a developer script.
That last detail matters. A demo script with copied logic tends to prove that the demo works. Calling the production path proves what the agent actually receives.
What this does not solve
The new layers make the state legible. They do not make market structure instantaneous or universally unambiguous.
The system still inherits the confirmation behavior of the installed SMC package. A different package version could alter event timing. The newest open candle can move sharply between tool calls. A sustained boundary breach is evidence of acceptance beyond a level, not a guarantee that the next confirmed event will carry the direction a trader expects.
There is also a deliberate asymmetry: the deterministic layer can say that price crossed a confirmed boundary, but it cannot explain why. Liquidity, displacement, order flow, session context and opposing POIs remain separate tools. A boundary breach should not become a container for every market narrative around it.
This is the same separation we wanted from price location: premium and discount describe where a price sits, not what the trade must do. Here, live structure describes what price has done around a confirmed boundary, not what SMC must eventually call it.
The invariant we wanted
The model should never have to reverse-engineer structural state from a stale label and a list of prices.
It should receive the hierarchy directly:
what the structure engine confirmed
what price has done since
what transition remains unconfirmed
how old the confirmed event isThat is the broader engineering lesson from this change. In an agentic system, confirmed history and current observation should not compete for one field. Put them in separate layers, name their authority, and make uncertainty explicit.
The result is not a smarter BOS detector.
It is a contract that stops an old BOS from speaking for the present market.

Public pen name of LiquidMind's founder and builder. Writing first-hand engineering notes and transparent performance reviews from the system's internal ledger.
Related Posts
Stay Liquid
New posts on transparency, engineering, and the LiquidMind thesis — no noise.
Loading discussion…