Tap-aware chart decimation: mean-reduce the bounded exposure series instead of a min/max band #111

Closed
opened 2026-06-22 14:17:39 +02:00 by Brummel · 0 comments
Owner

The serve-time decimation shipped in cut 2 (#108) reduces every series by per-bucket
min/max. That is the right envelope for a cumulative equity curve (drawdown
spikes survive), but the wrong reduction for a bounded level series — the C10
exposure stream (f64 ∈ [-1,+1]).

Symptom

The exposure overlay collapses to a solid −1..+1 band (it reads as per-point
oscillation between +1 and −1). Confirmed on real GER40 M1:

  • 2/4 demo run (1y): raw exposure flips sign on 29.8 % of bars (median run = 1
    bar) — genuinely whippy, so the band is partly real there.
  • BUT a realistic 50/200 sweep member (5y): only 0.81 % flips, median run 85
    bars — a healthy strategy — and it still bands out: 1997/2000 buckets straddle
    a flip, because each bucket spans ~812 bars. So over any multi-year window min/max
    renders every exposure as ±1 everywhere, hiding the net/duty-cycle level.

The equity curve is unaffected (min/max envelope is correct there).

Fix (A — tap-aware decimation)

decimate becomes reduction-kind-aware per series:

  • envelope series (equity, unbounded cumulative) → min/max, as today;
  • level series (exposure, bounded) → per-bucket mean, so the net/duty-cycle
    level survives decimation as a smooth line in [-1,+1] instead of a band.

The reduction kind is set by the chart builders (which know the tap): a small
reduce_for_tap keyed on the tap name (the only bounded level tap today is
exposure); decimate stays a mechanical transform that honours a per-series
ReduceKind. The shared spine is preserved (a mean series writes its bucket mean to
both boundary slots).

Scope

Hotfix-sized, confined to crates/aura-cli (render.rs Series/ReduceKind, main.rs
decimate + reduce_for_tap + the two builders). RED-first: a test pinning that a
bipolar series with many intra-bucket flips decimates to its bucket mean, not a
−1..+1 band. Engine + registry untouched (C14). A typed per-tap semantics marker
(instead of the name heuristic) is a later generalisation if more bounded taps land.

context: surfaced demonstrating cut 2 (#108) on real GER40 data.
depends on: nothing.

The serve-time decimation shipped in cut 2 (#108) reduces every series by per-bucket **min/max**. That is the right envelope for a cumulative **equity** curve (drawdown spikes survive), but the wrong reduction for a **bounded level** series — the C10 **exposure** stream (f64 ∈ [-1,+1]). ## Symptom The exposure overlay collapses to a solid −1..+1 band (it reads as per-point oscillation between +1 and −1). Confirmed on real GER40 M1: - 2/4 demo run (1y): raw exposure flips sign on **29.8 %** of bars (median run = 1 bar) — genuinely whippy, so the band is partly real there. - BUT a realistic 50/200 sweep member (5y): only **0.81 %** flips, median run 85 bars — a healthy strategy — and it **still** bands out: 1997/2000 buckets straddle a flip, because each bucket spans ~812 bars. So over any multi-year window min/max renders *every* exposure as ±1 everywhere, hiding the net/duty-cycle level. The equity curve is unaffected (min/max envelope is correct there). ## Fix (A — tap-aware decimation) `decimate` becomes reduction-kind-aware per series: - **envelope** series (equity, unbounded cumulative) → min/max, as today; - **level** series (exposure, bounded) → **per-bucket mean**, so the net/duty-cycle level survives decimation as a smooth line in [-1,+1] instead of a band. The reduction kind is set by the chart builders (which know the tap): a small `reduce_for_tap` keyed on the tap name (the only bounded level tap today is `exposure`); `decimate` stays a mechanical transform that honours a per-series `ReduceKind`. The shared spine is preserved (a mean series writes its bucket mean to both boundary slots). ## Scope Hotfix-sized, confined to `crates/aura-cli` (render.rs `Series`/`ReduceKind`, main.rs `decimate` + `reduce_for_tap` + the two builders). RED-first: a test pinning that a bipolar series with many intra-bucket flips decimates to its bucket mean, not a −1..+1 band. Engine + registry untouched (C14). A typed per-tap semantics marker (instead of the name heuristic) is a later generalisation if more bounded taps land. context: surfaced demonstrating cut 2 (#108) on real GER40 data. depends on: nothing.
Brummel added the bug label 2026-06-22 14:17:39 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Brummel/Aura#111