← Academy home
Deep Dive · Sentinel · Part A of D

Anatomy of the Night Watchman

A line-by-line tour of sentinel.py — every instrument in the watch-house, exactly as the code computes it. By the end you'll be able to predict when Sentinel fires before it does.

A.1The watch-house, from above

The docstring says "five lookouts." The code says something more precise: every bar walks through two gatekeepers who can cancel the watch entirely, then faces seven voters who must be unanimous, with one optional adviser (the ML gate, currently off). Any voter dissents → no trade, and Sentinel writes down exactly who dissented.

one closed bar arrives GATEKEEPER 1 · fog check (choppy filter) SSL width < 0.5 × smoothed ATR → "market is choppy — no trade" GATEKEEPER 2 · news blackout high-impact event window → "trade suppressed to avoid spike risk" SEVEN VOTERS — unanimity required, per direction ① Heiken Ashi colour ② SSL Channel side ③ NonLagDot side ④ 50 SMA slope ⑤ WAE active+rising ⑥ WAE direction ⑦ MACD signal bar (8/17/9) 7/7 agree → SIGNAL + a written reason naming every instrument's state, + confidence any dissent → HOLD "No full confluence for BUY — missing: sma50_rising, …"
The whole strategy in one picture. Everything below is a close-up of one box.

A.2The two gatekeepers

A.2.1The fog check — is_choppy

The watch is cancelled in fog. Sentinel's fog gauge compares two widths: the SSL channel's width (the gap between the 10-bar average of highs and of lows — how much room the recent road occupies) against the smoothed ATR (the sea state, averaged again over 20 bars to steady it).

Rule ssl_width < 0.5 × ATR(14) smoothed over 20 bars → choppy → no trade, full stop

Meaning when the channel is narrower than half a typical wave, price is going nowhere while still moving — the definition of chop. Trend-following in fog is how watchmen fall off cliffs.

Reason string "Market is choppy (SSL width < ATR threshold) — no trade"

A.2.2The news blackout

An injected flag: when the pipeline marks a bar as inside a high-impact news window (news_blackout=True), Sentinel refuses it before any lookout even wakes up. In our offline backtests this flag was never injected — worth remembering when comparing backtest trade counts to live ones.

A.3The seven voters, one by one

A.3.1Heiken Ashi colour — the noise-cancelled candle

From Lesson 6: candles averaged with their neighbours. The code builds them exactly like this — the HA close is the average of the bar's four prices, and each HA open is the midpoint of the previous HA candle's body, which is what chains them into smooth runs:

# sentinel.py — _heiken_ashi() ha_close = (open + high + low + close) / 4 ha_open = (previous ha_open + previous ha_close) / 2 vote: bullish if ha_close > ha_open # the candle's colour

One vote, one question: is the smoothed candle blue or red?

A.3.2SSL Channel side — the traffic light

Two 10-bar averages: one of highs, one of lows. Close above the high-average = green light; below = red. The code carries a battle scar worth reading — a comment explaining that an earlier version compared against the wrong line, which made the check "almost always true in both regimes." Someone caught a real bug here and documented it. That's the standard the rest of the system should meet.

Vote close > SMA10(highs) → bullish · below → bearish

Also feeds the channel width used by the fog check — one instrument, two jobs

A.3.3NonLagDot side — the scout

The zero-lag trick from the code: take a 30-bar EMA, then an EMA of that EMA, and use 2×EMA − EMA(EMA). The second smoothing estimates the lag, and subtracting it pushes the line back toward the present — a scout that reports turns earlier than a plain average would.

Vote close > nonlag_dot → bullish

Analogy check the scout is fast because it extrapolates — which is also why it alone is never trusted; it's one voice of seven

A.3.450 SMA slope — the tanker's rudder

Not "is price above the 50 SMA" (that's computed but not a voting condition) — the vote is whether the tanker itself is turning: the SMA's value now versus 3 bars ago.

# sentinel.py — _sma50() sma50_slope = sma50 − sma50 three bars ago vote: bullish if slope > 0 # the tanker is actually turning up

A.3.5Waddah Attar active + rising — the seismograph must shake

The energy check. The code computes a fast MACD (20/40), takes how much the MACD line moved this bar, and multiplies by the sensitivity (150) — that's the "explosion." It must beat the dead zone, and be larger than the previous bar's Bollinger width ("rising").

# sentinel.py — _waddah_attar_explosion() explosion = (macd_line − macd_line_prev) × 150 dead_zone = 30 pips × pip_value # ← note pip_value! vote ⑤: |explosion| > dead_zone AND |explosion| > previous BB width vote ⑥: sign of explosion matches trade direction
Honest findings — the seismograph needs a service check

① A documented interim fix. A comment dated 2026-06 explains the dead zone was once scaled by sensitivity twice, making it ~11× larger than any explosion ever observed — Sentinel could literally never trade. The fix works, but the comment itself warns: "not yet a faithful replica of the MT5 Waddah Attar indicator — must verify the exact formula against the chart." That verification never happened. It's now on our list.

② The "rising" test is unusual. It compares the explosion against the previous Bollinger band width — plausible for this indicator family, but the classic WAE compares explosion against its own previous value. Verify which the original Papingo template intended.

③ The pip_value trap — and a lead on the USDJPY mystery. The dead zone is 30 × pip_value. Defaults set pip_value = 0.0001 — correct for EURUSD, wrong for JPY pairs (a JPY pip is 0.01). On USDJPY with defaults, the dead zone is ~100× too small relative to JPY price scale — meaning the "must shake" check was effectively always passing in Proving Ground Rounds 1–3. Sentinel's famous USDJPY edge was measured with one lookout half-asleep. (Round 4's tuned config and the roster set pip_value: 0.01, which is why they behave differently.) Part B will dissect exactly what this changes.

A.3.6WAE direction — the same needle, read for sign

Vote ⑥ is the seismograph's direction: explosion positive = bullish energy, negative = bearish. Two votes from one instrument — energy exists, and energy points your way.

A.3.7MACD signal bar — the gap-meter, Papingo settings

The classic gap-meter from Lesson 6, at the original template's settings (8/17/9 — faster than the textbook 12/26/9). Vote: histogram above zero for BUY, below for SELL.

A.4The optional adviser — the ML gate

If an ml_confidence column is present and ml_min_confidence is set above zero, low-confidence bars are suppressed even when all seven voters agree. Currently disabled (threshold 0.0) — in backtests and in the roster config the adviser sits silent in the corner. The final confidence score you see on a signal is the vote share × the adviser's opinion; at full confluence with the adviser off, it's simply 1.0.

A.5Why unanimity makes it a specialist

Seven simultaneous yeses is a hard bar. If the voters were independent coin flips, full agreement would happen about once in 128 bars — but they're not independent: in a clean trend, most of them align for long stretches, and in chop they scatter. Unanimity is therefore a trend detector with a built-in silence mode — exactly why Sentinel goes quiet for days (a feature you learned to respect in Lesson 7) and why its trades cluster inside sustained moves.

⚙ The watch-house lab — you are the confluence

Set each instrument's state and watch the verdict. Try getting six of seven — then notice what the reason string tells you.

This is the exact logic of _evaluate_bar(): gatekeepers first, then unanimity per direction, then the written reason. Nothing is hidden.

A.6The reason strings — a watchman who writes reports

Every fired signal produces prose like this (generated by _build_reason()):

BUY signal — Heiken Ashi candle is blue (bullish), price is above SSL Channel (bullish), NonLagDot is bullish, 50 SMA is rising, Waddah Attar Explosion is active and rising, MACD Signal Bar confirms buy momentum. ATR=0.14200. ML confidence=1.00. All Sentinel conditions satisfied.

And every silent bar explains its silence: "No full confluence for BUY — missing: sma50_rising, wae_active_and_rising." When the dashboard's journal fills up in the forward campaign, these strings become your Lesson 12 raw material — the machine's half of every journal entry, pre-written.

A.7Part A verdict — and the fine-tune list it produces

What Part A established

Architecture: sound. Two vetoes + seven-vote unanimity + written reasons is a genuinely well-designed conservative trend system — the acceptance-gate philosophy, inside a strategy.

Open items for Parts B–C: ① verify the WAE formula against the real MT5 indicator (you can eyeball this on a USDJPY chart); ② settle the "rising" definition; ③ quantify the pip_value effect on the USDJPY results — was the edge measured with six lookouts or seven?; ④ the strategy's native exit (HA reversal beyond NonLagDot, ATR trail) is not what our engine simulates (fixed SL/TP) — measure what that changes.

One config note already actionable: the roster correctly sets pip_value: 0.01 for USDJPY. Any future JPY deployment must do the same — this should eventually be automatic, derived from the symbol.