a1908bd15ef078bcde50624cdcd7edbb30d4ba84
449 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1a6eafa056 |
refactor(composites): extract Stage-1 composite-builders into their own crate
Dissolve the aura-engine -> aura-std dependency by moving the vol_stop /
risk_executor composite-builders out of aura-engine into a new aura-composites
crate. This restores the engine's domain-free layering, which the
composites-in-engine placement (cycle 0065) had inverted.
Why: the engine routes type-erased Scalar records and never names a concrete
node -- summarize_r reads the PositionManagement record positionally, by column
index, without linking aura-std (which is exactly why aura-std was a dev-only
dependency before 0065). Parking the node-naming composite-builders inside
aura-engine forced the engine's production code to reference the node library
for the first time (aura-engine -> aura-std), an upside-down layering: the
engine is the foundation the node library builds upon, not the reverse.
The clean home is a dedicated crate that depends on BOTH the engine's
GraphBuilder and the std nodes, leaving each lower crate untouched:
aura-composites -> { aura-engine, aura-std } -> aura-core (acyclic)
aura-engine -> aura-core only (domain-free again)
aura-std -> aura-core only (lean node library)
aura-std reverts to a [dev-dependency] of aura-engine (the engine's own E2E
tests -- stage1_r_e2e, random_sweep_e2e, ger40_breakout -- still drive real
std nodes through a bootstrapped Harness; that test-only edge creates no cycle).
Moved with history (git mv):
- crates/aura-engine/src/composites.rs -> crates/aura-composites/src/lib.rs
- crates/aura-engine/tests/risk_executor.rs -> crates/aura-composites/tests/
- crates/aura-engine/tests/vol_stop_composite.rs -> crates/aura-composites/tests/
Consumers rewired: aura-cli imports risk_executor / StopRule from aura_composites;
the moved tests import the builders from the crate under test. The intra-doc link
to RollMode and the module rationale doc are updated for the new home.
Behaviour-preserving: workspace build clean; clippy --all-targets -D warnings
clean; full suite 513 passed / 0 failed, identical to baseline (the two moved
test files run unchanged under aura-composites); cargo doc --no-deps clean (no
broken intra-doc links).
|
||
|
|
c406b969f5 |
chore(stage1-r): finalize cycle 0065 — milestone closed, ephemeral spec/plans removed
The 'R-based signal quality (Stage 1)' milestone is complete and ratified (iter-1/2/3 + the corrective primitives mini, the tiny-pip fix, the metric rename; end-to-end fieldtest green). Closing the cycle: - Milestone container + issues #119/#126/#127/#128/#129 closed on the tracker. - The ephemeral per-cycle spec (0065) and plans (0065-0069) are git-rm'd per the cycle-close convention; the durable rationale lives in the design ledger (the C10 cycle-0065 realization note) plus git history. Follow-ons tracked: #130 (n-normalized SQN), #131 (CLI UX), #132 (harness polish), #133 (R-sweep + rank-by-sqn), #134 (strategy-output param-namespace rename). refs #117 |
||
|
|
c6c1d3bb10 |
feat(stage1-r): rename the exposure_sign_flips metric to bias_sign_flips (#126)
Complete the typed-field half of the exposure->bias rename - the one the cycle-0065
close audit flagged as aging worst (a serde-stable on-disk key).
- RunMetrics.exposure_sign_flips -> bias_sign_flips with
#[serde(alias = "exposure_sign_flips")], so legacy runs.jsonl still deserialises
(pinned by the legacy-read tests, which keep the old key); new output serialises
bias_sign_flips (the byte-pin tests updated to the new key).
- The registry rank metric: Metric::ExposureSignFlips -> BiasSignFlips, the rank
string accepts BOTH "bias_sign_flips" and "exposure_sign_flips" (CLI back-compat),
the error/listing updated; the rank tests exercise both names.
- The MC aggregate field renamed too (McAggregate is hand-rendered to JSON, not
serde-derived - no alias needed).
Scope held to the typed metric. The strategy-output PARAM NAMESPACE - the
.named("exposure") Bias instance, its exposure.scale knob path (Composite::param_space
derives the knob from the node name), and the exposure_scale manifest label - is a
coherent ~30-site, sweep-pinned surface (the implement loop correctly flagged renaming
the knob as an unscoped expansion touching walkforward + the cli_run byte-pins). Kept
uniformly as "exposure" and deferred to a follow-on so the cluster renames as one
consistent unit. SimBroker's pre-reframe exposure concept and the on-disk "exposure"
tap label stay by the #117 fork decision.
cargo test --workspace green; clippy --workspace --all-targets -D warnings clean; a
live `aura run --harness stage1-r` emits "bias_sign_flips".
closes #126
refs #117
|
||
|
|
34e8bbc803 |
plan: 0069 exposure->bias rename tail (#126)
The deferred rename of the strategy-output residue (the typed exposure_sign_flips
metric with serde back-compat, the registry rank vocab, the exposure_scale manifest
label, the .named("exposure") Bias instances), keeping SimBroker's pre-reframe
exposure concept and the on-disk exposure tap label (scope fork on #117).
refs #126 #117
|
||
|
|
4e6b1d9b53 |
audit(stage1-r): cycle-0065 close — ledger records the Stage-1 R signal-quality layer
Architect drift review of cycle 0065 (Stage-1 R: the exposure->bias rename, the
stop-rule + vol_stop, PositionManagement's dense R-record, the Sizer, summarize_r,
the public RiskExecutor, and the CLI surface): code is drift-clean - C10 fidelity
holds (bias->stop->Sizer->PositionManagement, flat-1R feed-forward, R stop-defined
and size-invariant, guarded by the cross-crate layout lockstep test), C9/C23
acyclic, the --harness selector is a compile-time match (no runtime registry/DSL,
C17). The milestone fieldtest is green after the tiny-pip tolerance fix (
|
||
|
|
2ee0d74a8c |
fix(stage1-r): scale-robust tolerance for the R-record redundancy guard
The PositionManagement redundancy `debug_assert` (position_management.rs:229) re-derives realized_r from the dense-record columns as dir*(exit-entry)/|entry-stop| and compared it to the stored value with an ABSOLUTE < 1e-9 tolerance. At tiny-pip price scale (EURUSD/GBPUSD entry ~1.1, vol-stop distance ~1e-6) the denominator reconstruction `entry - stop` is a catastrophic-cancellation subtraction that loses ~6 significant digits; with a deep gap-through-stop realising a large |R|, the recomputed R drifts from the (correct) stored realized_r by more than 1e-9 - so a debug build PANICS (exit 101) while a release build (assert compiled out) returns the correct R. GER40 (large pip) never trips it. Found by the Stage-1 R milestone fieldtest: `aura run --harness stage1-r --real EURUSD` crashed the headline "score real FX in R" use case on the default (debug) build, and the debug/release disagreement made the R numbers' validity unknowable from the public surface. Root cause (via debug): the stored realized_r uses the cancellation-free FROZEN latched_dist and is exact - only the guard's tolerance was wrong for small price scales. Fix: a scale-robust relative tolerance `< 1e-9 * realized.abs().max(1.0)` (floored at the historical 1e-9 for |R| <= 1, so no change for normal-scale runs). RED-first: the hermetic synthetic-tiny-pip test `tiny_pip_gap_stop_does_not_trip_the_redundancy_guard` reproduces the panic and is pinned here. Verified end-to-end: `aura run --harness stage1-r --real EURUSD` now returns the R block (exit 0) on a debug build. cargo test --workspace green; clippy --workspace --all-targets -D warnings clean. refs #117 |
||
|
|
a6fc48daa7 |
feat(stage1-r): operate the R layer from the CLI (iter 3)
`aura run --harness stage1-r [--real <SYM>] [--trace <n>]` now runs a Stage-1 R
backtest and prints its signal-quality metrics (the R block) in the RunReport
JSON - one shell call, the research loop's primary verb, ergonomic for Claude
to drive.
What shipped:
- vol_stop + risk_executor promoted from test fixtures to public aura-engine
composite-builders, with a StopRule{Fixed,Vol} axis (C11). aura-std becomes a
normal dependency of aura-engine (the composites are the engine's convenience
layer over the standard nodes - the sibling tier of summarize_r; the graph
stays acyclic: aura-engine -> aura-std -> aura-core). Both fixtures call the
public fns; a Vol-backed RED test pins the new match arm.
- stage1_r_blueprint fans one bias into BOTH a SimBroker (pip) and the
RiskExecutor (R), so one report carries both yardsticks honestly. run_stage1_r
folds summarize (pip) + summarize_r (R, round_trip_cost 0.0 - Stage-1 is
frictionless signal quality) and sets RunMetrics.r = Some(..). An r_equity tap
(cum_realized_r + unrealized_r via LinComb) persists through a separate 3-tap
persist_traces_r and renders via the existing `aura chart --tap r_equity`.
- `aura run --harness <sma|macd|stage1-r>`: a parse_run_args tokenizer replaces
the five `run` literal-slice arms so --harness/--real/--from/--to/--trace
compose in any order; --macd kept as a back-compat alias; --harness sma the
default. A compile-time match over Rust-authored built-ins - not a runtime node
registry, not a DSL (C9/C17): the CLI runs an authored harness, it does not
wire one.
Back-compat: --harness sma/macd leave RunMetrics.r = None, so skip_serializing_if
keeps pip-only and legacy runs.jsonl JSON byte-unchanged; the plain-run tap list
stays ["equity","exposure"].
Refactor beyond the plan (verified behavior-equivalent): the old standalone
parse_real_args was orphaned by the new tokenizer, so it and its now-redundant
unit test were removed and run's --real parsing inlined into parse_run_args. The
--real strictness (empty-symbol, non-i64 ms, repeated-flag, window-requires-real
-> exit 2) is preserved and now pinned by
parse_run_args_rejects_malformed_real_and_repeated_flags (added to restore the
deleted coverage). All five run_real integration tests stay green.
Verification: cargo build/test --workspace green (every crate, 0 failed); clippy
--workspace --all-targets -D warnings clean. The full diff was adversarially
reviewed (run-arg refactor equivalence against the removed function, harness
wiring, C1/C2/C9/C17, the serde back-compat pin) - verdict SOUND; the one flagged
coverage gap is closed here.
Follow-on (noted, not done): the stage1-r manifest reuses the
"sim-optimal(pip_size=...)" broker label; a dedicated "risk-executor" label would
read more honestly for a dual-tap harness that runs a RiskExecutor branch.
closes #129
refs #117 #128
|
||
|
|
13086b8590 |
plan: 0068 stage1-r CLI surface (iter-3)
Executable plan for iteration 3 of cycle 0065 - make the Stage-1 R layer operable from the command line. Three tasks: - Task 1: promote vol_stop + risk_executor + a StopRule axis from test fixtures to public aura-engine composite-builders (aura-std moves to a normal dep; the two fixtures rewired to call them; a Vol-backed RED test added). - Task 2: the stage1_r_blueprint dual-tap harness (one bias fanned into both SimBroker and the RiskExecutor) + run_stage1_r folding summarize_r into RunMetrics.r + persist_traces_r (a separate 3-tap helper that leaves the seven pip callers byte-unchanged). - Task 3: a parse_run_args tokenizer + --harness selector + run_dispatch, replacing the run literal-slice arms; binary-driven selector / flag- composition / r_equity-round-trip tests. Recon: two plan-recon agents (aura-cli surface, aura-engine promotion). Verified before handoff by a 3-critic adversarial pass (API-fidelity, plan-discipline, behavioral-correctness): discipline and behavior SOUND, one API fix applied (tempdir -> the existing temp_cwd helper, no tempfile dep). refs #129 #117 |
||
|
|
08d09df97d |
spec: 0065 stage1-r iter-3 CLI surface (boss-signed)
Amend the cycle-0065 spec with the "Iteration 3 - the CLI surface" design: make the Stage-1 R layer operable from the command line (the research loop's primary verb, ergonomic for Claude to drive), splitting the CLI/recording surface out of the originally-lumped iter-2. Design (forks derived under /boss, user-delegated "du entscheidest das Design", recorded on #117): - Promote vol_stop + risk_executor from test fixtures to public aura-engine composite-builders; the embedded stop generalizes to a StopRule{Fixed,Vol} enum (a C11 structural axis), vol_stop the volatility-defined default. - aura run --harness <name>: a compile-time selector (sma|macd|stage1-r) over Rust-authored built-ins - running, not wiring (C9/C17 intact: no runtime registry, no DSL). Replaces the run literal-slice arms with a parse_run_args tokenizer so --harness/--real/--trace compose orthogonally; --macd kept as an alias. - The stage1-r harness fans one bias into both SimBroker (pip) and the RiskExecutor (R), so one RunReport carries both yardsticks honestly; the run path folds summarize_r -> RunMetrics.r (additive, skip_serializing_if keeps pip-only/legacy JSON byte-unchanged), round_trip_cost=0.0 (Stage-1 is frictionless; a --cost knob is a follow-on). - An r_equity tap (LinComb of cum_realized_r+unrealized_r) persists via an extended persist_traces and renders through the existing aura chart --tap - no new viewer code. Scope line: "operable from the CLI" = run+inspect a Rust-authored harness (the #109 (a) reading the contracts settle), not authoring topology in the CLI; the broad #109 meta-level stays a separate future milestone. Deferred follow-ons: R-sweep families + rank-by-sqn, rich R-record charting, the bias_sign_flips metric rename (never carried out in iter-0; field is still exposure_sign_flips). Verified before signing: grounding-check PASS (all substrate + post-patch current-code claims ratified by green tests) - the autonomous /boss signature; plus an advisory 5-lens spec-skeptic panel (criterion/invariants/scope SOUND; ambiguity + plan-readiness BLOCKs resolved inline: the argv tokenizer, the 3-tap persist change, the round_trip_cost value, the dual-tap arg provenance), re-grounded after the patch. refs #129 #117 |
||
|
|
92591e5dc0 |
refactor(stage1-r): name the conviction column conviction_at_entry
Rename the dense PositionManagement record's column 9 from `bias_at_entry_abs` to `conviction_at_entry` (FIELD_NAMES, the `r_col`/test index constants, the cross-crate layout guard, and the doc comments). Why: the column holds |bias| at entry, which C10 defines AS conviction (bias = direction + conviction magnitude; ledger INDEX.md ~458). The record already names the other half of that decomposition by its domain concept (`direction` = sign, not `bias_sign`), so naming the magnitude half mechanically (`bias_at_entry_abs`) was an inconsistency; `conviction_at_entry` speaks the ledger's ubiquitous language and parallels `direction`, while `_at_entry` preserves the frozen-snapshot precision. Considered and rejected: collapsing `(direction, bias_at_entry_abs)` into one signed `bias_at_entry` and dropping `direction` as redundant — it is not redundant. On a reversal row col 4 (`direction`) tracks the reopened leg while col 9 tracks the closed trade's entry conviction, so the two describe different trades and cannot be losslessly merged; they are also distinct semantic axes (current-position direction vs per-trade entry conviction). Scope: domain name at the data boundary, mechanical name internally — the internal `bias.abs()` variables (`Open.bias_abs`, the fold's `Trade.bias_abs`) keep their computation-faithful names, exactly the raw-internal / domain-external split the record uses elsewhere. Behaviour-preserving: cargo build --workspace clean; cargo test --workspace 500 passed, 0 failed (unchanged); clippy --all-targets -D warnings clean. The layout guard (`r_col_indices_match_producer_field_layout`) confirms the renamed string. refs #117 |
||
|
|
b4e84335c4 |
feat(stage1-r): Sizer + RiskExecutor + R-metric enrichment (iter 2)
Iteration 2 of cycle 0065 (Stage-1 R signal quality) — the node + metric layer. Builds on iter-1's PositionManagement dense R-record + core summarize_r. What ships: - PositionManagement gains a 4th `size` input (slot 3 -> col 10), fed by the Sizer. R is computed size-INVARIANTLY (pure stop-distance ratio), so size never touches realized_r — pinned by a RED test (scaling size leaves every realized_r unchanged) at the node and again end-to-end through the executor. - Sizer (aura-std): `size = risk_budget / stop_distance` — flat-1R (risk_budget 1.0 => one risk unit per trade, size inversely proportional to the stop, not a constant). Reads bias (firing/presence) + stop_distance; the Stage-2 fixed-fractional sizer slots in unchanged (swap risk_budget for risk_fraction*equity, same node shape). - summarize_r enrichment: SQN (sqrt(n)*mean_R/sample-stdev_R; n<2 or zero-variance -> 0), conviction_terciles_r (E[R] by |bias_at_entry| tercile), net_expectancy_r (gross minus one round-trip cost per trade, charged in R via the latched_dist recovered from the entry_price/stop_price columns). summarize_r gains a `round_trip_cost` param (price units). The net-of-cost recovery is tested through the real producer->consumer seam, not just hand-built rows. - RunMetrics.r: Option<RMetrics> with #[serde(default, skip_serializing_if = "Option::is_none")] — legacy runs.jsonl (no `r` key) deserialise to None and a pip-only run's on-disk shape stays byte-unchanged (C14/C18 back-compat). Every RunMetrics literal threaded (report.rs, aura-registry, aura-engine/mc). - RiskExecutor (aura-engine integration-test fixture, sibling of vol_stop_composite): FixedStop -> Sizer -> PositionManagement behind open bias+price input roles, price fanned to both the stop and PM, the Sizer's size into PM. Bias is produced in-graph from the single price source (a second bias *source* would k-way-merge into separate cycles and mark stale prices). The Veto is a DOCUMENTED SEAM, not a runtime node (a pass-through identity is what C19/C23 DCE deletes). Tests: the composite bootstraps + runs + folds to the documented hand value; R invariant under risk_budget while the size column scales; a live-folded RMetrics survives the RunMetrics serde round-trip. The dense-record size column (10) is brought under the cross-crate layout guard (stage1_r_e2e r_col_indices_match_producer_field_layout) so the executor fixture's size-invariance read is drift-protected like the others. Scope: the CLI/recording surface (#129) is sub-split into a separate iteration 3 and is NOT in this commit. Verified: cargo build --workspace clean; cargo test --workspace 500 passed, 0 failed; cargo clippy --workspace --all-targets -D warnings clean. refs #117 #127 #128 #129 |
||
|
|
6e214957d1 |
plan: 0067 stage1-r iter-2 — Sizer + RiskExecutor + metric enrichment
Iteration-2 plan for cycle 0065 (Stage-1 R signal quality). Sub-splits the spec's iteration 2: this plan is the node + metric layer; the CLI/recording surface (#129) becomes a separate iteration 3. Five tasks, sequenced so each signature change threads all its callers before its build gate, and the two signature changes land before the composite that consumes both: 1. PositionManagement gains a 4th `size` input (col 10, from the Sizer); R stays size-INVARIANT (RED test pins it). Threads the PM unit tests + the stage1_r_e2e direct-eval column setups. 2. Sizer node (aura-std): `size = risk_budget / stop_distance`, flat-1R; the Stage-2 fixed-fractional seam slots in unchanged. 3. summarize_r enrichment: SQN, conviction terciles, net-of-cost; gains a `round_trip_cost` param (reads dense cols 6/7/9). Threads report.rs + the E2E callers and extends the cross-crate layout guard. 4. RunMetrics.r: Option<RMetrics> with serde(default, skip_serializing_if) for legacy runs.jsonl back-compat. Threads every RunMetrics literal (report.rs, aura-registry, aura-engine/mc). 5. RiskExecutor composite (aura-engine integration-test fixture, sibling of vol_stop_composite): FixedStop -> Sizer -> PositionManagement behind open bias+price roles, price fanned to both; bias produced in-graph (single source, no stale-price two-cycle merge). Veto is a documented seam, not a node. E2E equivalence + R-invariance under risk_budget. refs #117 |
||
|
|
0998f9aabf |
refactor(stage1-r): the vol stop is a composition, not a fused node
Applies the principle the user named: a node is a primitive only if it is NOT DAG-expressible from other primitives; a function that needs a missing primitive gets the primitive added, not fused away. - Remove the fused VolStop node (it was pure feed-forward arithmetic). - The volatility stop is now a composition of primitives, vol_stop(length, k): k * Sqrt(Ema(Mul(d,d), length)), d = Sub(price, Delay(price,1)) -- a rolling EWMA standard deviation. Built with GraphBuilder; proven end-to-end (bootstraps + runs + emits k*sigma) in tests/vol_stop_composite.rs. - Migrate the one VolStop caller (stage1_r_e2e.rs) off it: the R-is-stop-defined test now contrasts a tight vs a wide FixedStop (two constant distances still fold to different R, the property under test). Uses the Mul + Sqrt primitives added in the prior commit. FixedStop stays the only stop-rule primitive (a triggered constant). Full suite + clippy green. refs #117 #119 |
||
|
|
831092841e |
feat(aura-std): add Mul + Sqrt primitives
The two genuinely-missing arithmetic primitives (Mul = two-stream f64 product, Sqrt = one-input f64 root, negatives clamped to 0). They are the building blocks for the volatility stop as a composition (rolling EWMA stddev), replacing the fused VolStop node. Also corrects plan 0066 Task 3 (the VolStop removal must migrate its stage1_r_e2e.rs caller — a false premise the implementer caught). refs #117 #119 |
||
|
|
5de8576fd0 |
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 |
||
|
|
2c43296c2c |
feat(stage1-r): bias rename + stop-rule + position-management + summarize_r (iter 1)
Iteration 1 of the Stage-1 "R-based signal quality" cycle (spec 0065): a strategy's signal quality, measured in R on its unsized bias stream, feed-forward. New discrete-trade machinery, NOT a SimBroker extension. What lands: - exposure -> bias (refs #126): the Exposure node/type/file/output-field renamed to Bias (computation unchanged: clamp(signal/scale,-1,+1); the SEMANTICS change — the output is the unsized strategy bias, sizing leaves the strategy). Scoped to the semantic core; behaviour-preserving cosmetics are deferred (see below). - stop-rule nodes (refs #119): VolStop = k*EMA(|price - prev_price|) (one fused node; the volatility that defines 1R, close-to-close — true-range ATR deferred, needs OHLC) + FixedStop (constant, the test fixture / fixed-vs-vol structural-axis sibling). - PositionManagement (refs #127): the stateful heart. Latches the entry-cycle stop distance as the FROZEN R-denominator (never re-read), marks/exits no-look-ahead like SimBroker (a position earns from the next cycle; an exit realises against the cycle's close), and emits ONE dense per-cycle R-record (C8). Stop-outs are NOT capped at -1R (a gap through the stop realises R < -1 — the honest loss tail); R is computed size-invariantly (size = (exit-entry)*dir / latched_dist cancels). The trade ledger is the rows where closed_this_cycle; the R-equity is cum_realized + unrealized; a position open at window end is the last row (open=true) — explicit, never silent MtM. - summarize_r + RMetrics (refs #129): the post-run fold (NOT an in-graph node — recorders drain post-run). E[R], win-rate, profit-factor, avg win/loss R, by-trade max R-drawdown, n_open_at_end (window-end force-close). SQN / conviction terciles / net-of-cost / RunMetrics.r are iteration 2. Design adversarially hardened before implementation (12-juror refute panel; decisions on #117). Ratified implementer deviations from the plan snippet, verified by hand: - col-4 `direction` tracks the OPEN position at cycle end (window-end synthesis; names the reopened leg on a reversal), falling back to the closed trade's dir only when flat. summarize_r does not read col 4; the R-metrics are unaffected. - .named("exposure") kept on the 4 previously-unnamed Bias nodes so the auto-derived param-path stays exposure.scale (no manifest/dir-name drift) — the unnamed nodes would otherwise flip to bias.scale. - intra-doc links [`Exposure`] -> [`Bias`] in sim_broker/latch + the sample-model test pin (cosmetic, behaviour-neutral; broken intra-doc links fail cargo doc). - the E2E drives a full bootstrapped Harness (stronger than the plan's direct-node drive — exercises the real cross-crate producer->consumer seam). Deferred (behaviour-preserving, separate cosmetic pass — NOT this iteration): RunMetrics.exposure_sign_flips / Metric::ExposureSignFlips, SimBroker/LongOnly "exposure" input ports, the "exposure" tap/trace names, and the .named("exposure") instance labels. Iteration 2: the Sizer seam, the RiskExecutor composite (+ the Veto documented-seam), summarize_r enrichment, RunMetrics.r, and the CLI/recording surface. Verification (orchestrator-run, not agent-claimed): - cargo build --workspace: clean. - cargo test --workspace: all green, 0 failed (incl. the new bias/stop_rule/ position_management unit tests, the summarize_r arithmetic tests, and the stage1_r_e2e capstone + layout guard). - cargo clippy --workspace --all-targets -- -D warnings: clean (exit 0). - Keystone RED tests pass: no_lookahead_bias_exit_realises_the_held_move, stop_out_is_not_capped_at_minus_one_r, no_gap_stop_is_exactly_minus_one_r, reversal_closes_one_leg_and_reopens, open_at_window_end_is_carried_on_the_last_row. refs #117 #119 #126 #127 #129 |
||
|
|
c5f5e17297 | plan: 0065 stage1-r-signal-quality | ||
|
|
51c87e5a91 |
spec: 0065 stage1-r-signal-quality (boss-signed)
Stage-1 "R-based signal quality" cycle spec (milestone #119/#126/#127/#128/#129, contract C10). Signal quality measured in R on a strategy's unsized bias stream, feed-forward (no equity feedback): bias -> stop-rule -> position-management -> per-trade R-outcomes -> summarize_r fold -> E[R]/SQN. Design adversarially hardened (12-juror refute panel; decisions logged on #117): - R is stop-defined and LATCHED at entry (frozen R-denominator, never re-read). - Stop-outs are NOT capped at -1R (honest loss tail; gap-through -> R < -1). - Fills one-cycle-lagged (SimBroker no-look-ahead discipline; RED test mandatory). - First stop is volatility-scaled (VolStop = k * EMA(|d price|)), not constant (a constant makes R just rescaled pips). - position-management emits a dense per-cycle R-record (trade ledger + R-equity + window-end handling), tapped by a Recorder, folded post-run by summarize_r (a post-run fold, NOT an in-graph node). - Sizer = load-bearing flat-1R seam; R computed size-invariantly. Veto = doc seam, not a runtime node (DCE deletes an identity). - exposure -> bias rename lands first (compiler-driven). Grounding-check PASS (11 assumptions ratified by green tests; auto-signed under /boss). refs #117 |
||
|
|
f040b66f30 |
design(c10): reframe to a bias stream + R-unit signal quality
Adopt the R-reframe ratified in #117: the strategy's native unit is risk (R), not pips/dollars, and its primary output is a directional bias. - exposure -> bias: the primary output is an UNSIZED direction + conviction `f64 in [-1,+1]`; sizing and the protective stop leave the strategy. - Signal quality is measured in R (Stage 1): an R-evaluator integrates the per-trade R-outcomes of a flat-1R RiskExecutor into R-expectancy. R is defined by the stop, account-/instrument-agnostic; pips retired as the unit. Flat-1R needs no equity, so Stage 1 is feed-forward (no cycle). - Risk-based execution is a decoupled layer: a RiskExecutor composite (stop-rule -> Sizer -> Veto -> position-management) per symbol, nested in a Broker/Account composite; account mode (netting/hedging) = composition constraint. "Sizer" is not "risk-manager" (that names the Veto layer). - Currency P&L is Stage 2 (fixed-fractional, compounding). The only feedback (equity -> Sizer) is cut by a z^-1 register on the fill edge (mark-to-market stays a same-cycle price read), encapsulated in the executor; flat-1R vs compounding is a structural axis (the explicit register is mandatory because C23 reorders the flat graph). - Position-event table stays the decoupled Stage-2 audit layer = first difference of the book (deal = target - book - in_flight); the flawed 0064 exposure-integral derive is abandoned. Touches: ledger C10 + CLAUDE.md domain invariant #7 + glossary (adds bias, R, R-evaluator, RiskExecutor, Sizer, veto, Stage 1/2; revises broker, exposure stream, position table, sim-optimal broker, strategy, ...). Also fixes cross-file drift surfaced by adversarial review (C20 guarantee + front-matter provenance still claimed "exposure stream"; cross-refs corrected to C9/C19/C20/C23). Industry-grounded against LEAN / nautilus_trader / backtrader / QSTrader / vectorbt / zipline. refs #117 |
||
|
|
2e0500dedd |
docs(claude): permit the /boss rollback sandbox in the main-sacrosanct rule
The autonomous orchestrator may now git reset --hard its OWN unpushed commits made this run (above the session anchor) when a line of work dead-ends; pushed and user-ratified history stays strictly forward-only (git revert, never reset). This codifies the /boss rollback sandbox the skill assumed but the project rule forbade, resolving the conflict the auto-mode classifier surfaced. User-authorized. |
||
|
|
7d7d1f72d9 |
audit(broker-foundation): cycle close 0063 — ledger records the C10 A-side foundation (#113 + #114)
Cycle-close tidy for broker-foundation (InstrumentSpec deploy metadata #113 + PositionEvent schema #114). Architect drift review (3638d48..HEAD): code is drift-clean. PositionEvent matches the C10 column spec field-for-field (no open_ts, unsigned volume, direction-is-action, bare-i64 action); the engine run-loop is untouched (C14); InstrumentSpec stays Copy metadata at the ingestion edge (C7/C15); no instrument identity reaches the hot path. The only drift was stale ledger prose, fixed here: - C10 realization note: added a cycle-0063 entry recording that the position-event *schema* landed (PositionAction/PositionEvent), while derivation (#115) and the realistic broker nodes (#116) remain the deferred decoupled layer. - C15 realization note: replaced "minimal today (pip only)" with the six-field deploy-grade floor and the #124 currency-type forward note (quote_currency is &'static str; the #124 resolver chooses how its runtime tier carries currency). Regression (verbatim): cargo test --workspace = 458 passed / 0 failed (exit 0); cargo clippy --workspace --all-targets -- -D warnings = clean (exit 0). Carry-on (not actioned, justified): the inline reversal test is a shape-pin only — the behaviour test arrives with #115's derive, and the E2E twin already exercises the reversal wire shape. The quote_currency &'static str / #124 resolver concern is logged on #124 as a deliberate deferred design choice. This is drift-clean, NOT a milestone close — the realistic-broker milestone needs its end-to-end fieldtest green, which depends on #115/#116/#120-123 still to land. Removed the ephemeral cycle artifacts (spec + plan) per the lifecycle convention. |
||
|
|
b91c89f964 |
feat(broker-foundation): InstrumentSpec deploy metadata + PositionEvent schema
The realistic-broker milestone's (C10 A-side) two foundation value types — no broker, no derivation yet, fully tested. #113 — InstrumentSpec gains six deploy-grade fields (instrument_id, contract_size, pip_value_per_lot, min_lot, lot_step, quote_currency) and the vetted table is populated for GER40/FRA40/EURUSD/GBPUSD/USDCAD. The struct stays Copy (quote_currency is &'static str); the _ => None refuse-don't-guess arm is the permanent floor. This is the milestone's permanent authored floor (recorded-metadata tier is forward work, #124). #114 — PositionAction { Buy, Sell, Close } (closed enum, serde-encoded as a bare i64 0/1/2 via From/TryFrom) + PositionEvent in aura-engine beside RunMetrics, faithful to the C10 column spec: event_ts, action, position_id, instrument_id, unsigned volume; no open_ts; direction is the action (no signed-volume trick). Re-exported from the crate root. Derived fork decisions (recorded on #113): quote_currency &'static str; action as ordinal i64; stable instrument_id + overridable vetted values. Verified by the orchestrator: cargo test --workspace = 458 passed / 0 failed; clippy --all-targets -D warnings clean. An adversarial 4-lens review (ledger faithfulness, downstream soundness, serde/wire robustness, test honesty) returned 3 SOUND + 1 CONCERN: quote_currency &'static str cannot hold a runtime-sourced currency at the #124 resolver — not a blocker (#115/#116 read only the authored floor), logged on #124 as a deliberate deferred design choice. The implementer's deviation from the plan snippet (vec![..] -> [..]) avoids clippy::useless_vec; semantics identical. Two tester-authored consumer-boundary E2E tests added beyond the inline units: instrument_spec_deploy_metadata.rs (public resolution seam) and position_event_table.rs (persisted bare-int wire shape + reversal table). closes #113 #114 |
||
|
|
099aac87e1 |
plan: 0063 broker-foundation — InstrumentSpec deploy metadata + PositionEvent schema
Three tasks. Task 1 (#113, compile-atomic): RED-rewrite the lookup test to the six-field shape + invariant/uniqueness tests, then extend InstrumentSpec and populate the vetted table for the five instruments (refuse arm stays). Task 2 (#114, greenfield): RED the round-trip / try_from-Err / serde-bare-int / reversal tests, then add PositionAction (serde-as-i64) + PositionEvent in report.rs and re-export from the crate root. Task 3: workspace test + clippy gate. Recon-grounded line anchors; the redundant std::convert::TryFrom import dropped (2021 prelude). INDEX.md realization-note drift (C10/C15 "minimal today") deferred to the cycle-close audit, not folded here (code-only scope). refs #113 #114 |
||
|
|
cc0dce19a3 |
spec: 0063 broker-foundation — InstrumentSpec deploy metadata + PositionEvent schema (boss-signed)
The broker milestone's (C10 A-side) foundation: two pure value-type deliverables, no broker and no derivation yet. - #113: extend InstrumentSpec with deploy-grade fields (instrument_id, contract_size, pip_value_per_lot, min_lot, lot_step, quote_currency) + populate the vetted table for the five instruments; refuse-don't-guess arm stays. Permanent authored floor of the override hierarchy (#124). - #114: PositionAction { Buy, Sell, Close } (closed enum, serde-encoded as i64) + PositionEvent row in aura-engine, faithful to the C10 column spec (no open_ts, unsigned volume, direction is the action). Signed under /boss on a grounding-check PASS (all current-state assumptions ratified by green tests; the two new types are greenfield, expected). Derived fork decisions (quote_currency &'static str; action enum encoded as i64 0/1/2; stable instrument_id + overridable vetted values) recorded on #113. refs #113 #114 |
||
|
|
3638d4822e |
docs(ledger): record the tap-aware decimation refinement (#111) in the cut-2 note
The cut-2 amendment described decimate as a pure min/max transform; #111 made the per-bucket reduction tap-aware (mean for the bounded exposure, min/max for equity). Keep the always-loaded design memory accurate; note #112 (bars/OHLC rendering) and #110 as the remaining deferred refinements. refs #111 #112 |
||
|
|
2957561c30 |
fix(chart): mean-decimate the bounded exposure series instead of a min/max band
The serve-time decimation (#108) reduced every series by per-bucket min/max. That is the right envelope for a cumulative equity curve but wrong for the bounded exposure stream (C10, f64 ∈ [-1,+1]): over a multi-year window each ~hundreds-of-bars bucket straddles sign flips, so min/max is ±1 in nearly every bucket and the exposure collapses to a solid -1..+1 band that reads as per-point oscillation — even for a calm strategy (a 50/200 member flips only 0.81% of bars yet still bands out). Make decimation tap-aware (RED-first, #111): a new `ReduceKind {MinMax, Mean}` on each Series, set by the chart builders via `reduce_for_tap` (exposure -> Mean, else MinMax). `decimate` honours it — a Mean series emits the per-bucket mean (its net/duty-cycle level) in both spine slots, a MinMax series keeps min/max. The shared spine, the equity rendering, and the page payload are unchanged (reduce is server-side only, `#[serde(skip)]`). Verified on real GER40 1y M1: the served exposure series goes from a ±1 band to a smooth net-level line in [-0.38, +0.34]. Rendering the min/max envelope honestly (range bars / OHLC, vs today's polyline) is the deferred other half — filed as #112. Verified: cargo test --workspace = 447 passed / 0 failed (incl. the RED-then-GREEN decimate_mean_reduces_a_bipolar_series_to_its_bucket_level); clippy -D warnings clean. closes #111 refs #112 |
||
|
|
e01bdc3d5e |
audit(visual-world-cut-2): cycle close 0062 — ledger records cut 2 (#102 + #108)
Cycle-close tidy for visual-world-cut-2 (decimation #108 + run-context header #102).
Architect drift review (53eeba5..HEAD): the only drift was a STALE LEDGER — the C22
section still listed #102 (run-context header) and #108 (decimation) as deferred/open
after they shipped in
|
||
|
|
476342d7b1 |
feat(chart): decimate served pages + run-context header (visual World cut 2)
Hardens the families-comparison chart page (#107) so it is openable and legible on real multi-year data. Two halves, both confined to the CLI render path (engine + registry untouched, C14): #108 — serve-time min-max decimation. A pure `decimate(ChartData, buckets)` transform partitions the shared union-ts spine into <=~2000 buckets and emits each series' per-bucket min+max (nulls preserved), bounding the served page to a few thousand points regardless of the underlying M1 point count. Applied in emit_chart on both the single-run and family paths; a no-op under budget (every existing fixture). Full recorded data stays on disk; only the page is thinned. For a walk-forward family the null-fill blow-up collapses for free (an all-null bucket stays null). Deterministic (C1). #102 — a run-context header. A `ChartMeta` (name/commit/window/broker/seed/taps, + member count for a family, + bound params for a single run) is built from the RunManifest in build_chart_data / build_comparison_chart_data (which gain a `name` arg), injected into window.AURA_TRACES.meta, and rendered by a new pure buildHeader in chart-viewer.js (headless .mjs-guarded). The family window is the SPAN across members (min from, max to), not the first member's window — the only reading that labels a disjoint walk-forward family's true OOS coverage (it collapses to the shared window for sweep/MC). Reuses the existing render_value for param display (no new Scalar Display, keeping C7's tag-only param plane). The earlier "manifest is NOT carried into the page" render pin is flipped to a positive one. Verified: cargo test --workspace = 446 passed / 0 failed; cargo clippy --workspace --all-targets -D warnings clean. New coverage: 5 decimate unit tests, single-run + family meta wiring (incl. the span-window invariant), a buildHeader headless guard, and two end-to-end cli_run tests pinning the served-page meta at the binary boundary. closes #108 closes #102 |
||
|
|
12bfa50a4f |
plan: 0062 visual-world-cut-2 — decimation + run-context header
Three tasks on the aura-cli render path + a workspace gate: - Task 1 (#102, compile-atomic): ChartMeta + ChartData.meta, built from RunManifest in build_chart_data/build_comparison_chart_data (gain a `name` param), every call site threaded (emit_chart + 3 comparison unit tests + the render test helper), the manifest re-injected into window.AURA_TRACES, the negative render pin flipped. - Task 2 (#102): #ctx CHART_HEAD slot + a pure buildHeader in chart-viewer.js with a node-driven headless guard pair (chart_viewer_header.{mjs,rs}). - Task 3 (#108): a pure decimate(ChartData, buckets) min-max transform applied in emit_chart on both paths; engine + registry untouched (C14). - Task 4: cargo test --workspace + clippy -D warnings. refs #108 refs #102 |
||
|
|
a8e11e7733 |
spec: 0062 visual-world-cut-2 — decimation + run-context header (boss-signed)
Served-page hardening for the families-comparison chart, bundling two issues that share one code locus (the aura-cli render path) and one purpose (C22/C14: make the #107 view openable + legible on real multi-year families): - #108 — serve-time min-max decimation as a pure ChartData->ChartData transform in emit_chart (both single-run and family paths), bounding the served page to a few thousand points regardless of the underlying M1 point count; full data stays on disk; the WFO null-fill page collapses for free. Engine/registry untouched. - #102 — a ChartMeta threaded from RunManifest into window.AURA_TRACES, rendered by a new pure buildHeader in chart-viewer.js (headless .mjs guarded), showing name/window/broker/commit/taps (+ member count for a family). Signed under /boss on the Step-5 grounding-check PASS (10/10 load-bearing assumptions ratified by green tests). refs #108 refs #102 |
||
|
|
f51d54a1c1 |
docs(cell): name Scalar as the param-authoring surface; no tagged Cell ctors by design
Cell is the kind-erased hot-path carrier (C7): the value carries no tag, so there are deliberately no tuple-style/tagged constructors (`Cell::I64(..)`). Document on the struct that Scalar is the value to reach for when authoring a param / bind / sweep point / RunManifest field, and that the from_* ctors are the carrier-side entry. Resolves the discoverability papercut without softening the carrier split. closes #100 |
||
|
|
5a01179fd5 |
docs(ledger): record the analysis meta-level as an open thread pointing to #109
Add an "Open architectural threads" entry that ties together aura's differentiator (C21): the composable-orchestration rebuild (sweep/MC/walk-forward as composable tools, not hard-wired CLI verbs) + the project-as-crate authoring layer (aura new / experiment-builder API / Aura.toml / cdylib loading, C16/C17). Records the load-bearing open fork — "driven via the CLI" = running a Rust-authored experiment (compatible with C16/C17/C22) vs wiring nodes through the CLI itself (a deliberate amendment to C17/C22/C9) — to be resolved in the brainstorm before any cycle. Keeps the meta-level in focus in the always-loaded design memory; #109 is its tracker home. No design decided here (the fork is recorded, not resolved). refs #109 |
||
|
|
53eeba5e16 |
audit(families-view): cycle close 0061 — drift-clean, ledger records the comparison view
Architect drift review over 99fd32b..HEAD (the housekeeping commits #99/#98 plus the 0061 cycle): code drift-clean. Engine untouched (aura-engine + aura-core diff empty — C9/C14); C21 realized faithfully (the comparison view consumes a set of runs, a family is the first producer, no orchestration-axis rebuild); C18/C10 refuse-don't-guess honoured (every new error path exits 2, never panics; all six tracing entry points guarded before persist, so name resolution is total). The only finding was a ledger-realization gap, lifted in this commit. Ledger (docs/design/INDEX.md): added a C22 amendment recording the families-comparison view as realized (#107) — aura chart <family> overlays one tap across a family's members on a shared y-scale, union-ts spine giving one mechanism / three correct readings, the write-guard totalising name resolution; reconciled the two stale "families-comparison ... remain open" lines (the #104 note and the open-threads list) and named the deferred follow-ons (#102 run-context header, #47 multi-column tap selection, the orchestration-composability rebuild). Regression: cargo test --workspace green (no failures), clippy --workspace --all-targets -D warnings exit 0, cargo build --workspace clean — verified by the orchestrator before close. No baseline to update (aura's regression gate is the gated determinism suite, which is part of the workspace tests). Retire the cycle's ephemeral spec + plan (0061). refs #107 |
||
|
|
4c64feb9ed |
feat(chart): aura chart <family> overlays a family's members; --tap + write-guard
`aura chart <name>` now branches on the name's on-disk kind: a single run charts
its taps as before; a FAMILY overlays one tap (default equity) across all its
members in one self-contained uPlot page, consistent across sweep / MC /
walk-forward. Members share ONE y-scale (they measure one quantity — that is what
makes the overlay comparable, unlike the single-run overlay of different taps),
and align on the union-ts spine via the existing join_on_ts: sweep/MC members
share the window (true overlay), walk-forward members are disjoint OOS windows
(null-complementary -> the stitched curve). One mechanism, three correct readings.
- build_comparison_chart_data (one labelled Series per member, shared y_scale_id);
filter_to_tap (single-run --tap restricts to one tap; no --tap = all taps,
unchanged); parse_chart_args (`<name> [--tap <t>] [--panels]`, any order);
emit_chart name-kind branch; USAGE updated.
- Write-guard: ensure_name_free is now called once per tracing command (run /
run --macd / run --real -> Run; sweep / mc / walkforward -> Family) so a name
cannot be reused across kinds — name resolution stays a total function.
Refuse-don't-guess throughout (exit 2, never panic). Engine untouched (C9/C14).
Tasks 2-3 of plan 0061; Task 1 (registry) landed in
|
||
|
|
9b2adcbb1b |
feat(trace-store): name classification, family resolver, write-guard
aura-registry gains the read-side of total trace-name resolution and the
write-guard that makes it total:
- NameKind {Run, Family, NotFound} + TraceStore::name_kind — top-level index.json
is a single run; else ≥1 member subdir with index.json is a family; else absent.
- read_family -> Vec<FamilyMember{key, traces}> — every member of a family, read
via the existing read("<name>/<key>"), sorted by key (deterministic, C1); an
absent/empty family reads as empty (treat-as-absent), a malformed member
propagates Parse.
- ensure_name_free(name, WriteKind) + a TraceStoreError::NameTaken variant —
refuses only cross-kind name reuse, so the one ambiguous on-disk state (a name
used by both a run and a family) is unreachable; same-kind overwrite stays Ok.
Task 1 of plan 0061. Engine untouched (C9/C14). Tests: cargo test -p aura-registry
green (18 passed, incl. the 3 new), clippy -D warnings clean.
Note: the implement-loop quality gate exhausted its re-loop oscillating on the
method NAME (ensure_name_free "implies unused" vs a rename diverging from the
ratified plan/callers). Orchestrator overrule: the ratified name stands — it is
fixed by the spec/plan/#107 and the Task-3 call sites, and the doc comment already
states that Ok means "writable here", not "unused". The implementation is green,
clippy-clean, and spec-faithful.
refs #107
|
||
|
|
da5068e10a |
plan: 0061 families-comparison view
Three tasks: (1) aura-registry — NameKind/FamilyMember/WriteKind, name_kind, read_family, ensure_name_free + a NameTaken error variant; (2) aura-cli — the comparison ChartData builder (shared y-scale, union-ts spine), filter_to_tap, parse_chart_args, the emit_chart name-kind branch, the --tap flag, USAGE; (3) aura-cli — the write-guard calls in the six tracing fns + collision tests. Each task RED-first with per-crate gates; workspace test + clippy at the close. refs #107 |
||
|
|
5f31eccc7c |
spec: 0061 families-comparison view (boss-signed)
A read-side cycle: `aura chart <family>` overlays one tap (default equity) of all members of a sweep / MC / walk-forward family in one self-contained uPlot page, consistent across kinds via the existing union-ts spine. The view consumes a "set of runs to compare" (a family is its first producer) — the first step toward the programmable analysis meta-level (C21), without rebuilding the orchestration axes. aura-registry gains name_kind / read_family / ensure_name_free (a write-guard making name resolution a total function); aura-cli gains the comparison ChartData builder + emit_chart branch + --tap. aura-engine untouched (C9/C14). Brainstorm-ratified, grounding-check PASS (11/11 current-behaviour assumptions ratified by green tests). Signed under /boss on the grounding PASS. refs #107 |
||
|
|
e1ad0979de |
refactor(ger40-examples): source pip from instrument_spec, drop the baked literal
The runnable GER40/FRA40 examples baked `const PIP_SIZE = 1.0` and a dead pip column in the compare INSTRUMENTS table — a second source of pip truth parallel to the vetted `aura_ingest::instrument_spec` channel the CLI `--real` path already uses (#22). Route them all through one `pip_size_of(symbol)` helper: build_harness sources GER40 internally, ger40_breakout_blueprint gains a `pip_size` first arg fed from the lookup, and compare's run_cell sources each instrument's pip per cell. Value-identical (GER40/FRA40 both resolve to 1.0) and behaviour-preserving: the gated determinism / blueprint param-space / world tests stay green unchanged, and the frozen `sim-optimal(pip_size=1)` broker-label pins are untouched. closes #98 |
||
|
|
094d63bc47 |
fix(family-stdout): unify family-wrapper key order with the store
The sweep / walk-forward / mc family-member stdout lines routed the embedded RunReport through serde_json::json! -> a serde_json::Value, which re-alphabetizes the manifest keys (broker-first) and, for mc, even reordered the top-level keys (seed after report). That diverged from the commit-first declaration order that RunReport::to_json and the stored families.jsonl already use. Splice the pre-serialized report into a hand-built line via family_member_line / mc_member_line so each family-member stdout line is byte-identical to its families.jsonl record (commit-first manifest, seed between family_id and report). RED-first: the two new key-order tests fail against the old json! helpers and pass after the format! rewrite. closes #99 |
||
|
|
99fd32b1f9 |
feat(real-family): realistic strategy lengths over real data
The built-in family grid was calibrated for the synthetic demo streams (18/60
bars), where trend SMA 2-5 / MACD 2-4-3 are the only lengths that warm. Over real
M1 data those are noise — ~9000 exposure sign-flips per month, a strategy trading
the bid-ask wiggle, not a trend.
DataSource::strategy_lengths() makes the grid data-kind-dependent (like
wf_window_sizes): synthetic keeps {2,3}x{4,5} + MACD 2/4/3 (byte-unchanged); real
uses {50,100}x{200,400} trend SMAs + standard 12/26/9 MACD. Threaded through
sweep_family + sweep_over (the walk-forward in-sample sweep). Over real EURUSD the
monthly flip count drops from ~9000 to ~130-310 — a genuine trend cross.
This is a demo-strategy calibration patch (ledger C22 amendment notes it as such);
the real answer is project-authored strategies owning their own grid (C9), deferred
to the project-env work.
Verified: cargo test -p aura-cli (all green, synthetic byte-unchanged),
clippy -D warnings clean; real sweep probe shows ~130-310 flips/month.
refs #106
|
||
|
|
607e5487e9 |
audit(real-family): cycle close 0060 — ledger records the real-data family axis
Architect drift review (b92aab7..HEAD): the cycle is contract-clean — C9 (engine untouched, whole change CLI-side), C6/C1 (real bars streamed from the pre-recorded archive, real sweep byte-deterministic), C10 (refuse-don't-guess shared by run --real and the family path), C12 (MC-exclusion fenced: mc --real exits 2). One medium ledger-gap, now closed; one low item ratified as carry-on. - Ledger: docs/design/INDEX.md C22 gains a "real-data family source" amendment (#106) recording the opt-in real-data axis on sweep/walkforward families, the DataSource provider, the MC exclusion (Fork A), and the oos{ns} key widening. This was the only drift — a documentation gap, not code drift. - Ratified carry-on [low]: the real walk-forward member key oos{from.0} is now an epoch-ns integer; portable and collision-free, covered in practice by the walkforward_real test (>=2 distinct oos<ns> dirs) and the 9-window E2E probe. No explicit ns-uniqueness assertion beyond starts_with("oos") — acceptable; a dedicated pin is not worth an iteration. - Retire ephemeral spec/plan 0060 (git rm) per the cycle-artifact lifecycle; the durable record is this ledger amendment + the git history. No regression scripts configured (architect is the gate). Full workspace test + clippy -D warnings green at close. refs #106 |
||
|
|
8e5d14b2bb |
feat(real-family): --real parser + dispatch wiring; complete the feature
Plan 0060 Tasks 4-5 (#106): wire `aura sweep|walkforward --real` end to end. - A shared RealWindowGrammar accumulator holds the `--real <SYMBOL> [--from <ms>] [--to <ms>]` grammar (flag-repeat strictness, empty-symbol + window-without-real rejection) for both parsers, mirroring parse_real_args. - parse_sweep_args grows the DataChoice 4th element; parse_walkforward_args is new (no --strategy axis). The sweep + walkforward dispatch arms become rest-matched, build the DataSource via from_choice (build-or-refuse on un-vetted symbol / absent data), and run. USAGE documents the --real tails. mc stays exact-matched, so `aura mc --real` falls through to usage + exit(2) — Fork A: MC excluded (its seed varies a synthetic price-walk, undefined over real data). - Gated integration tests (skip on no local data): sweep --real EURUSD persists 4 portable member dirs over real bars and charts; walkforward --real EURUSD persists one oos<ns> dir per rolling window; same real sweep twice is byte-identical (C1). Un-gated: un-vetted symbol, window-without-real, and mc --real each refuse with exit 2. - Remove the now-redundant `#[allow(dead_code)]` from the Task-1 provider: every const, both enums, and every method are live once the wiring lands. Verified end-to-end over real EURUSD data: sweep --real -> 4 members; walkforward --real over 2024 -> 9 rolling OOS windows; all refusals exit 2. Full workspace test + clippy -D warnings green. closes #106 |
||
|
|
2a6ca47acd |
feat(real-family): thread DataSource + pip through the family builders
Plan 0060 Tasks 2-3 (#106): the family builders now take a &DataSource and draw their source, pip, window, and walk-forward roller sizes from it instead of the hardcoded synthetic VecSource / SYNTHETIC_PIP_SIZE. - sample_blueprint_with_sinks / momentum_blueprint_with_sinks take a pip_size (un-hardcode the SimBroker); every caller threads it. - sweep_family / momentum_sweep_family / walkforward_family / sweep_over / run_oos take &DataSource; run_sweep / run_walkforward take a DataSource and the dispatch passes DataSource::Synthetic (the --real parser is the next step). Synthetic behaviour is byte-unchanged. - Fix a design gap the implementer correctly bounced on: walk-forward is a windowed consumer whose span comes from the 60-bar walkforward_prices, NOT the 18-bar showcase that full_window() returns. Added DataSource::wf_full_span() (synthetic walk-forward span; real = the probed --from..--to) and pointed walkforward_family at it. Without it the synthetic roller (24,12,12) over span (1,18) exits SpanTooShort. Verified: cargo test -p aura-cli (all green, synthetic byte-unchanged), cargo clippy -p aura-cli --all-targets -D warnings (clean). refs #106 |
||
|
|
cf102172ec |
feat(real-family): DataSource provider + shared real-data helpers
Foundation for `aura sweep|walkforward --real` (#106, plan 0060 Task 1). - New CLI-side `DataChoice` (parsed) / `DataSource` (opened) provider: the synthetic-vs-real source choice as one type, supplying a member's source, pip, full/windowed sources, and walk-forward roller sizes. Not yet wired into the family builders (plan Tasks 2-5); `#[allow(dead_code)]` marks the wired-later items, to be removed once the builders thread `&DataSource`. - DRY: `run_sample_real`'s inline pip-refusal / no-data refusal / probe-window logic is extracted to shared `instrument_spec_or_refuse` / `no_real_data` / `probe_window` helpers, now reused by `DataSource::from_choice` / `full_window` instead of duplicated. - Real walk-forward roller-size constants (90d / 30d / 30d in ns, Fork D/F). The synthetic provider is consumer-dependent by design (full-window consumers draw showcase_prices; windowed consumers draw walkforward_prices) so the byte-unchanged guarantee holds across both family paths. Verified: cargo test -p aura-cli (all green incl. byte-unchanged synthetic path), cargo clippy -p aura-cli --all-targets -D warnings (clean). Full-workspace gate at iter close after Tasks 2-5. refs #106 |
||
|
|
bd5a08d71f |
plan: 0060 real-data source path for family runs
Five compile-gated tasks for spec 0060: (1) additive DataSource/DataChoice provider + ns roller constants, (2) pip-parametrize the two blueprints + thread all callers, (3) thread &DataSource into the five family builders + run_sweep/ run_walkforward (dispatch passes Synthetic), (4) --real parser grammar + build-or-refuse dispatch + USAGE, (5) gated real-path integration tests + full suite + clippy. Each task threads its signature change with all callers so `cargo build -p aura-cli --all-targets` is green per task; the byte-unchanged synthetic path is the regression guard. refs #106 |
||
|
|
9637730520 |
spec: 0060 real-data source path for family runs (boss-signed)
Opt-in `--real <SYMBOL> [--from <ms>] [--to <ms>]` for `aura sweep` and `aura walkforward`, mirroring `aura run --real`: each family member streams real M1 close bars from the local data-server archive instead of the synthetic stream. MC is excluded — its seed varies a synthetic price-walk realization, undefined over real data (one realization -> identical members; a real MC needs a bootstrap-resampling axis, its own cycle). Engine, ingest, and registry untouched: a CLI-side `DataSource` provider replaces the hardcoded `VecSource`. `M1FieldSource` already impls `Source`; `WindowRoller` already rolls over epoch-unit timestamps. Boss-signed: Step-5 grounding-check PASS — all 8 load-bearing current-behaviour assumptions ratified by named, currently-green tests. refs #106 |
||
|
|
b92aab713c |
audit(sweep-key): cycle close 0059 — drift-clean, retire spec/plan
Cycle-close tidy for the generic portable sweep member-key + bool-param demo strategy (spec/plan 0059, reference issue #105, implementations |
||
|
|
0b73a75d4b |
feat(momentum): bool-param demo strategy + aura sweep --strategy selector
Proves the #105 generic portable member key end-to-end: a demo strategy whose params have nothing in common with the SMA-cross demo, swept by the same machinery, with a bool axis surfacing — conformant — in the member dir names. - momentum strategy: price -> Ema(length:i64) -> Sub(price-ema) -> Exposure(scale:f64) -> LongOnly(enabled:bool) -> SimBroker. Three swept params of three kinds incl. a bool; the exposure sink taps the post-gate exposure so the bool's effect is in the trace. - aura sweep gains --strategy <sma|momentum> via a pure, unit-tested parse_sweep_args (mirroring parse_real_args); default = SMA-cross, so the existing bare / --name / --trace forms are unchanged. - `aura sweep --strategy momentum --trace mom` persists 8 member dirs named e.g. ema.length-5_exposure.scale-0.5_longonly.enabled-true — the bool in the dir name, every name matching [A-Za-z0-9._-], each chartable. - ledger (docs/design/INDEX.md): the C22/#101 member-key note's sweep clause amended to the generalised portable form (MC seed{N} / walk-forward oos{ns} unchanged). Self-verified: cargo build --workspace, cargo test --workspace (all green incl. momentum_param_space_is_ema_exposure_longonly, the 8-point determinism test, the parse_sweep_args grammar test, and the momentum_sweep_trace integration test asserting 8 portable bool-bearing dirs + a chartable member), cargo clippy --workspace --all-targets -D warnings. refs #105 |
||
|
|
27f850dc52 |
feat(sweep-key): generic filesystem-portable member key + LongOnly bool node
Generalises the #105 foot-gun: the family-member trace key was derived from two hardcoded axis names (f{fast}s{slow}), collision-free only over the built-in SMA grid. It is now derived from the axes that actually VARY (SweepBinder::varying_axes) and rendered as a filesystem-conformant directory component: - charset restricted to [A-Za-z0-9._-] (valid on Linux/Windows/macOS, also URL-path- and cloud-sync-safe); any other byte sanitised; - case-less values (i64/timestamp decimal, bool true/false, f64 via Display, no scientific notation) so two members of one family never differ only by letter case -> no silent overwrite on case-insensitive filesystems (NTFS/APFS); - length-capped (MAX_KEY=200) with a version-stable FNV-1a fallback for the unbounded-grid edge. Pinned singleton axes are omitted; the SMA sweep's member dirs change from f2s4 to signals.trend.fast.length-2_signals.trend.slow.length-4 (its --trace integration test + the stale prose updated accordingly). Adds the first aura-std node with a bool *param*, LongOnly (a long-only exposure gate: enabled=true clamps short exposure to >=0, false passes through) — the block the momentum demo strategy (next commit) sweeps to prove the key generic over a bool axis. The engine change is one additive pure read accessor; no existing signature changes, trace state stays out of the engine. Self-verified: cargo build --workspace, cargo test --workspace (all green incl. the LongOnly node tests, the member_key worked-examples corpus, the varying_axes accessor test, and the updated SMA sweep-trace integration test), cargo clippy --workspace --all-targets -D warnings. refs #105 |
||
|
|
44e3a1a678 |
plan: 0059 generic portable sweep key + bool-param demo strategy
Decomposes spec 0059 into 5 tasks: (1) engine SweepBinder::varying_axes accessor; (2) aura-std LongOnly bool-param node; (3) the portable member_key renderer replacing sweep_member_key (+ sweep_family refactor + the SMA sweep-trace assertion update); (4) the momentum strategy + --strategy selector (+ the momentum integration test); (5) ledger amendment + full-suite/lint gate. Each task is RED-first and builds green per task. refs #105 |