Files
foxhunt/AGENT_WIRE14_PAPER_TRADING_STATUS.md
jgrusewski 261bbef86e feat(wire-02): Document Wave D adaptive position sizer integration gap
CRITICAL FINDING: RegimeAdaptiveFeatures (Features 221-224) are fully
implemented but NOT integrated into trading decision flow.

Analysis Results:
-  RegimeAdaptiveFeatures: 644 lines, 12/12 tests passing
-  Database schema: regime_states, regime_transitions, adaptive_strategy_metrics
-  gRPC endpoints: GetRegimeState, GetRegimeTransitions defined
-  Trading Agent Service: NO regime integration in allocation.rs
-  Order Generation: NO stop-loss multiplier application

Impact:
- ML models train with regime features
- Production trading IGNORES regime state
- Position sizes remain STATIC (no 0.2x-1.5x adjustment)
- Expected Sharpe improvement: 0% (instead of +25-50%)

Integration Plan (11 hours):
1. Phase 1: Database query layer (2h) - regime.rs
2. Phase 2: Allocation integration (3h) - RegimeAdaptive method
3. Phase 3: Service wiring (2h) - RegimeDetector in service
4. Phase 4: Order generation (1h) - stop-loss multipliers
5. Phase 5: Testing (3h) - regime allocation tests

Code Changes:
- New files: regime.rs (200 lines), tests (300 lines)
- Modified: allocation.rs (+100), service.rs (+50), orders.rs (+30)
- Total: ~500 new lines, ~180 modified lines

Performance: +3ms latency (acceptable for +25-50% Sharpe)
Risk: Low (feature flag + 3-level rollback plan)

Recommendation: PROCEED before 225-feature ML retraining

Files:
- AGENT_WIRE02_ADAPTIVE_SIZER_INTEGRATION.md (full analysis)
- AGENT_WIRE02_QUICK_SUMMARY.md (executive summary)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 09:45:54 +02:00

14 KiB

AGENT WIRE-14: Paper Trading Executor Wave D Integration Status

Agent: WIRE-14 Mission: Verify paper trading executor uses Wave D features and adaptive sizing Status: ⚠️ PARTIAL INTEGRATION - Missing Wave D Features Priority: HIGH - Paper trading must test Wave D before live deployment Date: 2025-10-19


Executive Summary

The paper trading executor (services/trading_service/src/paper_trading_executor.rs) currently uses SharedMLStrategy but is NOT configured for Wave D features. Critical gaps identified:

  1. Uses SharedMLStrategy (ONE SINGLE SYSTEM architecture)
  2. NO Wave D feature configuration - Uses hardcoded defaults (20 lookback, 0.6 confidence)
  3. NO regime state queries - Does not check regime_states table
  4. NO adaptive position sizing - Uses fixed 1.0 contract size
  5. ⚠️ Kelly Criterion mentioned but not implemented (line 569 comment only)

Risk: Paper trading will test Wave C baseline (201 features) instead of Wave D (225 features + regime detection).


Code Analysis

1. ML Strategy Initialization

File: services/trading_service/src/paper_trading_executor.rs Lines: 154-157

pub fn new(db_pool: PgPool, config: PaperTradingConfig) -> Self {
    // Initialize with shared ML strategy (default configuration)
    let ml_strategy = SharedMLStrategy::new(20, 0.6);
    // ^^^ HARDCODED: 20 lookback, 0.6 confidence - NO Wave D config

Issue: SharedMLStrategy::new() does NOT accept FeatureConfig parameter. The constructor signature is:

pub fn new(lookback_periods: usize, min_confidence_threshold: f64) -> Self

Missing: No way to pass FeatureConfig::wave_d() to enable 225-feature extraction.


2. Position Sizing Logic

File: services/trading_service/src/paper_trading_executor.rs Lines: 567-575

fn calculate_position_size(&self, _prediction: &PendingPrediction) -> Result<f64> {
    // Simple fixed position size for paper trading
    // In production, this could use Kelly Criterion or volatility-adjusted sizing
    let position_size = 1.0; // 1 contract
    // ^^^ FIXED SIZE: No adaptive sizing based on regime or confidence

    if position_size > self.config.max_position_size {
        return Err(anyhow!("Position size {} exceeds maximum {}", position_size, self.config.max_position_size));
    }

    Ok(position_size)
}

Missing Wave D Adaptive Logic:

  • No regime state queries (SELECT regime FROM regime_states)
  • No adaptive multipliers (0.2x-1.5x based on regime)
  • No Kelly Criterion position sizing
  • No volatility-adjusted sizing

Expected Behavior (from Wave D design):

// Query regime state
let regime = sqlx::query!("SELECT regime FROM get_latest_regime($1)", symbol)
    .fetch_one(&self.db_pool).await?;

// Apply regime-adaptive multiplier
let base_size = 1.0;
let regime_multiplier = match regime.regime.as_str() {
    "Trending" => 1.5,      // Increase size in trending markets
    "Ranging" => 0.8,       // Reduce size in ranging markets
    "Volatile" => 0.5,      // Minimize size in volatile markets
    "Transition" => 0.2,    // Avoid trading during transitions
    _ => 1.0,               // Normal sizing for unknown regimes
};

let position_size = base_size * regime_multiplier * confidence_factor;

3. Regime State Integration

Search Results: NO regime queries found in paper_trading_executor.rs

$ grep -rn "regime_states\|regime_transitions\|get_latest_regime" \
  services/trading_service/src/paper_trading_executor.rs

# Result: 0 matches

Contrast with trading.rs (Trading Service):

// services/trading_service/src/services/trading.rs:992-1023
async fn get_regime_state(&self, req: Request<GetRegimeStateRequest>) -> Result<Response<GetRegimeStateResponse>, Status> {
    let regime_state = sqlx::query!(
        r#"SELECT regime, confidence, detected_at FROM get_latest_regime($1)"#,
        req.symbol
    ).fetch_one(&self.db_pool).await?;

    Ok(Response::new(GetRegimeStateResponse {
        current_regime: regime_state.regime.unwrap_or("Normal".to_string()),
        confidence: regime_state.confidence.unwrap_or(0.0),
        // ...
    }))
}

Paper Trading Executor: No equivalent logic.


4. Feature Configuration Architecture

Analysis: SharedMLStrategy uses MLFeatureExtractor which has a legacy field for feature count:

File: common/src/ml_strategy.rs (lines 66-84)

pub struct MLFeatureExtractor {
    pub lookback_periods: usize,
    /// Expected feature count (26=Wave A, 30=Wave A+4 extra, 36=Wave B, 65=Wave C)
    expected_feature_count: usize,  // ❌ Outdated comment - no Wave D (225)
    price_history: Vec<f64>,
    volume_history: Vec<f64>,
    // ...
}

Problem: MLFeatureExtractor does NOT use FeatureConfig from ml/src/features/config.rs which supports Wave D:

File: ml/src/features/config.rs (lines 345-355)

pub fn wave_d() -> Self {
    Self {
        enable_wave_a: true,
        enable_wave_b: true,
        enable_wave_c: true,
        enable_wave_d_regime: true,  // ✅ Enables 24 regime features (201→225)
        // ...
    }
}

Root Cause: Architecture mismatch between common::ml_strategy (legacy extractor) and ml::features::config (Wave D-aware).


Integration Gaps

Gap 1: No Wave D Feature Config

Current: SharedMLStrategy::new(20, 0.6) - hardcoded defaults Required: Pass FeatureConfig::wave_d() to enable 225-feature extraction Blocker: SharedMLStrategy constructor does NOT accept FeatureConfig

Solution:

// Option A: Add new constructor
impl SharedMLStrategy {
    pub fn new_with_feature_config(
        lookback: usize,
        confidence: f64,
        feature_config: FeatureConfig,
    ) -> Self {
        // ...
    }
}

// Option B: Modify existing constructor
pub fn new(
    lookback: usize,
    confidence: f64,
    feature_config: Option<FeatureConfig>,
) -> Self {
    let config = feature_config.unwrap_or(FeatureConfig::wave_a());
    // ...
}

Gap 2: No Regime State Queries

Current: No database queries for regime_states or regime_transitions Required: Query latest regime before position sizing decisions Blocker: Database access exists (self.db_pool) but not used

Solution:

async fn get_regime_for_symbol(&self, symbol: &str) -> Result<RegimeState> {
    let regime = sqlx::query!(
        r#"
        SELECT regime, confidence, detected_at
        FROM get_latest_regime($1)
        "#,
        symbol
    )
    .fetch_one(&self.db_pool)
    .await
    .context("Failed to fetch regime state")?;

    Ok(RegimeState {
        regime: regime.regime.unwrap_or("Normal".to_string()),
        confidence: regime.confidence.unwrap_or(0.0),
        detected_at: regime.detected_at,
    })
}

Gap 3: No Adaptive Position Sizing

Current: Fixed 1.0 contract size (line 570) Required: Regime-adaptive sizing (0.2x-1.5x) + confidence-based Kelly multiplier Blocker: Regime state not queried, Kelly logic not implemented

Solution:

async fn calculate_adaptive_position_size(
    &self,
    prediction: &PendingPrediction,
) -> Result<f64> {
    // Step 1: Get regime state
    let regime = self.get_regime_for_symbol(&prediction.symbol).await?;

    // Step 2: Apply regime-adaptive multiplier (Wave D design)
    let regime_multiplier = match regime.regime.as_str() {
        "Trending" => 1.5,
        "Ranging" => 0.8,
        "Volatile" => 0.5,
        "Transition" => 0.2,
        _ => 1.0,
    };

    // Step 3: Apply confidence-based Kelly multiplier
    // Kelly formula: f* = (p*b - q) / b
    // For trading: simplified to linear confidence scaling
    let confidence_factor = prediction.ensemble_confidence.clamp(0.6, 1.0);
    let kelly_multiplier = (confidence_factor - 0.6) / 0.4; // 0.6→0.0, 1.0→1.0

    // Step 4: Calculate final position size
    let base_size = 1.0; // Base contract size
    let adaptive_size = base_size * regime_multiplier * (1.0 + kelly_multiplier);

    // Step 5: Apply safety limits
    Ok(adaptive_size.clamp(0.2, 5.0))
}

Testing Implications

Current Paper Trading Behavior

  1. Feature Set: Uses Wave C baseline (201 features) - NO regime detection
  2. Position Sizing: Fixed 1.0 contracts - NO adaptive sizing
  3. Regime Awareness: None - trades blindly across all market conditions

Expected Wave D Behavior

  1. Feature Set: 225 features (201 + 24 regime detection)
  2. Position Sizing: 0.2x-1.5x adaptive multipliers based on regime
  3. Regime Awareness: Queries regime_states, avoids transitions

Risk Assessment

⚠️ HIGH RISK: Paper trading will NOT validate Wave D features before production deployment.

Scenario: If paper trading passes with Wave C config, we have NO evidence that:

  • 225-feature extraction works in production
  • Regime detection improves performance
  • Adaptive sizing reduces drawdowns

Recommendation: Block production deployment until paper trading uses Wave D config.


Action Items

Priority 1: Enable Wave D Features (2 hours)

  • Modify SharedMLStrategy::new() to accept FeatureConfig parameter
  • Update paper_trading_executor.rs to use FeatureConfig::wave_d()
  • Verify 225-feature extraction in paper trading logs

Priority 2: Implement Regime Queries (1 hour)

  • Add get_regime_for_symbol() method to PaperTradingExecutor
  • Query regime_states table before each trade
  • Log regime transitions for debugging

Priority 3: Adaptive Position Sizing (2 hours)

  • Replace calculate_position_size() with calculate_adaptive_position_size()
  • Implement regime multipliers (0.2x-1.5x)
  • Add confidence-based Kelly multiplier
  • Validate position size range (0.2-5.0 contracts)

Priority 4: Testing & Validation (1 hour)

  • Run paper trading with ES.FUT, NQ.FUT for 24 hours
  • Monitor regime transitions vs. position sizing
  • Compare performance: Wave C baseline vs. Wave D adaptive
  • Document results in PAPER_TRADING_WAVE_D_VALIDATION.md

Total Effort: 6 hours


Technical Debt

Issue 1: Architecture Mismatch

Problem: common::ml_strategy::MLFeatureExtractor does NOT use ml::features::config::FeatureConfig.

Current State:

  • MLFeatureExtractor has hardcoded feature count expectations (comment: "26=Wave A, 36=Wave B, 65=Wave C")
  • No mention of Wave D (225 features)
  • No integration with FeatureConfig::wave_d()

Solution:

// common/src/ml_strategy.rs
pub struct MLFeatureExtractor {
    pub lookback_periods: usize,
    feature_config: ml::features::config::FeatureConfig, // ✅ Use canonical config
    price_history: Vec<f64>,
    // ...
}

impl MLFeatureExtractor {
    pub fn new(lookback: usize, feature_config: FeatureConfig) -> Self {
        Self {
            lookback_periods: lookback,
            feature_config,
            // ...
        }
    }
}

Blocker: Cross-crate dependency (common depends on ml).


Issue 2: Kelly Criterion Stub

Problem: Line 569 comment says "could use Kelly Criterion" but NOT implemented.

Current Code:

// In production, this could use Kelly Criterion or volatility-adjusted sizing
let position_size = 1.0; // 1 contract

Required Implementation:

use risk::kelly_sizing::{KellyResult, KellySizer};

async fn calculate_kelly_position(&self, prediction: &PendingPrediction) -> Result<f64> {
    // Query historical performance for win rate
    let win_rate = self.get_strategy_win_rate(&prediction.symbol).await?;

    // Use ensemble confidence as win probability
    let win_prob = prediction.ensemble_confidence;
    let loss_prob = 1.0 - win_prob;

    // Expected profit/loss ratio (from historical data)
    let profit_loss_ratio = 1.5; // 1.5:1 risk/reward

    // Kelly formula: f* = (p*b - q) / b
    let kelly_fraction = (win_prob * profit_loss_ratio - loss_prob) / profit_loss_ratio;

    // Use fractional Kelly (25%) for safety
    let fractional_kelly = kelly_fraction * 0.25;

    Ok(fractional_kelly.clamp(0.0, 1.0))
}

Existing Code: services/trading_service/src/core/risk_manager.rs has KellySizer but NOT used in paper trading.


References

Codebase Files

  • services/trading_service/src/paper_trading_executor.rs (897 lines)
  • common/src/ml_strategy.rs (MLFeatureExtractor definition)
  • ml/src/features/config.rs (FeatureConfig::wave_d() implementation)
  • services/trading_service/src/services/trading.rs (GetRegimeState gRPC method)
  • services/trading_service/src/core/risk_manager.rs (KellySizer implementation)

Database Schema

  • migrations/045_regime_detection.sql (regime_states, regime_transitions tables)
  • Stored function: get_latest_regime(symbol TEXT)

Wave D Documentation

  • CLAUDE.md (Wave D Phase 6 status, production targets)
  • WAVE_D_DEPLOYMENT_GUIDE.md (regime detection integration guide)
  • WAVE_D_QUICK_REFERENCE.md (adaptive sizing formulas)

Conclusion

Status: ⚠️ PARTIAL INTEGRATION - CRITICAL GAPS

The paper trading executor is architecturally sound (uses SharedMLStrategy, ONE SINGLE SYSTEM) but NOT configured for Wave D testing:

  1. No 225-feature extraction (stuck on Wave C baseline)
  2. No regime state queries (blind to market conditions)
  3. No adaptive position sizing (fixed 1.0 contracts)

Recommendation: BLOCK production deployment until paper trading validates Wave D features. Implement action items (6 hours) and run 24-hour validation before proceeding.

Next Agent: WIRE-15 should implement calculate_adaptive_position_size() with regime multipliers and Kelly logic.


Agent WIRE-14 signing off. Mission: PARTIAL - Integration gaps identified, action plan provided. Handoff: WIRE-15 (Adaptive Position Sizing Implementation)