feat: per-instrument pip metadata channel for the sim-optimal broker
The data-server symbol stream carries only price bars, no instrument metadata.
So SimBroker was constructed with one global pip_size literal (0.0001, correct
only for 5-decimal FX), and `aura run --real <symbol>` emitted wrong-unit pip
equity for any non-FX instrument (AAPL.US, BTCUSD off by orders of magnitude).
Add the missing channel to *specify* per-instrument pip:
- aura-ingest: `InstrumentSpec { pip_size }` + `instrument_spec(symbol)` — a
typed, Rust-authored vetted lookup (NOT Aura.toml; the later Aura.toml schema
can subsume it). Seeded GER40/FRA40 = 1.0 (index points), EURUSD/GBPUSD/USDCAD
= 0.0001 (5-dp FX majors); None for an un-specced symbol.
- aura-cli: `sample_harness` and `sim_optimal_manifest` gain a `pip_size`
parameter; `run_sample_real` looks the pip up by symbol BEFORE any data access
and refuses (exit 2) an un-specced instrument rather than guessing — honest by
construction. The looked-up pip reaches both the broker divisor and the
manifest broker label (`format!`). Every synthetic caller passes the unchanged
0.0001, now named `SYNTHETIC_PIP_SIZE`. SimBroker (aura-std) is untouched.
Scope: narrowed to the CLI real path — the actual bug. The runnable GER40
examples already bake the correct 1.0 and are NOT buggy; centralizing them
through the lookup is deferred to #98.
Decisions (user-directed, recorded on #22): metadata channel = typed
InstrumentSpec at the source edge; un-specced real symbol refuses; seed =
GER40/FRA40 + FX majors; example refactor deferred. The spec went through the
grounding-check gate; the auto-sign panel surfaced the refusal-behaviour and
scope as genuine design decisions, escalated and resolved by the user.
Tests: instrument_spec unit; manifest pip rendering (1.0 -> "1", 0.0001
unchanged); non-gated CLI refusal (exit 2, no stdout leak, vetted symbol clears
the lookup); gated GER40 binary-boundary honesty (pip_size=1 in the emitted
manifest, deterministic). The pre-existing run_sample_real determinism test was
retargeted AAPL.US -> GER40 (AAPL.US is un-specced and would now refuse at the
lookup); the AAPL.US bounded-window streaming property still lives in
aura-ingest/tests/streaming_seam.rs (literal source, no spec lookup).
Verified: cargo build --workspace, clippy --all-targets -D warnings, and
cargo test --workspace all green; the gated GER40 path ran with local data.
closes #22
This commit is contained in:
@@ -339,10 +339,46 @@ pub fn default_data_server() -> Arc<DataServer> {
|
||||
Arc::new(DataServer::new(DEFAULT_DATA_PATH))
|
||||
}
|
||||
|
||||
/// Per-instrument reference metadata held beside the hot path (C7/C15): non-scalar,
|
||||
/// never streamed. Minimal today — extend with tick size / digits / quote currency
|
||||
/// as later cycles need, without breaking this signature.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct InstrumentSpec {
|
||||
/// Price units per pip / point (> 0). The sim-optimal broker's divisor.
|
||||
pub pip_size: f64,
|
||||
}
|
||||
|
||||
/// Look up per-instrument reference metadata by `symbol` at the ingestion edge.
|
||||
/// Rust-authored vetted table (NOT `Aura.toml`); `None` for an instrument with no
|
||||
/// vetted spec — the caller must refuse rather than guess a pip.
|
||||
///
|
||||
/// Seeded with the instruments the engine's own paths exercise. Indices quote in
|
||||
/// points (pip = 1.0); 5-decimal FX majors = 0.0001. NB: JPY pairs are 0.01, NOT
|
||||
/// 0.0001 — only vetted values are seeded.
|
||||
pub fn instrument_spec(symbol: &str) -> Option<InstrumentSpec> {
|
||||
let pip_size = match symbol {
|
||||
"GER40" | "FRA40" => 1.0,
|
||||
"EURUSD" | "GBPUSD" | "USDCAD" => 0.0001,
|
||||
_ => return None,
|
||||
};
|
||||
Some(InstrumentSpec { pip_size })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn instrument_spec_lookup_by_symbol() {
|
||||
// index points → pip 1.0
|
||||
assert_eq!(instrument_spec("GER40"), Some(InstrumentSpec { pip_size: 1.0 }));
|
||||
assert_eq!(instrument_spec("FRA40"), Some(InstrumentSpec { pip_size: 1.0 }));
|
||||
// 5-decimal FX major → pip 0.0001
|
||||
assert_eq!(instrument_spec("EURUSD"), Some(InstrumentSpec { pip_size: 0.0001 }));
|
||||
// un-specced → None (the honesty lever)
|
||||
assert_eq!(instrument_spec("NONEXISTENT"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unix_ms_to_epoch_ns_scales_ms_to_ns() {
|
||||
// data-server's own epoch fixture: 2017-03-01 00:00 UTC.
|
||||
|
||||
Reference in New Issue
Block a user