Files
Aura/crates/aura-ingest/tests/ger40_breakout_world.rs
T
Brummel 4928e289f7 feat(project): the project-as-crate load boundary (cycle 0102)
A research project is now a loadable external cdylib crate. Inside a
directory whose ancestry holds an Aura.toml, aura discovers the project
root cargo-style, locates the compiled dylib via cargo metadata (debug
default, --release opt-in), loads it load-and-hold, and refuses
mismatches before trusting anything: the AURA_PROJECT descriptor
(aura-core::project, #[repr(C)]) carries a C-ABI stamp prefix (rustc +
aura-core version, baked per consuming build by the new aura-core
build.rs) validated before any Rust-ABI field is read. The vocabulary
charter gates the merged resolution: project type ids are ::-namespaced
(std stays bare), duplicates refuse, and the enumerable type-id list
must agree with the resolver, so introspection can never silently omit
a project type.

All blueprint verbs resolve through the merged project + std vocabulary
via a per-invocation Env threaded through the dispatch chains;
registry, trace-store, and data paths anchor at the project runs root
(Aura.toml [paths], paths-only by design — instrument geometry stays
the recorded sidecar, C15). RunManifest gains the Tier-1 project
provenance field (namespace + dylib sha256 + best-effort commit),
stamped beside topology_hash on the blueprint-run paths; pre-0102
registry lines load unchanged. Default node names strip the namespace,
so :: never reaches the param-path address space.

Proven by the demo-project fixture (built by the e2e via cargo,
path-dep on this workspace): run twice bit-identical, provenance
recorded, introspection lists demo::* beside std, registry anchors at
the discovered root from a subdirectory; the badcharter fixture proves
the charter refusal through the real libloading path; a never-built
project refuses with a cargo-build hint. Outside a project every path
collapses to the previous literals — goldens and manifest pins
byte-identical.

Verification: cargo build --workspace clean; cargo test --workspace 862
passed / 0 failed (incl. 7 project_load e2e); clippy -D warnings clean
(one precedent-matching allow(too_many_arguments) on run_oos_blueprint,
whose arity the Env threading raised to 8); doc build unchanged.
Docs/ledger aligned: Aura.toml field lists are paths-only in
project-layout.md, glossary, C16/C17; new C13 realization note records
the per-invocation-reload reading and the load-and-hold one-shot scope
boundary.

New deps, per-case review (aura-cli leaf binary only, never the frozen
artifact): libloading, toml.

refs #180
2026-07-02 18:13:37 +02:00

277 lines
11 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Gated integration test: the GER40 session-breakout `Composite` blueprint 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 aura_ingest::{default_data_server, open_ohlc, DataServer, DEFAULT_DATA_PATH};
#[path = "../examples/shared/breakout_real.rs"]
mod breakout_real;
use breakout_real::*;
/// Bootstrap the shipped blueprint at one `{entry_bar, exit_bar}` point, run it
/// over the epoch-ns [`Timestamp`] window `[from, to]` 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: Timestamp, to: Timestamp) -> RunReport {
let (bp, taps) = ger40_breakout_blueprint(
pip_size_of(SYMBOL),
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(server, SYMBOL, from, to).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: (from, to),
seed: 0,
broker: "sim-optimal(pip_size=1)".to_string(),
selection: None,
instrument: None,
topology_hash: None,
project: None,
},
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 = default_data_server();
if !server.has_symbol(SYMBOL) {
eprintln!("skip: no local data at {DEFAULT_DATA_PATH} (symbol {SYMBOL} absent)");
return;
}
let (from, to) = utc_month_window(2024, 9);
let space = ger40_breakout_blueprint(
pip_size_of(SYMBOL),
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, to)
});
// 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 = default_data_server();
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(
pip_size_of(SYMBOL),
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_family = sweep(&is_grid, |pt: &[Cell]| {
run_point(&server_for_closure, pt, w.is.0, w.is.1)
});
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_report = run_point(&server_for_closure, &chosen, w.oos.0, w.oos.1);
// Re-run once on OOS to capture the recorded equity segment for stitching.
let (bp, taps) = ger40_breakout_blueprint(
pip_size_of(SYMBOL),
BAR_MINUTES,
SESSION_HOUR,
SESSION_MINUTE,
Berlin,
);
let mut h = bp.bootstrap_with_cells(&chosen).expect("chosen point kind-checked");
let sources = open_ohlc(&server_for_closure, SYMBOL, w.oos.0, w.oos.1)
.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",
);
}
}