plan: 0066 stage1-r primitives + spec correction (VolStop -> composition)

VolStop was a design error (a fused node justified by a missing primitive). A
node is a primitive only if not DAG-expressible from other primitives; a missing
primitive is added, not fused away. Adds Mul + Sqrt primitives, removes the fused
VolStop, expresses the vol stop as a composition (rolling EWMA stddev = k *
Sqrt(Ema(Mul(d,d), length))). Spec 0065 b gets a correction note; full record on #117.

refs #117 #119
This commit is contained in:
2026-06-24 00:37:53 +02:00
parent 2c43296c2c
commit 5de8576fd0
2 changed files with 374 additions and 5 deletions
+21 -5
View File
@@ -157,15 +157,31 @@ field is renamed to `bias_sign_flips` **with `#[serde(alias = "exposure_sign_fli
historical `runs.jsonl` still deserialises (the planner pins the exact sites; ~311
`exposure` / 82 `Exposure` references across `crates/`).
**(b) `VolStop` + `FixedStop` stop-rule nodes (#119) — new, `aura-std`.**
**(b) Stop-rule (#119).**
> **CORRECTION (2026-06-24, #117).** The "fused `VolStop` node (no Abs/Mul
> primitive exists, so fuse)" below was a design error. A node is a PRIMITIVE only
> if it is **not** DAG-expressible from other primitives; a missing primitive is
> **added**, not fused away. The vol stop is pure feed-forward arithmetic → a
> **composition**, not a primitive. Corrected design:
> - add primitives `Mul` (two-stream f64 product) + `Sqrt` to `aura-std`;
> - the vol stop is a **rolling stddev (EWMA volatility)**: `stop_distance =
> k · Sqrt(Ema(Mul(Δ,Δ), length))`, `Δ = Sub(price, Delay(price,1))`, scaled
> `k·σ` via the existing `LinComb` (k a param weight). `Sqrt` is mandatory
> (variance is price², `stop_distance` must be price so R is dimensionless);
> `Abs` is **not** added (Δ² not |Δ|);
> - this `vol_stop(length, k) → Composite` lives in `aura-engine` (composites need
> `GraphBuilder`); the fused `VolStop` node is removed;
> - `FixedStop` is **kept** as a legitimate primitive (a param constant gated on
> its price input — a source-less `Const` has no firing trigger in the push model).
>
> The block below is the superseded pre-correction shape, retained for ancestry.
```rust
// VolStop: one fused node (no Abs/Mul primitive exists, so abs+delta+EMA must fuse).
// SUPERSEDED (see CORRECTION above): the fused-node shape, not the shipped design.
pub struct VolStop { length: usize, k: f64, prev_price: Option<f64>, ema: f64, comp: f64, count: usize, out: [Cell; 1] }
// eval: d = |price - prev_price|; ema = EMA_length(d) (Kahan, like Sma); out = k * ema.
// None until warm (count < length). Input "price" (f64, Any). Output "stop_distance" (f64).
// Params length:i64, k:f64. prev_price updated AFTER use (intra-node z⁻¹, C2-clean).
pub struct FixedStop { distance: f64, out: [Cell; 1] } // out = distance, constant; test fixture + axis sibling
pub struct FixedStop { distance: f64, out: [Cell; 1] } // KEPT: out = distance, constant (a triggered-constant primitive)
```
**(c) `PositionManagement` node (#127) — new, `aura-std`. The dense R-record.**