Sentinel Suite · NinjaTrader 8 · living documentDocs home →
The authoritative reference for the syntax and nomenclature of every Sentinel dataset — the training corpus the Council learns from. If you are about to read, write, ingest, or fit against a .jsonl under Sentinel\Excursions\, this is the map.
This doc is generated from live source (BarsTypes\SentinelFlux_v1_0_0.cs, AddOns\SentinelCore.SystemBuilder.cs VoterCatalog, Indicators\Council_v1_0_0.cs, and Sentinel\Lab\). When those change, regenerate. Related: ML spec · Flux bars spec · Process Atlas.
Everything keys off a scope. A scope is one chart's worth of decision context — exactly the coordinate a model is defined over.
GC . 212201v12x48 @ STB24FCA
─┬─ ─────┬────── ───┬────
inst bartag lane
└──────── scope ──────────┘ scope = inst . bartag [ @lane ]
| Token | Example | Meaning |
|---|---|---|
| inst | GC | Master instrument (the NT MasterInstrument.Name). |
| bartag | 212201v12x48 | The bar construction = <BarsPeriodTypeId>v<Value>x<Value2>. |
| @lane | @STB24FCA | Optional human-named per-chart lane. Absent ⇒ bare scope. Sanitized to letters/digits. |
Why bartag and not "5-minute": bar granularity determines label optimism. A Renko-labelled row and a minute-labelled row do not describe the same world, so the Lab never pools bartypes — the bartag is a hard partition key, not a cosmetic tag.
Two IDs stitch a single fire's records together across files:
| Key | Example | Grammar | Role | |
|---|---|---|---|---|
| episodeId | GC-20260715-0053 | inst-yyyyMMdd-#### | Join key across corpus row ⇄ tick sidecar ⇄ Ledger. | |
| fireId | 20260715T175912741_GC_L_29 | `ts(ms)_inst_<L\ | S>_<sessionSeq>` | Unique per fire; also the tick-sidecar filename stem. L/S = long/short. |
The 212200–212299 block is reserved for the Sentinel bar-type family (declared in every Sentinel bars header). Those three are the ones that build a real corpus:
| Id | Tool | vNxM decode | Notes |
|---|---|---|---|
| 212201 | SentinelTBars | v(SS/2)x(SS×2) — v6x24 = Speed Setting 12 | The adaptive HA/Renko brick engine. Also publishes BrickState → the BRK voter. |
| 212202 | SentinelTbarsCount | same speed pair (v6x24) | Plain brick + ticks-to-next-brick HUD. |
| 212203 | SentinelFlux | v8 = imbalance threshold scale | Order-flow imbalance bars (López de Prado × TBars). Publishes FluxState → the FLUX voter. |
| 212204+ | (reserved) | — | Future Sentinel bars. |
Other IDs seen in older corpus files (present for completeness — these are not the Sentinel family and several are frozen/legacy):
| Id | Tool | Source |
|---|---|---|
0 | Tick (native NT) | 0v150x1 = a 150-tick chart (Value=ticks, Value2=1). |
2016 | ERP_Type_Bars | BarsTypes\ERP_Type_Bars.cs |
54321 | EdsRetraceBarsV2 | BarsTypes\EdsRetraceBarsV2.cs |
2018 | ReversalRenko | BarsTypes\ReversalRenko.cs |
2085 | RenkoATR | BarsTypes\RenkoATRBarsType.cs |
69696 / 69697 | TBarsElse / TbarsCount (legacy) | pre-Sentinel TBars lineage |
69700–69799 | TBarsNext family | reserved block |
212121–212123 | TbarsSudo v1/2/3 | frozen checkpoints |
All live under Sentinel\Excursions\. One writer, one schema per folder — the Council recorder (SentinelExcursionRecorder_v2_0_0) owns the council\ tree; the Candidate recorder (SentinelCandidateRecorder_v1_0_0) owns the candidates\ tree; legacy per-sensor recorders default OFF.
| Path | Family | Filename grammar | Written when | |
|---|---|---|---|---|
council\1.4\ | Labeled COUNCIL corpus (schema 1.4; frozen 1.3 beside it) | <sessionStart>__<inst>__<bartag[@lane]>.jsonl | One row per Council fire, streamed on 60-min window completion (endReason="window"). | |
council\ticks\ | Raw-tick path (ctick.2; ctick.1 frozen) | <fireId>.jsonl = `<ts_ms>_<inst>_<L\ | S>_<seq>.jsonl` | One file per fire: header line + tick tape, fire→resolution. |
candidates\cand.1\ | CLOCK-EDGE candidate corpus (schema cand.1, signal CONT) — see §5b | <sessionStart>__<inst>__<bartag[@lane]>.jsonl | One row per brick close (every-brick continuation candidate), streamed on 60-min window completion. | |
candidates\ticks\ | Candidate raw-tick path (ctick.2, kind candidate_tickpath) | <fireId>.jsonl | One file per candidate: header + tick tape. | |
Excursions\ root | Legacy per-sensor baselines | <sessionStart>__<inst>__<bartag>.jsonl | CBRK (CompressionBase) first-touch baseline; default off elsewhere. |
⚠ Never pool
council\andcandidates\. They answer different questions: the Council corpus is a fusion-gated sample ("the Council chose to fire"); the candidate corpus is the unfiltered clock-native population ("every brick close") — the only one that can measure whether the CLOCK itself carries a base rate. Thesignalfield (COUNCILvsCONT) andsource(councilvscandidatein the DB) keep them apart.
Filename anatomy (corpus): 20260715T090945__GC__212201v12x48@STB24FCA.jsonl
20260715T090945 — when the recorder instance loaded (session), yyyyMMddThhmmss. Not per-row.__ — double-underscore delimiter.212201v12x48@STB24FCA — the bartag, carrying @lane when the chart is laned..jsonl — one JSON object per line.One row = one Council fire and everything that happened to it. Fields group into four blocks.
| Field | Example | Meaning |
|---|---|---|
schema | "1.4" | Row schema version. 1.4 = 1.3 + the provenance block (recVer/coreVer/barLabel). 1.4 rows live in council\1.4\; the frozen pre-provenance 1.3 corpus stays in council\1.3\. Never pool schemas across a fit. |
recVer / coreVer / cnclVer | "2.1.5" / "1.36.0" / "1.8.0" | PROVENANCE (schema ≥ 1.4). Which recorder + SentinelCore + Council LOGIC wrote the row, so a logic change can't pool old/new rows invisibly. cnclVer is finer than coreVer (it catches a Council-only change that didn't bump Core). A null/absent coreVer = pre-provenance (old logic); a null cnclVer on a 1.4 row = pre-v2.1.5. |
barLabel | "SentinelFlux 8" | Human bartag (schema ≥ 1.4) — the display name of bartype, denormalized for readers. Machine key stays bartype; see §2. |
kind | "excursion" | Discriminator (ingesters skip non-excursion lines). |
signal | "COUNCIL" | Which decision source emitted it. The Lab fits only COUNCIL rows. |
inst / bartype | GC / 212201v12x48@STB24FCA | Partition keys (bartype carries @lane). The machine key — never changed; render for humans via SentinelCore.FriendlyBartag/FriendlyScope → barLabel. |
episodeId | GC-20260715-0053 | Join key to the Ledger. NB: per-episode, not per-fire (a fire↔sidecar 1:1 join uses inst+fireTime+firePx+dir). |
fireTime / firePx | 2026-…T16:25:21Z / 4043 | Entry timestamp (UTC) + entry price. ⚠ Realtime-only rule (§9): a fireTime far in the past = a REPLAY leak — the corpus_probe reports these as stale_dated_rows. |
| Field | Example | Meaning | ||
|---|---|---|---|---|
dir | -1 | Side: +1 long, -1 short, 0 flat. | ||
conviction | 0.5976 | Signed-agreement magnitude, 0–1. `= | netScore | / denom`. |
convBucket | "MID" | LOW / MID / HIGH. | ||
sizeMult | 0.5079 | Context-damped size multiplier, 0–1. 0 = vetoed / below floor. | ||
voters / agree / disagree | 10 / 4 / 2 | Roster tally. | ||
netScore | -2.45 | Σ(voteᵢ × wᵢ) — the raw fusion score. | ||
activeW | 4.65 | The kind-aware denominator conviction was divided by. | ||
votes{} | {EYE:0, TRND:-1, …} | Per-voter direction -1/0/+1. The Phase-2 feature vector. | ||
voteW{} | {EYE:1.4, CCI:1.2, …} | Per-voter weight at fire (Roster.conf / F6 override of the catalog default). | ||
reasons | EYE~ TRND▼ … denom 4.1/7.8 | Human audit string (see §6). |
| Field | Meaning |
|---|---|
regime / adx | Trend-vs-chop label + ADX value. |
clockPhase | 0 closed · 1 opendrive · 2 midday · 3 close. |
rvol | Time-normalized relative volume. |
mtfBias | Higher-timeframe ladder consensus -1/0/+1. |
levelInPath / levelName | A structural level sits in the trade's path (headwind). |
eyeHad / eyeScore / eyeDir / eyeAligned | Eye-specific legacy fields (may be null). |
| Field | Meaning |
|---|---|
maxMFE / maxMAE | Max favorable / adverse excursion over the window. |
barsToMFE / barsToMAE · msToMFE / msToMAE | When those maxima occurred. |
mfe1 mae1 … mfe60 mae60 | MFE/MAE at the 1 / 5 / 15 / 60-min milestones. |
barrierTicks | The ATR-scaled ±1R barrier used for first-touch. |
barsToTargetR / barsToStopR | Bars to touch +1R / -1R (-1 = never). |
firstTouch | THE label: +1 target hit first · -1 stop first · 0 neither by window end. |
ftAmbig | Both barriers touched the same bar (ambiguous — resolved pessimistically to loss). |
endReason / endTime | "window" = clean 60-min completion. |
ctick.2)A two-part file, one per fire. Line 1 = header (join keys + tick-resolution outcome), lines 2…N = the path. (ctick.2 = ctick.1 + the recVer/coreVer/barLabel provenance block, matching the row schema-1.4 bump; ctick.1 sidecars stay valid and load unchanged.)
Header fields of note: recVer/coreVer/barLabel (provenance, ctick.2) · fireId · episodeId · scope · firePx · conviction/sizeMult · barrierTicks · maxFavTicks/maxAdvTicks · msToMaxFav/msToMaxAdv · msToTargetR/msToStopR · firstTouchTick / ftAmbigTick · ticks (count) · trunc (buffer overflowed).
Path rows: {"ms": <ms since fireTime>, "px": <last-trade price>}.
The row's
firstTouch(bar resolution) and the sidecar'sfirstTouchTick(tick resolution) are the same label at two fidelities. The Lab prefersfirstTouch; the tick path is for grading fill quality andmsToFirstTouch.
cand.1)The second oven (2026-07-17; hypothesis in SENTINEL_CLOCK_EDGE_HYPOTHESIS.md). Tests whether the sampling clock, not the fused voters, carries the edge. SentinelCandidateRecorder_v1_0_0 fires one candidate per brick close — direction = the brick's own direction (the simplest CONTINUATION primitive), no gate, no fusion. Same fidelity spine as the Council recorder (realtime-gated, provenance-stamped, tick sidecar, window-streamed). Derive rule × regime × exit labels late; de-overlap in the Lab (adjacent candidates autocorrelate — use effective-N, not raw count).
Row = schema cand.1, kind:"candidate", signal:"CONT". Shares the Council row's IDENTITY block (recVer/coreVer/barLabel/inst/bartype/fireTime/firePx) and the entire OUTCOME/label block (§4d: maxMFE/maxMAE, milestones, barrierTicks, barsToTargetR/StopR, firstTouch, endReason). It has no vote vector / conviction / episodeId (there is no fusion). It ADDS:
| Field | Meaning |
|---|---|
runLength | Consecutive same-direction bricks incl. this one → exhaustion is a retroactive slice (high runLength). |
regime / adx | Computed locally by the recorder. |
rvol / volZ / climax / dryUp | Participation seam context at fire (null if Participation not on the chart). |
clockPhase / minsToClose | Clock seam (instrument-keyed). |
mtfBias | MTF ladder consensus -1/0/+1. |
fluxDir / fluxPressure / fluxDiverg | Flux seam (present on Flux charts — the Flux bars type publishes it; null/0 on TBars). |
All context is consulted, never fused — each field is a Lab-side retroactive filter, never a hardcoded gate.
Sidecar = candidates\ticks\<fireId>.jsonl, kind:"candidate_tickpath" (schema ctick.2) — same shape as §5 but carries runLength and no conviction/episodeId. Row↔sidecar join on inst+bartype+fireTime (exactly one brick closes per bar time). See §9b for the DB columns; the Lab reads source='candidate'.
reasons audit stringBuilt in Council_v1_0_0.BuildReasons(). Example:
EYE~ TRND▼ CCI▼ ADX▼ ENV▼ BRK▲ CMP~ IMKT▲ WAE~ GREV~ STF▼* · clk:Midday · vol×1.3 · roster 10/10 ?STF · denom 4.1/7.8
| Token | Meaning | ||
|---|---|---|---|
TAG▲ / TAG▼ / TAG~ | Voter direction: bullish (Dir>0) / bearish (Dir<0) / neutral (Dir=0). | ||
TAG* | Heard but NOT counted — w=0 explorer, or undeclared (drift). | ||
· clk:<phase> | Clock phase (Closed/OpenDrive/Midday/Close). | ||
· vol×N | RVOL size multiplier. | ||
· vs-MTF | Higher-timeframe ladder disagrees with the side. | ||
· into <level> | A structural level is in the path. | ||
· chop N | STF chop reading triggered the chop veto. | ||
· in-value · · hi-vol | In value-area · high-volatility regime. | ||
· +flowDiv / -flowDiv · · flux:absorb / · flux▲/▼ | Flow-divergence / Flux absorption or flow direction. | ||
· roster P/D | Present of Declared voters. | ||
⚠TAG,… | Declared but MISSING (crashed/stale/not loaded). | ||
?TAG,… | Present but UNEXPECTED (loaded, not in Roster.conf → excluded from fusion). | ||
· denom X/Y | Effective kind-aware denominator X vs static declared weight Y. `conviction = | netScore | /X`. |
The source of truth is SentinelCore.SystemBuilder.VoterCatalog, which the Council emits on load to the shared file Sentinel\Models\catalog.conf (tag|role|kind|defWeight|display|seam, one line each). The Python Lab reads that file, so both sides build the fit's feature columns from the same map — no more hardcoded voter list drifting behind the Council. Weights below are catalog DEFAULTS — a scope's Roster.conf (and the Council's F6) override them (e.g. live GC runs CCI at 1.2, not 0.8), and the Lab's fit baseline uses the recorded voteW{} per scope, not these defaults. KIND sets how a voter counts toward the conviction denominator: STATE always dilutes; TRIGGER dilutes only when it fired or is absent (a quiet trigger is absence of evidence, not evidence against).
Roster.conf surface)| Tag | Display | Kind | Def w | Seam | Note |
|---|---|---|---|---|---|
EYE | Eye | Trigger | 1.4 | EyeVerdict | GodTrades qualifier — strongest single voice. |
TRND | SentinelTrend | State | 1.0 | TrendState | Structural trailing-line trend. |
CCI | Woodies CCI | State | 0.8 | CciState | Woodies CCI bias (×1.5 strong). |
ADX | ADX Pro | State | 0.6 | AdxState | Regime/strength confirmer. |
ENV | Vol Envelope | State | 0.6 | EnvelopeState | Trend regime + drives the squeeze modulator. |
BRK | Brick | State | 0.5 | BrickState | Adaptive brick micro-trend (from the SentinelTBars bar type). |
CMP | Compression | Trigger | 0.7 | CompressionState | Held breakout off a compression base. |
IMKT | Intermarket | State | 0.6 | IntermarketState | Correlated-instrument lean (instrument-keyed). |
WAE | WAE | Trigger | 0.7 | WaeState | Waddah-Attar momentum-explosion breakout. |
GREV | God Reversal | Trigger | 0.9 | GodReversalState | Candle-grammar reversal (mean-reversion voice). |
STF | Stoch Filter | State | 0.0 | StfState | Gaussian midline slope. w=0 = exploration; drives the chop veto. |
FLOW | Flow | State | 0.9 | FlowState | Tick-rule CVD regime — not price-derived. |
STRC | Structure | State | 0.7 | StructureState | Swing HH/HL·LH/LL structure. |
EXH | Exhaustion | Trigger | 0.5 | ExhaustionState | Leledc reversal (mean-reversion). |
AVMA | ADXVMA | State | 0.6 | AdxvmaState | ADX-vol adaptive-MA trinary trend. |
SPRT | SuperTrend | State | 0.7 | SuperTrendState | ATR-band trailing flip (always ±1). |
PSAR | Parabolic SAR | State | 0.5 | SarState | Wilder SAR trend/stop (always ±1). |
ZSC | Z-Score | Trigger | 0.4 | ZScoreState | (Close−SMA)/σ fade voice. |
ARCH | Trend Architect | State | 0.7 | TrendArchitectState | Composite PRISM trend + regime gate. |
VDYA | VIDYA | State | 0.5 | VidyaState | Chande-CMO adaptive-MA trend. |
HARM | Harmonic | Trigger | 0.4 | HarmonicState | XABCD pattern completions (reversal). |
FLUX | Flux | State | 0.7 | FluxState | Net order-flow direction of the imbalance bar (from the SentinelFlux bar type). |
Roster.conf — toggled via the Council's Consult* settings)| Tag | Display | Role | Seam | Note |
|---|---|---|---|---|
CLOCK | Clock | Modulator | ClockState | Session phase / mins-to-close (instrument-keyed). |
PARTIC | Participation | Modulator | ParticipationState | RVOL + climax/dry-up (scope-keyed — load on every Council chart). |
MTF | MTF | Modulator | MtfState | HTF consensus ladder (counter-HTF penalty). |
LOC | Location | Veto | LevelState | VWAP/PDH-PDL/OR/IB levels in the path. |
LIQ | Liquidity | Veto | LiquidityState | Order-flow absorption walls. |
train.py consumes itSentinel\Lab\train.py — the offline fitter. Nothing here touches bin\Custom; it reads the corpus and emits Sentinel\Model.candidate.conf (gated to Model.conf only with --promote).
python train.py --inst GC --bartype 212201v6x24 --barrier 20 --cost 1.5
Pipeline (dataset.py → labels.py → train.py):
load_jsonl(council\<schema>\) reads every row where kind=="excursion", then filters by inst + bartype. Never pools bartypes (§1). council_rows keeps signal=="COUNCIL".make_labels: prefers firstTouch (+1→WIN, -1→LOSS, 0→CENSORED); for old 1.2 rows falls back to the pessimistic mfeN/maeN milestone. Censored rows are dropped from the fit.uniqueness_weights (AFML ch. 4): two fires 90 s apart share most of their forward window and are not independent. effective_n = Σ(avg uniqueness) is the N you may quote.x_i = voteᵢ × dir: "did the voter agree with the taken side." Halves the feature space and makes a fitted coefficient directly comparable to WeightEye — it drops straight into Model.conf.conviction, so schema 1.2 is enough): Platt-scale conviction → P(win) under purged walk-forward CV; expectancy_floor = the lowest conviction whose calibrated win-prob clears breakeven after cost → the learned ConvictionFloor (replaces the hand-set 0.20). nan = "no conviction level pays" — a real answer.votes{} vector): the voter set is data-driven per fit — active_voter_tags admits the catalog voters this bartype actually records above a support threshold (max(30, 5% of vector rows)) with ≥1 non-neutral vote, in catalog order. Rosters range 2→18 voters across bartypes, so a fixed list would drop real voters (e.g. STF) or fit all-zero columns; under-supported and undeclared tags are reported, not silently dropped. Then ridge-logistic on the folded features + orthogonal axes (clockPhase, log_rvol, mtfBias×dir, levelInPath, activeW, voters); grid over C; OOF AUC vs the recorded-voteW baseline. Fitted w.TAG values rescaled to the current weight total (conviction is scale-invariant) → the learned weights.status=ok|reject (reject if no profitable floor, or the fit didn't beat the hand weights). --promote refuses to overwrite the live Model.conf on reject.| Field(s) | Consumed by |
|---|---|
inst, bartype | Partition (never pooled). |
firstTouch / mfeN,maeN,barrierTicks | Label (y). |
fireTime + resolution time | Uniqueness weight / purged CV windows. |
conviction (or recomputed netScore/activeW) | Phase 1 calibration + the floor. |
votes{}, dir | Phase 2 folded feature vector. |
clockPhase, rvol, mtfBias, levelInPath, activeW, voters | Phase 2 orthogonal-axis features. |
votes{} keys | Data-driven voter set (which w.TAG columns the fit builds). |
voteW{} | Per-scope baseline weight (median) + compared against the fitted w.TAG. |
.jsonl through PowerShell Get-Content/Set-Content — it double-encodes UTF-8 (▼→â–¼). The files are clean UTF-8; the mangling is a display artifact of the cp1252 console.coreVers (schema ≥ 1.4) — a different SentinelCore/recorder version = potentially different fusion logic. A null coreVer = pre-provenance 1.3; treat it as its own (older, unknown-logic) cohort.council\; legacy recorders stay OFF..jsonl row is only trustworthy when written in State.Realtime — replayed (historical) verdicts carry lookahead. ⚠ This is NOT self-enforcing: the schema has no realtime flag, so a replay leak (a fireTime far in the past) sits in the corpus looking identical to a live row. Verify with Lab\health\corpus_probe.py (stale_dated_rows), and window-scope the fit by fireTime — do not assume the folder is clean.corpus_probe.pyLab\health\corpus_probe.py (read-only, mirrors probe.py) is the "prove the recording" surface — it never opens the corpus at record time, so nothing else validates it. Run python corpus_probe.py (or --loop 300). It writes corpus_integrity / corpus_folder / corpus_events to sentinel.db (Grafana-readable) with: reconciliation (Ledger fires ↔ excursion rows ↔ tick sidecars, joined on inst+fireTime+firePx+dir; a recon_gap, not pass/fail) · schema hygiene (mixed-schema / unexpected-for-folder, rows_13 vs rows_14) · provenance coverage (prov_coverage_pct + distinct coreVers — a silent logic change shows as two versions in the pool) · silent-loss (trunc sidecars, malformed lines) · stale_dated_rows (the realtime-leak detector). First live run (2026-07-16) flagged ~7,671 stale-dated rows (~83% of the 1.3 corpus, oldest fireTime 2025-12-09) — a replay/backfill leak that had been invisible.
sentinel.db) columnsingest.py reads council\1.3\ + council\1.4\, candidates\cand.1\, and the ticks/council\ticks\/ candidates\ticks\ sidecars. The trades table carries a source discriminator (council | candidate | manual), provenance (rec_ver, core_ver, bar_label, scope) + the context that used to be dropped (regime, adx, conv_bucket, agree, disagree, voters, end_reason, milestone curves + barsTo* in one milestones_json blob). Candidate columns (source='candidate'; NULL for council/manual): run_length, rvol, vol_z, climax, dry_up, clock_phase, mins_to_close, mtf_bias, flux_dir, flux_pressure, flux_diverg. Candidate row↔sidecar reconcile on inst+bartype+entry_utc (mirrors the council row folder; no vote vector). Indexed on (inst,bartype), episode_id, src, schema, bartype, entry_utc. The ingester prints a drop tally by reason (silent loss made visible). train.py reads the JSONL directly, not the DB — the DB is the derived warehouse (see SENTINEL_DB_MIGRATION_SPEC).