// Milestone fieldtest — axis (c): nest a composite INSIDE another composite, // build a Blueprint, compile() + bootstrap() + run(). The milestone promises // composites "nest arbitrarily" and that the compiled/flat view handles // nesting (#26 notes the CLUSTERED blueprint render has an `unimplemented!` on // nested composites — but that path is only reachable through `aura graph` on // the built-in sample, which a downstream consumer cannot point at their own // graph; see the spec finding). // // Here we verify the run-path promise from the consumer side: a composite that // CONTAINS a composite lowers (inlines, recursively) to a flat runnable // instance that produces a populated trace. // // Public interface only. use std::sync::mpsc; use aura_core::{Firing, Scalar, ScalarKind, Timestamp}; use aura_engine::{Blueprint, BlueprintNode, Composite, Edge, OutField, Role, SourceSpec, Target}; use aura_std::{Exposure, Recorder, SimBroker, Sma, Sub}; /// Inner composite: the SMA(2)/SMA(4) cross (one price role -> spread output). fn inner_cross() -> Composite { Composite::new( "sma_cross", vec![ Sma::factory().into(), Sma::factory().into(), Sub::factory().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 }], }], vec![], vec![OutField { node: 2, field: 0, name: "out".into() }], ) } /// Outer composite: wraps inner_cross then maps it through Exposure(0.5). /// Interior: 0 = inner_cross (Composite), 1 = Exposure(0.5). /// One price role fans into the inner composite's role 0. /// Output: Exposure's output field. fn strategy() -> Composite { Composite::new( "strategy", vec![ BlueprintNode::Composite(inner_cross()), Exposure::factory().into(), ], vec![Edge { from: 0, to: 1, slot: 0, from_field: 0 }], // cross -> Exposure // price -> inner role 0 vec![Role { name: "price".into(), targets: vec![Target { node: 0, slot: 0 }] }], vec![], vec![OutField { node: 1, field: 0, name: "out".into() }], ) } fn main() { let (tx, rx) = mpsc::channel::<(Timestamp, Vec)>(); // Top-level: 0 = strategy (nested Composite), 1 = SimBroker, 2 = Recorder. let blueprint = Blueprint::new( vec![ BlueprintNode::Composite(strategy()), SimBroker::factory(1e-4).into(), Recorder::factory(vec![ScalarKind::F64], Firing::Any, tx).into(), ], vec![SourceSpec { kind: ScalarKind::F64, targets: vec![ Target { node: 0, slot: 0 }, // strategy price role Target { node: 1, slot: 1 }, // broker price leg ], }], vec![ Edge { from: 0, to: 1, slot: 0, from_field: 0 }, // strategy -> broker.exposure Edge { from: 1, to: 2, slot: 0, from_field: 0 }, // broker -> Recorder ], ); // Param vector (depth-first, C12): strategy { inner_cross's two SMA lengths // (I64), Exposure scale (F64) }; Sub/SimBroker/Recorder declare none. let mut harness = blueprint .bootstrap_with_params(vec![Scalar::I64(2), Scalar::I64(4), Scalar::F64(0.5)]) .expect("nested-composite blueprint should bootstrap"); let prices: Vec = vec![ 1.00, 1.01, 1.02, 1.03, 1.05, 1.08, 1.06, 1.04, 1.02, 1.01, 1.03, 1.07, ]; let stream: Vec<(Timestamp, Scalar)> = prices .iter() .enumerate() .map(|(i, &p)| (Timestamp(60_000_000_000 * i as i64), Scalar::F64(p))) .collect(); harness.run(vec![stream]); drop(harness); let rows: Vec<(Timestamp, Vec)> = rx.iter().collect(); println!("nested-composite recorded rows: {}", rows.len()); assert!( !rows.is_empty(), "a composite-in-composite must inline to a flat runnable instance" ); println!("OK: composite-inside-composite inlined, bootstrapped, ran, recorded."); }