Skip to content
Skip to content
← all research
REJECTED

"RL v3 Meta-Allocator — REJECT (final ledger entry)"

2026-09-06

·

#Scope

Train a PPO agent to PICK from a 5-rule hand-coded rule set (EW, risk-parity, vol-target, credit-gate, trend-filter) instead of outputting a continuous weight vector. The meta-allocator changes the problem formulation: discrete action (5-way), simpler than the continuous 4-dim v3 action, and the rules are pre-validated candidates the agent can choose between.

#Verdict: REJECT

15 cells (5 walk-forward windows x 3 seeds). The agent picked EW 62% of the time and was statistically indistinguishable from the equal-weight baseline on every cell. The rule-switch cost alone (35.7 switches per 126-day OOS cell at 25 bps/switch = ~7 bps/day = 17% annualized drag) makes the net result worse than gross.

#Pre-registered pass bar (from rl_v3_hypothesis.md)

ADOPT requires ALL THREE:

  • OOS Sharpe > equal-weight baseline Sharpe + 0.10
  • Bootstrap p (agent > baseline) < 0.05
  • OOS max drawdown < 0.30

#Pipeline summary

StageDeliverableVerdict
envvalidation/rl_v3_meta.pyMetaRuleEnv with Discrete(5) action space, 9/9 tests pass
trainermodels/rl_v3_meta_trainer.py15 cells trained (3 seeds x 5 windows, 8k timesteps)
OOSreports/rl_v3_meta_oos.mdREJECT (median uplift -0.254, max p 0.646, maxDD -20.10%)
auditreports/rl_v3_meta_audit.mdmechanism: agent is materially different from EW (corr +0.012), NOT a SPY bet (corr -0.002), but DOES WORSE in bear regimes (-0.00058 vs +0.00209)
netreports/rl_v3_meta_net.mdREJECT (net uplift -1.674, S3 TRIGGERED)
ledgerthis reportREJECT

#OOS scoreboard (15 cells)

WindowSeedOOS startOOS endAgent SharpeEW SharpeUpliftp(agent>EW)
002023-11-152024-05-16+2.19+2.44-0.2540.535
012023-11-152024-05-16+2.52+2.44+0.0760.502
022023-11-152024-05-16+2.22+2.44-0.2280.531
102024-05-172024-11-14-0.16+0.61-0.7700.646
112024-05-172024-11-14+0.43+0.61-0.1840.562
122024-05-172024-11-14+0.70+0.61+0.0850.518
202024-11-152025-05-20-1.69-0.94-0.7540.591
212024-11-152025-05-20-1.71-0.94-0.7690.617
222024-11-152025-05-20-1.76-0.94-0.8240.609
302025-05-212025-11-18+1.25+1.81-0.5540.605
312025-05-212025-11-18+1.76+1.81-0.0430.514
322025-05-212025-11-18+2.17+1.81+0.3600.449
402025-11-192026-05-21+4.51+4.78-0.2700.536
412025-11-192026-05-21+4.40+4.78-0.3740.573
422025-11-192026-05-21+4.88+4.78+0.1040.458

Aggregate: median uplift -0.254, max bootstrap p 0.646, worst maxDD -20.10%.

#Mechanism audit

SleeveAgentEWdelta
eqmom0.2500.250+0.000
gate60_ETH0.2310.250-0.019
gate60_BTC0.2420.250-0.008
spy_bench0.2570.250+0.007

The mean agent allocation is within 0.02 of EW on every sleeve. The 5-17% rule choices (TREND_FILTER, VOL_TARGET, RISK_PARITY, CREDIT_GATE) collectively moved weights by ~1.5% per sleeve. That magnitude is too small to register against the noise in 6mo OOS windows.

RegimeAgentEWdelta
bull+0.00071+0.00076-0.00004
bear-0.00058+0.00209-0.00267

The agent does WORSE in bear regimes. The plan's pre-registered mechanism prediction (the agent should win in bear regimes) is not observed. This is a stronger failure than v3, where the agent was at least better in bear regimes (marginally).

#Net-cost analysis

RuleOOS daysShare
EW116262.0%
RISK_PARITY985.2%
VOL_TARGET20611.0%
CREDIT_GATE864.6%
TREND_FILTER32317.2%

The agent switched rules 35.7 times per OOS cell on average (35.7 / 126 = 28% of OOS days). At 25 bps per switch, that's ~7 bps/day or ~17% annualized drag. This single factor explains why the net result is uniformly worse than gross: even when the gross was slightly positive (window 0 seed 1: +0.08 uplift), the cost overwhelms it (net: -1.67).

The cost could be reduced by adding a hold_period (action frozen for N bars after any rule change) — but that is a different design than what was tested here.

#Stop conditions (hypothesis doc)

  • S1 (baseline Sharpe > 0.3): NOT triggered. EW median OOS Sharpe on the 13-window baseline was +0.61, well above the 0.3 floor.
  • S2 (median agent uplift >= 0): TRIGGERED. Median uplift -0.254.
  • S3 (net uplift >= 50% of gross): TRIGGERED. Net is 0% of gross (in fact, deeply negative).

#Combined RL study verdict (v3 + v4 + meta-allocator)

This is the third RL arm in this repo to close at REJECT. The pattern is now consistent:

StudyAction spaceCost in env?Verdict
rl_oos (v1)continuous [0,1]noREJECT (p=0.714)
rl_v3 (this repo)continuous [0,1.5]^4noREJECT (median -1.28)
rl_v4Δ-weights overlay, hard caps, weekly rebalance, cost in envyesREJECT (median -0.002)
rl_v3 metadiscrete {EW, RP, VT, CG, TF}post-hoc onlyREJECT (median -0.25)

Across all four arms:

  • The agent learns something (mechanism PASS in v3, v4)
  • But the magnitude is too small to clear p<0.05 on OOS
  • And the cost (whether modeled in env or post-hoc) overwhelms any marginal edge
  • And EW is genuinely hard to beat (median Sharpe +0.61 on the 13-window baseline, maxDD well within 30%)

This is now a settled finding for this sleeve set and data window. A future RL attempt would need either a different sleeve set (structural alpha the agent can exploit) or a different baseline (EW is the right null to beat; the question is whether the agent's edge is real or noise).

#What this means for the live book

Nothing changes. The validated equal-weight baseline (production/book_allocator.py) stays the production default. No production/ runner is registered. No live orders. No new data dependency. The v3, v4, and meta results all converge on the same conclusion: an RL allocator does not add value to this sleeve set / data window / cost model.

#What we WOULD change before another RL attempt

  • Different sleeve set. The eqmom flagship, gate60 crypto trend, and SPY bench are all positively correlated equity-like bets. The agent has no source of decorrelated alpha to exploit. A sleeve set that includes a genuine diversifier (trend-following on TLT, carry on FX, volatility-selling) would give the agent something to allocate.
  • Different baseline. EW is hard to beat. A weaker baseline (e.g. fixed-50%-equity) would be easier to improve on, but would not produce a useful live deployment.
  • A cost model that the agent can SEE during training. The meta-allocator's high switching cost was a surprise; the env didn't include it. Baking the cost into the reward (as v4 did) is necessary for the agent to learn to hold positions.
  • A hold-period constraint. If the action is discrete, the agent should be required to hold for N bars after any switch. This is a small change but it bounds the cost drag.

#Files this study produced

FilePurpose
validation/rl_v3_meta.pyenv + rule set + OOS replay
models/rl_v3_meta_trainer.pyPPO walk-forward trainer
validation/rl_v3_meta_audit.pymechanism audit
validation/rl_v3_meta_net.pynet-cost backtest
tests/test_rl_v3_meta.py9 unit tests (all pass)
models/checkpoints/rl_v3_meta/meta_main/15 PPO checkpoints
reports/rl_v3_meta_oos.md, rl_v3_meta_audit.md, rl_v3_meta_net.mdper-stage reports
reports/rl_v3_meta_decision.mdthis ledger entry
scripts/fetch_crypto_long.pyyfinance 1Day fetcher (used for the longer walk-forward)
validation/rl_v3_baseline.pyupdated to use longer crypto history

#Date locked

2026-09-04

All trading described is paper (simulated). Not investment advice.

← all research