feat(cli,docs): ship the ganged open examples + document the gang construct (#61 tasks 7-8)
The two open examples now expose their author-intended single knobs — the closed builders always bound these pairs to one value by hand; the open forms falsely offered them as independent axes: - r_breakout_open: channel_hi.length + channel_lo.length -> channel_length (the Donchian channel is structurally ONE parameter) - r_meanrev_open: mean_window.length + var_window.length -> window; the band factor stays an independent axis Regenerated via the emitters (never hand-edited); the closed examples are byte-unchanged. The carve builders gang in the open branch only. Test migration: the two axis-namespace pins, five --real e2e invocations (single gang axis, the 10,20 diagonal for the campaign pair — the mismatched 20,40/10,20 grid was exactly the configuration space the gang retires), and the param_stability row counts (4 -> 3). The r-sma walkforward golden anchor is untouched (it never swept a ganged pair). Docs: authoring-guide gains the seventh op + the third param state (open/bound/ganged) + the gang wrap note; README op list + Axis concept; glossary gang entry; ledger C24 records the gang verb and the pre-ship Tier-2 dormancy (no format-version bump while no out-of-repo reader exists). Verified: full workspace suite 1104/0 (--real e2e included, local data present), clippy -D warnings clean, cargo doc clean; live acceptance: introspect --params prints channel_length:I64 alone / window:I64 + band.factor:F64. closes #61
This commit is contained in:
@@ -1831,8 +1831,8 @@ const R_BREAKOUT_CHANNEL: i64 = 3;
|
||||
/// as blueprint data (#159 cut 2). The signal computation matches the retired fused
|
||||
/// builder's leg (same nodes, same wiring); the pip/R harness is the generic
|
||||
/// `wrap_r` wrapper, not part of the signal. `channel = Some(n)` binds both rolling
|
||||
/// nodes (closed); `None` leaves them open (two sweep axes — per-node exposure;
|
||||
/// ganging one knob into both is the separate #61). This carve has no production
|
||||
/// nodes (closed); `None` leaves them open, ganged into the single `channel_length`
|
||||
/// knob (#61) — the channel is structurally ONE parameter. This carve has no production
|
||||
/// (non-test) caller — it is exercised only by the regeneration + proof tests below;
|
||||
/// production code loads the shipped JSON, not this builder — hence `#[cfg(test)]`.
|
||||
#[cfg(test)]
|
||||
@@ -1847,6 +1847,9 @@ fn r_breakout_signal(channel: Option<i64>) -> Composite {
|
||||
}
|
||||
let mx = g.add(mx_b);
|
||||
let mn = g.add(mn_b);
|
||||
if channel.is_none() {
|
||||
g.gang("channel_length", [mx.param("length"), mn.param("length")]);
|
||||
}
|
||||
let gt_up = g.add(Gt::builder());
|
||||
let gt_down = g.add(Gt::builder());
|
||||
let up_latch = g.add(Latch::builder());
|
||||
@@ -1869,8 +1872,9 @@ fn r_breakout_signal(channel: Option<i64>) -> Composite {
|
||||
}
|
||||
|
||||
/// The EWMA window for the canonical closed r_meanrev example (ganged mean/var Ema
|
||||
/// length). Single source for the emitter and the proof tests that must all agree
|
||||
/// with the baked `examples/r_meanrev.json`.
|
||||
/// length; the open form gangs them structurally via the single `window` knob, #61).
|
||||
/// Single source for the emitter and the proof tests that must all agree with the
|
||||
/// baked `examples/r_meanrev.json`.
|
||||
#[cfg(test)]
|
||||
const R_MEANREV_WINDOW: i64 = 3;
|
||||
|
||||
@@ -1898,6 +1902,9 @@ fn r_meanrev_signal(window: Option<i64>, band_k: Option<f64>) -> Composite {
|
||||
let dev = g.add(Sub::builder()); // price - mean
|
||||
let sq = g.add(Mul::builder()); // dev * dev
|
||||
let var = g.add(var_b); // EWMA variance
|
||||
if window.is_none() {
|
||||
g.gang("window", [mean.param("length"), var.param("length")]);
|
||||
}
|
||||
let sigma = g.add(Sqrt::builder());
|
||||
let mut band_b = Scale::builder().named("band");
|
||||
if let Some(k) = band_k {
|
||||
|
||||
Reference in New Issue
Block a user