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.
140 lines
5.7 KiB
Rust
140 lines
5.7 KiB
Rust
//! Runnable demonstration: the GER40 15m session-breakout strategy — the
|
|
//! Phase-1 `aura-std` node vocabulary, proven synthetically in
|
|
//! `aura-engine/tests/ger40_breakout.rs` — driven over REAL GER40 M1 bars from
|
|
//! the local data-server archive (one calendar month).
|
|
//!
|
|
//! This is a *pure composition* of SHIPPED `aura-std` nodes: no project-specific
|
|
//! signal code, so it lives in the engine repo's `examples/` carveout (C9), not
|
|
//! a separate project crate. The strategy is authored as a World-consumable
|
|
//! `Composite` blueprint (`ger40_breakout_blueprint`) and bootstrapped
|
|
//! here by name (`entry_bar=3`, `exit_bar=5`); the four real `M1FieldSource`s
|
|
//! feed its four OHLC source roles. The blueprint reproduces the shipped
|
|
//! hand-wired `FlatGraph` EXACTLY (C23), so this prints the same report + trace.
|
|
//!
|
|
//! Run:
|
|
//! ```text
|
|
//! cargo run -p aura-ingest --example ger40_breakout_real
|
|
//! ```
|
|
//! Prints the [`RunReport`] metrics (total pips / drawdown / sign-flips) and a
|
|
//! compact entry/exit trace — each 0→1 entry and 1→0 exit with its timestamp —
|
|
//! so the strategy's behaviour over the ~20 sessions in the month is visibly
|
|
//! inspectable. Skips cleanly (no panic) where the archive is absent.
|
|
|
|
use std::sync::Arc;
|
|
|
|
use chrono::TimeZone;
|
|
use chrono_tz::Europe::Berlin;
|
|
use data_server::{DataServer, DEFAULT_DATA_PATH};
|
|
|
|
// The breakout wiring is defined once in the shared module and included by both
|
|
// this example and the gated determinism test — never duplicated.
|
|
#[path = "shared/breakout_real.rs"]
|
|
mod breakout_real;
|
|
use breakout_real::*;
|
|
|
|
// --- The window: September 2024 (inclusive), trivially changeable. -----------
|
|
const YEAR: i32 = 2024;
|
|
const MONTH: u32 = 9;
|
|
|
|
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) — \
|
|
nothing to demonstrate here."
|
|
);
|
|
return;
|
|
}
|
|
|
|
let (from_ms, to_ms) = utc_month_window_ms(YEAR, MONTH);
|
|
|
|
let Some(sources) = open_ohlc_sources(&server, from_ms, to_ms) else {
|
|
println!(
|
|
"skip: {SYMBOL} has no M1 file overlapping {YEAR}-{MONTH:02} — \
|
|
nothing to demonstrate here."
|
|
);
|
|
return;
|
|
};
|
|
|
|
// Author the breakout as a Composite blueprint and bootstrap it by name with
|
|
// the two genuine tuning knobs (entry bar 3, exit bar 5). The bar-period (15m),
|
|
// session open (09:00 Berlin) and delay.lag are structural — bound at
|
|
// construction, not exposed as params.
|
|
let (bp, taps) = ger40_breakout_blueprint(BAR_MINUTES, SESSION_HOUR, SESSION_MINUTE, Berlin);
|
|
let mut h = bp
|
|
.with("entry_bar.target", aura_core::Scalar::i64(3))
|
|
.with("exit_bar.target", aura_core::Scalar::i64(5))
|
|
.bootstrap()
|
|
.expect("breakout blueprint bootstraps with entry=3, exit=5");
|
|
h.run(sources);
|
|
|
|
// Drain the per-bar trace first (it consumes the equity/held/bars/breakout
|
|
// channels), then fold the report from that same trace so the mpsc channels
|
|
// are drained exactly once (shared with the determinism test).
|
|
let trace = drain_trace(&taps);
|
|
let report = report_from_trace(&trace, from_ms, to_ms);
|
|
let metrics = &report.metrics;
|
|
|
|
println!("=== GER40 15m session-breakout — REAL bars ===");
|
|
println!(
|
|
"symbol={SYMBOL} window={YEAR}-{MONTH:02} (UTC, inclusive) \
|
|
bars={} pip_size={PIP_SIZE}",
|
|
trace.len()
|
|
);
|
|
println!("session: {SESSION_HOUR:02}:{SESSION_MINUTE:02} Europe/Berlin, {BAR_MINUTES}m bars; entry bar 3, exit bar 5");
|
|
println!();
|
|
println!("--- metrics (sim-optimal broker, pips = index points) ---");
|
|
println!(" total_pips = {:.1}", metrics.total_pips);
|
|
println!(" max_drawdown = {:.1}", metrics.max_drawdown);
|
|
println!(" exposure_sign_flips = {}", metrics.exposure_sign_flips);
|
|
println!();
|
|
|
|
// --- 2. The compact entry/exit trace. -----------------------------------
|
|
// Each 0→1 is an entry (a strict breakout on session bar 3), each 1→0 is the
|
|
// bar-5 exit. Printing only the transitions keeps the ~20 sessions readable.
|
|
println!("--- entry / exit transitions (held 0↔1) ---");
|
|
let mut prev_held = 0.0_f64;
|
|
let mut entries = 0u32;
|
|
let mut exits = 0u32;
|
|
let mut entry_equity = 0.0_f64;
|
|
for b in &trace {
|
|
if b.held == 1.0 && prev_held == 0.0 {
|
|
entries += 1;
|
|
entry_equity = b.equity;
|
|
println!(
|
|
" ENTER {} bar={} breakout={} equity={:+.1}",
|
|
fmt_ts(b.ts),
|
|
b.bars_since_open,
|
|
b.breakout,
|
|
b.equity
|
|
);
|
|
} else if b.held == 0.0 && prev_held == 1.0 {
|
|
exits += 1;
|
|
println!(
|
|
" EXIT {} bar={} equity={:+.1} (session pnl {:+.1})",
|
|
fmt_ts(b.ts),
|
|
b.bars_since_open,
|
|
b.equity,
|
|
b.equity - entry_equity
|
|
);
|
|
}
|
|
prev_held = b.held;
|
|
}
|
|
if prev_held == 1.0 {
|
|
println!(" (note: still held at end of window — a partial last session)");
|
|
}
|
|
println!();
|
|
println!("sessions entered = {entries}, exited = {exits}");
|
|
}
|
|
|
|
/// Render an epoch-ns [`Timestamp`](aura_core::Timestamp) as a UTC wall-clock
|
|
/// `YYYY-MM-DD HH:MM` for the trace. Display only — never feeds the graph.
|
|
fn fmt_ts(ts: aura_core::Timestamp) -> String {
|
|
let secs = ts.0.div_euclid(1_000_000_000);
|
|
let nanos = ts.0.rem_euclid(1_000_000_000) as u32;
|
|
match chrono::Utc.timestamp_opt(secs, nanos).single() {
|
|
Some(dt) => dt.format("%Y-%m-%d %H:%M UTC").to_string(),
|
|
None => format!("ts={}", ts.0),
|
|
}
|
|
}
|