An ICT/SMC model, ported to Pine and checked bar by bar

Seven confluence modules feeding an ordered sweep → market-structure-shift → break-of-structure chain, with premium/discount gating and a daily-led higher-timeframe bias. Written twice, on purpose, so the two versions could be made to argue.

Built as
Reference implementation and public dashboard
Written in
Pine Script v6 and JavaScript
Verified over
18,048 fifteen-minute bars
Status
Live, with one open disagreement

The brief this answers

ICT and smart-money briefs arrive the same way: a chart with liquidity sweeps circled, a structure break marked, and a note saying "only take the entry when all of this lines up." The words are shared vocabulary. The definitions are not.

Your swing high is not the textbook swing high. Your sweep has to take out a level that mattered, not merely a wick from Tuesday. Whether a session counts before it has closed is a real decision with a real effect on the signal count, and nobody writes it down.

So the first deliverable is never code. It is the set of definitions, written out, that the code will be held to.

18,048

Fifteen-minute evaluations on BTC, 1 February to 8 August 2026 — the sample the model was measured against.

3 of those

Reached ENTRY READY, all seven modules green. 0.017% of evaluations. A confluence filter that fires often is not a confluence filter.

The part that goes wrong quietly

A break of structure needs a reference level to break. The obvious implementation grabs the most recent opposing pivot and checks whether price has closed beyond it.

That version fires almost every time. The pivot it picks has usually already been closed through before the structure shift even happened, so the break condition is satisfied on the very next bar, trivially. The chart fills with confirmations. The backtest looks tremendous. The rule has stopped meaning anything.

The fix is a staleness rule: the reference must be the most recent opposing pivot at or before the structure shift with no bar after it closing beyond it. If every candidate is stale, the break waits rather than firing. Seven bars in the replay flipped from a pass to a wait — and those seven were the tell that the original was wrong.

The rule both implementations were held to
// reference for a break of structure
ref = most recent opposing pivot p where
        p.time <= mss.time
    and no bar after p closes beyond p
    and no bar at-or-before mss.time closes beyond p

// if every candidate is stale
then break waits. it does not fire.

// staleness and scan windows both
// partition at the structure-shift cutoff,
// not at the current bar.

Stated as a rule rather than as source, because this is the artefact that mattered: both implementations were written against this text, separately.

Writing it twice

The Pine indicator is one implementation. A pure JavaScript engine — seven modules, closed candles only, ATR-scaled thresholds, no chart state — is the other. Neither was derived from the other; both were written from the definitions above.

A replay harness runs the engine over historical candles and dumps a per-bar status CSV. Pine logs the same columns from the chart. Then the two files get diffed by timestamp, and every row where they disagree has to be explained before the work is done.

This is the part clients rarely get, and it is the only reason I can tell you what the script does rather than what it was intended to do.

Per-bar row, emitted identically by both sides
time,overall,direction,bias,liquidity,
mss,bos,fvg_ob,volume,four_h

overall     0-3
direction   1 / -1 / 0
modules     1 / 0 / -1

# diff by timestamp_ms, no shift.
# canonical parity timeframe is 5m —
# a 15m chart export silently feeds
# 15m bars into the 5m leg.

What the diff said

Two forced-direction runs over a five-minute chart. The higher-timeframe leg agreed on every single bar in both runs, which is the leg most ports get wrong.

The residual disagreements are not noise I waved through. The largest group traces to a one-bar boundary definition — a Pine row at time T includes the five-minute bar opening at T, while an engine row keyed T is closed through T — so both sides fire on the same break and land on adjacent keys.

One disagreement is still open: twelve bars where the chains match exactly but the zone layer disagrees. It is named, bounded, and on the list. I would rather show you that than a table of green ticks.

Pine indicator against the reference engine, five-minute chart
RunBar-exactOverall + direction4H leg
Forced short94.3%98.2%0 divergent
Forced long92.4%96.6%0 divergent
Open item12 barszone layer onlychains agree

Timeframe legs evaluated per run: 17 daily, 97 four-hour, 1,537 fifteen-minute, 4,609 five-minute.

What it looks like running

The dashboard is public and keyless. It scans a live symbol list, and most of the time every row it shows you is a refusal — which is the honest resting state of a confluence tool and the thing a screenshot of an all-green table would hide.

At any given moment roughly half a symbol across the whole list sits at SETUP FORMING.

The ICT/SMC confluence dashboard on BCHUSDT reading SETUP FORMING, long. Higher-timeframe bias, liquidity, fifteen-minute market structure shift, five-minute break of structure, and fair value gap with order block all pass. Volume fails on below-average breakout participation. The four-hour framework is waiting because price sits in the four-hour premium.

BCHUSDT at SETUP FORMING. Five checks pass, volume fails on weak breakout participation, and the 4H framework waits because price is in the premium — so nothing fires. See it live, or watch a setup read end to end.

Back to work

Have a model that needs porting faithfully?

Send the chart with your marks on it. I will tell you which of your definitions are already mechanical, which ones need a decision from you before anyone can code them, and what the verification would cost on top of the build.