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.
182 lines
6.8 KiB
Rust
182 lines
6.8 KiB
Rust
//! Runnable World-family demo: **`compare`** the session-breakout `Composite`
|
|
//! blueprint 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 (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:?}"),
|
|
}
|
|
}
|