// Cycle-0049 fieldtest — example 1: the HEADLINE task. // // Tune a 3-param SMA-cross strategy by drawing N random points over declared // continuous `ParamRange`s and running the SAME `sweep` a grid would use — then // pick the best point by total_pips. A grid would explode here (fast x slow x // scale over continuous ranges is the curse of dimensionality); random sampling // over declared ranges is the standard tool. This is the code a researcher // writes (axis-hint 1). // // PUBLIC INTERFACE ONLY: API discovered from the design ledger // (docs/design/INDEX.md C12/C12.1), the glossary, spec 0049, and // `cargo doc --workspace --no-deps` rustdoc. No crates/*/src was read. use std::sync::mpsc; use aura_core::{Cell, Firing, Scalar, ScalarKind, Timestamp}; use aura_engine::{ f64_field, summarize, sweep, BlueprintNode, Composite, Edge, OutField, ParamRange, RandomSpace, Role, RunManifest, RunReport, SweepFamily, Target, VecSource, }; use aura_std::{Exposure, Recorder, SimBroker, Sma, Sub}; /// The reusable 2-SMA-cross composite (legs named so their knobs surface as /// `sma_cross.fast.length` / `sma_cross.slow.length`). fn sma_cross() -> Composite { Composite::new( "sma_cross", vec![ Sma::builder().named("fast").into(), Sma::builder().named("slow").into(), Sub::builder().into(), ], vec![ Edge { from: 0, to: 2, slot: 0, from_field: 0 }, Edge { from: 1, to: 2, slot: 1, from_field: 0 }, ], vec![Role { name: "price".into(), targets: vec![Target { node: 0, slot: 0 }, Target { node: 1, slot: 0 }], source: None, }], vec![OutField { node: 2, field: 0, name: "out".into() }], ) } /// A fresh root harness Composite plus the equity + exposure receivers it /// records to. A fresh build per point gives each sweep member its OWN /// drainable channels. /// /// param_space() = [sma_cross.fast.length: I64, sma_cross.slow.length: I64, /// exposure.scale: F64]. fn harness_with_sinks() -> ( Composite, mpsc::Receiver<(Timestamp, Vec)>, mpsc::Receiver<(Timestamp, Vec)>, ) { let (tx_eq, rx_eq) = mpsc::channel(); let (tx_ex, rx_ex) = mpsc::channel(); let bp = Composite::new( "harness", vec![ BlueprintNode::Composite(sma_cross()), Exposure::builder().into(), SimBroker::builder(1e-4).into(), Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx_eq).into(), // sink 0: equity Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx_ex).into(), // sink 1: exposure ], vec![ Edge { from: 0, to: 1, slot: 0, from_field: 0 }, // cross -> Exposure Edge { from: 1, to: 2, slot: 0, from_field: 0 }, // Exposure -> broker.exposure Edge { from: 2, to: 3, slot: 0, from_field: 0 }, // broker -> equity sink Edge { from: 1, to: 4, slot: 0, from_field: 0 }, // Exposure -> exposure sink ], vec![Role { name: "price".into(), targets: vec![Target { node: 0, slot: 0 }, Target { node: 2, slot: 1 }], source: Some(ScalarKind::F64), }], vec![], ); (bp, rx_eq, rx_ex) } fn synthetic_prices() -> Vec<(Timestamp, Scalar)> { // A trending-then-reverting series so different (fast, slow, scale) actually // produce different pip outcomes. let prices = [ 1.00, 1.01, 1.02, 1.03, 1.05, 1.08, 1.11, 1.10, 1.06, 1.04, 1.02, 1.01, 1.03, 1.07, 1.12, 1.15, 1.13, 1.09, 1.05, 1.02, ]; prices .iter() .enumerate() .map(|(i, &p)| (Timestamp(60_000_000_000 * i as i64), Scalar::f64(p))) .collect() } /// The author's per-point closure: build fresh, bootstrap by the point's cells, /// run, drain, summarize into a RunReport. `Fn(&[Cell]) -> RunReport`. fn run_one(point: &[Cell], prices: &[(Timestamp, Scalar)]) -> RunReport { let (bp, rx_eq, rx_ex) = harness_with_sinks(); let mut h = bp .bootstrap_with_cells(point) .expect("random point is kind-checked against the param-space"); h.run(vec![Box::new(VecSource::new(prices.to_vec()))]); drop(h); let eq_rows: Vec<_> = rx_eq.try_iter().collect(); let ex_rows: Vec<_> = rx_ex.try_iter().collect(); let equity = f64_field(&eq_rows, 0); let exposure = f64_field(&ex_rows, 0); let metrics = summarize(&equity, &exposure); RunReport { manifest: RunManifest { commit: "fieldtest".into(), params: vec![], window: (Timestamp(0), Timestamp(60_000_000_000 * 20)), seed: 0, broker: "sim-optimal".into(), }, metrics, } } fn main() { let prices = synthetic_prices(); // The param-space the random ranges are declared against (positional-parallel). let space = harness_with_sinks().0.param_space(); println!("param-space ({} slots):", space.len()); for (i, ps) in space.iter().enumerate() { println!(" slot {i}: {} : {:?}", ps.name, ps.kind); } // ONE declared continuous range per slot, in param_space() order. let ranges = vec![ ParamRange::i64(2, 8), // sma_cross.fast.length in [2, 8] (inclusive) ParamRange::i64(10, 30), // sma_cross.slow.length in [10, 30] (inclusive) ParamRange::f64(0.5, 4.0), // exposure.scale in [0.5, 4.0) (half-open) ]; // Draw 200 seeded points; validated against the param-space IN new(). let count = 200; let seed = 0xC0FFEE; let rand_space = RandomSpace::new(&space, ranges, count, seed) .expect("ranges are well-formed for the space"); println!("\nRandomSpace: {} points, seed {:#x}", rand_space.len(), seed); // SAME execution layer as the grid sweep — sweep is generic over impl Space. let family: SweepFamily = sweep(&rand_space, |point: &[Cell]| run_one(point, &prices)); assert_eq!(family.points.len(), count, "one point per draw"); // Pick the best point by total_pips. let mut best: Option<(usize, f64)> = None; for (i, pt) in family.points.iter().enumerate() { let p = pt.report.metrics.total_pips; if best.is_none() || p > best.unwrap().1 { best = Some((i, p)); } assert!(p.is_finite(), "every drawn point produces finite metrics"); } let (bi, bp) = best.unwrap(); // Readable named view of the winning coordinate (named_params reuses zip). let named = family.named_params(bi); println!("\nbest point #{bi} = {:.4} pips at:", bp); for (name, val) in &named { println!(" {name} = {}", scalar_str(val)); } // Confirm the family genuinely spread: more than one distinct metric. let distinct: std::collections::BTreeSet = family .points .iter() .map(|p| format!("{:.6}", p.report.metrics.total_pips)) .collect(); println!( "\ndistinct total_pips across {} points: {}", family.points.len(), distinct.len() ); assert!(distinct.len() > 1, "a random sweep over real ranges must not collapse"); // Show every drawn point landed inside its declared range (in-range draws). let mut fast_min = i64::MAX; let mut fast_max = i64::MIN; let mut scale_min = f64::INFINITY; let mut scale_max = f64::NEG_INFINITY; for pt in &family.points { fast_min = fast_min.min(pt.params[0].i64()); fast_max = fast_max.max(pt.params[0].i64()); scale_min = scale_min.min(pt.params[2].f64()); scale_max = scale_max.max(pt.params[2].f64()); } println!( "\nsampled ranges: sma_cross.fast.length in [{fast_min}, {fast_max}] (declared [2, 8]); \ exposure.scale in [{scale_min:.4}, {scale_max:.4}) (declared [0.5, 4.0))" ); assert!((2..=8).contains(&fast_min) && (2..=8).contains(&fast_max)); assert!(scale_min >= 0.5 && scale_max < 4.0); println!("\nOK: random continuous-range tuning produced a comparable, spread family."); } fn scalar_str(s: &Scalar) -> String { match s { Scalar::I64(v) => v.to_string(), Scalar::F64(v) => format!("{v:.4}"), Scalar::Bool(v) => v.to_string(), Scalar::Timestamp(t) => t.0.to_string(), } }