d858caf67b
docs/specs and docs/plans were retired (prior commit); the source/test
comments that cited them ("spec 0050 §4.1", "spec §Testing N", "per spec",
"the spec's ...") now point at nothing. Strip every such pointer while
preserving the technical substance, the design-ledger contract refs
(C1/C11/C20/C34/C12.1/...), and the Gitea issue refs (#41).
Comment/doc edits only across 20 files — no logic change; full workspace
suite green, clippy clean.
166 lines
6.1 KiB
Rust
166 lines
6.1 KiB
Rust
//! Runnable World-family demo: **`sweep`** the GER40 session-breakout `Composite`
|
||
//! blueprint 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:?}"),
|
||
}
|
||
}
|