From 09da04fe1865c84f299b3d988a30b6635af24dbd Mon Sep 17 00:00:00 2001 From: Brummel Date: Sat, 11 Jul 2026 14:20:48 +0200 Subject: [PATCH] =?UTF-8?q?fieldtest:=20milestone-33=20=E2=80=94=204=20exa?= =?UTF-8?q?mples,=2011=20findings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Milestone fieldtest for "Real-project readiness — role-complete research loop (no freeze)": a from-scratch ger40-lab project (aura new) with a brand-new native node (ger40_lab::Momentum) driven through the full research loop on real GER40/FRA40 archive data — sweep, walk-forward, mc, process+campaign with 2-regime risk and net-R cost block, cross-instrument generalize, bit-identical reproduce, charts — from the public interface only. Spine holds: 5 working, 1 bug (chart cannot open a sweep --trace family by its printed handle), 4 friction, 1 spec gap. Findings triaged separately. --- .../ger40-lab/.gitignore | 3 + .../ger40-lab/Aura.toml | 4 + .../ger40-lab/CLAUDE.md | 10 ++ .../ger40-lab/Cargo.toml | 14 ++ .../ger40-lab/blueprints/momentum.bp.json | 1 + .../ger40-lab/blueprints/momentum.ops.json | 8 ++ .../ger40-lab/blueprints/signal.json | 1 + .../campaigns/mom-ger40-fra40-net-r.json | 29 +++++ .../processes/mom-sweep-gate-generalize.json | 11 ++ .../ger40-lab/src/lib.rs | 122 ++++++++++++++++++ 10 files changed, 203 insertions(+) create mode 100644 fieldtests/milestone-real-project-readiness/ger40-lab/.gitignore create mode 100644 fieldtests/milestone-real-project-readiness/ger40-lab/Aura.toml create mode 100644 fieldtests/milestone-real-project-readiness/ger40-lab/CLAUDE.md create mode 100644 fieldtests/milestone-real-project-readiness/ger40-lab/Cargo.toml create mode 100644 fieldtests/milestone-real-project-readiness/ger40-lab/blueprints/momentum.bp.json create mode 100644 fieldtests/milestone-real-project-readiness/ger40-lab/blueprints/momentum.ops.json create mode 100644 fieldtests/milestone-real-project-readiness/ger40-lab/blueprints/signal.json create mode 100644 fieldtests/milestone-real-project-readiness/ger40-lab/campaigns/mom-ger40-fra40-net-r.json create mode 100644 fieldtests/milestone-real-project-readiness/ger40-lab/processes/mom-sweep-gate-generalize.json create mode 100644 fieldtests/milestone-real-project-readiness/ger40-lab/src/lib.rs diff --git a/fieldtests/milestone-real-project-readiness/ger40-lab/.gitignore b/fieldtests/milestone-real-project-readiness/ger40-lab/.gitignore new file mode 100644 index 0000000..af5cf3d --- /dev/null +++ b/fieldtests/milestone-real-project-readiness/ger40-lab/.gitignore @@ -0,0 +1,3 @@ +/target +Cargo.lock +/runs diff --git a/fieldtests/milestone-real-project-readiness/ger40-lab/Aura.toml b/fieldtests/milestone-real-project-readiness/ger40-lab/Aura.toml new file mode 100644 index 0000000..d45dade --- /dev/null +++ b/fieldtests/milestone-real-project-readiness/ger40-lab/Aura.toml @@ -0,0 +1,4 @@ +# Static project context only (C17); paths only. +[paths] +runs = "runs" +# data = "/path/to/archive" # the recorded-data root; defaults to the built-in path diff --git a/fieldtests/milestone-real-project-readiness/ger40-lab/CLAUDE.md b/fieldtests/milestone-real-project-readiness/ger40-lab/CLAUDE.md new file mode 100644 index 0000000..86e69ed --- /dev/null +++ b/fieldtests/milestone-real-project-readiness/ger40-lab/CLAUDE.md @@ -0,0 +1,10 @@ +# ger40-lab — an aura research project + +This crate is an aura project: a cdylib of node/strategy blueprints the +`aura` host loads during research (see the engine's docs/project-layout.md). + +- Build: `cargo build` (the next `aura` invocation loads the fresh dylib) +- Run: `aura run blueprints/signal.json` (from anywhere inside this dir) +- Nodes live in `src/`; each is registered in `vocabulary()`/`type_ids()` + under the `ger40_lab::` namespace prefix. +- Topology is data (`blueprints/*.json`), node logic is Rust. diff --git a/fieldtests/milestone-real-project-readiness/ger40-lab/Cargo.toml b/fieldtests/milestone-real-project-readiness/ger40-lab/Cargo.toml new file mode 100644 index 0000000..26d7733 --- /dev/null +++ b/fieldtests/milestone-real-project-readiness/ger40-lab/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "ger40-lab" +version = "0.1.0" +edition = "2024" +publish = false + +[lib] +crate-type = ["cdylib"] + +[dependencies] +aura-core = { path = "/home/brummel/dev/aura/.claude/worktrees/boss-real-project-readiness/crates/aura-core" } + +# Standalone workspace root: never a member of an enclosing workspace. +[workspace] diff --git a/fieldtests/milestone-real-project-readiness/ger40-lab/blueprints/momentum.bp.json b/fieldtests/milestone-real-project-readiness/ger40-lab/blueprints/momentum.bp.json new file mode 100644 index 0000000..7069ced --- /dev/null +++ b/fieldtests/milestone-real-project-readiness/ger40-lab/blueprints/momentum.bp.json @@ -0,0 +1 @@ +{"format_version":1,"blueprint":{"name":"graph","nodes":[{"primitive":{"type":"ger40_lab::Momentum","name":"mom"}},{"primitive":{"type":"Bias","name":"bias"}}],"edges":[{"from":0,"to":1,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0}],"source":"F64"}],"output":[{"node":1,"field":0,"name":"bias"}]}} \ No newline at end of file diff --git a/fieldtests/milestone-real-project-readiness/ger40-lab/blueprints/momentum.ops.json b/fieldtests/milestone-real-project-readiness/ger40-lab/blueprints/momentum.ops.json new file mode 100644 index 0000000..52394d7 --- /dev/null +++ b/fieldtests/milestone-real-project-readiness/ger40-lab/blueprints/momentum.ops.json @@ -0,0 +1,8 @@ +[ + {"op": "source", "role": "price", "kind": "F64"}, + {"op": "add", "type": "ger40_lab::Momentum", "name": "mom"}, + {"op": "feed", "role": "price", "into": ["mom.series"]}, + {"op": "add", "type": "Bias", "name": "bias"}, + {"op": "connect", "from": "mom.value", "to": "bias.signal"}, + {"op": "expose", "from": "bias.bias", "as": "bias"} +] diff --git a/fieldtests/milestone-real-project-readiness/ger40-lab/blueprints/signal.json b/fieldtests/milestone-real-project-readiness/ger40-lab/blueprints/signal.json new file mode 100644 index 0000000..817e7e4 --- /dev/null +++ b/fieldtests/milestone-real-project-readiness/ger40-lab/blueprints/signal.json @@ -0,0 +1 @@ +{"format_version":1,"blueprint":{"name":"ger40_lab_signal","nodes":[{"primitive":{"type":"SMA","name":"fast","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":2}}]}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":4}}]}},{"primitive":{"type":"Sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}},{"primitive":{"type":"ger40_lab::Scale","bound":[{"pos":0,"name":"factor","kind":"F64","value":{"F64":1.0}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0},{"from":3,"to":4,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":4,"field":0,"name":"bias"}]}} \ No newline at end of file diff --git a/fieldtests/milestone-real-project-readiness/ger40-lab/campaigns/mom-ger40-fra40-net-r.json b/fieldtests/milestone-real-project-readiness/ger40-lab/campaigns/mom-ger40-fra40-net-r.json new file mode 100644 index 0000000..ac212e0 --- /dev/null +++ b/fieldtests/milestone-real-project-readiness/ger40-lab/campaigns/mom-ger40-fra40-net-r.json @@ -0,0 +1,29 @@ +{ + "format_version": 1, + "kind": "campaign", + "name": "mom-ger40-fra40-net-r", + "seed": 42, + "data": { + "instruments": ["GER40", "FRA40"], + "windows": [ { "from_ms": 1725148800000, "to_ms": 1727740800000 } ] + }, + "risk": [ + { "vol": { "length": 3, "k": 1.5 } }, + { "vol": { "length": 3, "k": 3.0 } } + ], + "cost": [ + { "constant": { "cost_per_trade": 0.02 } }, + { "vol_slippage": { "slip_vol_mult": 0.1 } } + ], + "strategies": [ + { + "ref": { "content_id": "0a077701913753e745650f2d2d439980366acd2bcb77e995f8f75be971bbea0d" }, + "axes": { + "mom.length": { "kind": "I64", "values": [5, 10] }, + "bias.scale": { "kind": "F64", "values": [1.0] } + } + } + ], + "process": { "ref": { "content_id": "639bc1b1db0ffa87f1bcc16a6790e0119a9d0968237c8639899fa75c7390df73" } }, + "presentation": { "persist_taps": ["equity", "r_equity", "net_r_equity"], "emit": ["family_table", "selection_report"] } +} diff --git a/fieldtests/milestone-real-project-readiness/ger40-lab/processes/mom-sweep-gate-generalize.json b/fieldtests/milestone-real-project-readiness/ger40-lab/processes/mom-sweep-gate-generalize.json new file mode 100644 index 0000000..8d8ec5f --- /dev/null +++ b/fieldtests/milestone-real-project-readiness/ger40-lab/processes/mom-sweep-gate-generalize.json @@ -0,0 +1,11 @@ +{ + "format_version": 1, + "kind": "process", + "name": "mom-sweep-gate-generalize", + "description": "Sweep the momentum axis, gate on positive gross expectancy, floor across instruments.", + "pipeline": [ + { "block": "std::sweep", "metric": "sqn", "select": "argmax", "deflate": true }, + { "block": "std::gate", "all": [ { "metric": "expectancy_r", "cmp": "gt", "value": 0.0 } ] }, + { "block": "std::generalize", "metric": "expectancy_r" } + ] +} diff --git a/fieldtests/milestone-real-project-readiness/ger40-lab/src/lib.rs b/fieldtests/milestone-real-project-readiness/ger40-lab/src/lib.rs new file mode 100644 index 0000000..8c34fbc --- /dev/null +++ b/fieldtests/milestone-real-project-readiness/ger40-lab/src/lib.rs @@ -0,0 +1,122 @@ +//! ger40-lab — an aura research project: node logic + the exported vocabulary. +//! +//! Every node type this crate provides is registered in `vocabulary()` / +//! `type_ids()` under the `ger40_lab::` namespace prefix; the `aura` host loads +//! this cdylib per invocation and merges it with the std vocabulary. + +use aura_core::{ + Cell, Ctx, FieldSpec, Firing, Node, NodeSchema, ParamSpec, PortSpec, PrimitiveBuilder, + ScalarKind, +}; + +/// One-input scalar gain: emits `input * factor`. Emits `None` until its +/// input has a value (warm-up filter, C8). +pub struct Scale { + factor: f64, + out: [Cell; 1], +} + +impl Scale { + pub fn new(factor: f64) -> Self { + Self { factor, out: [Cell::from_f64(0.0)] } + } + pub fn builder() -> PrimitiveBuilder { + PrimitiveBuilder::new( + "ger40_lab::Scale", + NodeSchema { + inputs: vec![PortSpec { + kind: ScalarKind::F64, + firing: Firing::Any, + name: "value".into(), + }], + output: vec![FieldSpec { name: "value".into(), kind: ScalarKind::F64 }], + params: vec![ParamSpec { name: "factor".into(), kind: ScalarKind::F64 }], + }, + |p| Box::new(Scale::new(p[0].f64())), + ) + } +} + +impl Node for Scale { + fn lookbacks(&self) -> Vec { + vec![1] + } + fn eval(&mut self, ctx: Ctx<'_>) -> Option<&[Cell]> { + let w = ctx.f64_in(0); + if w.is_empty() { + return None; + } + self.out[0] = Cell::from_f64(w[0] * self.factor); + Some(&self.out) + } + fn label(&self) -> String { + format!("ger40_lab::Scale({})", self.factor) + } +} + +/// One-input momentum: emits `price_now - price_{length}_bars_ago`, i.e. the +/// raw change over `length` cycles. Positive => up-move, negative => down-move. +/// Reads a window of `length + 1` cycles (financial-style indexing, index 0 = +/// newest; design ledger C4/RingBuffer). Emits `None` until that much history +/// has accumulated (warm-up filter, C8). +pub struct Momentum { + length: usize, + out: [Cell; 1], +} + +impl Momentum { + pub fn new(length: i64) -> Self { + Self { length: length.max(1) as usize, out: [Cell::from_f64(0.0)] } + } + + pub fn builder() -> PrimitiveBuilder { + PrimitiveBuilder::new( + "ger40_lab::Momentum", + NodeSchema { + inputs: vec![PortSpec { + kind: ScalarKind::F64, + firing: Firing::Any, + name: "series".into(), + }], + output: vec![FieldSpec { name: "value".into(), kind: ScalarKind::F64 }], + params: vec![ParamSpec { name: "length".into(), kind: ScalarKind::I64 }], + }, + |p| Box::new(Momentum::new(p[0].i64())), + ) + } +} + +impl Node for Momentum { + fn lookbacks(&self) -> Vec { + vec![self.length + 1] + } + fn eval(&mut self, ctx: Ctx<'_>) -> Option<&[Cell]> { + let w = ctx.f64_in(0); + if w.len() <= self.length { + return None; + } + self.out[0] = Cell::from_f64(w[0] - w[self.length]); + Some(&self.out) + } + fn label(&self) -> String { + format!("ger40_lab::Momentum({})", self.length) + } +} + +fn vocabulary(type_id: &str) -> Option { + match type_id { + "ger40_lab::Scale" => Some(Scale::builder()), + "ger40_lab::Momentum" => Some(Momentum::builder()), + _ => None, + } +} + +fn type_ids() -> &'static [&'static str] { + &["ger40_lab::Scale", "ger40_lab::Momentum"] +} + +aura_core::aura_project! { + namespace: "ger40_lab", + vocabulary: vocabulary, + type_ids: type_ids, +}