spec: continuous-reasoning trader architecture (4-layer rebuild)
Integrated spec for the continuous-reasoning trader rework. Replaces
the rule-based discrete policy with a 4-layer signal-driven architecture:
Layer A: continuous control loop (every event, not every stride)
Layer B: signal-driven position management (multi-horizon fusion,
continuous sizing, conviction-degradation exit, open_trade_state
expanded 24→128 bytes)
Layer C: adaptive risk envelope (ISV-derived max_lots, threshold,
target_annual_vol)
Layer D: online weight adaptation (LoRA + EWC++, offline-batch
per-session, shadow-eval gated)
Phased gates: A unlocks B; B is where alpha lives; C amplifies B;
D amplifies whatever's working. Each gate has explicit pass criteria.
Conviction definition: ISV-weighted multi-horizon agreement.
weight_h = max(pnl_ema_win - pnl_ema_loss, 0) / (var + cost^2).
Net edge x SNR per horizon, scale-normalised, cold-start uniform fallback.
Pearl conformance: 13 existing pearls referenced and respected
(ISV-driven anchors, first-observation bootstrap, Wiener-alpha floor,
blend-with-floor, z-score normalisation, one-unbounded multiplicand,
trade-level vol bootstrap, deadline cadence, single-source-of-truth,
atomic refactor, adaptive-not-tuned).
Hardcoded boundary preserved for hardware/exchange realities (latency,
cost, annualisation, instrument bounds). Everything else adaptive.
Status: design — awaiting user review before implementation plan.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,492 @@
|
||||
# Continuous-Reasoning Trader — Architecture Design
|
||||
|
||||
**Date:** 2026-05-20
|
||||
**Branch context:** `ml-alpha-phase-a` (S2 closed at `8828e8ab1`)
|
||||
**Status:** Design — pending user review before implementation plan
|
||||
**Supersedes scope of:** `2026-05-19-isv-driven-stop-controller-design.md` (that spec ships clean stops; this spec replaces the rule-based policy that consumes those stops)
|
||||
|
||||
---
|
||||
|
||||
## 1. Motivation
|
||||
|
||||
The S2 cluster smoke (commit `8828e8ab1`, smoke `tgg4l`) ran on clean data with the ISV stop controller working end-to-end. Result: Sharpe_ann = -6.64, PF = 0.40, win_rate = 24.2%, max_dd = 53.5%. Three independent bug layers had been masking this baseline ([sentinel injection](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_behavioral_sentinel_values_poison_downstream_consumers.md), [decision-rate vs event-rate max_hold](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_stop_checks_run_at_deadline_cadence.md), no-PnL on force-close). Now visible: the policy layer is the alpha leak.
|
||||
|
||||
**The signal layer operates at event-rate, stateful, multi-horizon.** Mamba2 SSM state advances every snapshot; ISV per-horizon EMAs (`pnl_ema_loss`, `pnl_ema_win`, `realised_return_var`) track per-horizon. The trunk produces alpha probabilities at h100/h500/h1500/h6000 simultaneously. [State amplifies short-horizon into long-horizon](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_state_amplifies_short_horizon_into_long_horizon.md) — AUC 0.50→0.66 at k=6000.
|
||||
|
||||
**The policy layer collapses that continuous state into discrete actions every 200 events** with hardcoded contracts (`max_lots=5`, `max_hold_ns=60s`, `threshold=0.0`, `decision_stride=200`). Between decisions, ~199 events of evolved state are discarded. Hardcoded YAML values substitute for what should be signal-derived envelopes.
|
||||
|
||||
**Thesis: the policy layer must match the signal layer's continuity, statefulness, and multi-horizon dimensionality.** Hardcoded contracts replaced by ISV-derived envelopes. Time-in-trade emergent, not bound. Position size continuously adjusted to conviction. Online weight adaptation amplifies whatever the architecture is doing right.
|
||||
|
||||
This spec specifies the rebuild.
|
||||
|
||||
---
|
||||
|
||||
## 2. Architectural Vision
|
||||
|
||||
Four layers, sequenced. Each independently validatable. Each addresses a specific failure mode visible in the S2 baseline.
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────────────┐
|
||||
│ LAYER A: Continuous Control Loop │
|
||||
│ Controller runs every event. decision_stride deprecated. │
|
||||
│ Failure addressed: stale-state decisions. │
|
||||
├────────────────────────────────────────────────────────────────────┤
|
||||
│ LAYER B: Signal-Driven Position Management │
|
||||
│ Multi-horizon action fusion (h100/h500/h1500/h6000). │
|
||||
│ Continuous sizing: target_lots = conviction × envelope. │
|
||||
│ Conviction-degradation exit: replaces max_hold. │
|
||||
│ open_trade_state expanded 24 → 128 bytes for trajectory. │
|
||||
│ Failure addressed: cap-binding holds, no mid-trade adjustment. │
|
||||
├────────────────────────────────────────────────────────────────────┤
|
||||
│ LAYER C: Adaptive Risk Envelope │
|
||||
│ max_lots: ISV-derived from rolling Sharpe × drawdown × regime-vol.│
|
||||
│ threshold: conviction-percentile gate. │
|
||||
│ target_annual_vol: regime-vol-inverse. │
|
||||
│ Failure addressed: fixed risk in regime-varying market. │
|
||||
├────────────────────────────────────────────────────────────────────┤
|
||||
│ LAYER D: Online Weight Adaptation │
|
||||
│ LoRA adapter on frozen base. Trajectory-batch offline updates. │
|
||||
│ EWC++ regularizer. Shadow eval + circuit-breaker revert. │
|
||||
│ Failure addressed: static model in non-stationary market. │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Sequencing rationale: A unlocks event-rate consumption needed by B. B is where alpha lives. C amplifies B by shaping when and how much B trades. D amplifies whatever's working in B+C. Each gate's lift is conditional on the prior gate being green — [no-deferrals-for-complementary-fixes](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/feedback_no_deferrals_for_complementary_fixes.md) doesn't apply because the layers are amplifications of one another, not independent fixes for orthogonal mechanisms.
|
||||
|
||||
---
|
||||
|
||||
## 3. Layer A — Continuous Control Loop
|
||||
|
||||
### 3.1 Current state
|
||||
|
||||
`crates/ml-backtesting/src/sim/mod.rs` decision flow runs in the harness loop at `decision_stride=200` events. Between decisions: book updates, ATR EMA, ISV updates, fill processing, pnl_track close detection — all continuous. Controller (`decision_policy_default`/`decision_policy_program` + `seed_inflight_limits_batched`) — strictly periodic.
|
||||
|
||||
### 3.2 Target state
|
||||
|
||||
Controller runs every event. Trunk forward pass (already every event) feeds the latest alpha probabilities directly into the controller every event. `seed_inflight`'s target-delta semantics absorb continuous target changes by seeding incremental orders.
|
||||
|
||||
### 3.3 Files touched
|
||||
|
||||
- `crates/ml-backtesting/src/harness.rs`: remove `if event_idx % decision_stride == 0` gate around `step_decision`.
|
||||
- `crates/ml-backtesting/src/sim/mod.rs`: `step_decision` invoked every event from harness; internal kernel batching unchanged.
|
||||
- `bin/fxt-backtest/src/main.rs` + `config/ml/*.yaml`: keep `decision_stride` field for backwards-compat but ignore it (deprecation log line on first use).
|
||||
|
||||
### 3.4 Cost target
|
||||
|
||||
Per-event controller cost ≤ 10× current per-event cost. Sim throughput target ≥ 5000 events/sec (current ~21000 events/sec at stride=200 with controller every 200th event; expect ~5000-10000 events/sec at continuous controller).
|
||||
|
||||
### 3.5 Failure mode addressed
|
||||
|
||||
Decisions land on stale signal: at decision_stride=200 events on ES Q1 data (~3.9s sim-time per event), entries happen up to ~13 minutes after the alpha signaled. Exits trigger up to ~13 minutes after conviction collapsed.
|
||||
|
||||
### 3.6 Layer A acceptance criteria (Gate 1)
|
||||
|
||||
- Smoke completes with continuous controller in ≤ 30s wall-time vs ~95s current (or accept ≤ 5× slowdown).
|
||||
- No regression in n_trades vs Layer-A-off (continuous control should produce ≥ as many trades).
|
||||
- Trade-level entry timestamps cluster tighter to signal-spike timestamps than the stride=200 baseline (measurable via signal-vs-entry-ts cross-correlation).
|
||||
- `total_pnl_usd` and `sharpe_ann` within ±15% of stride=200 baseline (Layer A alone is not the alpha — its job is to unlock Layer B).
|
||||
|
||||
---
|
||||
|
||||
## 4. Layer B — Signal-Driven Position Management
|
||||
|
||||
This is the heart of the rebuild. Three sub-components.
|
||||
|
||||
### 4.1 Multi-horizon action fusion
|
||||
|
||||
**Current:** `decision_policy_default` consumes `alpha_probs[N_HORIZONS]` and reduces them to a single decision via simple aggregation. `stop_check_isv` uses `open_horizon_masks` for stop evaluation only.
|
||||
|
||||
**Target:** different horizons drive different actions.
|
||||
|
||||
| Horizon | Role | Action it influences |
|
||||
|---|---|---|
|
||||
| h100 | Fast / microstructure | Entry timing (wait for short-horizon agreement) |
|
||||
| h500 | Short-term | Mid-trade scale-up trigger |
|
||||
| h1500 | Medium | Mid-trade adjustment (scale up/down) |
|
||||
| h6000 | Slow / regime | Hold conviction; entry direction lock |
|
||||
|
||||
Concretely the policy logic becomes:
|
||||
|
||||
```
|
||||
1. Direction: sign of h6000 alpha (the slow thesis).
|
||||
2. Entry gate: h6000 |alpha − 0.5| > threshold AND h100 agrees in sign AND h500 agrees in sign.
|
||||
(Short-horizon agreement with long-horizon thesis = entry timing.)
|
||||
3. Sizing: target_lots = clamp(conviction × envelope_max, 0, envelope_max).
|
||||
4. Hold gate: h6000 conviction not yet degraded (see 4.3).
|
||||
5. Scale-up: h500 + h1500 confirm h6000 direction AND conviction trajectory rising.
|
||||
6. Scale-down: h500 OR h1500 diverge from h6000 (early-warning).
|
||||
7. Exit: conviction-degradation policy (see 4.3).
|
||||
```
|
||||
|
||||
### 4.2 Continuous sizing
|
||||
|
||||
`seed_inflight_limits_batched` already uses target-delta semantics (`crates/ml-backtesting/cuda/resting_orders.cu` line 652-672). Target changes any event → delta computed → incremental order seeded.
|
||||
|
||||
Sizing formula:
|
||||
```
|
||||
conviction = isv_weighted_agreement(alpha_probs, isv_state, mask) // see 4.4
|
||||
conviction_ema = EMA(conviction, α=isv_derived_alpha) // smoothing
|
||||
target_lots = round(conviction_ema × envelope_max)
|
||||
```
|
||||
|
||||
EMA prevents micro-oscillation churn. α derived per [pearl_wiener_optimal_adaptive_alpha](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_wiener_optimal_adaptive_alpha.md) with floor per [pearl_wiener_alpha_floor_for_nonstationary](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_wiener_alpha_floor_for_nonstationary.md).
|
||||
|
||||
### 4.3 Conviction-degradation exit
|
||||
|
||||
Replaces `max_hold_ns` as the dominant exit mechanism. `max_hold_ns` becomes a high-default circuit-breaker (e.g., 8 hours) for safety only.
|
||||
|
||||
**Per-trade conviction trajectory** stored in expanded `open_trade_state` (see §6). Tracks:
|
||||
- conviction at entry
|
||||
- conviction EMA over the trade lifetime
|
||||
- multi-horizon agreement at each decision
|
||||
- pnl-adjusted conviction = conviction × sign(unrealized_pnl)
|
||||
|
||||
**Exit fires when ANY of:**
|
||||
|
||||
1. **Net conviction degradation:** `conviction_ema_now < conviction_at_entry × degradation_factor` for K consecutive events. `degradation_factor` and K are ISV-derived (not hardcoded).
|
||||
2. **Multi-horizon disagreement:** h100 or h500 reversed sign relative to h6000 for ≥ K_short events.
|
||||
3. **pnl-adjusted conviction crosses zero:** signal says exit AND market agrees.
|
||||
4. **Hard stop:** SL/trail still active per current `stop_check_isv` logic (Layer B doesn't remove them — they're orthogonal safety).
|
||||
5. **Circuit-breaker:** `(current_ts − entry_ts) ≥ max_hold_circuit_breaker_ns` where the circuit-breaker default is 8 hours, ~480× the current 60s cap.
|
||||
|
||||
This makes time-in-trade emergent. Winners hold as long as conviction holds. Losers cut when conviction degrades, not when a clock fires.
|
||||
|
||||
### 4.4 Conviction definition (ISV-weighted multi-horizon agreement)
|
||||
|
||||
For each event:
|
||||
|
||||
```
|
||||
For each horizon h in {h100, h500, h1500, h6000}:
|
||||
alpha_h = alpha_probs[h]
|
||||
direction_h = sign(alpha_h − 0.5)
|
||||
magnitude_h = |alpha_h − 0.5| × 2 // ∈ [0, 1]
|
||||
|
||||
# ISV-derived weight per horizon. Rewards horizons with positive
|
||||
# net edge (win EMA exceeds loss EMA) AND low realised-return
|
||||
# variance (high SNR). Floor at 0 prevents negative-edge horizons
|
||||
# from dragging conviction; cost^2 in the denominator is the
|
||||
# bootstrap sentinel per pearl_trade_level_vol_for_stop_distance.
|
||||
net_edge_h = max(isv[h].pnl_ema_win − isv[h].pnl_ema_loss, 0)
|
||||
weight_h = net_edge_h / (isv[h].realised_return_var + cost^2)
|
||||
|
||||
weighted_mag_h = magnitude_h × weight_h × direction_h // signed
|
||||
|
||||
# Normalise by total weight. If all horizons have zero net edge
|
||||
# (cold start), fall back to uniform-weighted magnitude per
|
||||
# pearl_first_observation_bootstrap.
|
||||
total_weight = sum(|weight_h|)
|
||||
if total_weight > 0:
|
||||
conviction_signed = sum(weighted_mag_h) / total_weight // ∈ [-1, +1]
|
||||
else:
|
||||
conviction_signed = sum(magnitude_h × direction_h) / 4 // cold-start uniform
|
||||
|
||||
conviction = |conviction_signed| // ∈ [0, 1]
|
||||
direction = sign(conviction_signed)
|
||||
```
|
||||
|
||||
Notes:
|
||||
- Weights are ISV-derived ([pearl_controller_anchors_isv_driven](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_controller_anchors_isv_driven.md)). Per-horizon SNR drives how much that horizon contributes.
|
||||
- Net edge follows the asymmetric structural bound — losses are NOT symmetrically subtracted from gains since negative-edge horizons should drop out, not flip sign ([pearl_audit_unboundedness_for_implicit_asymmetry](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_audit_unboundedness_for_implicit_asymmetry.md)).
|
||||
- `cost^2` bootstrap follows [pearl_trade_level_vol_for_stop_distance](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_trade_level_vol_for_stop_distance.md): cost as sentinel-floor for variance until real var bootstraps.
|
||||
- Normalization makes conviction scale-invariant per [pearl_zscore_normalization_for_magnitude_asymmetric_signals](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_zscore_normalization_for_magnitude_asymmetric_signals.md).
|
||||
- Single unbounded multiplicand: `net_edge_h / (var + cost^2)` is the one unbounded factor; `magnitude_h × direction_h` is bounded in `[-1, +1]`. Per [pearl_one_unbounded_signal_per_reward](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_one_unbounded_signal_per_reward.md).
|
||||
- Disagreeing horizons reduce conviction magnitude even if individually large — opposing signed `weighted_mag_h` terms partially cancel in the sum.
|
||||
- Cold-start fallback (uniform weighting before ISV bootstraps) ensures the controller is functional from event 1; matches [pearl_first_observation_bootstrap](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_first_observation_bootstrap.md).
|
||||
|
||||
### 4.5 Layer B acceptance criteria (Gate 2)
|
||||
|
||||
- Hold-time distribution is no longer cap-binding: mean and max NOT clustered at the circuit-breaker.
|
||||
- `profit_factor > 1.0` OR a defined alpha-realization metric (e.g., realized-vs-theoretical PnL on tagged signal opportunities) shows ≥ 30% lift over Layer A baseline.
|
||||
- Win rate stable or up (24.2% baseline; target ≥ 25% with PF lift).
|
||||
- Drawdown not worsened by > 10% vs Layer A baseline.
|
||||
- Conviction degradation exits account for ≥ 60% of closes (rest = SL/trail/circuit-breaker). Validates the mechanism is dominant.
|
||||
|
||||
---
|
||||
|
||||
## 5. Layer C — Adaptive Risk Envelope
|
||||
|
||||
ISV-derived bounds replace YAML constants.
|
||||
|
||||
### 5.1 `max_lots` envelope
|
||||
|
||||
Replaces hardcoded `max_lots: 5`.
|
||||
|
||||
```
|
||||
rolling_sharpe_short = isv_aggregated rolling sharpe over last N closed trades
|
||||
drawdown_pct = current_dd / peak_equity
|
||||
regime_vol_inverse = 1 / (regime_atr_ema + cost)
|
||||
|
||||
envelope_raw = base_lots × rolling_sharpe_short_clamped × (1 − drawdown_pct/dd_cap) × regime_vol_inverse
|
||||
envelope_max = clamp(envelope_raw, envelope_floor, envelope_hard_cap)
|
||||
```
|
||||
|
||||
Floor and hard cap follow [pearl_blend_formulas_must_have_permanent_floor](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_blend_formulas_must_have_permanent_floor.md): `max(real, floor)`, not blend. Hard cap stays as safety (default 50 lots, configurable).
|
||||
|
||||
### 5.2 `threshold` (conviction-percentile gate)
|
||||
|
||||
Replaces hardcoded `threshold: 0.0`. Conviction-percentile-based — trade only when current conviction exceeds the recent N-conviction percentile target.
|
||||
|
||||
```
|
||||
conviction_history = circular buffer of last N convictions (per backtest, on-device)
|
||||
percentile_target = ISV-derived (start at 0.75, learn toward maximizing realized PF)
|
||||
threshold = percentile(conviction_history, percentile_target)
|
||||
```
|
||||
|
||||
Self-tuning: percentile_target drifts toward whatever value maximizes recent realized PF, bounded by a hard floor (e.g., 0.5 — never trade below median conviction).
|
||||
|
||||
### 5.3 `target_annual_vol_units`
|
||||
|
||||
Replaces hardcoded `target_annual_vol_units: 50.0`. Regime-vol-inverse.
|
||||
|
||||
```
|
||||
target_annual_vol = base_vol_target × (regime_vol_baseline / current_regime_vol)
|
||||
```
|
||||
|
||||
Defensive in volatile regimes, aggressive in calm regimes. Bounded by configured floor/cap.
|
||||
|
||||
### 5.4 Layer C acceptance criteria (Gate 3)
|
||||
|
||||
- Drawdown reduction at equal PnL (envelope-on vs envelope-off side-by-side smoke).
|
||||
- OR PnL lift at equal drawdown.
|
||||
- Envelope adapts smoothly — no thrash (envelope EMA on a slower timescale than trade-rate).
|
||||
- Floor activates correctly when signal-derived envelope would underflow.
|
||||
|
||||
---
|
||||
|
||||
## 6. Layer D — Online Weight Adaptation
|
||||
|
||||
Most ambitious, deferred until A+B+C green.
|
||||
|
||||
### 6.1 Architecture
|
||||
|
||||
LoRA adapter on frozen base checkpoint. Low-rank delta applied to selected weight matrices. ~1-5% of base parameter count.
|
||||
|
||||
### 6.2 Update schedule
|
||||
|
||||
**Offline-batch, per session** — NOT per-trade in-loop.
|
||||
|
||||
```
|
||||
For each session end (or every N trades):
|
||||
1. Collect trajectories: (state_at_decision, action, outcome_realized_pnl) for all closed trades.
|
||||
2. Compute outcome-conditional loss:
|
||||
- Value regression (predicted_value vs realized)
|
||||
- Direction loss (predicted_direction vs realized_pnl_sign)
|
||||
- Conviction calibration (conviction vs |realized_pnl|)
|
||||
3. EWC++ regularizer toward base checkpoint (prevents catastrophic forgetting).
|
||||
4. Gradient step on LoRA adapter only (base frozen).
|
||||
5. Shadow eval: run adapter-on vs adapter-off on a held-out validation window.
|
||||
6. If shadow eval shows PF degradation > X% vs baseline: revert adapter to checkpoint.
|
||||
```
|
||||
|
||||
### 6.3 References
|
||||
|
||||
- [feedback_safla_neural](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/) — SAFLA pattern for memory-persistent learning
|
||||
- `sona_learning_optimizer` agent — SONA LoRA tooling already available
|
||||
- EWC++ for catastrophic forgetting in continual learning
|
||||
|
||||
### 6.4 Layer D acceptance criteria (Gate 4)
|
||||
|
||||
- Walk-forward across ≥ 3 sessions shows positive PF lift in ≥ 75% of folds.
|
||||
- Shadow eval correctly catches degradation in a synthetic-bad-update test.
|
||||
- Circuit-breaker revert works end-to-end.
|
||||
- No catastrophic forgetting: base+adapter performs ≥ base on prior windows.
|
||||
|
||||
---
|
||||
|
||||
## 7. State Representation: `open_trade_state` expansion
|
||||
|
||||
Current: 24 bytes per backtest (entry_ts:8, entry_px_x100:4, entry_size:4, realized_at_open:4, horizon_idx:1, pad:3).
|
||||
|
||||
Target: **128 bytes per backtest** (single source of truth per trade per [feedback_single_source_of_truth_no_duplicates](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/feedback_single_source_of_truth_no_duplicates.md)).
|
||||
|
||||
Proposed layout:
|
||||
|
||||
```
|
||||
Offset Size Field
|
||||
─────────────────────────────────────────────────────────────────────
|
||||
0 8 entry_ts_ns (u64)
|
||||
8 4 entry_px_x100 (i32)
|
||||
12 4 entry_size (i32)
|
||||
16 4 realized_at_open (f32)
|
||||
20 1 horizon_idx_dominant_at_entry (u8)
|
||||
21 3 pad
|
||||
24 4 conviction_at_entry (f32)
|
||||
28 4 conviction_ema (f32)
|
||||
32 16 conviction_per_horizon_at_entry [4 × f32] (h100, h500, h1500, h6000)
|
||||
48 16 conviction_per_horizon_ema [4 × f32]
|
||||
64 4 pnl_adjusted_conviction_ema (f32)
|
||||
68 4 degradation_consecutive_events (u32)
|
||||
72 4 short_disagreement_consecutive_events (u32)
|
||||
76 4 peak_unrealized_pnl (f32)
|
||||
80 4 trough_unrealized_pnl (f32)
|
||||
84 4 scale_up_events_count (u32)
|
||||
88 4 scale_down_events_count (u32)
|
||||
92 4 max_target_lots_reached (i32)
|
||||
96 4 min_target_lots_reached (i32)
|
||||
100 28 reserved for future trajectory features
|
||||
```
|
||||
|
||||
128 bytes total. Aligns to cache line.
|
||||
|
||||
### 7.1 Migration
|
||||
|
||||
Every reader and writer of `open_trade_state` migrates atomically per [feedback_no_partial_refactor](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/feedback_no_partial_refactor.md). Sites to update:
|
||||
|
||||
- `crates/ml-backtesting/cuda/pnl_track.cu` — `OPEN_TRADE_STATE_BYTES` constant from 24 to 128; all `st + offset` reads/writes updated.
|
||||
- `crates/ml-backtesting/cuda/decision_policy.cu` — `stop_check_isv` reads entry_ts via `+ 24` indexing; update.
|
||||
- `crates/ml-backtesting/cuda/resting_orders.cu` — S2.2 max_hold event-rate check also reads via `+ 24`; update.
|
||||
- `crates/ml-backtesting/src/sim/mod.rs` — allocation site, `read_open_trade_state` helper.
|
||||
- Any tests asserting layout.
|
||||
|
||||
---
|
||||
|
||||
## 8. Boundary: hardcoded vs adaptive
|
||||
|
||||
| Field | Status | Source / Rationale |
|
||||
|---|---|---|
|
||||
| `latency_ns` | **HARDCODED** (config) | Physical wire time. Real constraint. |
|
||||
| `cost_per_lot_per_side` | **HARDCODED** (config) | Exchange-determined fee. |
|
||||
| `annualisation_factor` | **HARDCODED** | Definitional constant. |
|
||||
| `min/max_reasonable_px` | **HARDCODED** (config, per-instrument) | Instrument bounds. |
|
||||
| `max_hold_ns` | **CIRCUIT-BREAKER** (default 8h) | Safety net only. Behavior emerges from §4.3. |
|
||||
| `decision_stride` | **DEPRECATED** | Layer A makes obsolete. Field kept for backwards-compat. |
|
||||
| `max_lots` | **ADAPTIVE** (Layer C §5.1) | Envelope from rolling Sharpe × DD × regime vol. |
|
||||
| `threshold` | **ADAPTIVE** (Layer C §5.2) | Conviction-percentile gate. |
|
||||
| `target_annual_vol_units` | **ADAPTIVE** (Layer C §5.3) | Regime-vol-inverse. |
|
||||
| `kelly_frac_floor` | **HARDCODED** (config) | Bootstrap sentinel. [pearl_first_observation_bootstrap](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_first_observation_bootstrap.md). |
|
||||
| `sharpe_weight_floor` | **HARDCODED** (config) | Bootstrap sentinel. Same rationale. |
|
||||
|
||||
---
|
||||
|
||||
## 9. Phased Implementation Gates
|
||||
|
||||
### Gate 1 (Layer A): Continuous controller
|
||||
|
||||
**Pass criteria:**
|
||||
- Continuous controller compiles and runs in cluster smoke.
|
||||
- Smoke wall-time ≤ 5× current.
|
||||
- n_trades ≥ Layer-A-off baseline.
|
||||
- Sharpe/PnL within ±15% of baseline.
|
||||
- No new NaN/sentinel counter fires.
|
||||
|
||||
**Validation:** single-cell smoke `config/ml/sweep_smoke.yaml` with `decision_stride=200` (ignored) vs new continuous control. Side-by-side wall-time + trade metrics.
|
||||
|
||||
### Gate 2 (Layer B): Signal-driven position management
|
||||
|
||||
**Pass criteria:**
|
||||
- Hold-time mean NOT cap-binding (max_hold circuit-breaker rarely fires; < 5% of closes).
|
||||
- `profit_factor > 1.0` OR ≥ 30% lift on a defined alpha-realization metric vs Layer A baseline.
|
||||
- Win rate ≥ 24.2% (current).
|
||||
- Drawdown not worsened > 10%.
|
||||
- Conviction degradation accounts for ≥ 60% of closes.
|
||||
- Per-trade trajectory data shows conviction tracks predicted alpha (sanity check on the formula).
|
||||
|
||||
**Validation:** parametric sweep over (degradation_factor, disagreement_K, conviction_EMA_α) on the smoke window. Pick the winner. Then walk-forward on 3 folds.
|
||||
|
||||
### Gate 3 (Layer C): Adaptive risk envelope
|
||||
|
||||
**Pass criteria:**
|
||||
- Drawdown reduction at equal PnL OR PnL lift at equal drawdown (envelope-on vs envelope-off).
|
||||
- Envelope stays bounded by floor and hard-cap during smoke.
|
||||
- No envelope thrash (envelope EMA smoothing works).
|
||||
- Self-tuning percentile drifts to a stable region.
|
||||
|
||||
**Validation:** side-by-side smoke. Three configs: Layer A only, Layer A+B, Layer A+B+C. Compare drawdown/PnL/turnover.
|
||||
|
||||
### Gate 4 (Layer D): Online weight adaptation
|
||||
|
||||
**Pass criteria:**
|
||||
- Walk-forward across ≥ 3 sessions, ≥ 75% positive PF lift.
|
||||
- Shadow eval catches synthetic-bad-update.
|
||||
- Circuit-breaker revert works.
|
||||
- Base+adapter ≥ base on held-out windows (no catastrophic forgetting).
|
||||
|
||||
**Validation:** walk-forward fold structure from training pipeline. Per-fold adapter checkpoint. Compare PF/Sharpe/DD adapter-on vs adapter-off.
|
||||
|
||||
---
|
||||
|
||||
## 10. Risks and Mitigations
|
||||
|
||||
### 10.1 Layer A — controller cost blowup
|
||||
|
||||
If event-rate controller is slow, sim throughput tanks. 200× more invocations.
|
||||
|
||||
**Mitigations:**
|
||||
- Profile early. Cap controller cost to ≤ 10× current per-event cost.
|
||||
- ISV math is light; the heavy cost is the trunk forward, which already runs every event.
|
||||
- If too slow: introduce adaptive sub-stride that runs controller every event in active regimes, every K events in quiet regimes.
|
||||
|
||||
### 10.2 Layer B — conviction-degradation deadlock
|
||||
|
||||
If conviction never degrades (the signal keeps lying to itself), trade never closes. Real losers might hold high conviction.
|
||||
|
||||
**Mitigations:**
|
||||
- pnl-adjusted conviction = conviction × sign(unrealized_pnl). When pnl negative, conviction is negated. Deep negative pnl → deeply negative pnl-adjusted conviction → trip degradation exit.
|
||||
- Absolute circuit-breaker (max_hold ≈ 8h) catches pathological cases.
|
||||
- Multi-horizon disagreement is independent of degradation: if h100 reverses while h6000 holds, that's an exit signal even without conviction collapse.
|
||||
|
||||
### 10.3 Layer C — envelope feedback loop on PnL
|
||||
|
||||
Drawdown shrinks envelope → smaller positions → less ability to recover → deeper drawdown.
|
||||
|
||||
**Mitigations:**
|
||||
- Envelope adapts on slower timescale than trade timescale (EMA α floor).
|
||||
- Envelope_floor is never zero (hard floor in lots).
|
||||
- Drawdown weighting is logarithmic (not linear) — small DD doesn't crush envelope.
|
||||
|
||||
### 10.4 Layer D — online learning instability
|
||||
|
||||
Famous failure mode.
|
||||
|
||||
**Mitigations:**
|
||||
- Offline-batch (not in-loop) — bounded update frequency.
|
||||
- EWC++ regularizer toward base — prevents catastrophic forgetting.
|
||||
- Shadow eval gates every adapter update — catches bad updates before they go live.
|
||||
- Circuit-breaker revert — automatic rollback on rolling Sharpe degradation.
|
||||
- Adapter is small (1-5% of base) — limited expressivity to misbehave.
|
||||
|
||||
---
|
||||
|
||||
## 11. Open questions / future work
|
||||
|
||||
These are recognized scope-cuts. Document for future iteration.
|
||||
|
||||
1. **Discretionary trader features not in scope:** spread-clearing intent, queue-position gaming, hidden order detection, news-event awareness. The architecture supports them as future additions but does not implement them here.
|
||||
2. **Multi-instrument fusion:** spec covers single-instrument (ES). Cross-instrument signal fusion (ES + NQ + cross-correlations) is a separate spec.
|
||||
3. **Layer D adapter scope:** which weight matrices get LoRA adapters? Likely trunk-output + controller-input. Decided at implementation time based on profiling.
|
||||
4. **Conviction-percentile bootstrap:** §5.2 has self-tuning percentile target. Bootstrap behavior in first N trades (before history builds) follows first-observation pattern, but the exact init value is implementation-time decision.
|
||||
|
||||
---
|
||||
|
||||
## 12. Acceptance criteria summary (rollup)
|
||||
|
||||
```
|
||||
GATE 1 (Layer A): Continuous controller live, no PnL regression, ≤ 5× wall-time
|
||||
GATE 2 (Layer B): PF > 1.0 OR +30% alpha-realization, hold-time emergent, 60% degradation exits
|
||||
GATE 3 (Layer C): DD reduction at equal PnL OR PnL lift at equal DD
|
||||
GATE 4 (Layer D): Walk-forward 75% positive lift, no catastrophic forgetting
|
||||
```
|
||||
|
||||
Each gate is a stop. If a gate fails, do not advance until the gate is reopened with revised criteria or revised implementation.
|
||||
|
||||
---
|
||||
|
||||
## 13. Pearl conformance summary
|
||||
|
||||
- [pearl_controller_anchors_isv_driven](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_controller_anchors_isv_driven.md) — Layer C envelopes are ISV-derived.
|
||||
- [pearl_first_observation_bootstrap](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_first_observation_bootstrap.md) — conviction trajectory bootstrap uses sentinel=0, first observation replaces directly.
|
||||
- [pearl_wiener_optimal_adaptive_alpha](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_wiener_optimal_adaptive_alpha.md) — conviction EMA α adaptive.
|
||||
- [pearl_wiener_alpha_floor_for_nonstationary](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_wiener_alpha_floor_for_nonstationary.md) — α floor in conviction tracking (non-stationary by definition).
|
||||
- [pearl_blend_formulas_must_have_permanent_floor](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_blend_formulas_must_have_permanent_floor.md) — envelope floor uses `max(real, floor)`.
|
||||
- [pearl_zscore_normalization_for_magnitude_asymmetric_signals](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_zscore_normalization_for_magnitude_asymmetric_signals.md) — multi-horizon weights are scale-normalized.
|
||||
- [pearl_one_unbounded_signal_per_reward](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_one_unbounded_signal_per_reward.md) — conviction formula has exactly one unbounded multiplicand.
|
||||
- [pearl_trade_level_vol_for_stop_distance](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_trade_level_vol_for_stop_distance.md) — `cost^2` bootstrap for ISV weight denominator.
|
||||
- [pearl_stop_checks_run_at_deadline_cadence](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_stop_checks_run_at_deadline_cadence.md) — Layer A makes ALL checks event-rate.
|
||||
- [feedback_single_source_of_truth_no_duplicates](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/feedback_single_source_of_truth_no_duplicates.md) — open_trade_state is single source.
|
||||
- [feedback_no_partial_refactor](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/feedback_no_partial_refactor.md) — open_trade_state migration is atomic.
|
||||
- [feedback_isv_for_adaptive_bounds](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/feedback_isv_for_adaptive_bounds.md) — adaptive bounds live in ISV, not constants.
|
||||
- [feedback_adaptive_not_tuned](../../../../home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/feedback_adaptive_not_tuned.md) — signal-driven, not tuned.
|
||||
|
||||
---
|
||||
|
||||
**End of design. Awaiting user review before implementation plan.**
|
||||
Reference in New Issue
Block a user