Navigation
DashboardThree-Axis Probes › How features are built

opendeviationbar · feature-building · plain-English explainer

How These Features Are Built PLAIN-ENGLISH

Status as of 2026-08-10 — the counts below are out of date. The narrative on this page was last substantively edited 2026-06-26. Since then batch 6 shipped three further bar_close columns, all live in ClickHouse with COMMENTs: bar_hoeffding_phi_squared_midreturn_duration (#568, card 78), bar_vg_time_directed_clustering_meangap (#570, card 60) and bar_hvg_forward_visibility_horizon_mean (#579, card 59). So §7's “12 contributed columns” and §4's “latest: batch 5” both undercount. Canonical count is crates/opendeviationbar-core/data/feature_manifest.tomlbar_close (15), cross-checked by bar_close_schema_contract_four_sources_agree. Nothing else on this page has been altered — the promotion evidence for the 12 described columns stands as written. The badge on §6's Rotation Orthogonal Probe (NOT OPERATIONAL) also predates #676 “make the rotation and legacy probes operational”, which is merged; that one needs the author's confirmation rather than a mechanical edit.

A walkthrough, in my own words, of what I have been doing: how I find a candidate feature, how I decide it is worth keeping (genuinely new, not a re-labelled copy of something we already have), how I check that judgement holds up as the market changes, and how I turn a survivor into a real production column I can trust. Every claim links to the page that grounds it.

The one-sentence version. I screen candidate features on real market data to find the few that carry information no existing column already carries, I confirm that independence survives across many different market regimes, and then I build each survivor as a bit-exact copy of a trusted reference formula — so what runs in production is provably the same maths I validated.

1 · What I am actually trying to do

The strategy already has a large panel of feature columns. Adding another column is only worth it if it tells us something the panel does not already tell us. So the whole job is a filter: take a long list of candidate features, and keep only the ones that are (a) orthogonal — genuinely new information, (b) parameter-free — no hand-tuned magic numbers, and (c) agnostic — one well-defined algorithm two honest people would implement the same way. Those three tests are the Three-Axis Probes, and one gate combines them into a single keep / skip decision.

🎯 Orthogonal

Does it tell us something no existing column already does? Measured on real bars, across symbols and regimes.

🔢 Parameterless

No secret tuned numbers, no if window≥500 switch. Runs first because it is free.

🧬 Agnostic

One algorithm — two correct implementations converge to the same answer (~1e-9).

🚪 The Gate

keep = orthogonal ∧ parameterless ∧ agnostic. A tunable knob vetoes everything.

2 · How I decide a candidate is orthogonal — the metrics, divided

I never trust a feature because its paper says it is novel — I measure its redundancy on our own bars. The metrics fall into three buckets: the operational ones that actually decide the verdict today, a complementary second check that is built but not yet wired into the decision, and the ones that are only reported, never gating.

① Operational — how a candidate is actually screened OPERATIONAL

The real in-production screen: "is this candidate new information vs the columns we already ship?", scored on real bars, worst-case across market regimes. Important: crypto and forex were screened by two different probes — so I show them separately, exactly as they ran.

🪙 Crypto — orthogonality_probe.py (crypto-only since 2026-05-30). Scored across crypto symbols × all production thresholds × 21 two-month slices grouped into 7 regime epochs (a candidate must hold up in every epoch). The gate is Spearman |ρ| + h_norm — that is all the probe computes. Chatterjee ξ appears in the code only as a deferred extension marked "intentionally NOT wired in" — it is not computed on crypto.
MetricWhat it asksRole
Spearman |ρ|A rank / monotone copy of a shipped column?GATES. Declared Spearman ρ bands: ≤0.85 orthogonal · 0.85–0.95 WATCH · >0.95 BAN.
h_normDoes the column collapse to near-constant?GATES. <0.05 = constant-like → rejected.
Chatterjee ξNot computed by the crypto probe (deferred extension).
Promotion rule (crypto). The latest crypto batch (the 8 batch-5 features, PR #544) was promoted by the Median Rule: KEEP = median max|ρ| ≤ 0.85 AND pooled-pass ≥ 60% AND min-regime-pass ≥ 50%. The earlier bar_close features (petrosian/katz FD, dispersion entropy, CECP velocity) passed an older, stricter harness that additionally gated on R² ≤ 0.85 (panel / leave-out reconstructability) — a tougher bar the named probe does not carry.
💱 Forex — multislice_orthogonality_probe.py (a separate, two-stage pipeline). The crypto probe does not cover forex, so the 13 forex features were screened by a different tool across 4 FX / metal symbols × 1–50 dbps × event episodes + intraday sessions (Asian / London-NY).
StageWhat it doesRole
1 · Spearman screenSame ρ bands as crypto (BAN >0.95 · WATCH 0.85–0.95) + h_norm / degeneracyShortlists survivors.
2 · Chatterjee ξ keeper gateAsymmetric ξ vs production, per-symbol median, with cross-symbol replicationGATES. ξ > 0.50 per-symbol-median + ≥2-symbol replication → absorb/drop; else independent. implementation_ready flips true only after ξ passes.
Key difference. On forex, Chatterjee ξ is a binding gate (ξ bands <0.30 independent · 0.30–0.50 WATCH · >0.50 absorb) — the opposite of crypto, where ξ is not even computed. All 13 came out independent (per-symbol median ξ ≤ 0.29).
On the cutoff numbers (don't conflate the two scales). The crypto 0.85 / 0.95 are Spearman ρ bands, declared project constants (constants_registry.toml: WATCH_LO=0.85, BAN_HI=0.95); the forex ξ gate uses a different scale (0.50 on ξ). Whether crypto's 0.85 is "principled" vs a chosen policy is a separate meta-evaluation question (matrix-eval iter 7: policy, not derived), not part of this operational decision.

② Complementary second check — built, not yet operational NOT OPERATIONAL

A different axis: instead of "new vs production", it asks "is this candidate redundant with the other candidates in the batch?", to weed out near-duplicates among the new features. Terry classes this as a 2nd-layer / complementary check — explicitly not a meta-evaluation. It lives only inside the rotation probe (rotating_slice_orthogonality_probe.py), which is not wired into the operational decision.

MetricWhat it asksWhy we use it
Leave-out R² (LOO-R²)How well can the other candidates predict this one?Regress the candidate on its siblings; high R² = the others already explain it → it adds nothing. Removes candidates redundant with each other.
max sibling |ρ|What is its single closest sibling?Pairwise sibling redundancy — the nearest twin among the candidates being considered.

How the rotation probe scores (also not operational): each candidate is tested across many strata — its grid is asset class × symbol × threshold × volatility × session × trend, run across both forex and crypto — and only a candidate that survives the hardest stratum and repeats in both markets is kept. Here "asset class" = crypto vs forex, and it is a dimension only in this probe — the operational probe above is crypto-only. Detector sanity check: a shipped feature scored against itself returns ρ ≈ ξ ≈ LOO-R² ≈ 1.0 — it correctly flags a perfect duplicate.

③ Other metrics — measured and reported, but NOT gating

MetricWhat it is used for
W1 (Wasserstein-1) distribution viewReport-only. The verdict collapses every cell to the single worst ("redundant somewhere?"); W1 reports the mean across cells ("redundant everywhere?"). It explains whether a worst-cell hit is a regime-local spike or broad redundancy — but it never gates.
CODEC · O-informationHigher-order / conditional redundancy given the rest of the panel. Measured and held (reported), not gated — there is no faithful FOSS null yet, so they inform, they do not decide.

References: the operational decision is the orthogonal dashboard (gating metrics + the report-only distribution view); the complementary LOO-R² check is the rotation probe; see also slicing methodology and the named regime slices.

3 · How I build a feature once it is chosen

I do not invent a new engineering path each time. I review the previous implementations and follow the same robustness standard, building the new feature step by step in an autonomous loop. The loop walks a feature from "trusted formula on paper" toward a live, backfilled production column — but it never merges, deploys, or backfills by itself: it stops at an open PR, and a human takes it the rest of the way.

The full lifecycle — chosen candidate → live, backfilled column

① Chosen candidateorthogonality-proven formula
② Implementation loopautonomous · stops at PR
③ PR openedloop hands off — no auto-merge
④ Operator squash-mergea human reviews & merges
⑤ Deploymise run deploy:bigblack
⑥ Sidecar restartlive engine picks up the column
⑦ Backfillrecompute across history
⑧ Live + backfilledfilled forward & back

Stages ①–③ are the autonomous loop (detailed in §4). Stages ④–⑦ are operator-gated: the human merges, deploys with mise run deploy:bigblack (builds + installs under the odb service account), restarts the sidecar so new live bars carry the column, then runs the historical backfill so it also fills back across history — consistent at the live↔historical boundary. (Forex is the same shape from /opt/mql5fxview-sidecar.)

Before I merge — closing the loop out

Once the loop reaches its open PR, I close it out in four steps (these are the loop's own P2 / P3 phases plus my operator read):

  1. Review the surfaced findings — read everything the loop flagged.
  2. Advisory audit — a pass dedicated to closing any identified gaps.
  3. Attacker / defender review session — adjudicate the gaps the two adversarial agents surface.
  4. Further advisory audits — confirm everything is aligned and genuinely mergeable.

4 · Loop-in phases — crypto and forex

Crypto and forex run as two separate autonomous campaigns, each with its own pipeline. They share the same discipline (bit-exact oracle → causal Rust kernel → full wiring → adversarial challenge) but differ in shape: crypto runs a 16-stage-per-feature pipeline across four phases; forex runs a leaner per-feature lane with stacked PRs.

🪙 Crypto — loop-in phases (latest: batch 5 · PR #544 · 8 features)

Four campaign phases. Inside P1, each feature walks all 16 stages before the next begins; then the whole batch is verified and adversarially challenged.

P0 · Bootstrapcohort minted · SSoT pins
P1 · Featureseach feature → 16 stages
P2 · Verifyadvisory audit · close gaps
P3 · ChallengeAttacker/Defender held
PR #544operator merges

The 16 per-feature stages inside P1 (in order):

1 oracle2 xcheck3 kernel4 wire5 causality6 determinism7 schema8 contract9 manifest10 parity11 robust12 adr13 mise14 checkfull15 challenge16 commit
Stage groupStagesPlain meaning
Prove the mathsoracle · xcheckMatch a trusted reference bit-exactly, then cross-check against a second independent implementation.
Build the kernelkernel · wire · causality · determinismFast Rust kernel, threaded through the bar struct / API / writer; proven to read only past bars and give the same answer every run.
Store itschema · contract · manifestAdd the ClickHouse column (DDL + comment), column-count / type tripwires, and register it in the feature catalogue.
Verify itparity · robustStreaming output equals batch output; degenerate inputs and oversized windows are handled (robustness witnesses).
Ship itadr · mise · checkfull · challenge · commitDecision record, slash-commands, the full quality gate (fmt + lint + test + deny), the adversarial Attacker/Defender audit, then open the PR — the loop stops here.

🪙 Forex — loop-in phases (latest: RQW + CREx · stacked PRs #124 → #126 → #127)

Forex runs one lane per feature, each stacked on the previous PR; schema and code ship together in a single PR (no split-brain).

Bootstrapv3 cohort · numpy-pin SSoT
worktreeisolated · stacked
gap-aware oracleε 1e-9 vs numpy
kernel portRust · causal
wireBarRow · schema.sql · feature_metadata
SOTA robustnessscale / sign-witness (§6+§6.RG)
all-gatesgreen
no-split-brainschema + code, one PR
challenge-heldattacks held · 0 conceded
stacked PRoperator squash-merges

Both flows then enter the operator-gated tail in §3 (merge → deploy → sidecar restart → backfill). Source campaigns are the newest in the dashboard: crypto 2026-06-19-bar-orth-batch5-implementation (PR #544) and forex 2026-06-24-forex-batch34-rqw-crex-implementation (PRs #124 / #126 / #127). Browse from the crypto and forex candidate hubs.

🗺 The whole pipeline at a glance — discovery → ClickHouse column

The current, operational path a crypto feature actually travels, end to end. Green = running today; amber = a human must trigger it (the loop stops at the PR and never auto-deploys); grey = not in this chain.

Operational Operator-gated Not operational ★ key step 1 · Candidate discovered A new feature formula is proposed — one that might carry information our existing columns do not already hold. 2 · Free static gates (parameterless + agnostic) Parameterless (no hand-tuned magic numbers) and agnostic (one well-defined algorithm). Run first — they cost nothing. 🪙 CRYPTO · orthogonality_probe.py Spearman |ρ| + h_norm ONLY (ξ not computed), crypto symbols × thresholds × 7 regime epochs (21 two-month slices). Promotion differs by group: the Median Rule (median|ρ|≤0.85 ∧ pooled≥60% ∧ min-regime≥50%) promoted ONLY the batch-5 features; the earlier bar_close features used a stricter R²-gated harness (ρ≤0.85 ∧ R²≤0.85 ∧ h_norm≥0.05) on ~10 slices. 💱 FOREX · multislice_orthogonality_probe.py Two-stage: Spearman screen → Chatterjee ξ KEEPER GATE (ξ>0.50 per-symbol-median + ≥2-symbol replication). Here ξ GATES. Sessions + 1–50 dbps. 4 · Three-axis gate → keep / skip One decision: keep = orthogonal AND parameterless AND agnostic. Anything else is blocked or pending. 5 · 16-stage build loop (autonomous, stops at PR) oracle → … → commit: builds a bit-exact Rust kernel + the ClickHouse column, then opens a PR and halts. 6 · Operator squash-merge A human reviews the findings, runs the audits, and merges. The loop never auto- merges. 7 · Deploy (mise run deploy:bigblack) Operator builds + installs the new code under the odb account on bigblack. 8 · Sidecar restart Streaming engine restarts so new live bars start carrying the column. 9 · Historical backfill Column is recomputed back across history — consistent at the live↔historical boundary. 10 · LIVE ClickHouse column A real production column, filled forward (live) and back (history) — the exact maths that was validated.

5 · Why I trust the result

6 · What I have been doing

Beyond implementing individual features, recent work has been about strengthening the orthogonality judgement and looking ahead — a forward-looking probe, a complementary cross-check, and a hardened candidate registry. Important: these are research / evaluation surfaces — only the production decision in §2 is operational today; the three below are not yet wired into the live gate. Each is grounded in a page you can open:

🔮 Forward predictivenessEVALUATING

Asks whether a feature that is orthogonal today stays orthogonal tomorrow. Grounded by an out-of-sample walk-forward (replicated): orthogonal-today features persist ~94–95% into the next regime, with a declared set bounded at FDR ≤ 10%. Why it is effective: it turns "independent now" into a regime-scoped, error-bounded bet instead of a forever-claim — and it is honest that 0 of 92 features are invariant across all regimes, so every declaration carries a regime scope. The live expiry monitor is specified but not yet built (hence EVALUATING).

🗃 Candidate registryNOT OPERATIONAL

Hardens the candidate registry and turns the manual "is this already covered?" novelty check into a fast, automatic, evidence-backed uniqueness pre-filter with a tamper-evident, hash-chained history. Why it is effective: it screens a candidate for uniqueness against everything already catalogued before spending the expensive empirical sweep — so duplicates and near-duplicates are caught at registry level, and the costly orthogonality screen is reserved for genuinely novel candidates.

♻️ Rotation orthogonal probeNOT OPERATIONAL LOO-R²

The rotating-slice probe tests a candidate across every market condition (strata) and fuses both redundancy layers per stratum: vs production (Spearman |ρ| + Chatterjee ξ) and vs siblings (Leave-out R² + max sibling-|ρ|). A candidate passes only if it survives the worst stratum and replicates across forex + crypto. Why it is effective: the worst-stratum rule kills one-regime flukes, and the LOO-R² layer removes candidates redundant with each other — the exact second layer described in §2.

Reference note: none of these three is wired into the operational gate yet — the forward probe and rotation probe are EVALUATING, and the candidate registry is a hardening campaign over a tracking ledger. Only the production decision in §2 is operational today.

♻️ Rotation Orthogonal Probe — full workflow NOT OPERATIONAL

The complementary cross-check, step by step (built, not wired into the live gate). The cyan callouts flag where the crypto and forex orthogonality probes genuinely differ.

Operational Operator-gated Not operational ★ key step 1 · Candidate discovery (shared kernel registry) A long list of candidate formulas — the shared 47-kernel registry, so the exact same maths is tested on both forex and crypto. 2 · Registry novelty pre-filter Throw out obvious duplicates before any market test. Built, but not wired into the decision. 3 · Build the stratum grid Lay out every market situation up front: asset-class × symbol × threshold × volatility × session × trend (81 base cells). CRYPTO vs FOREX Crypto screen has no intraday axis — just 7 regime epochs. Forex adds Asian / London-NY sessions + a regime×session grid (174 cell-slices). 4 · Fetch bars + compute on causal windows Pull ~80k real bars per cell once, then run every kernel over backward-only 200-bar windows (never peeking ahead). CRYPTO vs FOREX Different tables: crypto open_deviation_bars vs forex forex_bars. Forex adds spread / quote-count substrates + a weekend de-gap; crypto has neither (trades 24/7). 5 · Stratify on outputs, not bars Attach the market-condition label to each computed output row afterward, so slicing never shatters the 200-bar window. 6 · Deficit-first pick + block-bootstrap Each batch refills the least-covered stratum, resampling real rows in short blocks (Politis–Romano) so no lucky window decides it. 7 · Fused screen A — candidate vs production Score against shipped columns: Spearman |ρ| (rank redundancy) + Chatterjee ξ (curved / non-linear dependence Spearman misses). CRYPTO vs FOREX The biggest probe difference. Forex runs a FULL ξ engine: effect-size ξ>0.50 + ≥2-symbol replication + bootstrap CI + FWER. The crypto probe is Spearman-only. 8 · Fused screen B — candidate vs siblings Same sweep: panel-R² (replaced the earlier Leave-out R²) + max |Spearman| vs any sibling — weeds out near-duplicate new features. 9 · Loop until every stratum covered Batches repeat until each feasible stratum has 20 paths — coverage is provable from the ledger, not hoped from randomness. 10 · Verdict — worst stratum + cross-asset Pass only if it survives the single hardest stratum AND replicates across BOTH forex and crypto. Passing on average is not enough. CRYPTO vs FOREX Forex bakes ≥2-symbol replication into the verdict and drops 29 forward-label columns to block leakage; crypto bans per-cell and reports per-regime min pass-rate. 11 · → 16-stage build → PR → merge → deploy → backfill A survivor hands off to the same operator-gated build tail as the main pipeline. 12 · LIVE ClickHouse column Ends as a real production column — only after a human merges, deploys, and backfills.

🗃 Registry-based candidate discovery — full workflow NOT OPERATIONAL

How a candidate would flow from an idea to a live column through the hardened registry. The key idea: a cheap novelty pre-filter (is it already in our catalogue?) is a different gate from the expensive orthogonality test (does it add new information?).

Operational Operator-gated Not operational ★ key step 1 · Candidate identified A new feature idea enters the funnel at lifecycle stage 'identified'. 2 · Name pre-check (--check) Instant name lookup (exact + containment) catches an obvious re-spelling of something we already have. 3 · Ledger intake (--add) One row appended to the candidate ledger: id, name, family, stage, axis verdicts, provenance. 4 · Automatic novelty pre-filter 'Already covered?' — a 3-tier check (content-hash/exact → lexical near-dup → optional semantic). REFUSED if covered; blocked if it needs review. NOVELTY ≠ ORTHOGONALITY NOVELTY = is it already in our catalogue? Cheap + automatic. This is the pre-filter — a different question from the expensive orthogonality test below. 5 · Hash-chained tamper-evident history Every ledger event is hash-chained, so any out-of-band edit to a candidate's past is detectable. 6 · Only novel candidates proceed Catalogue-unique candidates move on — the expensive testing budget is never spent re-evaluating something already shipped or ruled out. 7 · Orthogonality screen (Stage 2 — separate) The separate, expensive probe on real bars: does it add NEW information vs the columns already in production? NOVELTY ≠ ORTHOGONALITY ORTHOGONALITY = does it add new info vs production columns? Expensive, on real bars — a separate Stage-2 gate from the cheap novelty pre-filter. 8 · Three-axis gate Cost ladder: parameterless (free) → agnostic (cheap) → orthogonal (expensive). Promote only if all three PASS. 9 · Verdict pinning + promotion guard Moving to 'promoting' is machine-blocked unless all three axes PASS and each verdict is pinned to the exact data + code SHA. 10 · 16-stage build → PR → merge → deploy → backfill Built in Rust through the implementation loop; cannot reach 'promoted' without a real bar_* column. Then the operator-gated tail. 11 · LIVE ClickHouse column Finishes as a populated bar_* column in open_deviation_bars; the ledger row is set to 'promoted'.

7 · Candidates contributed so far — and how each was promoted

Full transparency on what shipped and the exact bar each one cleared. There is no single promotion standard — three were used, and they differ by group:

🪙 Crypto — 12 contributed columns

FeatureHow it was promoted
bar_close group · PR #509 / #515 / CECP campaign · predecessor fractal / entropy / CECP harness · gate ρ≤0.85 ∧ R²≤0.85 ∧ h_norm≥0.05 (worst-valid-cell, ~10 slices) — stricter than the named probe; pre-dates it
bar_petrosian_fdPR #509; promoted as a panel column, re-confirmed KEEP later by the literal probe (median |ρ| 0.306)
bar_katz_fdPR #509; the weakest KEEP — min-regime |ρ| 0.86 (touches the WATCH band in some regimes)
bar_dispersion_entropyPR #515; entropy-spike clone (orthogonality_probe_full_ch.py) — also gated on LOO-R² + combinatorial-R²
bar_cecp_velocityCECP campaign (2026-06-10); ρ≤0.85 ∧ R²≤0.85 ∧ h_norm≥0.05
batch-5 · PR #544 · the literal orthogonality_probe.py (441 cells = 21 symbol×threshold × 21 slices; Spearman |ρ| + h_norm; ξ NOT computed) · Median Rule: median|ρ|≤0.85 ∧ pooled-pass≥60% ∧ min-regime-pass≥50%
bar_categorical_recurrence_rateMedian Rule KEEP
bar_sign_markov_fluxMedian Rule KEEP
bar_ramsey_rothman_bicov_lag1Median Rule KEEP
bar_ehlers_increment_asymmetryMedian Rule KEEP
bar_cox_stuart_trend_zMedian Rule KEEP
bar_groeneveld_meeden_b3_skewnessMedian Rule KEEP
bar_l_kurtosis_tau4Median Rule KEEP (max |ρ| 0.872 in an isolated cell — clears the median rule)
bar_bartels_rank_vn_ratioMedian Rule KEEP

💱 Forex — 13 contributed columns

FeatureHow it was promoted
batch 1–2 · PRs #91–#99 · multislice_orthogonality_probe.py (Stage 1 Spearman screen → Stage 2 ξ keeper gate) · 10 slices (8 episodes + 2 sessions) · ξ keeper gate: ξ>0.50 per-symbol-median + ≥2-symbol replication → all came out independent (median ξ ≤ 0.29) · 9 shipped as columns #1–9 (5 deferred)
anderson_darling_a2ξ-confirmed independent
edge_spread_bpsξ-confirmed independent
hvg_clusteringξ-confirmed independent
kendall_taub_ret_spreadξ-confirmed independent
lziv_complexity_signsξ-confirmed independent
mann_kendall_zξ-confirmed independent; stands in for a 4-way monotone-twin clique (the others dropped as duplicates)
sevcik_fdξ-confirmed independent (a worst-cell ξ 0.526 was ignored — not replicated across ≥2 symbols)
sign_two_state_entropy_rateξ-confirmed independent
spectral_log_slopeξ-confirmed independent
batch 3–4 · forex batch34 · same two-stage probe, run on the crossed regime×session 24-slice panel (341 cell-slices) · same ξ keeper gate (median ξ ~0.13) · 4 shipped as columns #10–13 (of 15 ξ-cleared)
pomeau_irreversibility_lag1ξ-confirmed independent
hvg_degree_assortativity_coefficientξ-confirmed independent
hvg_mean_shortest_path_lengthξ-confirmed independent
psd_wiener_spectral_flatnessξ-confirmed independent

Honest notes: (1) the FD pair (bar_petrosian_fd / bar_katz_fd) were promoted before orthogonality_probe.py existed — their "re-confirmed by the probe" rests on antropy determinism (recompute == materialized is asserted, not measured). (2) On the counts: the batch-1/2 ξ stage produced 14 ξ-confirmed keepers (13 KEEP + mann_kendall_z as the direction-clique representative), of which only 9 became live columns (5 deferred — substrate / parameter-blocked); batch 3-4 ξ-cleared 15, of which 4 shipped. The 13 contributed columns on this page are those 9 + 4, and mann_kendall_z is one of the 9 (column #6) — not an extra on top of the 13. Full per-column reference: forex feature catalog.

8 · Where everything connects

The probes & gate
Dashboard