# AGENT WIRE-11: Trading Agent Decision Flow Map **Agent**: WIRE-11 **Mission**: Trace complete decision flow from market data to order submission **Status**: ✅ COMPLETE **Date**: 2025-10-19 --- ## ðŸŽŊ Executive Summary **CRITICAL FINDING**: The Trading Agent Service currently has **PLACEHOLDER implementations** for the core decision flow. The allocation, asset selection, and order generation methods return empty results. **Current State**: - ✅ Universe selection: OPERATIONAL (database-backed) - ✅ Strategy coordination: OPERATIONAL (database-backed) - ❌ Asset selection: PLACEHOLDER (returns empty list) - ❌ Portfolio allocation: PLACEHOLDER (returns empty list) - ❌ Order generation: PLACEHOLDER (returns empty list) - ❌ ML prediction integration: NOT CONNECTED **Missing Integration**: - Kelly Criterion: ❌ NOT WIRED - Adaptive Position Sizer: ❌ NOT WIRED - Regime Detection: ❌ NOT WIRED --- ## 📍 Current Architecture ### Service Flow (As Implemented) ``` ┌─────────────────────────────────────────────────────────────────┐ │ API Gateway (Port 50051) │ │ Routes gRPC calls to services │ └────────────────────────────┮────────────────────────────────────┘ │ ▾ ┌─────────────────────────────────────────────────────────────────┐ │ Trading Agent Service (Port 50055) │ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ 1. SelectUniverse (OPERATIONAL) │ │ │ │ ├─ UniverseSelector::select_universe() │ │ │ │ ├─ Queries: asset_universe, universe_instruments │ │ │ │ └─ Returns: List of instruments with metrics │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ 2. SelectAssets (⚠ïļ PLACEHOLDER) │ │ │ │ └─ Returns: Empty list (NOT IMPLEMENTED) │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ 3. AllocatePortfolio (⚠ïļ PLACEHOLDER) │ │ │ │ └─ Returns: Empty allocations (NOT IMPLEMENTED) │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ 4. GenerateOrders (⚠ïļ PLACEHOLDER) │ │ │ │ └─ Returns: Empty order list (NOT IMPLEMENTED) │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ 5. SubmitAgentOrders (⚠ïļ PLACEHOLDER) │ │ │ │ └─ Returns: Empty results (NOT IMPLEMENTED) │ │ │ └────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ ``` ### Trading Service ML Flow (Separate from Agent) ``` ┌─────────────────────────────────────────────────────────────────┐ │ Trading Service (Port 50052) │ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ ExecuteMLTrade (OPERATIONAL) │ │ │ │ ├─ Uses: common::ml_strategy::SharedMLStrategy │ │ │ │ ├─ Feature extraction (26/30/65 features) │ │ │ │ ├─ ML ensemble prediction (DQN, PPO, MAMBA, TFT) │ │ │ │ ├─ Asset selection via AssetSelector │ │ │ │ ├─ Portfolio allocation via PortfolioAllocator │ │ │ │ └─ Order generation via OrderGenerator │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ │ NOTE: Trading Service has FULL implementation but is NOT │ │ connected to Trading Agent Service │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## 🔍 Detailed Flow Analysis ### Phase 1: Universe Selection (✅ OPERATIONAL) **File**: `/home/jgrusewski/Work/foxhunt/services/trading_agent_service/src/service.rs` **Method**: `select_universe()` **Lines**: 80-143 **Flow**: ```rust 1. Convert proto UniverseCriteria → InternalCriteria ├─ Asset classes (Futures, Equities, Currencies) ├─ Min liquidity score ├─ Max volatility └─ Regions (default: North America) 2. UniverseSelector::select_universe(criteria) ├─ Queries database: asset_universe table ├─ Filters by criteria └─ Returns Universe with instruments + metrics 3. Convert internal Instrument → proto └─ Returns SelectUniverseResponse with: ├─ instruments: Vec ├─ metrics: UniverseMetrics ├─ timestamp └─ universe_id ``` **Database Tables Used**: - `asset_universe`: Universe definitions - `universe_instruments`: Instrument-universe relationships --- ### Phase 2: Asset Selection (❌ PLACEHOLDER) **File**: `/home/jgrusewski/Work/foxhunt/services/trading_agent_service/src/service.rs` **Method**: `select_assets()` **Lines**: 241-260 **Current Implementation**: ```rust async fn select_assets( &self, _request: Request, ) -> Result, Status> { info!("SelectAssets called (placeholder)"); Ok(Response::new(SelectAssetsResponse { assets: vec![], // ⚠ïļ EMPTY - NOT IMPLEMENTED metrics: Some(SelectionMetrics { assets_evaluated: 0, assets_selected: 0, avg_composite_score: 0.0, min_score: 0.0, max_score: 0.0, }), timestamp: chrono::Utc::now().timestamp_nanos_opt().unwrap_or(0), })) } ``` **Available Implementation** (NOT WIRED): - **File**: `/home/jgrusewski/Work/foxhunt/services/trading_agent_service/src/assets.rs` - **Component**: `AssetSelector` - **Capabilities**: - Multi-factor scoring (ML: 40%, Momentum: 30%, Value: 20%, Liquidity: 10%) - Feature-based scoring using Wave A indicators - Top-N selection, threshold filtering, quantile selection **INTEGRATION POINT #1: Asset Selection** ```rust // RECOMMENDED IMPLEMENTATION: async fn select_assets( &self, request: Request, ) -> Result, Status> { let req = request.into_inner(); // Step 1: Get ML predictions for universe let ml_predictions = self.get_ml_predictions(&req.universe_id).await?; // Step 2: Extract features for each asset let asset_scores = self.compute_asset_scores(&req.universe_id, &ml_predictions).await?; // Step 3: Use AssetSelector to rank and filter let selector = AssetSelector::with_thresholds( req.min_ml_confidence.unwrap_or(0.5), req.min_composite_score.unwrap_or(0.6), ); let selected = selector.select_top_n(asset_scores, req.max_assets as usize); // Step 4: Convert to proto and return Ok(Response::new(SelectAssetsResponse { assets: selected.into_iter().map(|s| convert_to_proto(s)).collect(), metrics: compute_metrics(&selected), timestamp: Utc::now().timestamp_nanos_opt().unwrap_or(0), })) } ``` --- ### Phase 3: Portfolio Allocation (❌ PLACEHOLDER) **File**: `/home/jgrusewski/Work/foxhunt/services/trading_agent_service/src/service.rs` **Method**: `allocate_portfolio()` **Lines**: 275-298 **Current Implementation**: ```rust async fn allocate_portfolio( &self, _request: Request, ) -> Result, Status> { info!("AllocatePortfolio called (placeholder)"); Ok(Response::new(AllocatePortfolioResponse { allocations: vec![], // ⚠ïļ EMPTY - NOT IMPLEMENTED metrics: Some(AllocationMetrics { total_weight: 0.0, portfolio_volatility: 0.0, portfolio_sharpe: 0.0, var_95: 0.0, max_drawdown_estimate: 0.0, }), timestamp: chrono::Utc::now().timestamp_nanos_opt().unwrap_or(0), allocation_id: uuid::Uuid::new_v4().to_string(), })) } ``` **Available Implementation** (NOT WIRED): - **File**: `/home/jgrusewski/Work/foxhunt/services/trading_agent_service/src/allocation.rs` - **Component**: `PortfolioAllocator` - **Strategies Available**: 1. ✅ Equal Weight 2. ✅ Risk Parity 3. ✅ Mean-Variance (Markowitz) 4. ✅ ML-Optimized 5. ✅ Kelly Criterion **INTEGRATION POINT #2: Portfolio Allocation (KELLY CRITERION INSERTION)** ```rust // RECOMMENDED IMPLEMENTATION: async fn allocate_portfolio( &self, request: Request, ) -> Result, Status> { let req = request.into_inner(); // Step 1: Get regime state for adaptive strategy selection let regime = self.get_current_regime(&req.strategy_id).await?; // Step 2: Select allocation method based on regime let allocation_method = match regime.regime_type { RegimeType::Trending => AllocationMethod::KellyCriterion { fraction: 0.25 }, RegimeType::Ranging => AllocationMethod::RiskParity, RegimeType::Volatile => AllocationMethod::MeanVariance { lambda: 2.0 }, _ => AllocationMethod::MLOptimized, }; // Step 3: Build asset info from selected assets let asset_info = self.build_asset_info(&req.selected_assets, ®ime).await?; // Step 4: Run allocation let allocator = PortfolioAllocator::new(allocation_method); let allocations = allocator.allocate(&asset_info, total_capital)?; // Step 5: Apply Adaptive Position Sizer adjustments let adaptive_sizer = AdaptivePositionSizer::new(db_pool.clone()); let adjusted_allocations = adaptive_sizer.adjust_allocations( allocations, ®ime, portfolio_volatility, ).await?; // Step 6: Store and return self.store_allocation(&adjusted_allocations).await?; Ok(Response::new(convert_to_proto(adjusted_allocations))) } ``` --- ### Phase 4: Order Generation (❌ PLACEHOLDER) **File**: `/home/jgrusewski/Work/foxhunt/services/trading_agent_service/src/service.rs` **Method**: `generate_orders()` **Lines**: 335-357 **Current Implementation**: ```rust async fn generate_orders( &self, _request: Request, ) -> Result, Status> { info!("GenerateOrders called (placeholder)"); Ok(Response::new(GenerateOrdersResponse { orders: vec![], // ⚠ïļ EMPTY - NOT IMPLEMENTED metrics: Some(OrderGenerationMetrics { orders_generated: 0, total_notional: 0.0, avg_order_size: 0.0, }), timestamp: chrono::Utc::now().timestamp_nanos_opt().unwrap_or(0), order_batch_id: uuid::Uuid::new_v4().to_string(), })) } ``` **Available Implementation** (NOT WIRED): - **File**: `/home/jgrusewski/Work/foxhunt/services/trading_agent_service/src/orders.rs` - **Component**: `OrderGenerator` - **Capabilities**: - Delta calculation (target vs. current positions) - Rebalance threshold checking - Order size validation (min/max) - Database persistence (agent_orders table) **INTEGRATION POINT #3: Order Generation** ```rust // RECOMMENDED IMPLEMENTATION: async fn generate_orders( &self, request: Request, ) -> Result, Status> { let req = request.into_inner(); // Step 1: Get allocation and current positions let allocation = self.get_allocation(&req.allocation_id).await?; let current_positions = self.get_current_positions().await?; // Step 2: Generate orders with OrderGenerator let generator = OrderGenerator::new( self.db_pool.clone(), MIN_ORDER_SIZE, MAX_ORDER_SIZE, ); let orders = generator.generate_orders(&allocation, ¤t_positions).await?; // Step 3: Validate with risk checks for order in &orders { self.validate_risk_limits(order).await?; } // Step 4: Return orders (don't submit yet - that's next phase) Ok(Response::new(GenerateOrdersResponse { orders: orders.into_iter().map(|o| convert_to_proto(o)).collect(), metrics: compute_order_metrics(&orders), timestamp: Utc::now().timestamp_nanos_opt().unwrap_or(0), order_batch_id: uuid::Uuid::new_v4().to_string(), })) } ``` --- ### Phase 5: Order Submission (❌ PLACEHOLDER) **File**: `/home/jgrusewski/Work/foxhunt/services/trading_agent_service/src/service.rs` **Method**: `submit_agent_orders()` **Lines**: 359-379 **Current Implementation**: ```rust async fn submit_agent_orders( &self, _request: Request, ) -> Result, Status> { info!("SubmitAgentOrders called (placeholder)"); Ok(Response::new(SubmitAgentOrdersResponse { results: vec![], // ⚠ïļ EMPTY - NOT IMPLEMENTED metrics: Some(OrderSubmissionMetrics { orders_submitted: 0, orders_accepted: 0, orders_rejected: 0, acceptance_rate: 0.0, }), timestamp: chrono::Utc::now().timestamp_nanos_opt().unwrap_or(0), })) } ``` **INTEGRATION POINT #4: Order Submission (Trading Service Connection)** ```rust // RECOMMENDED IMPLEMENTATION: async fn submit_agent_orders( &self, request: Request, ) -> Result, Status> { let req = request.into_inner(); // Step 1: Connect to Trading Service gRPC let mut trading_client = TradingServiceClient::connect( "http://localhost:50052" ).await?; // Step 2: Submit each order to Trading Service let mut results = Vec::new(); for order in req.orders { let submit_request = SubmitOrderRequest { symbol: order.symbol, side: order.side, quantity: order.quantity, order_type: order.order_type, // ... other fields }; let result = trading_client.submit_order(submit_request).await; results.push(OrderSubmissionResult { order_id: order.order_id, status: result.is_ok(), message: format!("{:?}", result), }); } // Step 3: Update database self.store_submission_results(&results).await?; // Step 4: Return results Ok(Response::new(SubmitAgentOrdersResponse { results, metrics: compute_submission_metrics(&results), timestamp: Utc::now().timestamp_nanos_opt().unwrap_or(0), })) } ``` --- ## ðŸšĻ Missing ML Integration ### Current Problem **Trading Agent Service** has NO ML prediction capability: - ❌ No `SharedMLStrategy` instance - ❌ No `MLFeatureExtractor` usage - ❌ No model inference calls - ❌ No connection to ML models (DQN, PPO, MAMBA, TFT) **Trading Service** has FULL ML implementation but is separate: - ✅ `SharedMLStrategy` fully operational - ✅ Feature extraction (26/30/65 features) - ✅ ML ensemble predictions - ✅ Asset selection with ML scores - ✅ Portfolio allocation with ML - ✅ Order generation ### Solution: Add ML to Trading Agent Service **File**: `/home/jgrusewski/Work/foxhunt/services/trading_agent_service/src/service.rs` **Step 1: Add SharedMLStrategy to struct** ```rust pub struct TradingAgentServiceImpl { db_pool: PgPool, universe_selector: UniverseSelector, strategy_coordinator: StrategyCoordinator, metrics: TradingAgentMetrics, ml_strategy: Arc, // ← ADD THIS } ``` **Step 2: Initialize in constructor** ```rust impl TradingAgentServiceImpl { pub fn new(db_pool: PgPool) -> Self { let ml_strategy = Arc::new( SharedMLStrategy::new(db_pool.clone()) .expect("Failed to initialize ML strategy") ); Self { universe_selector: UniverseSelector::new(db_pool.clone()), strategy_coordinator: StrategyCoordinator::new(db_pool.clone()), metrics: TradingAgentMetrics::new(), ml_strategy, // ← ADD THIS db_pool, } } } ``` **Step 3: Use in asset selection** ```rust async fn select_assets(&self, request: Request) -> Result, Status> { let req = request.into_inner(); // Get instruments from universe let universe = self.universe_selector.get_universe(&req.universe_id).await?; // Get ML predictions for each instrument let mut asset_scores = Vec::new(); for instrument in &universe.instruments { // Extract features let features = self.ml_strategy.extract_features(&instrument.symbol).await?; // Get ML ensemble prediction let prediction = self.ml_strategy.predict_ensemble(&features).await?; // Calculate multi-factor score let momentum = calculate_momentum_from_features(&features); let value = calculate_value_from_features(&features); let liquidity = calculate_liquidity_from_features(&features); let score = AssetScore::with_model_scores( instrument.symbol.clone(), prediction.model_scores, momentum, value, liquidity, ); asset_scores.push(score); } // Select top assets let selector = AssetSelector::with_thresholds(0.5, 0.6); let selected = selector.select_top_n(asset_scores, req.max_assets as usize); Ok(Response::new(SelectAssetsResponse { assets: selected.into_iter().map(convert_to_proto).collect(), // ... metrics })) } ``` --- ## ðŸŽŊ Feature Integration Points ### 1. Kelly Criterion Integration **Location**: `allocate_portfolio()` method **File**: `/home/jgrusewski/Work/foxhunt/services/trading_agent_service/src/allocation.rs` **Status**: ✅ Implementation exists, ❌ NOT WIRED **Integration Code**: ```rust // In allocate_portfolio(): // Step 1: Determine if Kelly is appropriate for current regime let regime = self.get_current_regime(&req.strategy_id).await?; let use_kelly = regime.regime_type == RegimeType::Trending && regime.confidence > 0.7; // Step 2: Select allocation method let allocation_method = if use_kelly { AllocationMethod::KellyCriterion { fraction: 0.25 // Quarter Kelly for safety } } else { AllocationMethod::MLOptimized }; // Step 3: Build AssetInfo with win rates and avg win/loss let asset_info: Vec = selected_assets .iter() .map(|asset| { let stats = self.get_asset_stats(&asset.symbol).await?; AssetInfo { symbol: asset.symbol.clone(), expected_return: asset.ml_score * 0.10, // Scale prediction volatility: stats.volatility, ml_score: asset.ml_score, win_rate: stats.win_rate, // ← REQUIRED for Kelly avg_win: stats.avg_win, // ← REQUIRED for Kelly avg_loss: stats.avg_loss, // ← REQUIRED for Kelly } }) .collect(); // Step 4: Run allocation let allocator = PortfolioAllocator::new(allocation_method); let allocations = allocator.allocate(&asset_info, total_capital)?; ``` **Database Query Required**: ```sql -- Get historical win/loss stats for Kelly SELECT symbol, COUNT(*) FILTER (WHERE pnl > 0) * 1.0 / COUNT(*) as win_rate, AVG(pnl) FILTER (WHERE pnl > 0) as avg_win, ABS(AVG(pnl) FILTER (WHERE pnl < 0)) as avg_loss, STDDEV(pnl) as volatility FROM positions WHERE symbol = $1 AND closed_at > NOW() - INTERVAL '30 days' GROUP BY symbol; ``` --- ### 2. Adaptive Position Sizer Integration **Location**: `allocate_portfolio()` method (post-allocation adjustment) **File**: `adaptive-strategy/src/risk/position_sizer.rs` (need to import) **Status**: ✅ Implementation exists, ❌ NOT WIRED **Integration Code**: ```rust use adaptive_strategy::risk::AdaptivePositionSizer; // In allocate_portfolio(), AFTER initial allocation: // Step 1: Get regime state let regime = self.regime_detector.detect_current_regime(&market_data).await?; // Step 2: Initialize adaptive sizer let adaptive_sizer = AdaptivePositionSizer::new( self.db_pool.clone(), AdaptivePositionSizerConfig { min_position_multiplier: 0.2, // 20% min in volatile regimes max_position_multiplier: 1.5, // 150% max in trending regimes base_volatility_target: 0.02, // 2% daily volatility target regime_adjustment_factor: 1.0, ..Default::default() } ); // Step 3: Adjust allocations based on regime let adjusted_allocations = adaptive_sizer.adjust_allocations( allocations, // Base allocations from Kelly/ML ®ime, // Current regime (Trending/Ranging/Volatile) portfolio_volatility, // Current portfolio vol ).await?; // Example adjustments: // - Trending regime + high confidence → 1.5x multiplier // - Volatile regime + low confidence → 0.2x multiplier // - Ranging regime + medium confidence → 1.0x multiplier ``` **Regime Adjustment Logic**: ```rust // From adaptive-strategy/src/risk/position_sizer.rs: fn calculate_regime_multiplier(&self, regime: &RegimeDetection) -> f64 { match regime.regime_type { RegimeType::Trending => { // Scale up in strong trends 1.0 + (regime.confidence - 0.5) * 1.0 // Range: 0.5 - 1.5 }, RegimeType::Volatile => { // Scale down in volatility 0.2 + (1.0 - regime.confidence) * 0.8 // Range: 0.2 - 1.0 }, RegimeType::Ranging => { // Neutral in ranging markets 0.8 + regime.confidence * 0.4 // Range: 0.8 - 1.2 }, _ => 1.0, } } ``` --- ### 3. Regime Detection Integration **Location**: Multiple points in decision flow **Files**: - `ml/src/regime_detection.rs` (detection engine) - Database: `regime_states`, `regime_transitions` tables (migration 045) - gRPC: `GetRegimeState`, `GetRegimeTransitions` methods **Status**: ✅ Implementation exists, ❌ NOT WIRED to Trading Agent **Integration Points**: #### Point A: Before Asset Selection ```rust // Get current regime to filter universe let regime = self.get_regime_state("MARKET").await?; match regime.regime_type { RegimeType::Trending => { // Select momentum assets universe_criteria.min_momentum_score = 0.6; }, RegimeType::Ranging => { // Select mean-reversion assets universe_criteria.max_momentum_score = 0.4; }, RegimeType::Volatile => { // Select low-beta, stable assets universe_criteria.max_volatility = 0.15; }, } ``` #### Point B: During Allocation (shown above) ```rust // Select allocation strategy based on regime let allocation_method = match regime.regime_type { RegimeType::Trending => AllocationMethod::KellyCriterion { fraction: 0.25 }, RegimeType::Ranging => AllocationMethod::RiskParity, RegimeType::Volatile => AllocationMethod::MeanVariance { lambda: 2.0 }, _ => AllocationMethod::MLOptimized, }; ``` #### Point C: After Allocation (Adaptive Sizing) ```rust // Apply regime-aware position sizing let adaptive_sizer = AdaptivePositionSizer::new(db_pool.clone()); let adjusted = adaptive_sizer.adjust_allocations( allocations, ®ime, portfolio_volatility, ).await?; ``` **Database Queries**: ```sql -- Get current regime state SELECT regime_type, confidence, volatility, trend_strength FROM regime_states WHERE symbol = $1 ORDER BY detected_at DESC LIMIT 1; -- Get recent regime transitions SELECT from_regime, to_regime, confidence_delta, duration_seconds FROM regime_transitions WHERE symbol = $1 AND transition_timestamp > NOW() - INTERVAL '24 hours' ORDER BY transition_timestamp DESC; ``` **gRPC Method** (needs implementation in Trading Agent): ```rust async fn get_regime_state( &self, request: Request, ) -> Result, Status> { let req = request.into_inner(); // Query database for latest regime let regime = sqlx::query_as!( RegimeState, r#" SELECT regime_type, confidence, volatility, trend_strength, detected_at FROM regime_states WHERE symbol = $1 ORDER BY detected_at DESC LIMIT 1 "#, req.symbol ) .fetch_one(&self.db_pool) .await .map_err(|e| Status::not_found(format!("No regime data: {}", e)))?; Ok(Response::new(GetRegimeStateResponse { regime_type: regime.regime_type, confidence: regime.confidence, volatility: regime.volatility, trend_strength: regime.trend_strength, detected_at: regime.detected_at.timestamp_nanos_opt().unwrap_or(0), })) } ``` --- ## 📊 Complete Decision Flow (RECOMMENDED) ### End-to-End Trading Decision Sequence ``` ┌──────────────────────────────────────────────────────────────────┐ │ TRADING AGENT DECISION FLOW │ │ (RECOMMENDED WIRING) │ └──────────────────────────────────────────────────────────────────┘ 1. Market Data Arrives (Every 100ms via DBN stream) │ ├─ Update feature extractors ├─ Detect regime changes └─ Trigger decision cycle (every 5 seconds) 2. Regime Detection │ ├─ Call: RegimeDetectionEngine::detect_current_regime() ├─ Query: regime_states table for current regime ├─ Analyze: CUSUM, ADX, volatility, trend strength └─ Output: RegimeDetection { type, confidence, volatility } 3. Universe Selection (✅ OPERATIONAL) │ ├─ Call: UniverseSelector::select_universe() ├─ Filter by regime-appropriate criteria: │ ├─ Trending → High momentum assets │ ├─ Ranging → Mean-reversion candidates │ └─ Volatile → Low-beta, stable assets └─ Output: Universe { instruments: Vec } 4. ML Feature Extraction (⚠ïļ NEEDS WIRING) │ ├─ For each instrument in universe: │ ├─ Call: MLFeatureExtractor::extract_features() │ ├─ Wave A: 26 features (technical indicators) │ ├─ Wave C: 201 features (advanced) │ └─ Wave D: 225 features (+ regime detection) └─ Output: HashMap> 5. ML Ensemble Prediction (⚠ïļ NEEDS WIRING) │ ├─ For each instrument: │ ├─ Call: SharedMLStrategy::predict_ensemble() │ ├─ DQN prediction (200Ξs) │ ├─ PPO prediction (324Ξs) │ ├─ MAMBA-2 prediction (500Ξs) │ ├─ TFT prediction (3.2ms) │ └─ Weighted ensemble vote └─ Output: HashMap 6. Asset Selection (⚠ïļ NEEDS WIRING) │ ├─ Call: AssetSelector::select_top_n() ├─ Multi-factor scoring: │ ├─ ML score: 40% weight │ ├─ Momentum: 30% weight │ ├─ Value: 20% weight │ └─ Liquidity: 10% weight ├─ Filter: min_ml_confidence = 0.5, min_composite = 0.6 └─ Output: Vec (top 5-10 assets) 7. Portfolio Allocation (⚠ïļ NEEDS WIRING) │ ├─ Select allocation method based on regime: │ ├─ Trending + high confidence → Kelly Criterion (0.25 fraction) │ ├─ Ranging → Risk Parity │ ├─ Volatile → Mean-Variance (Îŧ=2.0) │ └─ Default → ML-Optimized │ ├─ Call: PortfolioAllocator::allocate() │ ├─ Build AssetInfo (with win_rate, avg_win, avg_loss for Kelly) │ ├─ Run allocation algorithm │ └─ Clamp individual positions to 20% max │ └─ Output: HashMap (capital allocations) 8. Adaptive Position Sizing (⚠ïļ NEEDS WIRING) │ ├─ Call: AdaptivePositionSizer::adjust_allocations() ├─ Apply regime multipliers: │ ├─ Trending → 1.0 - 1.5x │ ├─ Ranging → 0.8 - 1.2x │ └─ Volatile → 0.2 - 1.0x ├─ Volatility scaling (target: 2% daily vol) └─ Output: HashMap (adjusted allocations) 9. Order Generation (⚠ïļ NEEDS WIRING) │ ├─ Call: OrderGenerator::generate_orders() ├─ Get current positions from database ├─ Calculate deltas (target - current) ├─ Filter by rebalance threshold (5%) ├─ Validate order sizes (min: $100, max: $100k) ├─ Convert dollar amounts → contract quantities └─ Output: Vec 10. Risk Validation │ ├─ For each order: │ ├─ Check position limits (max 20% per asset) │ ├─ Check portfolio leverage (<2.0x) │ ├─ Check VaR (95% < $10k daily) │ └─ Check circuit breakers └─ Output: Vec (validated) 11. Order Submission (⚠ïļ NEEDS WIRING) │ ├─ Connect to Trading Service (gRPC: localhost:50052) ├─ For each order: │ ├─ Call: TradingService::SubmitOrder() │ ├─ Await confirmation │ └─ Update agent_orders table └─ Output: Vec 12. Performance Tracking │ ├─ Store ML predictions → ml_predictions table ├─ Store allocations → portfolio_allocations table ├─ Store orders → agent_orders table ├─ Update Prometheus metrics └─ Monitor regime transitions ``` --- ## 🛠ïļ Implementation Roadmap ### Phase 1: Core ML Integration (2-3 hours) 1. Add `SharedMLStrategy` to `TradingAgentServiceImpl` 2. Wire ML predictions into `select_assets()` 3. Test with 26-feature models (Wave A) ### Phase 2: Asset Selection (1-2 hours) 1. Implement `select_assets()` using `AssetSelector` 2. Connect multi-factor scoring 3. Add database persistence ### Phase 3: Portfolio Allocation (2-3 hours) 1. Implement `allocate_portfolio()` using `PortfolioAllocator` 2. Wire Kelly Criterion for trending regimes 3. Add regime-based strategy selection 4. Test with real capital constraints ### Phase 4: Adaptive Position Sizing (2-3 hours) 1. Import `AdaptivePositionSizer` from adaptive-strategy crate 2. Wire regime multipliers 3. Add volatility scaling 4. Test position size adjustments ### Phase 5: Regime Integration (1-2 hours) 1. Add `get_regime_state()` gRPC method 2. Query regime_states table 3. Wire regime detection into asset selection 4. Wire regime detection into allocation ### Phase 6: Order Generation (2-3 hours) 1. Implement `generate_orders()` using `OrderGenerator` 2. Add delta calculation logic 3. Wire rebalance threshold checks 4. Add database persistence ### Phase 7: Order Submission (1-2 hours) 1. Implement `submit_agent_orders()` 2. Add gRPC client to Trading Service 3. Handle submission results 4. Update agent_orders table ### Phase 8: End-to-End Testing (3-4 hours) 1. Integration test: Market data → Orders 2. Validate Kelly Criterion behavior 3. Validate Adaptive Position Sizing 4. Validate Regime Detection impact 5. Load test with 100 concurrent requests **Total Estimated Time**: 14-22 hours --- ## 📝 Database Schema Requirements ### Existing Tables (✅ READY) - `asset_universe`: Universe definitions - `universe_instruments`: Instrument mappings - `strategies`: Strategy configurations - `regime_states`: Current regime data (Wave D) - `regime_transitions`: Regime changes (Wave D) - `adaptive_strategy_metrics`: Performance tracking (Wave D) - `ml_predictions`: ML prediction history (Trading Service) - `agent_orders`: Order history ### New Tables Needed (❌ MISSING) ```sql -- Asset selection history CREATE TABLE asset_selections ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), universe_id TEXT NOT NULL, strategy_id TEXT NOT NULL, selected_symbols TEXT[] NOT NULL, selection_scores JSONB NOT NULL, -- {symbol: {ml, momentum, value, liquidity}} selection_timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(), regime_type TEXT, regime_confidence DOUBLE PRECISION ); -- Portfolio allocation history CREATE TABLE portfolio_allocations ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), allocation_id TEXT NOT NULL UNIQUE, strategy_id TEXT NOT NULL, total_capital NUMERIC(20, 2) NOT NULL, allocations JSONB NOT NULL, -- {symbol: capital_amount} allocation_method TEXT NOT NULL, -- "Kelly", "RiskParity", "MLOptimized" regime_type TEXT, regime_multiplier DOUBLE PRECISION, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- Asset statistics for Kelly Criterion CREATE TABLE asset_statistics ( symbol TEXT PRIMARY KEY, win_rate DOUBLE PRECISION NOT NULL, avg_win DOUBLE PRECISION NOT NULL, avg_loss DOUBLE PRECISION NOT NULL, volatility DOUBLE PRECISION NOT NULL, last_updated TIMESTAMPTZ NOT NULL DEFAULT NOW() ); ``` --- ## ðŸŽŊ Key Insertion Points Summary ### 1. Kelly Criterion - **Location**: `allocate_portfolio()` → `PortfolioAllocator::new(AllocationMethod::KellyCriterion)` - **Trigger**: Trending regime + high confidence (>0.7) - **Data Required**: `win_rate`, `avg_win`, `avg_loss` from asset_statistics table - **Clamping**: 0.25 fractional Kelly, max 20% per asset ### 2. Adaptive Position Sizer - **Location**: `allocate_portfolio()` → Post-allocation adjustment - **Component**: `AdaptivePositionSizer::adjust_allocations()` - **Input**: Base allocations + regime + portfolio_volatility - **Output**: Scaled allocations (0.2x - 1.5x multiplier) ### 3. Regime Detection - **Location A**: `select_assets()` → Filter universe by regime - **Location B**: `allocate_portfolio()` → Select allocation method - **Location C**: `allocate_portfolio()` → Apply regime multipliers - **Data Source**: `regime_states` table + gRPC `GetRegimeState()` --- ## ✅ Action Items ### Immediate (Next Session) 1. ✅ **WIRE-12**: Implement `select_assets()` with ML predictions 2. ✅ **WIRE-13**: Implement `allocate_portfolio()` with Kelly Criterion 3. ✅ **WIRE-14**: Integrate Adaptive Position Sizer 4. ✅ **WIRE-15**: Integrate Regime Detection ### Short-Term (This Week) 5. ✅ **WIRE-16**: Implement `generate_orders()` with OrderGenerator 6. ✅ **WIRE-17**: Implement `submit_agent_orders()` with Trading Service 7. ✅ **WIRE-18**: Add missing database tables 8. ✅ **WIRE-19**: End-to-end integration test ### Medium-Term (Next Week) 9. ✅ **WIRE-20**: Load testing (100 concurrent decisions) 10. ✅ **WIRE-21**: Performance optimization (<5s decision loop) 11. ✅ **WIRE-22**: Production monitoring setup 12. ✅ **WIRE-23**: Documentation updates --- ## 📚 Reference Files ### Core Implementation Files - **Trading Agent Service**: `/home/jgrusewski/Work/foxhunt/services/trading_agent_service/src/service.rs` - **Asset Selection**: `/home/jgrusewski/Work/foxhunt/services/trading_agent_service/src/assets.rs` - **Portfolio Allocation**: `/home/jgrusewski/Work/foxhunt/services/trading_agent_service/src/allocation.rs` - **Order Generation**: `/home/jgrusewski/Work/foxhunt/services/trading_agent_service/src/orders.rs` ### ML Infrastructure - **Shared ML Strategy**: `/home/jgrusewski/Work/foxhunt/common/src/ml_strategy.rs` - **Feature Extractor**: `/home/jgrusewski/Work/foxhunt/common/src/ml_strategy.rs` (MLFeatureExtractor) - **Kelly Criterion**: `/home/jgrusewski/Work/foxhunt/ml/src/risk/kelly_optimizer.rs` - **Adaptive Sizer**: `/home/jgrusewski/Work/foxhunt/adaptive-strategy/src/risk/position_sizer.rs` - **Regime Detection**: `/home/jgrusewski/Work/foxhunt/ml/src/regime_detection.rs` ### Database - **Migration 045**: `/home/jgrusewski/Work/foxhunt/migrations/045_regime_detection.sql` - **Tables**: regime_states, regime_transitions, adaptive_strategy_metrics ### Proto Definitions - **Trading Agent**: `/home/jgrusewski/Work/foxhunt/services/trading_agent_service/proto/trading_agent.proto` --- ## 🎉 Conclusion **Status**: Decision flow fully traced and documented. **Critical Finding**: Trading Agent Service has complete implementation of allocation algorithms (including Kelly Criterion) but they are NOT connected to the gRPC API. All methods return placeholder empty results. **Next Steps**: 1. Wire ML predictions into asset selection 2. Wire Kelly Criterion into portfolio allocation 3. Wire Adaptive Position Sizer for regime-aware scaling 4. Wire Regime Detection into all decision points 5. Connect to Trading Service for order execution **Estimated Completion**: 14-22 hours for full integration --- **AGENT WIRE-11: MISSION COMPLETE** Decision flow mapped. Integration points identified. Ready for implementation.