Public-API research deep-dive driving the just-shipped GER40 real-data
session-breakout demo (5b5f034) through the escalating sequence a serious
researcher attempts next: scale to multi-year, walk-forward, structural-param
sweep, multi-instrument compare. Standalone consumer crate (path-deps only;
public interface = ledger + glossary + example corpus + cargo doc; no
crates/*/src read). All runs against the real /mnt/tickdata/Pepperstone archive.
Findings: 3 bugs, 1 friction, 3 spec_gap, 1 working. Triaged to the tracker:
- spec_gap: the flagship breakout ships as a raw FlatGraph the World API cannot
consume (sweep / walk_forward / compare all forced a hand re-author to a
Composite, which reproduced the FlatGraph numbers EXACTLY) — refs #94.
- bug: process residency is O(window) (6->173 MiB across 1mo->10y) despite the
documented O(one-chunk) claim; the aura ring stays <=1024 but data-server
retention scales, and streaming_seam.rs only measures the ring — refs #95.
- bug: the bar-period is one knob split across Resample (tunable param) and
Session (baked) and desyncs silently to 0.0 pips; Delay.lag leaks into the
default param_space (the C34 deform-vs-tune discriminator) — refs #96.
- spec_gap: the walk_forward empty-space idiom for param-free strategies is
undefined — refs #97.
- spec_gap (no per-instrument pip registry) and friction (one-directional
ns->ms conversion) re-confirm the known #22 and #80.
- working: C1 determinism over 7y / 148k bars, the lazy streaming source, and
the sweep/WFO cores held under real multi-year load; the Composite re-author
was behaviour-preserving (C23).
Scratch consumer crate + run transcripts committed under fieldtests/ per the
fieldtest convention (matching cycle-0049). The crate path-deps the engine and
reads the real archive; its bins are the repros cited in the issues above.
16 KiB
Fieldtest — research deep-dive on the GER40 session-breakout demo — 2026-06-17
Status: Draft — awaiting orchestrator triage Author: fieldtester (downstream-quant-researcher dispatch)
Scope
The just-shipped runnable demo crates/aura-ingest/examples/ger40_breakout_real.rs
(+ examples/shared/breakout_real.rs, commit 5b5f034) backtests the 15m
Frankfurt session-breakout over real GER40 M1 bars for one month. This dispatch
pushes that strategy through the escalating sequence a serious researcher attempts
next: (1) scale to a full year and a 7-year span; (2) walk-forward across real
history; (3) sweep the structural params (entry/exit bar, bar period); (4) compare
signal quality across instruments (GER40 vs FRA40). Worked ONLY from the public
surface (design ledger, glossary, the example corpus, cargo doc); no engine
crates/*/src was read. All runs are against the real /mnt/tickdata/Pepperstone
archive. Binaries path-dep the engine crates (HEAD source), built --release.
The central, recurring tension: the demo ships the breakout as a hand-wired raw
FlatGraph, but every World orchestration family (sweep / walk_forward /
optimize) consumes a Composite blueprint with param_space() /
bootstrap_with_cells. A FlatGraph has no param space, so to take this exact
strategy into the World a researcher must first re-author it as a Composite —
re-typing the 13-node wiring by hand. The demo provides no such blueprint.
Examples
All fixtures live under fieldtests/research-breakout-deepdive/ (a standalone
C16-style consumer crate). Output transcripts are the sibling out_*.txt.
t1_scale.rs — scale the breakout over long horizons
- Reuses the shipped
shared/breakout_real.rswiring verbatim (via#[path]), changing only the window: month → year → 7 years; adds an RSS probe + timer. - Fits scope: the first thing a researcher does with a one-month demo is run it over real history; the docs make a load-bearing O(one-chunk) residency claim.
- Outcome: built, ran. 2024-09 −45.0 pips / 5 sessions (matches the shipped demo). 2024 full year +203.4 / 62 sessions. 2018..2024 (7y) +520.2 / 401 sessions in 1.12s. Determinism held (rerun report JSON bit-identical). But RSS grew 8→127 MiB on the 7y run.
t1b_residency.rs / t1c_residency_scaling.rs — residency attribution
- Isolates where the memory goes: drives 4 raw
M1FieldSources with no harness/ sinks, then the full harness; and sweeps one source over 1mo→10yr windows. - Fits scope: pin down whether the O(one-chunk) claim holds at the process level.
- Outcome: built, ran.
resident_records ≤ 1024(the docs' asserted predicate) holds at every window length, yet VmHWM scales linearly with window: 6 → 15 → 24 → 78 → 173 MiB across 1mo → 6mo → 1y → 4y → 10y of one symbol's Close.
t2_walkforward.rs — walk-forward the shipped breakout (2018..2024)
- Rolls the breakout with
walk_forward(12mo IS / 3mo OOS / 3mo step, Rolling), stitching the OOS pip curve. - Fits scope: the headline World promise on the headline strategy.
- Outcome: built, ran. 24 windows; stitched OOS curve 131508 points ending
+618.8 pips. But the walk-forward is degenerate — see finding below: the
breakout being a raw
FlatGraphmeansspace = vec![],chosen_params = vec![], and there is NO in-sample optimization (the whole point of WFO).
t3_sweep.rs / t3b_period_desync.rs — sweep the structural params
- Re-authors the breakout as a
Composite, printsparam_space(), runs a 3×3 entry_bar×exit_bar grid, and demonstrates the period-coupling failure. - Fits scope: the mission's explicit coupling question (Resample period ==
Session period; entry/exit as
EqConsttargets). - Outcome: built, ran. Grid works;
entry_bar=3,exit_bar=5reproduces the shipped −45.0 exactly (re-authoring fidelity). The coupling is not expressible, and violating it fails silently (period-desync run → 0.0 pips, no error).
t4_compare.rs — GER40 vs FRA40 signal quality
- Reuses the
Composite, loops over(symbol, pip_size), compares 2024. - Fits scope: the structural "which instrument" axis + the per-asset pip question.
- Outcome: built, ran. GER40 +203.4 / 62 sessions vs FRA40 +83.7 / 63 sessions (both pip=1.0 index points). Comparison is honest only because the researcher hand-supplied matching pip sizes — there is no instrument registry.
Findings
[spec_gap] The shipped breakout is a raw FlatGraph, unreachable by the World API
- Example(s): t2_walkforward, t3_sweep, t4_compare (all three had to re-author it).
- What happened:
sweep/walk_forward/optimizeoperate on aComposite(they needparam_space()+bootstrap_with_cells). The demo's breakout is a hand-wiredFlatGraphbuilt byHarness::bootstrap, which has no param space. To do any sweep/optimize/WFO of the shipped strategy I re-typed all 13 nodes + 14 edges + 4 roles as aComposite(t3_sweep.rsbreakout_composite). - Why spec_gap: C20/C21 promise the strategy blueprint is "reused across backtest, sweep, visual workspaces"; the milestone fixtures demonstrate this for SMA-cross. But the just-shipped, real-data flagship strategy ships in a form the World cannot consume, and the design ledger does not say which form a shippable strategy should take. A researcher cannot tell from the public surface whether the FlatGraph form is intentional or a gap. The re-reading I picked (re-author as Composite) is plausible; "the demo should have shipped a Composite" is equally plausible — the ledger is silent.
- Recommended action: ship the breakout as a reusable
Compositeblueprint (alongside or instead of the FlatGraph), so the demo's strategy is World-ready; or document in the example that the FlatGraph is a substrate-level fixture and a blueprint is the consumer form.
[bug] Process residency scales with window length despite the O(one-chunk) claim
- Example(s): t1_scale, t1b_residency, t1c_residency_scaling.
- What happened: the docs (C12 cycle-0041 realization;
tests/streaming_seam.rsresidency_is_bounded_by_one_chunk_independent_of_window_length) claim "a 20-year window streams in the same memory as a one-day one". Empirically the aura-level predicateresident_records ≤ CHUNK_SIZE (1024)holds at every window length, but VmHWM (OS peak RSS) grows ~linearly: 6→15→24→78→173 MiB for 1mo→6mo→1y→4y→10y of ONE symbol's Close field; the 7-year 4-field breakout hits 127 MiB streaming, 210 MiB afterdrain_trace. The test's predicate measures only the auraM1FieldSourcering, not the underlyingdata-serverchunk/file retention, so it passes while the process-level promise is false. - Why bug: a load-bearing, documented, test-asserted guarantee ("streaming residency is O(one chunk), independent of window length") is contradicted by the process's own VmHWM. A researcher who budgets RAM on the doc's promise (one chunk ≈ tens of KiB) will be off by 100-1000×; a real multi-symbol sweep over decades would OOM where the docs say it streams flat.
- Repro:
cargo run --release --manifest-path fieldtests/research-breakout-deepdive/Cargo.toml --bin t1c_residency_scaling→ VmHWM column grows monotonically with the record count while theaura-resident<=1024?column staystrue. - Recommended action:
debug— either thedata-serverchunks/decoded buffers are retained across pulls (release them), or the residency claim must be narrowed in the docs/test to "aura-level ring residency" (which is all the predicate proves) and the true per-window process cost documented.
[spec_gap] walk_forward demands a param-space even with nothing to optimize
- Example(s): t2_walkforward.
- What happened:
walk_forward(roller, space: Vec<ParamSpec>, F)withWindowRun { chosen_params: Vec<Cell>, .. }. For a fixed-param OOS roll of a param-free strategy I passedspace = vec![]andchosen_params = vec![]. It ran, but the result is a walk-forward with no in-sample optimization — the OOS verdict of a strategy that was never tuned per window. The signature gives no hint whether the empty-space idiom is the intended way to express "OOS-only, no optimize", or whether WFO without per-window optimize is even a supported use. - Why spec_gap: I guessed the empty-space reading; "WFO requires a non-empty space and per-window optimize" is an equally plausible reading the ledger does not settle. Combined with the FlatGraph finding, the headline WFO demo on the headline strategy is unreachable in its optimizing form without re-authoring.
- Recommended action:
ratifythe param-free OOS-roll idiom (document that empty space + empty chosen_params is a valid fixed-strategy walk-forward), or tightenwalk_forwardto make the optimize-per-window contract explicit.
[bug] Structural deformers (Delay.lag, Resample.period_minutes) leak into param_space
- Example(s): t3_sweep (the grid panicked on the un-enumerated
delay.lagslot). - What happened: the breakout
Composite'sparam_space()is{ resample.period_minutes, delay.lag, entry_bar.target, exit_bar.target }.delay.lagis the breakout's "previous bar's high" — a STRUCTURAL constant (varying it deforms the strategy, the exact C34 deform-vs-tune discriminator), yetDelay::builder()shipslagas a tunable param. SoGridSpace::newREQUIRES the researcher to enumeratedelay.lag(or it errors), and a naive random sweep would silently deform the strategy. C34'sbind()can remove it, but the node pollutes the space by default. - Why bug: this is the precise failure C34 (cycle 0034) exists to prevent — a
structural deformer presented as a tuning knob. The strategy author did not opt
into it; the node's default
builder()forces the pollution onto every consumer. - Repro:
cargo run --release --manifest-path fieldtests/research-breakout-deepdive/Cargo.toml --bin t3_sweep→param_space()listsdelay.lag; removing its grid axis makesGridSpace::newreject the grid. - Recommended action:
debug/planner— decide whetherDelay.lag(andResample.period_minutes) are tuning params or structural constants; if structural, they should not appear in a defaultparam_space()(or the demo shouldbind()them out and the example should model that for consumers).
[bug] The Resample/Session period coupling is unenforceable and fails silently
- Example(s): t3_sweep, t3b_period_desync.
- What happened: the breakout's bar period lives in TWO places —
Resample::builder()exposesperiod_minutesas a tunable param, butSession::builder(open, tz, period)BAKES its period (its rustdoc: "NOT scalar params, the declared param list is empty"). Soparam_space()hasresample.period_minutesbut NO session period. Sweepingresample.period_minutes = 30while Session stays baked at 15 bootstraps and runs with NO error, but the two clocks desync:entry_bar.target == 3no longer means "3rd 30m bar". The desynced run produced 0.0 pips / no error; a coherent 30m strategy (+63.0 pips) was reachable ONLY by rebuilding theCompositewithsession_period = 30(a different blueprint — topology, not a sweep). - Why bug: the public sweep API silently admits a parameter combination that is structurally meaningless, and the failure mode is a quiet 0.0-pip result rather than a diagnostic. The two periods are one logical knob split across a tunable param and a baked factory arg with no coupling mechanism on the public surface.
- Repro:
cargo run --release --manifest-path fieldtests/research-breakout-deepdive/Cargo.toml --bin t3b_period_desync→ the resample=30/session=15 row printstotal_pips=0.0with no error. - Recommended action:
debug/planner— either make Session's period a tunable param so a single swept value feeds both (the C8/C12 way), or provide a coupling/ derived-param mechanism, or have the demobindthe period as a structural constant so it cannot be swept into a desync. A silent 0.0 is the worst outcome.
[spec_gap] No per-instrument pip/metadata registry; cross-asset honesty is manual
- Example(s): t4_compare.
- What happened:
SimBroker::new(pip_size)takes a baref64. There is no instrument/pip-metadata registry on the public surface —aura-ingestexposes only M1 streaming, andAura.toml(which the glossary says carries "instrument/pip metadata") does not exist (open architectural thread). So the researcher hard-codes pip_size per symbol from outside knowledge (GER40=1.0, FRA40=1.0, EURUSD=0.0001). The GER40-vs-FRA40 comparison is honest only because both happen to be index points and the researcher knew to pass 1.0 for each; nothing flags or prevents comparing GER40 index-pips to EURUSD fx-pips. - Why spec_gap: C10/C20 lean on "per-instrument pip metadata (reference data beside the hot path)" for the sim-optimal broker's currency-free yardstick, but the public surface offers no place to register or look up that metadata, so the yardstick's cross-asset honesty is entirely on the researcher. The ledger names the metadata; the surface does not realize it. (Acknowledged as an open thread, so this is a confirmation that the gap bites at the first real comparison.)
- Recommended action:
ratify/planner— sequence theAura.tomlinstrument/pip schema (or a minimal pip-metadata source) so cross-asset comparison can be honest by construction, not by researcher discipline.
[working] Determinism, the streaming source, and the sweep/WFO cores are solid
- Example(s): t1_scale, t1c, t2, t3, t4.
- What happened: every long-horizon run was bit-identical on rerun (C1 held over
7 years / 148k bars). The lazy
M1FieldSourcestreamed a 7-year, 4-field, 8.6M- record window in 1.12s with no errors. TheCompositere-authoring reproduced the shipped FlatGraph's numbers EXACTLY (entry=3,exit=5→ −45.0; 2024 full year → +203.4 / 62 sessions, identical in t1 and t4) — strong evidence the bootstrap/compile andbootstrap_with_cellspaths are behaviour-preserving (C23).sweepparallelized the 9-point grid andwalk_forwardproduced 24 windows + a correctly stitched, timestamp-monotone OOS curve. The named-param surface (param_space,named_params,GridSpace::newvalidating axes against the space) is clean and self-describing where the strategy IS a Composite. - Why working: these are the load-bearing guarantees the whole World rests on, and they held under real multi-year load on the first or second try. Worth recording so they are protected from drift.
- Recommended action:
carry-on.
[friction] ns↔ms conversion is one-directional; residency probe not on the trait
- Example(s): t2_walkforward, t1b_residency.
- What happened: (a)
WindowBounds/RunManifest.windoware epoch-ns butM1FieldSource::openwants Unix-ms;aura-ingestexportsunix_ms_to_epoch_nsbut NOT its inverse, so every real-data walk-forward hand-dividests.0 / 1_000_000(the milestonemw_3fixture hit the identical seam). (b)resident_records()is an inherent method on the concreteM1FieldSource, not on the publicSourcetrait, so a consumer drivingBox<dyn Source>(asrundoes) cannot read residency — I had to drop to the concrete type to probe it. - Why friction: both tasks completed, but the surface forced a verbose dance the ledger's "producer-supplied window" / "residency is O(one chunk)" framing implied would be cleaner.
- Recommended action:
plan— addepoch_ns_to_unix_mstoaura-ingest(mirror of the existing forward fn), and consider surfacing residency on theSourcetrait (or document that it is concrete-only).
Recommendation summary
| Finding | Action |
|---|---|
| spec_gap — breakout ships as FlatGraph, not a World-consumable Composite | planner (ship a Composite blueprint) |
| bug — process residency scales with window despite O(one-chunk) claim | debug |
| spec_gap — walk_forward empty-space idiom undefined for param-free strategy | ratify |
| bug — Delay.lag / Resample.period_minutes leak into default param_space | debug / planner |
| bug — Resample/Session period coupling unenforceable, desyncs silently to 0 pips | debug / planner |
| spec_gap — no per-instrument pip/metadata registry; cross-asset honesty manual | ratify / planner |
| working — C1 determinism, streaming source, sweep/WFO cores, re-author fidelity | carry-on |
| friction — one-directional ns/ms conversion; residency not on Source trait | plan |