feat(aura-ingest): World-family demos prove the GER40 breakout is World-consumable (spec 0051)

The shipped Composite blueprint (ger40_breakout_blueprint) is now driven by the
World orchestration families on real GER40, with NO re-authoring — the #94
friction is gone:
- examples/ger40_breakout_sweep.rs — sweep the {entry_bar, exit_bar} grid built
  directly from param_space() (best entry=3,exit=4 -> 214.0 over 2024).
- examples/ger40_breakout_walkforward.rs — non-degenerate walk_forward: a real
  non-empty space optimized per window, chosen_params populated everywhere (#97).
- examples/ger40_breakout_compare.rs — one blueprint across two structural axes:
  instrument (GER40 vs FRA40) and bar period (15m vs 30m — different strategies,
  C34), no re-authoring.
- tests/ger40_breakout_world.rs — gated: sweep returns the ranked 9-point grid
  over the two named params; walk_forward yields non-empty windows each with a
  non-empty chosen_params (executable #97 proof).

No blueprint reshape needed — the factory returns a fresh (Composite, Taps) per
call, so the World families re-instantiate per grid point / window (the SMA-cross
pattern); baked recorders do not block multi-bootstrap.

closes #94, closes #96, closes #97
This commit is contained in:
2026-06-17 19:29:39 +02:00
parent 391f07dc36
commit bbd6738d69
4 changed files with 837 additions and 0 deletions
@@ -0,0 +1,181 @@
//! Runnable World-family demo: **`compare`** the session-breakout `Composite`
//! blueprint (spec 0051) across two structural axes (C11/C20 experiment matrix),
//! on REAL data, with NO re-authoring:
//!
//! 1. **Instrument** — GER40 vs FRA40 (both `pip_size = 1.0`, index points), the
//! SAME blueprint bootstrapped at the same `{entry=3, exit=5}` point over each
//! symbol. The pip honesty is by construction (both quote in index points; the
//! engine has no pip registry yet — #22, filed).
//! 2. **Bar period** — a 15m blueprint vs a 30m blueprint. The bar period is a
//! STRUCTURAL axis (§2 / C34): `ger40_breakout_blueprint` is instantiated TWICE,
//! at 15 and at 30, each a *different strategy* (the period is bound at
//! construction, locking Resample and Session equal — it is NOT a sweep param).
//!
//! A "comparison" here is a plain Rust loop over the matrix (C20): one blueprint
//! per matrix cell, each bootstrapped + run + summarized, the reports tabulated.
//!
//! Run:
//! ```text
//! cargo run -p aura-ingest --example ger40_breakout_compare
//! ```
//! Prints both comparison tables. Skips a row cleanly where a symbol is absent.
use std::sync::Arc;
use aura_core::{Cell, Scalar, Timestamp};
use aura_engine::{summarize, RunMetrics, Source};
use chrono::TimeZone;
use chrono_tz::Europe::Berlin;
use data_server::{DataServer, DEFAULT_DATA_PATH};
use aura_ingest::{M1Field, M1FieldSource};
#[path = "shared/breakout_real.rs"]
mod breakout_real;
use breakout_real::*;
// The baseline tuning point both axes hold fixed (so only the structural axis
// varies): entry bar 3, exit bar 5 — the shipped baseline.
const ENTRY_BAR: i64 = 3;
const EXIT_BAR: i64 = 5;
// GER40 and FRA40 both quote in index points, so pip_size = 1.0 for both — the
// only reason a cross-instrument pip comparison is honest here (no engine
// registry, #22). (symbol, pip_size, human note)
const INSTRUMENTS: &[(&str, f64, &str)] = &[
("GER40", 1.0, "index point"),
("FRA40", 1.0, "index point"),
];
/// Bootstrap the blueprint (at `bar_period`) at `{entry=3, exit=5}`, run it over
/// `[from_ms, to_ms]` of real `symbol` OHLC, and summarize — or `None` if the
/// symbol has no file overlapping the window. The `pip_size` rides on the
/// blueprint's baked SimBroker (always 1.0 here; both indices). Returns the
/// report plus the entry count (0→1 transitions of held).
fn run_cell(
server: &Arc<DataServer>,
symbol: &str,
bar_period: i64,
from_ms: i64,
to_ms: i64,
) -> Option<(RunMetrics, u32)> {
let (bp, taps) = ger40_breakout_blueprint(bar_period, SESSION_HOUR, SESSION_MINUTE, Berlin);
let point: Vec<Cell> = bp
.param_space()
.iter()
.map(|ps| match ps.name.as_str() {
"entry_bar.target" => Scalar::i64(ENTRY_BAR).cell(),
"exit_bar.target" => Scalar::i64(EXIT_BAR).cell(),
other => panic!("unexpected param slot {other}"),
})
.collect();
let mut h = bp.bootstrap_with_cells(&point).expect("point kind-checked");
let fields = [M1Field::Open, M1Field::High, M1Field::Low, M1Field::Close];
let mut srcs: Vec<Box<dyn Source>> = Vec::with_capacity(4);
for &f in &fields {
let s = M1FieldSource::open(server, symbol, Some(from_ms), Some(to_ms), f)?;
srcs.push(Box::new(s));
}
h.run(srcs);
drop(h);
let equity: Vec<(Timestamp, f64)> = taps
.equity
.try_iter()
.map(|(t, r)| (t, as_f64(&r[0])))
.collect();
let held: Vec<(Timestamp, f64)> = taps
.held
.try_iter()
.map(|(t, r)| (t, as_f64(&r[0])))
.collect();
// Count entries (0 -> 1 transitions of held).
let mut entries = 0u32;
let mut prev = 0.0_f64;
for (_, v) in &held {
if *v == 1.0 && prev == 0.0 {
entries += 1;
}
prev = *v;
}
Some((summarize(&equity, &held), entries))
}
fn main() {
let server = Arc::new(DataServer::new(DEFAULT_DATA_PATH));
if !server.has_symbol(SYMBOL) {
println!("skip: no local data at {DEFAULT_DATA_PATH} (symbol {SYMBOL} absent).");
return;
}
// Full year 2024 for body.
let from_ms = chrono::Utc
.with_ymd_and_hms(2024, 1, 1, 0, 0, 0)
.unwrap()
.timestamp_millis();
let to_ms = chrono::Utc
.with_ymd_and_hms(2024, 12, 31, 23, 59, 59)
.unwrap()
.timestamp_millis();
println!("=== GER40 session-breakout — World `compare` (structural matrix) ===");
println!("window=2024 (UTC, full year), fixed tuning point entry={ENTRY_BAR}, exit={EXIT_BAR}\n");
// --- Axis 1: instrument (GER40 vs FRA40), 15m blueprint. -----------------
println!("--- axis 1: instrument (same 15m blueprint, GER40 vs FRA40) ---");
println!(
"{:<8} {:<14} {:>9} {:>11} {:>10} {:>7}",
"symbol", "pip-meaning", "sessions", "total_pips", "max_dd", "flips"
);
println!("{}", "-".repeat(62));
for &(symbol, _pip, note) in INSTRUMENTS {
if !server.has_symbol(symbol) {
println!("{symbol:<8} (no local data — skip)");
continue;
}
match run_cell(&server, symbol, BAR_MINUTES, from_ms, to_ms) {
Some((m, entries)) => println!(
"{symbol:<8} {note:<14} {entries:>9} {:>11.1} {:>10.1} {:>7}",
m.total_pips, m.max_drawdown, m.exposure_sign_flips
),
None => println!("{symbol:<8} (no overlapping OHLC files — skip)"),
}
}
println!();
// --- Axis 2: bar period (15m vs 30m blueprint), GER40. -------------------
// The STRUCTURAL axis: ger40_breakout_blueprint instantiated at 15 AND at 30
// — two *different strategies* (the period binds Resample AND Session equal at
// construction; it is not a sweep param). C34 / C11.
println!("--- axis 2: bar period (GER40, 15m blueprint vs 30m blueprint) ---");
println!(
"{:<10} {:>9} {:>11} {:>10} {:>7}",
"bar-period", "sessions", "total_pips", "max_dd", "flips"
);
println!("{}", "-".repeat(52));
for &period in &[15_i64, 30] {
match run_cell(&server, SYMBOL, period, from_ms, to_ms) {
Some((m, entries)) => println!(
"{:<10} {entries:>9} {:>11.1} {:>10.1} {:>7}",
format!("{period}m"),
m.total_pips,
m.max_drawdown,
m.exposure_sign_flips,
),
None => println!("{period}m (no data — skip)"),
}
}
println!("\nOK: one blueprint, two structural axes (instrument + bar period) compared");
println!(" with no re-authoring — the bar period is a construction arg (15m and 30m");
println!(" are different strategies, C34), the instrument a plain matrix loop (C20).");
}
fn as_f64(s: &Scalar) -> f64 {
match s {
Scalar::F64(v) => *v,
other => panic!("expected f64, got {other:?}"),
}
}
@@ -0,0 +1,165 @@
//! Runnable World-family demo: **`sweep`** the GER40 session-breakout `Composite`
//! blueprint (spec 0051) over its two genuine tuning knobs — `entry_bar.target` ×
//! `exit_bar.target` — on REAL GER40 M1 bars, ranked by `total_pips`. This is the
//! core proof that the shipped strategy is World-consumable: the SAME
//! `ger40_breakout_blueprint` the single backtest bootstraps is driven through the
//! engine's `sweep` machinery with NO re-authoring (the #94 friction is gone), its
//! `param_space()` exercised directly.
//!
//! Run:
//! ```text
//! cargo run -p aura-ingest --example ger40_breakout_sweep
//! ```
//! Prints the ranked entry×exit grid (best `total_pips` first) over a full-year
//! 2024 window. Skips cleanly (no panic) where the archive is absent.
use std::sync::Arc;
use aura_core::{Cell, Scalar, Timestamp};
use aura_engine::{sweep, summarize, GridSpace, RunManifest, RunReport};
use chrono_tz::Europe::Berlin;
use data_server::{DataServer, DEFAULT_DATA_PATH};
#[path = "shared/breakout_real.rs"]
mod breakout_real;
use breakout_real::*;
// The sweep grid: entry bar ∈ {2,3,4}, exit bar ∈ {4,5,6} — the two EqConst
// targets that ARE the blueprint's param_space().
const ENTRY_BARS: &[i64] = &[2, 3, 4];
const EXIT_BARS: &[i64] = &[4, 5, 6];
/// Bootstrap the blueprint at one grid point, run it over `[from_ms, to_ms]` of
/// real GER40 OHLC, and fold the recorded equity/held taps into a `RunReport`
/// (`summarize` over the drained channels). A fresh blueprint per call (fresh
/// nodes + channels) keeps the disjoint sweep points independent (C1).
fn run_point(server: &Arc<DataServer>, point: &[Cell], from_ms: i64, to_ms: i64) -> RunReport {
let (bp, taps) = ger40_breakout_blueprint(BAR_MINUTES, SESSION_HOUR, SESSION_MINUTE, Berlin);
let mut h = bp
.bootstrap_with_cells(point)
.expect("sweep point kind-checked against param_space");
let sources =
open_ohlc_sources(server, from_ms, to_ms).expect("window overlaps GER40 data");
h.run(sources);
drop(h);
let equity: Vec<(Timestamp, f64)> = taps
.equity
.try_iter()
.map(|(t, r)| (t, as_f64(&r[0])))
.collect();
let exposure: Vec<(Timestamp, f64)> = taps
.held
.try_iter()
.map(|(t, r)| (t, as_f64(&r[0])))
.collect();
RunReport {
manifest: RunManifest {
commit: "ger40-breakout-sweep".to_string(),
params: vec![],
window: (
aura_ingest::unix_ms_to_epoch_ns(from_ms),
aura_ingest::unix_ms_to_epoch_ns(to_ms),
),
seed: 0,
broker: "sim-optimal(pip_size=1)".to_string(),
},
metrics: summarize(&equity, &exposure),
}
}
fn main() {
let server = Arc::new(DataServer::new(DEFAULT_DATA_PATH));
if !server.has_symbol(SYMBOL) {
println!("skip: no local data at {DEFAULT_DATA_PATH} (symbol {SYMBOL} absent).");
return;
}
// Full-year 2024.
let (from_ms, _) = utc_month_window_ms(2024, 1);
let (_, to_ms) = utc_month_window_ms(2024, 12);
// The grid is built from the blueprint's param_space() directly — the two
// EqConst targets are the swept axes (no re-authoring).
let space = ger40_breakout_blueprint(BAR_MINUTES, SESSION_HOUR, SESSION_MINUTE, Berlin)
.0
.param_space();
let grid = GridSpace::new(
&space,
space
.iter()
.map(|ps| match ps.name.as_str() {
"entry_bar.target" => ENTRY_BARS.iter().map(|&v| Scalar::i64(v)).collect(),
"exit_bar.target" => EXIT_BARS.iter().map(|&v| Scalar::i64(v)).collect(),
other => panic!("unexpected param slot {other}"),
})
.collect(),
)
.expect("grid well-formed for the breakout param_space");
println!("=== GER40 session-breakout — World `sweep` over entry×exit ===");
println!(
"symbol={SYMBOL} window=2024 (UTC, full year) bars=15m pip_size={PIP_SIZE} \
grid={}×{} = {} points",
ENTRY_BARS.len(),
EXIT_BARS.len(),
grid.len(),
);
println!("param_space() (the only swept knobs): {:?}", space.iter().map(|p| p.name.as_str()).collect::<Vec<_>>());
println!();
let server_for_closure = Arc::clone(&server);
let family = sweep(&grid, |pt: &[Cell]| {
run_point(&server_for_closure, pt, from_ms, to_ms)
});
// Rank the family by total_pips, descending.
let mut ranked: Vec<usize> = (0..family.points.len()).collect();
ranked.sort_by(|&a, &b| {
family.points[b]
.report
.metrics
.total_pips
.partial_cmp(&family.points[a].report.metrics.total_pips)
.unwrap_or(std::cmp::Ordering::Equal)
});
println!(
"{:>4} {:<10} {:<9} {:>12} {:>10} {:>7}",
"rank", "entry_bar", "exit_bar", "total_pips", "max_dd", "flips"
);
println!("{}", "-".repeat(58));
for (rank, &i) in ranked.iter().enumerate() {
let named = family.named_params(i);
let entry = named.iter().find(|(n, _)| n == "entry_bar.target").map(|(_, v)| v.as_i64()).unwrap_or(-1);
let exit = named.iter().find(|(n, _)| n == "exit_bar.target").map(|(_, v)| v.as_i64()).unwrap_or(-1);
let m = &family.points[i].report.metrics;
println!(
"{:>4} {:<10} {:<9} {:>12.1} {:>10.1} {:>7}",
rank + 1,
entry,
exit,
m.total_pips,
m.max_drawdown,
m.exposure_sign_flips,
);
}
println!();
let best = ranked[0];
let best_named = family.named_params(best);
println!(
"best point: {:?} total_pips={:.1}",
best_named
.iter()
.map(|(n, v)| format!("{}={}", n.replace(".target", ""), v.as_i64()))
.collect::<Vec<_>>(),
family.points[best].report.metrics.total_pips,
);
println!("\nOK: the shipped breakout blueprint was swept over its param_space() — no re-authoring.");
}
fn as_f64(s: &Scalar) -> f64 {
match s {
Scalar::F64(v) => *v,
other => panic!("expected f64, got {other:?}"),
}
}
@@ -0,0 +1,230 @@
//! Runnable World-family demo: **`walk_forward`** the GER40 session-breakout
//! `Composite` blueprint (spec 0051) across multi-year real GER40 history, with a
//! per-window in-sample optimize of its two genuine tuning knobs
//! (`entry_bar.target` × `exit_bar.target`). This is the executable #97
//! resolution: the roll is NON-DEGENERATE — a real, non-empty `param_space()` is
//! optimized per window, so every window's `chosen_params` is populated (contrast
//! the earlier fieldtest's empty-space degenerate roll over the raw FlatGraph, on
//! which no in-sample tuning was possible).
//!
//! Run:
//! ```text
//! cargo run -p aura-ingest --example ger40_breakout_walkforward
//! ```
//! Prints each IS/OOS window with the entry/exit it chose in-sample and its OOS
//! pips, plus the stitched out-of-sample equity curve. Skips cleanly where the
//! archive is absent.
use std::sync::Arc;
use aura_core::{Cell, Scalar, Timestamp};
use aura_engine::{
sweep, summarize, walk_forward, GridSpace, RollMode, RunManifest, RunReport, WindowBounds,
WindowRoller, WindowRun,
};
use chrono::TimeZone;
use chrono_tz::Europe::Berlin;
use data_server::{DataServer, DEFAULT_DATA_PATH};
#[path = "shared/breakout_real.rs"]
mod breakout_real;
use breakout_real::*;
// The in-sample grid the per-window optimize searches: entry ∈ {2,3,4}, exit ∈
// {4,5,6} — the two EqConst targets that ARE the blueprint's param_space().
const ENTRY_BARS: &[i64] = &[2, 3, 4];
const EXIT_BARS: &[i64] = &[4, 5, 6];
/// ns → ms: `open_ohlc_sources` opens by Unix-ms, but `WindowBounds` are
/// epoch-ns. `aura-ingest` exports only the forward direction, so the inverse is
/// a local hand-divide (#80, filed).
fn ns_to_ms(ts: Timestamp) -> i64 {
ts.0 / 1_000_000
}
/// Bootstrap the blueprint at one grid point, run it over `[from_ms, to_ms]` of
/// real GER40 OHLC, and fold the recorded equity/held taps into `(RunReport,
/// equity-segment)`. Fresh blueprint per call (C1).
fn run_point(
server: &Arc<DataServer>,
point: &[Cell],
from_ms: i64,
to_ms: i64,
) -> (RunReport, Vec<(Timestamp, f64)>) {
let (bp, taps) = ger40_breakout_blueprint(BAR_MINUTES, SESSION_HOUR, SESSION_MINUTE, Berlin);
let mut h = bp
.bootstrap_with_cells(point)
.expect("point kind-checked against param_space");
let sources =
open_ohlc_sources(server, from_ms, to_ms).expect("window overlaps GER40 data");
h.run(sources);
drop(h);
let equity: Vec<(Timestamp, f64)> = taps
.equity
.try_iter()
.map(|(t, r)| (t, as_f64(&r[0])))
.collect();
let exposure: Vec<(Timestamp, f64)> = taps
.held
.try_iter()
.map(|(t, r)| (t, as_f64(&r[0])))
.collect();
let report = RunReport {
manifest: RunManifest {
commit: "ger40-breakout-wfo".to_string(),
params: vec![],
window: (
aura_ingest::unix_ms_to_epoch_ns(from_ms),
aura_ingest::unix_ms_to_epoch_ns(to_ms),
),
seed: 0,
broker: "sim-optimal(pip_size=1)".to_string(),
},
metrics: summarize(&equity, &exposure),
};
(report, equity)
}
fn main() {
let server = Arc::new(DataServer::new(DEFAULT_DATA_PATH));
if !server.has_symbol(SYMBOL) {
println!("skip: no local data at {DEFAULT_DATA_PATH} (symbol {SYMBOL} absent).");
return;
}
println!("=== GER40 session-breakout — World `walk_forward` (2018..2024) ===");
// The roll span: 2018-01 .. 2024-12. is=12mo, oos=3mo, step=3mo.
let day_ns: i64 = 86_400_000 * 1_000_000;
let month_ns: i64 = 30 * day_ns;
let origin = aura_ingest::unix_ms_to_epoch_ns(
chrono::Utc.with_ymd_and_hms(2018, 1, 1, 0, 0, 0).unwrap().timestamp_millis(),
);
let end = aura_ingest::unix_ms_to_epoch_ns(
chrono::Utc.with_ymd_and_hms(2024, 12, 31, 23, 59, 59).unwrap().timestamp_millis(),
);
let roller = WindowRoller::new(
(origin, end),
12 * month_ns,
3 * month_ns,
3 * month_ns,
RollMode::Rolling,
)
.expect("span fits >= 1 window");
// The non-empty space the per-window in-sample sweep optimizes over: the two
// genuine tuning knobs (the #97 resolution — a real space to optimize).
let space = ger40_breakout_blueprint(BAR_MINUTES, SESSION_HOUR, SESSION_MINUTE, Berlin)
.0
.param_space();
println!(
"optimizing param_space() per window: {:?} (in-sample grid {}×{})",
space.iter().map(|p| p.name.as_str()).collect::<Vec<_>>(),
ENTRY_BARS.len(),
EXIT_BARS.len(),
);
println!();
let server_for_closure = Arc::clone(&server);
let space_for_closure = space.clone();
let result = walk_forward(roller, space, move |w: WindowBounds| -> WindowRun {
// In-sample optimize: sweep the entry×exit grid over the IS window, choose
// the point with the best in-sample total_pips. The blueprint is consumed
// directly via its param_space() — no re-authoring.
let is_grid = GridSpace::new(
&space_for_closure,
space_for_closure
.iter()
.map(|ps| match ps.name.as_str() {
"entry_bar.target" => ENTRY_BARS.iter().map(|&v| Scalar::i64(v)).collect(),
"exit_bar.target" => EXIT_BARS.iter().map(|&v| Scalar::i64(v)).collect(),
other => panic!("unexpected param slot {other}"),
})
.collect(),
)
.expect("IS grid well-formed");
let (is_from, is_to) = (ns_to_ms(w.is.0), ns_to_ms(w.is.1));
let is_family = sweep(&is_grid, |pt: &[Cell]| {
run_point(&server_for_closure, pt, is_from, is_to).0
});
let best = is_family
.points
.iter()
.max_by(|a, b| {
a.report
.metrics
.total_pips
.partial_cmp(&b.report.metrics.total_pips)
.unwrap_or(std::cmp::Ordering::Equal)
})
.expect("the IS grid is non-empty");
let chosen = best.params.clone();
// Apply the chosen params on the OOS window — the honest out-of-sample run.
let (oos_from, oos_to) = (ns_to_ms(w.oos.0), ns_to_ms(w.oos.1));
let (oos_report, oos_equity) =
run_point(&server_for_closure, &chosen, oos_from, oos_to);
WindowRun { chosen_params: chosen, oos_equity, oos_report }
});
println!("walk-forward produced {} windows\n", result.windows.len());
println!(
"{:>3} {:<23} {:<10} {:<9} {:>10} {:>9}",
"win", "oos-window (UTC date)", "entry_bar", "exit_bar", "oos_pips", "oos_dd"
);
println!("{}", "-".repeat(72));
for (k, w) in result.windows.iter().enumerate() {
let named = result.named_params(k);
let entry = named.iter().find(|(n, _)| n == "entry_bar.target").map(|(_, v)| v.as_i64()).unwrap_or(-1);
let exit = named.iter().find(|(n, _)| n == "exit_bar.target").map(|(_, v)| v.as_i64()).unwrap_or(-1);
let m = &w.run.oos_report.metrics;
let win = format!(
"{}..{}",
fmt_date(w.bounds.oos.0),
fmt_date(w.bounds.oos.1),
);
println!(
"{:>3} {:<23} {:<10} {:<9} {:>10.1} {:>9.1}",
k,
win,
entry,
exit,
m.total_pips,
m.max_drawdown,
);
}
let stitched = &result.stitched_oos_equity;
println!(
"\nstitched OOS curve: {} points, final cumulative = {:.1} pips",
stitched.len(),
stitched.last().map(|(_, v)| *v).unwrap_or(0.0),
);
println!(
"\nOK: NON-DEGENERATE walk-forward — every window optimized the breakout's\n\
{}-param space in-sample (chosen_params populated). The #97 empty-space\n\
degenerate roll is resolved: the strategy ships with a real space to tune.",
space_param_count(&result.space),
);
}
fn space_param_count(space: &[aura_engine::ParamSpec]) -> usize {
space.len()
}
/// Render an epoch-ns timestamp as a UTC `YYYY-MM-DD` for the window column.
/// Display only — never feeds the graph.
fn fmt_date(ts: Timestamp) -> String {
let secs = ts.0.div_euclid(1_000_000_000);
match chrono::Utc.timestamp_opt(secs, 0).single() {
Some(dt) => dt.format("%Y-%m-%d").to_string(),
None => format!("ts={}", ts.0),
}
}
fn as_f64(s: &Scalar) -> f64 {
match s {
Scalar::F64(v) => *v,
other => panic!("expected f64, got {other:?}"),
}
}
@@ -0,0 +1,261 @@
//! Gated integration test: the GER40 session-breakout `Composite` blueprint
//! (spec 0051) is driven by the World *orchestration families* — `sweep` and
//! `walk_forward` — on REAL GER40 M1 bars, with NO re-authoring of the strategy.
//! This is the milestone-completing executable proof that the #94 friction is
//! gone: the same `ger40_breakout_blueprint` the single backtest bootstraps is
//! consumed verbatim by the World machinery.
//!
//! Mirrors `tests/ger40_breakout_real.rs` / `real_bars.rs`: skips with a note
//! where the local Pepperstone archive is absent, so `cargo test --workspace`
//! stays green anywhere; exercises the real path where the files exist.
//!
//! Two properties, both over the shipped blueprint:
//! - `sweep` returns a ranked grid over the two named params
//! (`entry_bar.target` × `exit_bar.target`), each point's `total_pips` finite —
//! the `param_space()` is driven directly.
//! - `walk_forward` returns a NON-EMPTY roll, every window carrying a NON-EMPTY
//! `chosen_params` (the executable #97 non-degenerate proof: a real, non-empty
//! space is optimized per window — contrast the fieldtest's empty-space
//! degenerate roll over the raw FlatGraph).
use std::sync::Arc;
use aura_core::{Cell, Scalar, Timestamp};
use aura_engine::{
sweep, summarize, walk_forward, GridSpace, RollMode, RunManifest, RunReport, WindowBounds,
WindowRoller, WindowRun,
};
use chrono::TimeZone;
use chrono_tz::Europe::Berlin;
use data_server::{DataServer, DEFAULT_DATA_PATH};
#[path = "../examples/shared/breakout_real.rs"]
mod breakout_real;
use breakout_real::*;
/// ns → ms: the shipped `open_ohlc_sources` opens by Unix-ms, but the World
/// `WindowBounds` are epoch-ns. `aura-ingest` exports only the forward direction
/// (`unix_ms_to_epoch_ns`), so the inverse is a local hand-divide (#80, filed).
fn ns_to_ms(ts: Timestamp) -> i64 {
ts.0 / 1_000_000
}
/// Bootstrap the shipped blueprint at one `{entry_bar, exit_bar}` point, run it
/// over `[from_ms, to_ms]` of real GER40 OHLC, and fold the recorded equity/held
/// taps into a `RunReport` (`summarize` over the drained channels — exactly as
/// the SMA-cross sweep fixture and the fieldtest's `run_point` do). A fresh
/// blueprint per call (fresh nodes + channels) keeps the disjoint sweep points
/// independent (C1).
fn run_point(server: &Arc<DataServer>, point: &[Cell], from_ms: i64, to_ms: i64) -> RunReport {
let (bp, taps) = ger40_breakout_blueprint(BAR_MINUTES, SESSION_HOUR, SESSION_MINUTE, Berlin);
let mut h = bp
.bootstrap_with_cells(point)
.expect("sweep point kind-checked against param_space");
let sources = open_ohlc_sources(server, from_ms, to_ms).expect("window overlaps GER40 data");
h.run(sources);
drop(h);
let equity: Vec<(Timestamp, f64)> = taps.equity.try_iter().collect::<Vec<_>>()
.iter()
.map(|(t, r)| (*t, scalar_f64(&r[0])))
.collect();
let exposure: Vec<(Timestamp, f64)> = taps.held.try_iter().collect::<Vec<_>>()
.iter()
.map(|(t, r)| (*t, scalar_f64(&r[0])))
.collect();
RunReport {
manifest: RunManifest {
commit: "ger40-breakout-world".to_string(),
params: vec![],
window: (
aura_ingest::unix_ms_to_epoch_ns(from_ms),
aura_ingest::unix_ms_to_epoch_ns(to_ms),
),
seed: 0,
broker: "sim-optimal(pip_size=1)".to_string(),
},
metrics: summarize(&equity, &exposure),
}
}
fn scalar_f64(s: &Scalar) -> f64 {
match s {
Scalar::F64(v) => *v,
other => panic!("expected f64, got {other:?}"),
}
}
/// Property: the World `sweep` family CONSUMES the shipped breakout blueprint
/// over its two named params on real GER40 — it returns a full ranked grid (no
/// re-authoring), and every grid point's `total_pips` is finite (a backtest
/// actually ran at each point). The grid is built from the blueprint's
/// `param_space()` directly, so the two `EqConst` targets are the swept axes.
#[test]
fn sweep_consumes_blueprint_over_named_params() {
let server = Arc::new(DataServer::new(DEFAULT_DATA_PATH));
if !server.has_symbol(SYMBOL) {
eprintln!("skip: no local data at {DEFAULT_DATA_PATH} (symbol {SYMBOL} absent)");
return;
}
let (from_ms, to_ms) = utc_month_window_ms(2024, 9);
let space = ger40_breakout_blueprint(BAR_MINUTES, SESSION_HOUR, SESSION_MINUTE, Berlin)
.0
.param_space();
// The grid is exactly over `param_space()`: entry ∈ {2,3,4}, exit ∈ {4,5,6}.
let grid = GridSpace::new(
&space,
space
.iter()
.map(|ps| match ps.name.as_str() {
"entry_bar.target" => vec![Scalar::i64(2), Scalar::i64(3), Scalar::i64(4)],
"exit_bar.target" => vec![Scalar::i64(4), Scalar::i64(5), Scalar::i64(6)],
other => panic!("unexpected param slot {other}"),
})
.collect(),
)
.expect("grid well-formed for the breakout param_space");
let server_for_closure = Arc::clone(&server);
let family = sweep(&grid, |pt: &[Cell]| {
run_point(&server_for_closure, pt, from_ms, to_ms)
});
// A full 3×3 grid was enumerated over the two named params (no re-authoring).
assert_eq!(family.points.len(), 9, "the 3×3 entry×exit grid must enumerate 9 points");
assert_eq!(
family.space.iter().map(|p| p.name.as_str()).collect::<Vec<_>>(),
vec!["entry_bar.target", "exit_bar.target"],
"the swept space is exactly the two genuine tuning knobs",
);
// Every point's metrics are finite — a real backtest ran at each grid point.
for (i, p) in family.points.iter().enumerate() {
assert!(
p.report.metrics.total_pips.is_finite(),
"point {i} {:?} produced a non-finite total_pips",
family.named_params(i),
);
}
// The best point (max total_pips) is finite — the ranked grid has a real head.
let best = family
.points
.iter()
.map(|p| p.report.metrics.total_pips)
.fold(f64::NEG_INFINITY, f64::max);
assert!(best.is_finite(), "the best grid point's total_pips must be finite");
}
/// Property: the World `walk_forward` family CONSUMES the shipped breakout
/// blueprint NON-DEGENERATELY (the executable #97 resolution). The roll over
/// multi-year GER40 yields a NON-EMPTY set of windows, and EACH window carries a
/// NON-EMPTY `chosen_params` — a real `{entry_bar, exit_bar}` point was selected
/// by an in-sample sweep per window (contrast the fieldtest's empty-space roll
/// over the raw FlatGraph, where `chosen_params == vec![]` everywhere).
#[test]
fn walk_forward_consumes_blueprint_non_degenerate() {
let server = Arc::new(DataServer::new(DEFAULT_DATA_PATH));
if !server.has_symbol(SYMBOL) {
eprintln!("skip: no local data at {DEFAULT_DATA_PATH} (symbol {SYMBOL} absent)");
return;
}
// A short 2-window roll over 2023..2024 keeps the gated test cheap while still
// exercising the per-window in-sample optimize. is=6mo, oos=3mo, step=3mo.
let day_ns: i64 = 86_400_000 * 1_000_000;
let month_ns: i64 = 30 * day_ns;
let origin = aura_ingest::unix_ms_to_epoch_ns(
chrono::Utc.with_ymd_and_hms(2023, 1, 1, 0, 0, 0).unwrap().timestamp_millis(),
);
let end = aura_ingest::unix_ms_to_epoch_ns(
chrono::Utc.with_ymd_and_hms(2024, 6, 30, 23, 59, 59).unwrap().timestamp_millis(),
);
let roller = WindowRoller::new(
(origin, end),
6 * month_ns,
3 * month_ns,
3 * month_ns,
RollMode::Rolling,
)
.expect("span fits >= 1 window");
// The non-empty space the in-sample sweep optimizes over per window: the two
// genuine tuning knobs. This is the #97 resolution — a real space, so the
// chosen params are populated, never an empty degenerate vec.
let space = ger40_breakout_blueprint(BAR_MINUTES, SESSION_HOUR, SESSION_MINUTE, Berlin)
.0
.param_space();
let server_for_closure = Arc::clone(&server);
let result = walk_forward(roller, space.clone(), move |w: WindowBounds| -> WindowRun {
// In-sample optimize: a tiny entry×exit grid over the IS window, choose the
// point with the best in-sample total_pips. The blueprint is consumed
// directly via its param_space() — no re-authoring.
let is_grid = GridSpace::new(
&space,
space
.iter()
.map(|ps| match ps.name.as_str() {
"entry_bar.target" => vec![Scalar::i64(2), Scalar::i64(3)],
"exit_bar.target" => vec![Scalar::i64(5), Scalar::i64(6)],
other => panic!("unexpected param slot {other}"),
})
.collect(),
)
.expect("IS grid well-formed");
let (is_from, is_to) = (ns_to_ms(w.is.0), ns_to_ms(w.is.1));
let is_family = sweep(&is_grid, |pt: &[Cell]| {
run_point(&server_for_closure, pt, is_from, is_to)
});
let best = is_family
.points
.iter()
.max_by(|a, b| {
a.report
.metrics
.total_pips
.partial_cmp(&b.report.metrics.total_pips)
.unwrap_or(std::cmp::Ordering::Equal)
})
.expect("the IS grid is non-empty");
let chosen = best.params.clone();
// Apply the chosen params on the OOS window.
let (oos_from, oos_to) = (ns_to_ms(w.oos.0), ns_to_ms(w.oos.1));
let oos_report = run_point(&server_for_closure, &chosen, oos_from, oos_to);
// Re-run once on OOS to capture the recorded equity segment for stitching.
let (bp, taps) =
ger40_breakout_blueprint(BAR_MINUTES, SESSION_HOUR, SESSION_MINUTE, Berlin);
let mut h = bp.bootstrap_with_cells(&chosen).expect("chosen point kind-checked");
let sources = open_ohlc_sources(&server_for_closure, oos_from, oos_to)
.expect("OOS window overlaps data");
h.run(sources);
drop(h);
let oos_equity: Vec<(Timestamp, f64)> = taps
.equity
.try_iter()
.collect::<Vec<_>>()
.iter()
.map(|(t, r)| (*t, scalar_f64(&r[0])))
.collect();
WindowRun { chosen_params: chosen, oos_equity, oos_report }
});
// Non-degenerate: a non-empty roll, EACH window carrying a non-empty chosen
// point (the #97 proof — a real space was optimized per window).
assert!(!result.windows.is_empty(), "walk_forward must yield >= 1 window");
for (k, w) in result.windows.iter().enumerate() {
assert!(
!w.run.chosen_params.is_empty(),
"window {k}: chosen_params must be NON-empty (the #97 non-degenerate proof)",
);
assert_eq!(
w.run.chosen_params.len(),
2,
"window {k}: chosen_params spans the two-param space",
);
assert!(
w.run.oos_report.metrics.total_pips.is_finite(),
"window {k}: OOS total_pips must be finite",
);
}
}