# 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:
1. **Pure directional reward**: `reward = sign(next_return) * |action_dir|`
   (binary correct/incorrect signal, no magnitude).
2. **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):
1. Aux drops 78%→49% across fold boundaries (within-fold overfit at
   aux level). May need cross-fold stabilization or feed calibrated
   probability not argmax.
2. Aux predicts per-bar; trades hold ~1.7 bars. Aux output at
   trade-open should persist for trade duration to avoid flicker.
3. 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:

1. ✗ **H1 (label horizon)** — FALSIFIED 2026-05-12. Aux learns at
   H=200 (78% acc) but doesn't propagate to policy (trunk separation).
2. **H6 (aux→policy state bridge)** — PRIMARY now. Bridges proven
   aux capability into policy.
3. **H3 (reward shape)** — if H6 falsified, address per-step cost
   dominance (`pearl_event_driven_reward_density_alignment`).
4. **H2 (Hold disable)** — if H3 inconclusive.
5. **H4 (MTF features)** — higher engineering cost.
6. **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).
