a9d36ddd70
Give a measurement run a standalone post-run quality score: the Information
Coefficient (IC), corr(signal_t, forward_return_{t+h}), with a permutation
null model. Before this a measurement run persisted only tap names and series
(MeasurementReport) — inspectable but not rankable or deflatable. IC is the
first metric measurement supplies: the tap-to-scalar reduction the C28
process-column / metric-interface seam anticipates ("a named metric vocabulary
supplied by measurement and backtest instead of baked in R-only").
Surface: `aura measure ic <run> --signal <tap> --price <tap> [--horizon]
[--permutations] [--seed]`. It reads the run's two recorded tap traces, builds
the forward-return series offline over the recorded price spine, aligns
signal -> return by exact timestamp, computes the IC, and prints it with a
one-sided permutation-null overfit probability (Laplace-smoothed, mirroring the
R deflation arm's formula in optimize_deflated).
Placement follows the C28 ladder: the generic pieces — pearson_corr and a
Fisher-Yates permute over SplitMix64 — land in aura-analysis (the domain-free
statistics foundation, beside resample_block); the IC semantics (forward-return
horizon, the offline join) and the CLI verb land in aura-cli (the shell). The
run path (run_measurement), MeasurementReport, the trace store, and the registry
metric vocabulary are untouched — existing `aura run` output stays byte-identical
(the C18 golden run_prints_json_and_exits_zero stays green).
Causality (C2): the forward-return read close_{t+h} is over a completed run's
recorded trace, not an in-graph node. A look-ahead signal is structurally
impossible in a causal run, so the engineered-vs-noise acceptance property is
unit-tested over hand-built offline series (perfect signal -> IC ~= 1, p < 0.05;
a varying but exactly-uncorrelated signal -> IC = 0, not significant;
constant/degenerate -> the (0.0, 1.0) floor), while the E2E validates the CLI
wiring, report well-formedness, determinism, and the error paths over a real run.
This lands the second, structurally distinct null-model computation (permutation,
beside the moving-block bootstrap behind R). That makes the deferred registry
deflation-vocabulary abstraction (#147 item 2) demand-driven rather than
speculative — the next cycle.
Verified: cargo test --workspace green (incl. the new aura-analysis unit tests,
the aura-cli ic_* unit tests, and the measure_ic E2E); cargo clippy --workspace
--all-targets -D warnings clean; the C18 byte-identity golden unchanged.
closes #290
refs #147
67 lines
3.1 KiB
TOML
67 lines
3.1 KiB
TOML
[package]
|
|
name = "aura-cli"
|
|
edition.workspace = true
|
|
version.workspace = true
|
|
license.workspace = true
|
|
publish.workspace = true
|
|
|
|
[[bin]]
|
|
name = "aura"
|
|
path = "src/main.rs"
|
|
|
|
[dependencies]
|
|
aura-core = { path = "../aura-core" }
|
|
aura-engine = { path = "../aura-engine" }
|
|
# aura-composites: the RiskExecutor / vol_stop composite-builders the
|
|
# r-sma harness wires (kept out of aura-engine so the engine stays domain-free).
|
|
aura-composites = { path = "../aura-composites" }
|
|
aura-registry = { path = "../aura-registry" }
|
|
aura-research = { path = "../aura-research" }
|
|
# aura-campaign: campaign-execution semantics (preflight, cell loop, stage
|
|
# sequencing, realization record); the CLI implements its MemberRunner seam
|
|
# and renders its outcome (#198).
|
|
aura-campaign = { path = "../aura-campaign" }
|
|
aura-std = { path = "../aura-std" }
|
|
aura-strategy = { path = "../aura-strategy" }
|
|
aura-backtest = { path = "../aura-backtest" }
|
|
aura-analysis = { path = "../aura-analysis" }
|
|
aura-vocabulary = { path = "../aura-vocabulary" }
|
|
aura-ingest = { path = "../aura-ingest" }
|
|
# data-server: the local M1 archive `aura run --real` streams from. Mirrors the
|
|
# git line in crates/aura-ingest/Cargo.toml verbatim (same source of truth).
|
|
data-server = { git = "http://192.168.178.103:3000/Brummel/data-server.git", branch = "main" }
|
|
# serde derives Serialize on the chart-trace payload (Series) so render_chart_html
|
|
# can embed it via serde_json::json!; admitted under the per-case dependency policy,
|
|
# same as aura-engine.
|
|
serde = { workspace = true }
|
|
# serde_json renders the walk-forward summary line (MetricStats + stitched total)
|
|
# and the injected chart traces (window.AURA_TRACES);
|
|
# admitted under the per-case dependency policy, same as aura-engine.
|
|
serde_json = { workspace = true }
|
|
# libc: restore the default SIGPIPE disposition at startup (see `fn main`) so a
|
|
# closed stdout pipe (`aura sweep | head`, a closed UI pane) terminates the
|
|
# process cleanly instead of panicking the runtime's SIG_IGN default into an
|
|
# EPIPE. The standard vetted crate for signal disposition, already transitive in
|
|
# Cargo.lock; admitted under the per-case dependency policy.
|
|
libc = "0.2"
|
|
# sha2: the run's topology_hash (#158) is the SHA256 of the canonical signal
|
|
# blueprint serialization. A vetted RustCrypto crate (per-case C16 review,
|
|
# SHA256 user-settled); lives in the research-side CLI, off the frozen engine
|
|
# (invariant 8).
|
|
sha2 = "0.10"
|
|
libloading = "0.8"
|
|
toml = "0.8"
|
|
# clap: the vetted standard argument parser. Adopted (C16 per-case review) to
|
|
# replace the hand-rolled argv parser + ten duplicated help surfaces with one
|
|
# declarative source, giving scoped --help, --version, --flag=value, and
|
|
# long-option abbreviation. Research-side CLI only (invariant 8): a dev-loop
|
|
# compile tax, never a frozen-artifact tax.
|
|
clap = { version = "4", features = ["derive"] }
|
|
|
|
[dev-dependencies]
|
|
# zip: hand-packs a tiny synthetic M1 archive (tests/common/mod.rs) in the
|
|
# exact on-disk format data-server reads — already transitive via the
|
|
# data-server dependency above; declared directly here so test code may
|
|
# `use zip::...` (#250, no new dependency actually enters the build graph).
|
|
zip = "2"
|