H1 result (commit9adbca826, smoke train-5zmkr, reverted ate8814079d): aux_dir_acc HD[2] = 78% with H=200 — aux head LEARNS direction at longer horizon. But policy WR stayed pinned at 50.1-50.2% — the learned aux signal does NOT propagate to policy (per pearl_separate_aux_trunk_when_shared_starves: aux on separate trunk with stop-grad to policy). H6 design (synthesized from all v5-v11 + H1 evidence): Wire the aux head's directional probability into the policy STATE as an input feature. - Policy gradient flows THROUGH the feature (uses it) - Stop-grad blocks gradient BACK (aux trunk unaffected, pearl preserved) - Uses existing padding slot [121..128) in STATE_DIM=128 (no layout growth) Why this is the structural fix: - Aux PROVED directional signal is in the features (78% at H=200) - Policy PROVED it can't extract direction (WR=50% across all v5-v11 conditions) - Bridge connects the two without violating trunk separation - Information-theoretic: gives policy a feature it provably can't compute itself Test outcome interpretation: WR > 50.5% → Mechanism 1 was binding (trunk separation gap) WR pinned → Mechanism 2 (reward density) or Mechanism 3 (V/A unidentifiability) dominates → H3 or V/A fix next Files changed: - docs/plans/2026-05-12-sp22-wr-plateau-investigation.md: H6 added as new primary hypothesis after H1; experiment order revised - docs/dqn-wire-up-audit.md: H6 design entry with three-mechanism synthesis Implementation scope (separate commit): 1. State layout: claim slot in padding [121..128) for aux_dir_prob 2. Aux trunk export: pull "up" probability per bar from aux forward 3. Experience collection: write aux_dir_prob into per-bar state 4. Stop-grad verification: confirm policy gradient blocked 5. Trade-open persistence: latch aux_dir_prob for trade duration Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8.7 KiB
SP22 — WR Plateau Investigation
Author: jgrusewski + assistant pair
Date: 2026-05-12
Branch: TBD (off sp20-aux-h-fixed after merge)
Predecessor: SP21 T2.2 (cascade wiring; closed docs/dqn-wire-up-audit.md 2026-05-12)
Goal: Move WR from the current ~50.1% plateau toward project_goal_wr_55_pf_2's WR≥55% / PF≥2.0.
State at session start
SP21 T2.2 cascade is feature-complete:
d482efc41 fix(sp21): T2.2 Phase 8.4-fix — winner_conc z-score separation
9f2d0fffb fix(sp21): T2.2 Phase 8.7 — default ISV buffer
518670198+ ... Phase 8.x stack ...
2937da889 fix(sp21): Return v3 (later v3.1)
All E1-E8 enrichment producers wired to consumers. Local compute-sanitizer
clean on eval-baseline closure path. v9 cycle 1 confirms cascade live:
win_conc=1.72, curric_conc=0.31, hindsight_mag=1.47e-5.
Binding constraint (v10 hypothesis test, 8 epochs, fold 0):
- val_Sharpe peaks at E3 (174), monotonically degrades to E8 (66, -62%)
- WR pinned at 50.1-50.2% across ALL 8 epochs
- PF pinned at 1.00-1.01
- Trade volume contracts as policy degrades (492K → 402K)
- Cascade controllers stable but cannot move the policy off the plateau
Hypotheses
Each hypothesis gets its own atomic experiment. Compare against the v9 baseline (3-epoch, full cascade, fold 0 peak val=174, WR=50.1%).
H1: Label horizon mismatch (project_label_horizon_is_bottleneck)
Theory: The model predicts next-bar return but the actual directional signal lives at a different time scale (e.g., 5-bar, 30-bar lookback in volume bars ≈ 4-25 seconds). With volume bars firing every ~0.78s at 34_496 bars/day, single-bar prediction is dominated by micro-noise.
Test: Modify the training reward (and aux-head label if applicable) to use N-bar forward return for N ∈ {5, 20, 60} bars. Compare WR/PF peak across the three settings.
Predicted outcome if H1 is true: WR jumps above 50.5% at N=20 or N=60.
Predicted outcome if H1 is false: WR stays at 50.1-50.2% across all N.
Cost: 3 smokes × ~30 min each on L40S = 90 min wall-clock.
H2: Action-space pathology (project_hold_hides_directional_signal_hypothesis)
Theory: The 4-direction action space (Short/Hold/Long/Flat) collapses directional signal. Hold (keep position) and Flat (close position) both register as "no-op" in metrics but differ structurally — the model may be learning to predict Hold/Flat as a coward strategy that breaks even without committing direction.
Test: Run with Hold action disabled (force the model to choose between Short, Long, Flat each bar). If WR ≥ 50.1% with no Hold, directional learning is structurally suppressed by Hold. If WR drops significantly below 50% without Hold, Hold is the right thing and the plateau is elsewhere.
Cost: One smoke + one comparison run. ~60 min.
Caveat: requires action-space change in training config + state-layout fields. Larger code change than H1.
H3: Reward shape doesn't produce directional gradient
Theory: Current reward is some combination of per-bar return,
trade-cost penalty, hold-time bonus etc. (per pearl_reward_shape_drives_quality_over_quantity).
With WR=50% and PF=1.0, the reward gradient may be too noisy to teach
direction — model finds break-even policies more easily than directional
ones.
Test: Two variants:
- Pure directional reward:
reward = sign(next_return) * |action_dir|(binary correct/incorrect signal, no magnitude). - Asymmetric loss-aversion (per
pearl_audit_unboundedness_for_implicit_asymmetry): amplify losing trades 1.5× to penalize indecisive HOLD-as-default.
Predicted outcome if H3 is true: variant 1 either rises (model learns direction without magnitude confounders) or collapses (magnitude noise was actually helping); variant 2 forces model out of indecision.
Cost: 2 smokes × 30 min each = 60 min.
H4: Feature representation gap
Theory: At 34_496 bars/day on volume bars, the feature set
(42-dim market features from extract_ml_features) may not capture
multi-bar context needed for directional prediction. MTF features
exist in state layout (16 dims at indices 90-106) but eval-baseline
feeds zeros for these.
Test: Train with real MTF features computed (not zero-padded). If WR rises ≥ 0.5%, MTF carries the directional signal. If not, MTF is also noise at this resolution.
Cost: Feature pipeline change + one smoke. Higher engineering cost than H1-H3.
H5 (longshot): Bar-resolution itself is too noisy
Theory: 34_496 bars/day = 0.78s/bar is below the timescale where
directional signal exists in ES.FUT order flow. Per
project_bar_resolution_is_actual_architecture, the "hybrid tick→bar"
architecture is aspirational not built. Maybe the model needs longer
bars to find signal.
Test: Switch to time bars (e.g., 1-min or 5-min) and re-train. Compare WR/PF against volume-bar baseline.
Cost: Significant — different data ingestion path, retrain from scratch, may need hyperparam re-tuning. Longest experiment.
H6 (added 2026-05-12 after H1 falsification): Aux→Policy state bridge
Theory (synthesized from H1 result + memory pearls): The aux head
proved it can extract directional signal at H=200 (78% accuracy
within-fold), but pearl_separate_aux_trunk_when_shared_starves
walls off the aux trunk from the policy trunk via stop-grad. The
proven directional capability sits idle while the policy can't
extract direction on its own (WR=50.1% across all conditions).
The bridge: Wire the aux head's directional probability into the
policy's STATE as an input feature. Policy gradient flows THROUGH the
feature (uses it) but NOT BACK (stop-grad → aux trunk unaffected).
Uses one of the existing padding slots [121..128) in STATE_DIM=128.
Test: Add aux probability state slot → run smoke → watch WR. If WR breaks past 50.5%, mechanism 1 (trunk separation gap) was the binding constraint. If WR stays pinned, the bottleneck is downstream of policy state input (Mechanism 2 reward density or Mechanism 3 V/A unidentifiability).
Caveats (must be scoped into implementation):
- Aux drops 78%→49% across fold boundaries (within-fold overfit at aux level). May need cross-fold stabilization or feed calibrated probability not argmax.
- Aux predicts per-bar; trades hold ~1.7 bars. Aux output at trade-open should persist for trade duration to avoid flicker.
- Reward density (Mechanism 2) might still kill the gradient even with direction info in state — follow-up scope.
Cost: ~1 day engineering (state layout, experience collection wireup, aux probability extraction) + 1 smoke ~30min.
Experiment order (revised 2026-05-12)
Run in this priority order, killing early on each as soon as the hypothesis is conclusively resolved:
- ✗ H1 (label horizon) — FALSIFIED 2026-05-12. Aux learns at H=200 (78% acc) but doesn't propagate to policy (trunk separation).
- H6 (aux→policy state bridge) — PRIMARY now. Bridges proven aux capability into policy.
- H3 (reward shape) — if H6 falsified, address per-step cost
dominance (
pearl_event_driven_reward_density_alignment). - H2 (Hold disable) — if H3 inconclusive.
- H4 (MTF features) — higher engineering cost.
- H5 (bar resolution) — last resort.
Stop after the first hypothesis that produces WR ≥ 51% as a clear signal.
Success criteria
Phase 1 milestone: WR ≥ 51% on val window for ≥ 1 fold (any hypothesis). Phase 2 milestone: WR ≥ 53% sustained across 3 folds. Phase 3 milestone: WR ≥ 55% AND PF ≥ 2.0 (the SP20+ goal).
Each milestone gates the next phase. Phase 1's hypothesis becomes the core architectural change going into Phase 2.
Non-goals (out of scope for SP22)
- Cascade controller tuning (SP21 T2.2 is closed)
- Eval pipeline architecture (Phase 8.3-8.7 wraps it; OFI/TLOB/MTF fidelity is a separate deferred concern from SP21)
- Hyperopt — premature until the WR plateau is broken
- Multi-symbol generalization — single-symbol (ES.FUT) until plateau resolves
Risk: confounded signal from multiple changes
The cascade is active — its controllers will react to whatever training-side change we make. If WR jumps, distinguishing "cascade amplification of new signal" from "the new signal alone" requires the cascade-off comparison.
Mitigation: For any hypothesis that produces WR ≥ 51%, immediately run a comparison smoke with cascade disabled (set all enrichment producers to constant defaults). If WR holds → signal is real. If WR drops back to 50% → cascade was amplifying the signal (expected and desirable, but flag for the writeup).
Audit pointer
Findings + commit log: docs/dqn-wire-up-audit.md (continue appending
under ## 2026-05-12 — SP22 ... headers).