chore: scrub dangling references to deleted specs/plans from sources
docs/specs and docs/plans were retired (prior commit); the source/test
comments that cited them ("spec 0050 §4.1", "spec §Testing N", "per spec",
"the spec's ...") now point at nothing. Strip every such pointer while
preserving the technical substance, the design-ledger contract refs
(C1/C11/C20/C34/C12.1/...), and the Gitea issue refs (#41).
Comment/doc edits only across 20 files — no logic change; full workspace
suite green, clippy clean.
This commit is contained in:
@@ -908,14 +908,14 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn walkforward_report_is_deterministic() {
|
||||
// spec §Testing 10: the built-in WFO render is byte-identical across two
|
||||
// The built-in WFO render is byte-identical across two
|
||||
// calls (C1).
|
||||
assert_eq!(walkforward_report(), walkforward_report());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn walkforward_report_has_one_oos_line_per_window_plus_summary() {
|
||||
// spec §Testing 11: N per-window OOS RunReport lines + one summary line.
|
||||
// N per-window OOS RunReport lines + one summary line.
|
||||
let out = walkforward_report();
|
||||
let lines: Vec<&str> = out.lines().collect();
|
||||
assert_eq!(lines.len(), 4); // built-in roll = 3 windows + 1 summary
|
||||
@@ -1116,7 +1116,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// E2E acceptance (#41 / spec 0019, the worked example): the real MACD strategy
|
||||
/// E2E acceptance (#41, the worked example): the real MACD strategy
|
||||
/// blueprint's swept param surface qualifies the three otherwise-indistinguishable
|
||||
/// EMA `length` slots by node name to `macd.fast.length` / `macd.slow.length` /
|
||||
/// `macd.signal.length` — the named composite boundary visible end-to-end through
|
||||
@@ -1149,7 +1149,7 @@ mod tests {
|
||||
/// C1-deterministic — two runs of the same window are bit-identical JSON.
|
||||
///
|
||||
/// Gated like the ingest `streaming_seam` test: skip (early return) when the
|
||||
/// local Pepperstone archive is absent, so the spec never fails on a machine
|
||||
/// local Pepperstone archive is absent, so the test never fails on a machine
|
||||
/// without the data. Uses the verified bounded 2006-08 `AAPL.US` window (the
|
||||
/// same FROM_MS/TO_MS the ingest seam test drives) so the two-run determinism
|
||||
/// check stays fast.
|
||||
|
||||
@@ -88,7 +88,7 @@ mod tests {
|
||||
/// deterministic model as the viewer's data source, carries the Graphviz-WASM
|
||||
/// bootstrap, and keeps the C4 four-colour palette. Structural, not pixel:
|
||||
/// the DOT/SVG are Graphviz-version-dependent and are exercised by hand
|
||||
/// against the prototype (spec Testing strategy).
|
||||
/// against the prototype.
|
||||
#[test]
|
||||
fn render_html_is_self_contained_and_embeds_the_model() {
|
||||
let html = render_html(&sample_blueprint());
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! `GraphBuilder` — name-based blueprint authoring (spec 0039).
|
||||
//! `GraphBuilder` — name-based blueprint authoring.
|
||||
//!
|
||||
//! A fluent, additive authoring surface that wires a blueprint by typed node
|
||||
//! handles and port/field *names*, resolving them to the raw-index `Composite`
|
||||
@@ -65,7 +65,7 @@ pub enum BuildError {
|
||||
}
|
||||
|
||||
/// A fluent, additive accumulator that authors a [`Composite`] by typed handles
|
||||
/// and port/field names. See the module docs and spec 0039.
|
||||
/// and port/field names. See the module docs.
|
||||
pub struct GraphBuilder {
|
||||
name: String,
|
||||
nodes: Vec<BlueprintNode>,
|
||||
|
||||
@@ -217,7 +217,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn monte_carlo_runs_one_draw_per_seed_in_input_order() {
|
||||
// spec §Testing 1: N seeds -> N draws, seeds carried in INPUT order.
|
||||
// N seeds -> N draws, seeds carried in INPUT order.
|
||||
let family = monte_carlo(&base_point(), &[1, 2, 3], run_draw);
|
||||
assert_eq!(family.draws.len(), 3);
|
||||
assert_eq!(
|
||||
@@ -228,7 +228,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn family_is_deterministic_across_thread_counts() {
|
||||
// spec §Testing 2: order = seed input, not completion (C1).
|
||||
// order = seed input, not completion (C1).
|
||||
let point = base_point();
|
||||
let seeds: Vec<u64> = (1..=8).collect();
|
||||
let one = monte_carlo_with_threads(&point, &seeds, 1, run_draw);
|
||||
@@ -239,7 +239,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn draw_equals_independent_seeded_run() {
|
||||
// spec §Testing 3: each draw == an independent run of that (seed, point)
|
||||
// each draw == an independent run of that (seed, point)
|
||||
// — MC adds enumeration + execution, never a metrics change (C1).
|
||||
let point = base_point();
|
||||
let family = monte_carlo(&point, &[10, 11, 12], run_draw);
|
||||
@@ -251,7 +251,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn distinct_seeds_produce_distinct_draws() {
|
||||
// spec §Testing 4: the seed actually perturbs each run — the family does
|
||||
// the seed actually perturbs each run — the family does
|
||||
// not collapse to one metric.
|
||||
let family = monte_carlo(&base_point(), &[1, 2, 3, 4, 5], run_draw);
|
||||
let first = family.draws[0].report.metrics.total_pips;
|
||||
@@ -263,14 +263,14 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn aggregate_is_a_pure_reduction() {
|
||||
// spec §Testing 5: the stored aggregate is recomputable from the draws.
|
||||
// the stored aggregate is recomputable from the draws.
|
||||
let family = monte_carlo(&base_point(), &[1, 2, 3, 4], run_draw);
|
||||
assert_eq!(McAggregate::from_draws(&family.draws), family.aggregate);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aggregate_stats_on_known_fixture() {
|
||||
// spec §Testing 6: type-7 quantile + mean over known metric values
|
||||
// type-7 quantile + mean over known metric values
|
||||
// [0,1,2,3,4]: mean=2.0, p50=2.0, p5≈0.2, p95≈3.8.
|
||||
let agg = McAggregate::from_draws(&draws_with_metrics(&[0.0, 1.0, 2.0, 3.0, 4.0]));
|
||||
assert_eq!(agg.total_pips.mean, 2.0);
|
||||
@@ -281,7 +281,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn quantile_endpoints_and_singleton() {
|
||||
// spec §Testing 7: singleton returns the sole value; p=0 -> min, p=1 -> max.
|
||||
// singleton returns the sole value; p=0 -> min, p=1 -> max.
|
||||
assert_eq!(quantile(&[42.0], 0.0), 42.0);
|
||||
assert_eq!(quantile(&[42.0], 0.5), 42.0);
|
||||
assert_eq!(quantile(&[42.0], 1.0), 42.0);
|
||||
@@ -292,7 +292,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn metric_stats_from_values_matches_known_fixture() {
|
||||
// spec §Testing 9: type-7 quantile + mean over [0,1,2,3,4], directly on the
|
||||
// type-7 quantile + mean over [0,1,2,3,4], directly on the
|
||||
// extracted reduction: mean=2.0, p50=2.0, p5≈0.2, p95≈3.8 (same numbers the
|
||||
// aggregate fixture pins, now on from_values).
|
||||
let s = MetricStats::from_values(&[0.0, 1.0, 2.0, 3.0, 4.0]);
|
||||
@@ -304,7 +304,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn metric_stats_serde_round_trips() {
|
||||
// spec §Concrete code shapes: MetricStats gains serde (consistent with the
|
||||
// MetricStats gains serde (consistent with the
|
||||
// report types) so the CLI summary renders it and #70 lineage can persist.
|
||||
let s = MetricStats::from_values(&[1.0, 2.0, 3.0]);
|
||||
let json = serde_json::to_string(&s).expect("serialize MetricStats");
|
||||
|
||||
@@ -252,7 +252,7 @@ impl Space for RandomSpace {
|
||||
// inclusive [lo, hi]; span via i128 then u64 handles a negative lo.
|
||||
// (Modulo bias for spans not dividing 2^64 is an accepted,
|
||||
// documented simplification — param search needs no crypto
|
||||
// uniformity, spec §"Error handling".)
|
||||
// uniformity.)
|
||||
ScalarKind::I64 => {
|
||||
let (lo, hi) = (r.lo.as_i64(), r.hi.as_i64());
|
||||
// span is 1..=2^64; the full-width span [i64::MIN, i64::MAX]
|
||||
|
||||
@@ -331,7 +331,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn walk_forward_runs_one_window_per_split_in_roll_order() {
|
||||
// spec §Testing 5: N bounds -> N outcomes, bounds in roll order.
|
||||
// N bounds -> N outcomes, bounds in roll order.
|
||||
let roller =
|
||||
WindowRoller::new((Timestamp(0), Timestamp(24)), 10, 5, 5, RollMode::Rolling)
|
||||
.expect("valid");
|
||||
@@ -349,7 +349,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn walk_forward_is_deterministic_across_thread_counts() {
|
||||
// spec §Testing 6: order is roll order, not completion (C1).
|
||||
// order is roll order, not completion (C1).
|
||||
let cfg = || {
|
||||
WindowRoller::new((Timestamp(0), Timestamp(100)), 20, 5, 5, RollMode::Rolling)
|
||||
.expect("valid")
|
||||
@@ -362,7 +362,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn stitched_curve_carries_equity_forward() {
|
||||
// spec §Testing 7: segs [(t0,2),(t1,5)] then [(t2,1),(t3,3)] ->
|
||||
// segs [(t0,2),(t1,5)] then [(t2,1),(t3,3)] ->
|
||||
// [(t0,2),(t1,5),(t2,6),(t3,8)] (offset by prior segment's final value 5).
|
||||
let windows = vec![
|
||||
outcome(vec![(Timestamp(0), 2.0), (Timestamp(1), 5.0)], vec![]),
|
||||
@@ -381,7 +381,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn stitched_curve_passes_through_empty_segment() {
|
||||
// spec §Testing 7b: an empty OOS segment adds 0.0 to the offset and no
|
||||
// an empty OOS segment adds 0.0 to the offset and no
|
||||
// points: [(t0,2),(t1,5)], [], [(t2,1)] -> [(t0,2),(t1,5),(t2,6)].
|
||||
let windows = vec![
|
||||
outcome(vec![(Timestamp(0), 2.0), (Timestamp(1), 5.0)], vec![]),
|
||||
@@ -396,7 +396,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn param_stability_reduces_chosen_params_per_slot() {
|
||||
// spec §Testing 8: on-demand reduction over chosen params across windows.
|
||||
// on-demand reduction over chosen params across windows.
|
||||
// slot 0 [2,2,3,3] -> mean 2.5, p50 2.5; slot 1 [1,1,2,2] -> mean 1.5.
|
||||
let mk = |a: i64, b: f64| WindowOutcome {
|
||||
bounds: WindowBounds {
|
||||
@@ -423,7 +423,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn param_stability_reduces_bool_slot_to_fraction_true() {
|
||||
// spec §Testing: a Bool param slot coerces 0/1, so mean = fraction of
|
||||
// a Bool param slot coerces 0/1, so mean = fraction of
|
||||
// windows that chose `true`. 3 of 4 true -> 0.75.
|
||||
let mk = |flag: bool| WindowOutcome {
|
||||
bounds: WindowBounds {
|
||||
@@ -490,7 +490,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn roller_rolling_emits_consecutive_oos_windows() {
|
||||
// spec §Testing 1: rolling, origin=0, is_len=10, oos_len=5, step=5, end=24.
|
||||
// rolling, origin=0, is_len=10, oos_len=5, step=5, end=24.
|
||||
// w0 is(0,9) oos(10,14); w1 is(5,14) oos(15,19); w2 is(10,19) oos(20,24);
|
||||
// w3 would need oos(25,29) > 24 -> stop. 3 windows; OOS tiles (step==oos_len).
|
||||
let roller =
|
||||
@@ -514,7 +514,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn roller_anchored_fixes_is_start_grows_is_end() {
|
||||
// spec §Testing 2: anchored, same config — IS always starts at origin (0)
|
||||
// anchored, same config — IS always starts at origin (0)
|
||||
// and grows; OOS positions identical to rolling.
|
||||
let roller =
|
||||
WindowRoller::new((Timestamp(0), Timestamp(24)), 10, 5, 5, RollMode::Anchored)
|
||||
@@ -538,7 +538,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn roller_every_split_has_no_lookahead() {
|
||||
// spec §Testing 3 (C2): every emitted split has oos.0 > is.1, zero ticks.
|
||||
// (C2): every emitted split has oos.0 > is.1, zero ticks.
|
||||
for mode in [RollMode::Rolling, RollMode::Anchored] {
|
||||
let roller = WindowRoller::new((Timestamp(0), Timestamp(100)), 20, 7, 3, mode)
|
||||
.expect("valid config");
|
||||
@@ -550,7 +550,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn roller_rejects_nonpositive_lengths_and_short_span() {
|
||||
// spec §Testing 4: typed config faults before any run.
|
||||
// typed config faults before any run.
|
||||
let span = (Timestamp(0), Timestamp(100));
|
||||
assert_eq!(
|
||||
WindowRoller::new(span, 0, 5, 5, RollMode::Rolling).unwrap_err(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! End-to-end verification of the GER40 / DAX 15-minute session-breakout
|
||||
//! strategy (spec 0050 §6, build-step 8 — the milestone capstone), hand-wired
|
||||
//! strategy (the milestone capstone), hand-wired
|
||||
//! from the seven new `aura-std` nodes into a raw-index [`FlatGraph`] and run
|
||||
//! over one synthetic Frankfurt session.
|
||||
//!
|
||||
@@ -178,7 +178,7 @@ fn build_harness(
|
||||
/// bar1 high=100; bar2 high=110, close=108; bar3 close=112 (> bar2 high → the
|
||||
/// strict breakout, and bars_since_open==3 → ENTRY); bar4 close=115; bar5
|
||||
/// close=120; then a single 10:15 rollover tick (bar6) that CLOSES bar5 (else the
|
||||
/// partial last bar is dropped and exposure leaks to EOF — the spec's trap (a)).
|
||||
/// partial last bar is dropped and exposure leaks to EOF — trap (a)).
|
||||
/// bars 1 and 2 precede the bar3 entry so `Delay[1]` is warm (trap (b)).
|
||||
fn entry_session_ticks() -> Vec<M1> {
|
||||
vec![
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! End-to-end coverage for the random param-sweep axis (spec 0049, C12.1):
|
||||
//! End-to-end coverage for the random param-sweep axis (C12.1):
|
||||
//! `RandomSpace` + `ParamRange` driven through the **public** `sweep` surface a
|
||||
//! downstream researcher actually writes (the spec's "Worked author example").
|
||||
//! downstream researcher actually writes (the worked author example).
|
||||
//!
|
||||
//! The in-module unit tests in `sweep.rs` reach into crate internals
|
||||
//! (`bootstrap_with_cells`, `sweep_with_threads`, `SplitMix64`); these tests use
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! Runnable World-family demo: **`compare`** the session-breakout `Composite`
|
||||
//! blueprint (spec 0051) across two structural axes (C11/C20 experiment matrix),
|
||||
//! blueprint across two structural axes (C11/C20 experiment matrix),
|
||||
//! on REAL data, with NO re-authoring:
|
||||
//!
|
||||
//! 1. **Instrument** — GER40 vs FRA40 (both `pip_size = 1.0`, index points), the
|
||||
@@ -7,7 +7,7 @@
|
||||
//! symbol. The pip honesty is by construction (both quote in index points; the
|
||||
//! engine has no pip registry yet — #22, filed).
|
||||
//! 2. **Bar period** — a 15m blueprint vs a 30m blueprint. The bar period is a
|
||||
//! STRUCTURAL axis (§2 / C34): `ger40_breakout_blueprint` is instantiated TWICE,
|
||||
//! STRUCTURAL axis (C34): `ger40_breakout_blueprint` is instantiated TWICE,
|
||||
//! at 15 and at 30, each a *different strategy* (the period is bound at
|
||||
//! construction, locking Resample and Session equal — it is NOT a sweep param).
|
||||
//!
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
//! This is a *pure composition* of SHIPPED `aura-std` nodes: no project-specific
|
||||
//! signal code, so it lives in the engine repo's `examples/` carveout (C9), not
|
||||
//! a separate project crate. The strategy is authored as a World-consumable
|
||||
//! `Composite` blueprint (`ger40_breakout_blueprint`, spec 0051) and bootstrapped
|
||||
//! `Composite` blueprint (`ger40_breakout_blueprint`) and bootstrapped
|
||||
//! here by name (`entry_bar=3`, `exit_bar=5`); the four real `M1FieldSource`s
|
||||
//! feed its four OHLC source roles. The blueprint reproduces the shipped
|
||||
//! hand-wired `FlatGraph` EXACTLY (C23), so this prints the same report + trace.
|
||||
@@ -59,7 +59,7 @@ fn main() {
|
||||
// Author the breakout as a Composite blueprint and bootstrap it by name with
|
||||
// the two genuine tuning knobs (entry bar 3, exit bar 5). The bar-period (15m),
|
||||
// session open (09:00 Berlin) and delay.lag are structural — bound at
|
||||
// construction, not exposed as params (spec 0051 §2).
|
||||
// construction, not exposed as params.
|
||||
let (bp, taps) = ger40_breakout_blueprint(BAR_MINUTES, SESSION_HOUR, SESSION_MINUTE, Berlin);
|
||||
let mut h = bp
|
||||
.with("entry_bar.target", aura_core::Scalar::i64(3))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! Runnable World-family demo: **`sweep`** the GER40 session-breakout `Composite`
|
||||
//! blueprint (spec 0051) over its two genuine tuning knobs — `entry_bar.target` ×
|
||||
//! blueprint over its two genuine tuning knobs — `entry_bar.target` ×
|
||||
//! `exit_bar.target` — on REAL GER40 M1 bars, ranked by `total_pips`. This is the
|
||||
//! core proof that the shipped strategy is World-consumable: the SAME
|
||||
//! `ger40_breakout_blueprint` the single backtest bootstraps is driven through the
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! Runnable World-family demo: **`walk_forward`** the GER40 session-breakout
|
||||
//! `Composite` blueprint (spec 0051) across multi-year real GER40 history, with a
|
||||
//! `Composite` blueprint across multi-year real GER40 history, with a
|
||||
//! per-window in-sample optimize of its two genuine tuning knobs
|
||||
//! (`entry_bar.target` × `exit_bar.target`). This is the executable #97
|
||||
//! resolution: the roll is NON-DEGENERATE — a real, non-empty `param_space()` is
|
||||
|
||||
@@ -194,7 +194,7 @@ pub fn build_harness() -> (Harness, Taps) {
|
||||
}
|
||||
|
||||
/// Author the GER40 session-breakout as a World-consumable [`Composite`]
|
||||
/// blueprint (spec 0051) — the canonical shippable form of the strategy, of which
|
||||
/// blueprint — the canonical shippable form of the strategy, of which
|
||||
/// [`build_harness`]'s hand-wired `FlatGraph` is the compiled substrate (C11/C23).
|
||||
///
|
||||
/// This is the SAME 13-node breakout `build_harness` wires by raw index, authored
|
||||
@@ -203,7 +203,7 @@ pub fn build_harness() -> (Harness, Taps) {
|
||||
/// `param_space()` / the by-name binder / `bootstrap_with_cells`, so `sweep` /
|
||||
/// `walk_forward` / compare consume it with NO re-authoring (the #94 friction).
|
||||
///
|
||||
/// The spec-0051 structural decisions are baked here, not left to the caller:
|
||||
/// The structural decisions are baked here, not left to the caller:
|
||||
/// - `bar_period_minutes` binds BOTH the period-bearing nodes at construction —
|
||||
/// `Resample`'s `period_minutes` param AND `Session`'s baked period — so the two
|
||||
/// are LOCKED EQUAL and a sweep cannot desync them to 0.0 pips (#96 / C34: a
|
||||
@@ -238,8 +238,8 @@ pub fn ger40_breakout_blueprint(
|
||||
|
||||
let mut g = GraphBuilder::new("ger40_breakout");
|
||||
|
||||
// Strategy nodes. The two period-bearing nodes are bound EQUAL by construction
|
||||
// (§2): Resample's `period_minutes` param and Session's baked period both take
|
||||
// Strategy nodes. The two period-bearing nodes are bound EQUAL by construction:
|
||||
// Resample's `period_minutes` param and Session's baked period both take
|
||||
// `bar_period_minutes`, so no sweep can desync them. `delay.lag` is bound out.
|
||||
let resample = g.add(
|
||||
Resample::builder().bind("period_minutes", Scalar::i64(bar_period_minutes)),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! Gated integration test: the GER40 session-breakout shipped as a
|
||||
//! World-consumable `Composite` blueprint (spec 0051). Proves the blueprint
|
||||
//! World-consumable `Composite` blueprint. Proves the blueprint
|
||||
//! reproduces the shipped hand-wired `FlatGraph` EXACTLY (C23) and that its
|
||||
//! `param_space()` is the two genuine tuning knobs only — no `delay.lag`, no
|
||||
//! bar-period (the desync trap, #96).
|
||||
@@ -44,7 +44,7 @@ fn param_space_is_exactly_entry_and_exit_bar_targets() {
|
||||
assert_eq!(
|
||||
names,
|
||||
vec!["entry_bar.target", "exit_bar.target"],
|
||||
"param_space() must be exactly the two EqConst targets (spec 0051 §2)",
|
||||
"param_space() must be exactly the two EqConst targets",
|
||||
);
|
||||
|
||||
// Belt-and-braces on the two specific leaks #96 named, independent of order.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! Gated integration test: the GER40 session-breakout `Composite` blueprint
|
||||
//! (spec 0051) is driven by the World *orchestration families* — `sweep` and
|
||||
//! Gated integration test: the GER40 session-breakout `Composite` blueprint is
|
||||
//! driven by the World *orchestration families* — `sweep` and
|
||||
//! `walk_forward` — on REAL GER40 M1 bars, with NO re-authoring of the strategy.
|
||||
//! This is the milestone-completing executable proof that the #94 friction is
|
||||
//! gone: the same `ger40_breakout_blueprint` the single backtest bootstraps is
|
||||
|
||||
@@ -7,7 +7,7 @@ publish.workspace = true
|
||||
|
||||
[dependencies]
|
||||
aura-core = { path = "../aura-core" }
|
||||
# DST-correct wall-clock math for the `Session` node (spec 0050 §4.5). A vetted
|
||||
# DST-correct wall-clock math for the `Session` node. A vetted
|
||||
# standard crate for timezone/DST — never hand-rolled (C16 per-case policy).
|
||||
# `chrono` is already a transitive workspace dep (0.4 via aura-ingest's
|
||||
# data-server); aligned to that major. `chrono-tz` brings the IANA `Europe/Berlin`
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
//! no params. **Emits `f64` directly**, not `bool`: a held position *is*
|
||||
//! exposure `+1`, flat *is* `0.0`, so the output feeds [`SimBroker`](crate::SimBroker)'s
|
||||
//! `f64` `exposure` slot with no cast — this is the resolution of the `bool → f64`
|
||||
//! seam (spec 0050 §4.2; no separate `Exposure`/cast node).
|
||||
//! seam (no separate `Exposure`/cast node).
|
||||
//!
|
||||
//! **State.** A persisted `held: f64`, initial `0.0`, mutated in `eval` and
|
||||
//! carried across cycles in the struct — exactly how [`SimBroker`](crate::SimBroker)
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
//!
|
||||
//! **Timestamp (C4).** The completed bar carries no timestamp of its own — the
|
||||
//! engine stamps the emission cycle's `ctx.now()` (the new bucket's first M1
|
||||
//! instant, which is the close instant of the bar just emitted, spec 0050 §4.1).
|
||||
//! instant, which is the close instant of the bar just emitted).
|
||||
//! The node only returns the 4 OHLC values.
|
||||
//!
|
||||
//! **Partial last bar is DROPPED.** There is no end-of-stream flush: a bucket with
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
//! build (like `SimBroker::builder(fee)` bakes its fee); a timezone is not a
|
||||
//! scalar, so it is captured by the closure, not declared as a param.
|
||||
//!
|
||||
//! **Semantics (spec 0050 §4).** Let `local = <ctx.now() epoch-ns UTC> converted
|
||||
//! **Semantics.** Let `local = <ctx.now() epoch-ns UTC> converted
|
||||
//! to tz` (chrono-tz, DST-correct). Let
|
||||
//! `mins = (local.hour()*60 + local.minute()) - (open_hour*60 + open_minute)`.
|
||||
//! Emit `bars_since_open = mins / period_minutes` (i64 division). The resampler
|
||||
@@ -26,7 +26,7 @@
|
||||
//! / `EqConst(==5)` gates simply never match; there is no separate in-session
|
||||
//! bool gate, `bars_since_open` alone is the contract).
|
||||
//!
|
||||
//! **Off-by-one (close-instant indexing, spec 0050 §4.1).** `ctx.now()` at a
|
||||
//! **Off-by-one (close-instant indexing).** `ctx.now()` at a
|
||||
//! resampler emission is the **just-closed** bar's *close instant*, so "the bar
|
||||
//! that closed at 09:45" reads `bars_since_open == 3`.
|
||||
//!
|
||||
@@ -85,8 +85,8 @@ impl Node for Session {
|
||||
if ctx.f64_in(0).is_empty() {
|
||||
return None; // cold: trigger has not fired yet
|
||||
}
|
||||
// Convert the engine's epoch-ns UTC instant (close-instant, spec 0050
|
||||
// §4.1) into tz-aware LOCAL wall-clock — chrono-tz applies the correct
|
||||
// Convert the engine's epoch-ns UTC instant (close-instant) into
|
||||
// tz-aware LOCAL wall-clock — chrono-tz applies the correct
|
||||
// DST offset for this date, so the same local minute reads the same bar
|
||||
// index in summer (CEST) and winter (CET).
|
||||
let local = self.tz.timestamp_nanos(ctx.now().0);
|
||||
@@ -135,7 +135,7 @@ mod tests {
|
||||
fn session_indexes_bars_since_frankfurt_open_in_local_dst_aware_time() {
|
||||
// THE HEADLINE PROPERTY: bars_since_open = (local wall-clock minutes past
|
||||
// the 09:00 Frankfurt open) / period, in tz-aware LOCAL time. Because the
|
||||
// close instant is the bucket boundary (:00/:15/:30/:45, spec 0050 §4.1),
|
||||
// close instant is the bucket boundary (:00/:15/:30/:45),
|
||||
// in-session bar closes are exact positive multiples of the bar index.
|
||||
//
|
||||
// Built from Berlin local wall-clock via chrono-tz so the assertion is
|
||||
|
||||
@@ -211,7 +211,7 @@ mod tests {
|
||||
// param-carrying single nodes
|
||||
assert_eq!(Exposure::new(0.5).label(), "Exposure(0.5)");
|
||||
assert_eq!(SimBroker::new(0.0001).label(), "SimBroker(0.0001)");
|
||||
// bare-kind nodes (identity is not a mis-wiring axis here, per spec)
|
||||
// bare-kind nodes (identity is not a mis-wiring axis here)
|
||||
assert_eq!(Sub::new().label(), "Sub");
|
||||
assert_eq!(Add::new().label(), "Add");
|
||||
assert_eq!(LinComb::new(vec![1.0, -1.0]).label(), "LinComb");
|
||||
|
||||
Reference in New Issue
Block a user