Weekly Report ยท 21โ27 July 2026
What I did this week, what it proved, and what I plan to do next โ across
opendeviationbar-research, the new subordinator repo, and
opendeviationbar-py.
This week I built the strategy-killing engine and then used it to kill things. Between Tuesday and tonight the whole rejector went from nothing to a working P0โP4 pipeline, and the first thing I pointed it at came back 616 trading rules tested, zero survivors โ the promoted bar-close features carry essentially no directional information at a 3-second horizon. On Saturday I built the cross-sectional machinery that I think is the most promising next direction โ running it is this week's work. Today I opened one new line of work (subordinators) and, separately, established that my own orthogonality probe fails its own control test and cannot currently certify anything.
Under our doctrine the deaths are the deliverable โ clean, adversarially-verified negatives that close off large search spaces cheaply. But I want to be direct with you: we do not have a tradeable edge to show, and nothing in this report should be read as one.
| Day | What landed |
|---|---|
Tue 21 | P0 gate โ causality-typed column contract, crypto/forex adapters, leakage guard T1โT4, then an adversarial review of my own guards that found 12 issues, all fixed |
Wed 22 | P0.5 โ P3 in one day โ worst-fill crypto labels, Sharpe-free tail metrics + block-bootstrap CIs, exposure-matched drift null, FM-05 concentration falsifier, FM-13 cascade rejectors, DSPOT cascade dating, FM-12 OOD/LORO robustness |
Fri 24 | The campaigns โ single-instrument pre-flight โ DRY, the 5-symbol ร 120-rule sieve (600 rules, 0 survivors), the BTCUSDT falsification loop, and the stop-and-ask ClickHouse rule added to CLAUDE.md |
Sat 25 | Cross-sectional C1โC3 machinery โ causal panel + dollar-neutral factor P&L (PR #29). Running it is this week's work โ ยง1.3. |
Mon 27 | P4 validation (purge+embargo WFO, CSCV-PBO, e-BH, trial ledger), CI pipeline + /ci:* namespace, 8 correctness fixes to my own engine, PR #668 probe arbitration, and subordinator v1.0.0 |
| ยง | Topic | Where it stands |
|---|---|---|
| 1 | opendeviationbar-research โ the engine, and what it proved | Main negative โ 616 rules, 0 survivors, from a rejector built in five days |
| 2 | Subordinators โ opened today | New โ working measurement bench, no claim of edge, wants your go/no-go |
| 3 | opendeviationbar-py โ probe, grounding, Axis-2 | Mixed โ one campaign complete at 19/1; the orthogonality probe found defective |
| 4 | Validation machinery โ built vs actually run | Reported โ including which guards have never been exercised |
| 5 | What I plan to do next | Where your perspective would help |
| 6 | How to check any number here | Every figure traces to a committed artifact |
opendeviationbar-research โ the engine, and what it provedThis is the repo I started after our call, and it is where almost all of this week went. Its job is not to find trading strategies. Its job is to kill them โ to take any proposed strategy and attack it with every known way a backtest can lie, so that anything surviving has actually earned it.
This week I built that attacker end to end, and then ran the promoted bar-close features through it. Nothing survived. Below is what I built, and then what it proved.
The pipeline is a funnel of increasingly expensive tests. Cheap rejectors run first, so a bad strategy dies before I spend compute on it. Each gate targets a specific named failure mode from our FM catalog.
| Gate | Landed | What it does | Guards |
|---|---|---|---|
P0 intake | Tue 21 ยท #8 | Causality-typed column contract + crypto/forex adapters โ every column is tagged with what it may legally depend on | FM-01 |
P0 leakage guard | Tue 21 ยท #9, #11 | T1โT4 leakage tests. Then I ran an adversarial review against my own guards and it found 12 issues, all fixed | FM-01 |
P0.5 labels | Wed 22 ยท #20 | Verify and adopt the crypto worst-fill labels โ every P&L is priced at the worst realistic fill from the first line | FM-02 |
P1 metrics | Wed 22 ยท #21 | Sharpe-free tail metrics (CVaR, CDaR, MaxTuW, Rachev, Omega) + block-bootstrap confidence intervals on real resampled data | FM-07 |
P1 nulls | Wed 22 ยท #22, #23 | Exposure-matched crypto-strata null โ the first-class drift/beta rejector โ plus the FM-05 concentration falsifier | FM-03 ยท FM-05 |
P2 cascade | Wed 22 ยท #24, #25 | Cascade-conditional crash-premium rejectors, and DSPOT cascade dating with severity corroboration | FM-13 |
P3 OOD | Wed 22 ยท #26 | Out-of-distribution robustness โ leave-one-regime-out plus covariate-shift detection | FM-12 |
P4 validation | Mon 27 ยท #31 | Purged + embargoed walk-forward, CSCV-PBO, e-BH multiplicity under arbitrary dependence, and an auto-tightening trial ledger | FM-04 ยท FM-09 ยท FM-10 |
FM-08 says don't build infrastructure the edge doesn't yet justify. Its operational form is cheap rejectors first. That is why P0/P0.5/P1 โ the gates that cost almost nothing to run โ landed before the expensive P4 deflation battery, and it is why the campaigns in ยง1.2 ran on Friday rather than waiting for the full stack. As it turned out, nothing got past the cheap gates, so the expensive machinery was never invoked. That is the funnel working, not a gap.
With the gates in place I pointed them at the promoted bar-close features on Friday. Three separate campaigns, three independent methods, one conclusion. I am reporting them in order of how much they cover, not chronologically.
I took every bar-close feature that had been promoted upstream, turned each one into simple trading rules โ "when this number is unusually high, buy; when it's unusually low, sell" โ and tested all of them on real crypto history. 616 rules. None of them made money. More importantly, they didn't fail narrowly. They failed by a factor of ten to a hundred, which tells us something much more useful than a near miss would have.
For each symbol I enumerated a complete, operator-specified grammar:
15 promoted features ร 4 quantile tails (p10/p25/p75/p90) ร 2 directions = 120 rules.
Signals are built from past-only rolling quantiles (rolling.quantile.shift(1)), so every
rule is causal by construction. Each rule is priced at worst-fill, given a moving-block bootstrap
confidence interval on real resampled data, then corrected for multiple testing with both
Bonferroni and BH-FDR.
| Symbol | Rules | Tradeable bars | Cost floor (bps) | Largest gross move (bps) | Best net CI-low (bps) | Verdict |
|---|---|---|---|---|---|---|
BTCUSDT | 120 | 868,969 | 16.90 | 0.31 | โ18.73 | Dry |
ETHUSDT | 120 | 1,198,928 | 15.07 | โ0.52 | โ17.62 | Dry |
SOLUSDT | 120 | 1,334,374 | 15.49 | 0.00 | โ18.15 | Dry |
XRPUSDT | 120 | 1,498,374 | 18.26 | โ0.39 | โ25.98 | Dry |
DOGEUSDT | 120 | 2,596,409 | 48.05 | 1.33 | โ52.01 | Dry |
| Total | 600 | 7,497,054 | โ | โ | โ | 0 survivors |
Every one of the 600 rules carries verdict: "NO ALPHA" with
death_reason: "no-directional-edge". Statistically: 0/600 significant under Bonferroni,
0/600 under BH-FDR, 0/600 with a positive confidence-interval lower bound. The outcome is completely
homogeneous โ not one rule even reached the later cost-ceiling or multiplicity branches of the
decision tree, because none of them got past the first gate.
Earlier and deeper: a single feature (bar_petrosian_fd, the one producer-proven column)
split into 5 quantile bins, each traded in its natural direction, each pushed through the full
8-gate failure-mode battery over 8.5 years of history.
| Round | Variant | Trades | Mean net (bps) | Profitable folds | Verdict |
|---|---|---|---|---|---|
| 1 | LONG bin-0 (smoothest) | 209,229 | โ21.19 | 0 / 5 | Falsified ยท FM-02 |
| 2 | LONG bin-1 | 176,380 | โ23.06 | 0 / 5 | Falsified ยท FM-02 |
| 3 | LONG bin-2 | 149,906 | โ29.88 | 0 / 5 | Falsified ยท FM-02 |
| 4 | SHORT bin-3 | 145,238 | โ48.54 | 0 / 5 | Falsified ยท FM-02/03/11 |
| 5 | SHORT bin-4 (choppiest) | 167,657 | โ81.61 | 0 / 5 | Falsified ยท FM-02/03/11 |
| โ | Total | 848,410 | โ | 0 / 25 | 5 deaths, 0 survivors |
The monotone pattern is itself informative: net P&L worsens strictly as bars get choppier (โ21 โ โ23 โ โ30 โ โ49 โ โ82 bps), driven by the worst-fill cost floor rising from ~13 to ~57 bps. Choppier bars are more expensive to trade and no better directionally. There is no bin, and no direction, where the feature pays.
I ran 60 independent skeptic recomputations (12 failure modes ร 5 rounds), each re-deriving the verdict from the raw parquet. 59 upheld, 1 refuted โ and the single refutation reinforced a death rather than overturning it. A separate attacker/defender pass on PR #7 conceded 6 challenges and forced real corrections: my original "real signal buried by cost" narrative was wrong. Once I stripped the worst-fill inflation and applied a block bootstrap that respects autocorrelation, the apparent directional tilt largely vanished. The honest reading became "no signal, plus cost" โ a stronger and less flattering conclusion than the one I started with. Not one core verdict was overturned, but the explanation was materially rewritten.
Before committing compute I ran a cheap sanity sweep: 11 baseline rules on BTCUSDT (momentum, reversion, order-flow imbalance, VWAP, plus an always-long control), then a read-only ClickHouse-side sweep across the entire liquid universe โ 21 USDT pairs ร 4 thresholds (100/250/500/750 dbps). No rows were pulled; only medians were computed server-side.
Coarser thresholds do not rescue it. The threshold sizes the move that already happened by the time the bar closes; the forward move stays โ0 at every threshold while cost stays flat.
Blue = the largest gross directional move any of 600 rules produced, before any cost is subtracted. Red = what it costs to trade, round-trip. Bars share one linear scale.
If the features carried a real 20 bps edge that costs were eating, the answer would be engineering โ better fills, a cheaper venue, maker orders, negotiated fees. That would be solvable. That is not what I found. The best gross move across 600 rules is 1.33 bps and it sits on DOGE, the most expensive symbol in the set. On the four majors it is under 0.6 bps. Even if execution were completely free the edge would still be economically meaningless. At a 3-second horizon these features describe the bar that just closed; they do not predict the next one.
The mild upward tilt that lights up a naive screen (P(up) โ 0.5109) is market drift โ the fact that crypto went up over 8.5 years โ not feature skill. Compared against a drift-matched null instead of a coin flip, it strips to โ0.
Single-instrument, single-feature threshold rules on promoted bar-close features at 250 dbps / 3s, on the liquid crypto majors. This surface is exhausted. Widening it โ 2-feature combos, more symbols โ combines near-zero-information signals and mostly just inflates the multiple-testing tax.
Longer horizons than 3s. Cross-sectional / market-neutral construction (rank the universe, trade winners against losers โ this cancels the drift that dominated here). Multi-bar formations rather than single-bar features. Maker-side execution that earns the spread instead of paying it.
Every rule that died in ยง1.2 was a single-instrument rule: look at Bitcoin, decide about Bitcoin. The post-mortem showed the thing that beat them was not cost โ it was market-wide drift. Crypto went up over 8.5 years, that lifts everything, and it swamped whatever tiny signal the features carried.
The standard fix is to stop betting on the market and start betting on relative performance: rank the whole universe by a feature, buy the top, short the bottom, hold equal dollars on each side. Whatever the market does as a whole then cancels out.
The machinery landed Saturday โ PR #29, C1โC3: a causal panel with an as-of join, and
dollar-neutral factor P&L, reviewed and tested. That is the part that is done.
C4 โ the run against real data โ is this week's work, and it has not started. Pulling
the panel needs a ClickHouse read, which under the stop-and-ask rule I added to CLAUDE.md
on Friday (#19) means writing the mini-ADR first โ exact tables, columns, symbols, the
precise read-only query, where it runs, and why the local slice cannot answer it. That is the next thing
I do. So I am reporting this as in flight, not as a result: there are no cross-sectional verdicts yet
and I have not pre-judged any.
It attacks the exact mechanism that killed everything in ยง1.2. Ranking the universe and trading winners against losers cancels the market-wide drift that defeated every single-instrument rule. It is the one hypothesis last week's negative result actively points toward rather than away from, which is why I built the machinery before anything else on the list.
A rejector with bugs in it is worse than no rejector, because it issues verdicts that look authoritative. So before doing anything else with it I spent Monday night attacking my own code. Eight correctness fixes landed, plus CI.
| PR | Fix | Why it mattered |
|---|---|---|
#32 | Normalise SHORT P&L by the entry fill, not the exit fill | Made rounds 4โ5 ~1.7% optimistic. No verdict moved โ both were already catastrophically negative โ but the arithmetic was wrong. |
#33 | Reject non-finite and non-binary labels in auc() | A NaN or a non-binary label would previously produce a plausible-looking number instead of an error. |
#34 | Test bins against the unconditional base up-rate, not a coin flip | Comparing to 0.5 makes market drift look like skill. This is FM-03 inside my own screen. |
#35 | Fail closed when a selected symbol has no realisation | Silent-NaN class of bug in the cross-sectional path โ exactly what this week's work will depend on. |
#38 | Causal guard perturbs by permutation and resampling, not absolute noise | Absolute-noise perturbation is defeated by quantised columns, so the FM-01 leak detector could pass a leaky feature. |
#39 | First tests for panel.py + fail closed on unprovable FM-01 checks | The as-of join is the documented leakage risk in the panel and it had zero tests. |
#40 | Replace synthetic-verdict tests with real known-verdict replays | HARD RULE 5 compliance โ no synthetic data, even in the test suite. The tests now replay real documented episodes. |
#37 | Footnote rounds 4/5 as ~1.7% optimistic under the old formula | Keeps the published ledger honest about a number that has since been corrected. |
CI landed as PR #36 โ a GitHub Actions pipeline plus a /ci:* slash-command
namespace, per our cross-repo standard. #41 unblocked the ruff ratchet.
The 112 MB data slice is gitignored and unavailable to runners, so CI runs a data-less subset
only. The job is literally named tests (NO real data โ partial coverage) and
prints "This job passing does not mean the engine is validated." The suite refuses to run data-less
unless explicitly opted in, so a skipped suite can never silently read as green.
Current totals for the repo: 272 test functions across 19 files.
A subordinator is a different clock for chopping market data into candles. Instead of "a new candle every minute", you say "a new candle every 500 trades", or "every $1 million traded". That's it. The repo I opened today is a bench for measuring what that choice actually does to your data.
Almost all market analysis starts by chopping the raw stream of trades into bars โ open, high, low, close, volume. By convention we chop by the wall clock: one bar per minute. But markets do not run at a constant speed. At 3am nothing happens. During a crash, thousands of trades land per second. So a 3am one-minute bar and a crash one-minute bar carry the same label and wildly different amounts of market.
You are measuring with a ruler whose markings stretch and shrink โ and then feeding those measurements to a model as if they were comparable rows in a table.
The fix is old: Mandelbrot & Taylor (1967) and Clark (1973) proposed you stop counting time and
start counting activity. Close a bar every N trades, or every N dollars exchanged. Now every
bar holds roughly the same amount of market. And the famous "fat tails" of returns โ the
too-many-extreme-moves problem that breaks standard statistics โ largely go away.
They were partly an artifact of using the wrong clock, not a property of the market. The mathematical
name for re-indexing one process onto another process's clock is subordination
(X(t) = Y(T(t))); T is the subordinator.
Every bar type that exists is just one choice of T. Time bars, tick bars,
volume bars, dollar bars, imbalance bars, directional-change bars โ these are not seven different
things. They are one thing with seven settings. Which means they can be put on a single bench and
compared fairly, instead of argued about.
One design decision does all the work: a clock only ever answers one question โ where does each bar end? It never builds the bar. A single shared piece of code turns cut-points into candles, identically for every clock. Two clocks can therefore differ only in where they cut, never in what happens afterwards. Any difference you then observe is attributable to the clock and nothing else.
Adding a new bar type costs one method and one line in the registry, and it immediately inherits every test and every comparison. There are currently 12 clocks across 8 families.
Directional-change bars close on a price reversal, so consecutive bars alternate up-down-up by construction. Measured bar-to-bar correlation: โ0.43 to โ0.76 across days. Anyone who "discovers mean reversion" on those bars has discovered the ruler, not the market. A real finding has to beat that baseline first.
The directional-change clock produced 9 bars on a quiet day and 54,412 on a crash day โ a 6,046ร swing. A model trained on calm data sees a handful of rows per day; deployed into a crash it sees tens of thousands. That is not a shift in the features, it is a shift in the sampling process itself โ and no standard robustness check on features will catch it.
ODB is a subordinator. It is a price clock: it cuts a new bar whenever price moves a fixed distance ฮด from that bar's opening price. So everything above applies directly to the bars underneath every number in this report.
Its nearest published relative is the directional-change clock, and the spec pins the exact difference: DC's reference point is a trailing extremum that keeps moving with the trend (so it confirms late), whereas ODB's is fixed at the bar's open for the bar's whole life (so it cuts every ฮด of displacement). That single difference is the entire distinction between the two families โ and until now it was an argument, not a measurement.
An earlier comparison of mine said ODB was far more regime-stable than dollar bars โ 1.83 vs 7.76. Re-run with bar counts matched, it reversed: dollar bars were more stable, and my original result had been measuring threshold calibration, not clock design.
What did survive count-matching was different, and more interesting: ODB's tail truncation. Kurtosis 9.8 vs dollar bars' 98.5 on the worst day. So there is a real ODB property there โ just not the one I originally claimed.
A clean-room ODB clock written from the Rust source reproduced the production golden fixture bit-exactly: 45/45 bars matching on OHLC and timestamps, volume to 3e-13. That is the strongest evidence available that "cut-points plus one shared aggregator" loses nothing versus a real bar engine โ which is what makes the whole comparison bench trustworthy.
The repo explicitly refuses to claim any of this yields profit. It cites the one rigorous head-to-head study in the literature โ 60.5M EURUSD ticks, properly cross-validated, permutation-tested โ which found that ranking bar types by statistical niceness did not predict ranking by trading profitability. Every family landed at essentially coin-flip accuracy.
So the repo treats those as two separate claims and only measures the first, precisely. Success is defined as "measured it correctly", not "found an edge." Given that ยง1.2 of this report is 616 dead rules, I think that is the right posture for a measurement instrument โ but it is a deliberate constraint and you should know I chose it.
The test suite is the part I'd defend: six invariants ร 12 clocks, plus targeted checks. The important one is C3 non-repainting โ re-run the clock on a truncated tape, and already-closed bars must be bit-identical. That is the FM-01 look-ahead guard expressed as an executable assertion. There is also a scale-invariance taxonomy test that multiplies all prices by k and asserts tick/volume/DC are unchanged while dollar must change โ the family taxonomy as a test rather than a comment. Per HARD RULE 5, no synthetic data anywhere: where a test needs a perturbation it uses a metamorphic transform of the real tape.
Two PRs so far. #1 is merged: an ADR rejecting my own proposal to self-host ClickHouse on a
1TB USB drive โ killed on four independent grounds, including that exFAT cannot host a MergeTree at all
for lack of hard links. Adopted instead: hot tier on internal SSD, cold tier as Parquet queried with
DuckDB. #2 is open and substantive: the clock-zoo baseline across 6 regime-diverse days,
with committed artifacts, SHA-pinned tapes, and tables auto-generated from the evidence so the
prose cannot drift from the data.
This is a working, self-validated measurement instrument with one campaign in flight โ more than scaffolding, and clearly short of externally validated. It is hours old. I opened it because ยง1.2's result made me want to check an assumption that sits underneath everything we have tested: not "which feature predicts?" but "is the row the right row?"
I am not asking you to bless it as a workstream. I would rather you tell me whether that question is worth my time at all right now, or whether I should stay strictly on the crypto panel until it is exhausted. It is genuinely your call and I will drop it without argument.
opendeviationbar-py โ this weekODB-py is the engine that makes the data โ it turns raw exchange trades into bars and computes the numbers that describe each one. My job there is quality control on the raw material: deciding which of those numbers are trustworthy enough to become permanent production columns.
Three things happened there this week. One campaign finished, one campaign started, and one piece of my own measurement apparatus was found defective. I'm leading with the defective one because it is the most important.
Scope note: ODB-py has been running since April, but everything below is 21โ27 July only. Earlier workstreams are deliberately out of scope for a weekly report.
Before a new feature is allowed to become a permanent column, it has to prove it is telling us something we don't already know. That sounds obvious, but it's surprisingly hard: a new number can look novel on one slice of history and turn out to be a near-copy of an existing column once you look across different market conditions.
The rotational orthogonality probe is my attempt at a fair test. Instead of judging a candidate on one calendar window, it lays out a grid of market conditions โ asset, symbol, threshold, volatility low/medium/high, Asian vs US-EU session, up/flat/down trend โ keeps a ledger of how well each cell has been covered, and repeatedly attacks whichever condition is least tested until every reachable one has been hit twenty times. The idea is that a feature can't hide in a corner of the market I forgot to look at.
Mechanically it runs a five-stage cascade โ h_norm โ sibling โ ฯ โ panel-Rยฒ โ ฮพ โ and fuses
the stages by worst-of, so a candidate is only as good as its weakest stage
(BAN > WATCH > PENDING > PASS). It runs on 62/62 grounded crypto cells at 80,000
bars per cell, read-only against ClickHouse on bigblack. Its intended job was to set
axis.orthogonal on the candidate registry โ that is, to be the promotion gate
for the orthogonality axis, replacing the legacy |ฯ| + h_norm banlist.
Two batches of ten, plus smoke iterations โ 22 full-cascade evaluations, and zero PASS verdicts.
Every one of the 20 candidates still has axis.orthogonal deferred/null in the registry.
| Candidate | What it measures | Rotational | Legacy (fair) | Died at |
|---|---|---|---|---|
l_skewness_tau3 | L-moment skewness (ฯ3) | Watch | Watch | rho |
l_cv_tau2 | L-moment coefficient of variation (ฯ2) | Watch | Watch | sibling |
edge_spread_bps | EDGE effective-spread estimator | Ban | Watch | sibling |
roll_spread | Roll effective-spread estimator | Ban | Pass | sibling |
corwin_schultz | CorwinโSchultz high/low spread | Ban | Watch | sibling |
abdi_ranaldo | AbdiโRanaldo spread (degenerate, h_normโ0) | Ban | Ban | hnorm |
arcsine_occupation | Arcsine-law occupation time | Pending | Pass | โ (ฮพ fail-safe) |
arcsine_argmax | Arcsine-law argmax location | Watch | Pass | panel_r2 (0.7277) |
longest_excursion | Longest one-sided excursion | Watch | Pass | panel_r2 (0.5514) |
parkinson | Parkinson range volatility | Ban | Watch | sibling |
| Tally | 0 PASS ยท 4 WATCH ยท 5 BAN ยท 1 PENDING | 4 PASS ยท 5 WATCH ยท 1 BAN | 7/10 diverge | |
| Candidate | What it measures | Rotational | Legacy (fair) | Died at |
|---|---|---|---|---|
rogers_satchell_var | Drift-independent OHLC volatility | Ban | Ban | hnorm |
mann_kendall_z | MannโKendall S trend statistic | Ban | Ban | sibling |
psd_wiener_spectral_flatness | Wiener entropy of the bar periodogram | Ban | Pass | sibling (ฯ=0.9783) |
psd_spectral_centroid_normfreq | Spectral centroid of the periodogram | Ban | Pass | sibling (ฯ=0.9783) |
dhvg_indeg_outdeg_kld | Directed-HVG irreversibility KLD | Pending | Pass | โ (ฮพ, 3/62 unstable) |
guzik_increment_magnitude_asymmetry | Guzik Poincarรฉ asymmetry | Ban | Ban | sibling (ฯ=0.9954) |
porta_increment_sign_asymmetry | Porta accel/decel asymmetry | Ban | Ban | sibling (ฯ=0.9954) |
hvg_degree_assortativity_coefficient | HVG degree assortativity | Watch | Pass | panel_r2 (0.6327) |
bowley_quantile_skew | Bowley quartile skewness | Ban | Ban | hnorm |
moors_octile_kurtosis | Moors octile kurtosis | Watch | Pass | panel_r2 (0.6111) |
| Tally | 0 PASS ยท 2 WATCH ยท 7 BAN ยท 1 PENDING | 5 PASS ยท 0 WATCH ยท 5 BAN | 5/10 diverge | |
Twenty candidates and zero passes is either a very demanding gate or a broken one. There is a standard
way to tell the difference: run the probe on things you already know are good. So I
took seven features the project already ships and already labels axis.orthogonal = PASS,
and fed them back through the unmodified cascade as a negative control.
Rotational: 0/7 PASS. Legacy: 7/7 PASS on the same values (6/7 when the real legacy
probe is run on its own full 120-column panel). Every single rotational elimination happened at the
same stage โ panel_r2 โ and unanimously. The mechanism is a mis-set threshold: a
panel_r2 PASS requires r2_worst < 0.50, but the shipped features sit at
0.65โ0.91. The PASS path was effectively unreachable.
The saving grace, and the reason this is a calibration bug rather than a catastrophe: the verdicts came back WATCH, not BAN. The probe was not wrongly rejecting the shipped set โ it was unable to certify it. Max redundancy breadth across all seven was 0.0323 against a BAN dial requiring 0.80, so by its own breadth criterion none of them is broadly redundant.
I then ran the legacy probe on real data for the first time, and it has the opposite defect. Its panel
carries 120 columns to the rotational probe's 112 โ the extra eight being
open ยท high ยท low ยท close ยท vwap ยท lookback_vwap_raw ยท first_agg_trade_id ยท last_agg_trade_id.
In other words, the legacy probe was ranking raw price levels and a monotone trade-ID counter as
redundancy drivers: self_correlation_driven was true at 7/7, and 4 of 27 candidates
had a price level or row counter as their worst driver.
#668, open now| Panel hygiene | Threshold calibration | Usable as a gate? | |
|---|---|---|---|
| Rotational | Correct | PASS unreachable | No โ cannot certify |
| Legacy | Broken | Can certify | No โ certifies partly for wrong reasons |
"Each probe holds exactly the half the other is missing. This is why the arbitration was never resolvable by running the two against each other, and it makes the repair small rather than a redesign."
The campaign verdict file states it plainly: "HALF-SETTLED. Both probes measured; both found defective; neither usable as-is." My decision on 27 July was repair both, don't pick a winner โ because each one is sound exactly where the other fails.
| Aspect | Status |
|---|---|
As a promotion gate (axis.orthogonal) | Not usable as-is โ 0 PASS in 22 evaluations, and 0/7 on its own shipped controls. The pre-registration fixed the criterion in advance ("โฅ1 shipped feature PASS โ probe can certify; 0 PASS โ non-functional gate"), so this is that rule resolving, not a judgement call. The three-dial repair is frozen and the defect is calibration, not design. |
| Sibling stage (candidate-vs-candidate duplicates) | Usable, genuinely additive โ legacy structurally cannot see this. It caught psd_wiener_flatness โ psd_spectral_centroid at ฯ=0.9783 and guzik โ porta at ฯ=0.9954. Legacy rated both members of the first pair PASS. |
| Sibling LOO-Rยฒ leg | Suspended from verdict authority โ a multivariate Rยฒ was being graded on a Spearman-scaled line. Blast radius: 1 verdict. |
| panel-Rยฒ band | Measured mis-set โ applied to the 66 shipped columns it yields 5 PASS ยท 47 WATCH ยท 14 BAN, i.e. 92.4% non-PASS |
| ฮพ stage | Over-quarantines โ returns PENDING on 10 of 20 candidates |
three_axis_gate.py | Still wired to legacy โ and legacy is now known to rank trade IDs as redundancy. Must not be treated as authoritative until the hygiene fix lands. |
I froze a three-dial repair declaration before touching any code, so the fix can't be tuned to taste:
B_pass = 0.20 frozen; the panel-Rยฒ band anchored to Pโ
โ of the shipped columns, k=50 frozen. Measured: Pโ
โ = 0.9259 over n=66. Falsifiability check passes โ l_kurtosis_tau4 (0.9142) now clears it, so the new band is not vacuous.
The re-run that would prove the repair works (R2) aborted with
Code: 241 MEMORY_LIMIT_EXCEEDED on DOGEUSDT@100. Diagnosing it turned up a
real hole in my own resource discipline: the "5-core / 5 GB hard cap" I had declared capped only the
Python client โ the ClickHouse server side was uncapped
(max_memory_usage=0, max_threads=auto(32)). Over 741 probe fetches the server
hit p50 1.61 GiB, p95 12.47 GiB, max 32.97 GiB, with 17 of 63 cells over 5 GiB.
I have written that up as an amendment rather than quietly re-running with a bigger budget.
So: two candidates are carried forward โ dhvg_indeg_outdeg_kld (panel-Rยฒ 0.4752) and
arcsine_occupation (0.1671), the only 2 of 20 that cleared every substantive stage and
were held back solely by the ฮพ fail-safe โ and nothing gets promoted on this axis until R2
completes.
Self-correlation confound. A first BTCUSDT@250 spot check showed only 0.69 and nearly let a duplicate through. Worst-cell driver attribution โ asking not just "how redundant?" but "redundant with what?" โ is what caught it.
Silent kernel death. 5 of 27 kernels returned all-NaN because only numpy/pandas/scipy
were installed. They failed as PENDING โ indistinguishable from a genuine fail-safe.
A silent failure that looks exactly like a legitimate verdict is the worst category there is, and this
one was in my own research code. A 27/27 finite-value preflight is now mandatory before any cascade runs.
Section 3.1 asks "is this feature new?" This campaign asks the harder question: "is what this feature claims to measure actually real, and is it useful?" A number can be perfectly novel and still measure nothing. This ran every promoted column up a grounding ladder (F0โF4) until each one reached a terminal verdict.
It ran across the week โ the laptop-drives-bigblack loop harness on Wednesday (#655), a
SEAL-PASS cycle the same day (#657), cycle 2 on Thursday (#658), and closure on
Friday (#661).
19 GROUNDED ยท 1 REJECTED ยท 0 PARKED, plus 11 discovered and 0 left under evaluation. This is the only major campaign in this report that reached a terminal state on every single item.
The rejection (#12) is the load-bearing part. A grounding ladder that grounds
everything is a rubber stamp. One column went in with the same standing as the other 19 and
did not come out โ which is the only evidence I have that the other 19 verdicts mean anything.
We are trying to find features that let us build two specific kinds of strategy: strategies that stay robust out of distribution โ that keep working when the market stops looking like the data they were fitted on โ and strategies that are liquid and cascade-free, meaning we can actually get in and out without being caught when liquidity evaporates.
That goal is why the framework exists. It is not a generic feature-scoring tool; it is built backwards from those two properties.
It reconciles three strands of work that had been running separately โ the forward-predictiveness measurement stack, the realness/usefulness instruments, and the new cascade-free candidate harvest โ into a single funnel with two named axes:
Regime-invariance. A feature counts as orthogonal only if it stays non-redundant and keeps its meaning across market climates โ tested with causal-invariance methods rather than a correlation score. This is what "out-of-distribution robust" reduces to at the feature level.
Liquidity and crowding. How liquid and un-crowded a position is โ orthogonal to price by construction. This is what "liquid, cascade-free" reduces to at the feature level.
The 19 grounded instruments from ยง3.2 all ask one question: "does this predict forward return?" Axis-2 candidates are orthogonal to price by construction. So pointing those instruments at these 120 candidates would score every one of them at roughly zero and reject the entire cohort โ and it would look like a clean, rigorous result.
The fix is to swap the target. A liquidity metric is judged on whether it predicts what it costs to trade, and above all on whether it warns before costs blow up โ against the cost-realism labels from ยง3.4, which already record exactly that. So it is an existing yardstick, not one invented for the occasion.
#656, #659, #662) produced 120 candidates across order-flow toxicity, systemic fragility, jump/tail ignition, illiquidity and price impact, crypto liquidity integrity, and order-book depth. Zero duplicates across all six โ so the space is not yet saturated.#664): every gate, threshold and control fixed before any candidate was measured.Cheapest filters first, so the most candidates die for the least compute. Default is EXCLUDE โ a candidate advances only by clearing a gate. One gate per loop firing.
| Gate | Asks | Kind |
|---|---|---|
SEAL | Do the controls themselves work? A C1โC4 self-test, before any real candidate is touched | self-test |
F0 | Computable at all โ spot-legal, no order-book requirement, a Python path exists | deterministic |
F1 | Non-degenerate and dense enough | inferential |
F2 | Not a duplicate โ against the shipped set and within the cohort | inferential |
F3 | Beats the raw-volume baseline โ expected to do most of the killing | inferential |
F4 | Real and useful against the cost targets โ multiplicity-corrected, power-backed | inferential |
F5 | Holds across market regimes โ the ship bar | inferential |
Frozen thresholds: F2 BAN at max|ฯ| > 0.95 ยท F3 retention R โฅ 0.5 ยท
F4 null FPR โค 0.05, BY-FDR at q = 0.10, PBO โค 0.2, power
โฅ 0.80 ยท F5 k-of-N binomial exceedance at ฮฑ = 0.05.
120 in the frozen cohort ยท 0 decided ยท 0 admitted ยท SEAL gate due next. Nothing has been evaluated yet, and that is deliberate rather than incomplete โ the rulebook was frozen first precisely so the anticipated failure above could not be dressed up as a finding. Evaluation begins from the SEAL self-test.
For calibration: the comparable Axis-1 campaign ended 16 admitted against 45 killed, and the pre-registration expects roughly 19 of the 120 to park for want of order-book data this system does not carry.
The methodology above is published as a live page โ the question, the trap, the funnel, the rules that cannot be bent, and an append-only ledger that fills in as gates fire:
findings/dashboard/probes/2026-07-24-axis2-cascade-free-evaluation/index.html โ odb.epatner.com/opendeviationbar-py/dashboard/probes/2026-07-24-axis2-cascade-free-evaluation/
The narrative overview that sets up both axes in plain English, if you want the thesis before the
mechanics, is probes/regime-invariant-orthogonality-campaign-2026-07-22.html
("Orthogonality Direction โ Design Preview").
One housekeeping note: neither page is currently linked from the dashboard hub, site-map, or probes
index, so they are reachable only by direct URL. My own check-orphan-pages.py is what should
have caught that, and I am regenerating the nav.
This framework is a bet on what matters. I have aimed it at out-of-distribution robustness and liquidity/cascade-freedom because those are the two properties I think a strategy needs to survive contact with a real market โ and everything downstream inherits that choice. If you read the axes differently, or would weight them differently, the time to say so is now, while nothing has been evaluated and the cohort is still frozen.
The label_fwd_3s_* family is what makes the honest-cost numbers in ยง1.2 possible. The kernel
itself landed earlier in July; this week was the hardening pass after the first real production run.
| PR | What it fixed |
|---|---|
#627 | Written audit of the BTC label backfill โ execution, incidents, and robustness follow-ups. The record of what actually happened, not what was supposed to. |
#631 | Real-ClickHouse test harness + anti-join integration tests โ the resume logic is now tested against a real database, not a mock. |
#635 | Memory backpressure โ bounds the parallel backfiller's RSS so it can't take the box down. |
#648 | Order-independent sequential resume via anti-join and a retire watermark โ a restart no longer depends on the order work completed in. |
#649 | Operator runbook โ step-by-step safe procedure for a human driving the backfiller. |
#650 | Rebuild-coupling: orphan-label preflight, so a rebuild can't leave labels pointing at bars that no longer exist. |
#647 | Substrate-validity gate โ closed the ฮพ substrate caveat by validating the result on the actual substrate rather than a proxy. |
#660 (closed Thursday): a test fixture bar was leaking into the production
open_deviation_bars table via the Rust sink path, because the guard against it
was Python-only. Test data reaching a production table is the exact class of silent corruption that is
invisible until someone trusts the numbers. Worth checking whether any other guard in the system is
similarly implemented on one side of the language boundary only.
Context for ยง3.1 and ยง3.2, because "110 candidates in the registry" reads far more complete than it is:
axis.orthogonalBy orthogonality verdict: 88 null ยท 12 PASS ยท 5 BAN ยท 4 WATCH ยท 1 PENDING. Of the 88 unresolved, 13 are runnable today, 9 need only a reuse-confirmation, and 66 need a kernel implemented before they can even be tested. After batch-02 there were 3 runnable candidates left in the queue. The machinery is considerably further along than the coverage โ and per ยง3.1, the gate that would resolve the rest is currently the thing that's broken.
I want to be precise here, because "we built a CSCV-PBO module" and "we have a PBO number" are very different claims and it would be easy to blur them.
| Component | Guards against | Status |
|---|---|---|
| Worst-fill honest-cost P&L | FM-02 optimistic costs | Run ยท committed |
| Causal quantile binning / Phase-1 screen | FM-01 look-ahead | Run ยท committed |
| Moving-block bootstrap CIs (real-data resampling) | autocorrelation false alarms | Run ยท committed |
| Bonferroni + BH-FDR | FM-09 multiple testing | Run ยท committed |
| Exposure-matched random-entry null | FM-03 drift-as-alpha | Run ยท committed |
| Purged/embargoed walk-forward folds | FM-04 overfit / winner's curse | Run ยท committed |
| Permutation null | FM-05 concentration | Run ยท committed |
| Cascade-dependence check | FM-12/13 crash-premium | Run ยท committed |
| CSCV-PBO (252 splits, MaxTuW-scored) | FM-04/09 backtest overfitting | Built ยท never run |
| e-BH multiplicity (Wang & Ramdas 2022) | FDR under arbitrary dependence | Built ยท never run |
| Trial ledger (auto-tightening denominator) | FM-09/10 hidden search cost | Built ยท never run |
| OOD / LORO + covariate shift | FM-12 regime fragility | Built ยท never run |
| Cascade DSPOT dating + LOCO | FM-13 liquidity-cascade premium | Built ยท never run |
| Tail metrics (CVaR, CDaR, MaxTuW, Rachev, Omega) | FM-07 Sharpe worship | Built ยท never run |
| Cross-sectional panel + factor P&L | the drift problem | Execution gated |
| P5 trade-by-trade reconciliation oracle | implementation error | Planned only |
| P6 discovery engine (GP / PySR) | โ | Not built yet |
FM-08 says don't build infrastructure the edge doesn't yet justify, and its operational form is cheap rejectors first. The heavy deflation battery โ CSCV-PBO, e-BH, OOD/LORO โ is reserved for rules that clear the cheap cost gate. Zero rules cleared it, so the expensive machinery was never invoked. That is the funnel working as designed, not a gap.
The honest flip side: there is no computed PBO, DSR, or Sharpe value anywhere in the research repo. The first is because CSCV-PBO has only ever been exercised in unit tests; the latter two are banned outright by FM-07. If you ask me for a Sharpe ratio I do not have one, by policy.
Every verdict in this report cites one of these. The catalog started at 10 codes, now stands at 13, and grows toward the 29-bucket canonical inventory in the patterns repo. Worth noting that FM-13 was minted and then withdrawn during the PR-#7 adversarial pass โ its diagnostic turned out to be a worst-fill artifact โ and later re-grounded on BrunnermeierโPedersen (2009). The catalog gets falsified too.
| Code | Name | One line |
|---|---|---|
FM-01 | Look-ahead / feature leak | A feature or label-scaling window using information not available at the decision instant |
FM-02 | Optimistic (mid-price) costs | Pricing fills at mid instead of worst-fill โ manufactures edge โ the round-trip spread |
FM-03 | Drift mistaken for alpha | Confusing the market's own directional drift with strategy skill |
FM-04 | Overfitting / winner's curse | Edges that exist only because the window was short or the candidate was best-of-many |
FM-05 | P&L concentration | Total profit carried by one year, one fold, or a handful of trades |
FM-06 | Tautological / redundant rules | Always-true rules that inflate survivor counts and corrupt the multiple-testing denominator |
FM-07 | Sharpe / smooth-metric worship | Ranking by Sharpe on non-Normal, non-IID event-bar returns โ rewards artifact smoothness |
FM-08 | Infrastructure before edge | Building throughput before an edge is validated โ zero added capability, multiplied mistakes |
FM-09 | Uncorrected multiple testing | Significance without accounting for N hypotheses โ applies on the break side too |
FM-10 | Overfitting to the battery | The failure mode of the method itself โ a fixed N tests becomes a fitting signal |
FM-11 | Sub-cost directional ceiling | An ODB bar closes on a fixed ยฑthreshold, so mean per-bar move is pinned by the threshold |
FM-12 | Distribution-shift fragility | Resampling controls selection overfitting under one regime, not regime change |
FM-13 | Liquidity-cascade premium | Implicitly-short-vol strategies look like clean alpha in-distribution, then blow up |
Source of truth: research-audit/failure-modes/README.md (not the top-level README, which
only links to it).
Besides the cross-sectional run in ยง1.3, which is the main one:
| Repo | Open | Status |
|---|---|---|
opendeviationbar-py | #668 โ probe arbitration, negative control + real-legacy head-to-head | Open โ repair frozen, R2 validation aborted on a memory limit |
opendeviationbar-py | Axis-2 evaluation โ 120 frozen candidates, SEAL gate due next | Open โ ยง3.3, evaluation begins from the self-test |
subordinator | #2 โ clock-zoo baseline across 6 regime days | Open โ full provenance + telemetry artifacts |
The eight correctness PRs on the research repo (#32โ#41) all landed Monday
night, so that queue is clear.
data/PROVENANCE.md entry and live on bigblack rather than in the repo, and the BTCUSDT
15-feature slice has no SHA256 recorded anywhere. Backfilling it is cheap, and until it is done the
result is documented but not reproducible from repo contents.
axis.orthogonal cannot be set and 78
unevaluated candidates stay where they are.
Nothing here is waiting on a decision from you โ the list above proceeds either way. But there are three places where your read would genuinely change what I do, and this is the natural point to raise them.
Single-instrument, single-feature rules at a 3-second horizon are now closed off on crypto. The two live continuations are cross-sectional โ market-neutral at the same horizon, which cancels the drift that beat everything โ and longer-horizon, same construction at 30sโ5min, which grows the signal against a roughly fixed cost. Both are scoped and ready. I have taken cross-sectional first because the post-mortem points there, but I hold that view loosely and I do not think it is worth widening the current surface any further in either case.
The framework in ยง3.3 is a bet on what matters โ out-of-distribution robustness and liquidity cascade-freedom โ and everything downstream of it inherits that choice. The cohort is frozen and nothing has been evaluated, so this is the cheapest moment it will ever be to re-aim it. The same applies to the subordinator work in ยง2, which I am deliberately keeping small until it earns more.
This is the first weekly cut in this shape. If the balance is wrong โ too much method and not enough result, or the reverse โ it is easy to change, and I would rather adjust the format now than keep sending something that is not what you want to read.
Every figure here traces to a committed artifact. The primary sources, in order of authority:
| Claim | Source of truth |
|---|---|
| 600 rules, 5 symbols, 0 survivors | research-audit/loop-runs/enumerate-sieve-*-250-15feat.json (primary) ยท research-audit/findings/2026-07-24-single-instrument-crypto-dry.md |
| 5-round BTCUSDT loop | research-audit/loop-runs/round-{1..5}-*-report.json ยท findings/loop-ledger.md |
| Skeptic recomputations | research-audit/loop-runs/skeptics/ ยท round-{1..5}-skeptics.json |
| Attacker/defender concessions | research-audit/loop-runs/pr7-attacker.md ยท pr7-defender.md ยท round-*-adversarial-corrections.json |
| 11-rule pre-flight | research-audit/loop-runs/preflight-2026-07-22-btcusdt-250dbps.json |
| 21-symbol universe sweep | scripts/preflight_ch_sweep.sh (exact read-only SQL) ยท audit spoke 02 |
| Cost model | src/odb_research/{costs,labeling,labels}.py ยท data/PROVENANCE.md |
| Failure-mode definitions | research-audit/failure-modes/FM-{01..13}-*.md |
uv run python scripts/enumerate_sieve.py --all-features \ --data data/<SYMBOL>_250dbps_15feat.parquet \ --out research-audit/loop-runs/enumerate-sieve-<SYMBOL>-250-15feat.json \ --table research-audit/loop-runs/enumerate-sieve-<SYMBOL>-250-15feat.md
Runs on bigblack under a 5-core / 5 GB hard cap
(systemd-run --user --scope -p CPUQuota=500% -p MemoryMax=5G -p MemorySwapMax=0);
peak RSS โ 1 GB, 3โ6 min per symbol. Note the provenance gap flagged in ยง5 โ the input slices live on
bigblack, not in this repo.
Read-only reconciliation across four repositories. No code, data, or artifact was modified to produce this document. All results are the repos' own committed outputs; I did not re-execute the test suite, any SQL, or any reproduce script for this report.
Doctrine reminder. Verdicts here are falsified or survived this round โ never pass, fail, or go. Nothing in this report constitutes a claim of tradeable edge. Default verdict for every strategy remains NO ALPHA.