# Implementation Plan: Autonomous Trading Engine ## Overview This plan implements a fully autonomous trading engine as a new service (`services/trading/`) that consumes recommendations from the existing three-layer signal aggregation pipeline, applies confidence-based position sizing with reserve pool management, enforces dynamic stop-loss/take-profit levels, manages circuit breakers, and submits orders through the existing Broker Service queue. The implementation extends existing services (broker_service, recommendation, risk engine, query API, dashboard) without replacing them. Tasks are ordered so each step builds on the previous, with property-based tests validating core computation logic early. ## Tasks - [x] 1. Database migration and shared infrastructure - [x] 1.1 Create PostgreSQL migration `infra/migrations/018_autonomous_trading_engine.sql` - Add `trading_engine_config` table with all configuration fields (enabled, paused, risk_tier, reserve_siphon_pct, polling intervals, gradual entry params, circuit breaker thresholds, active pool minimum, emergency drawdown threshold, correlation thresholds, earnings windows, micro-trading params, notification settings, timestamps) - Add `reserve_pool_ledger` table with amount, balance_after, trigger_type (profit_siphon, emergency_liquidation, manual_adjustment, initial), reference_id, notes, created_at; index on created_at DESC - Add `risk_tier_history` table with previous_tier, new_tier, trigger_source, trigger_metrics JSONB, created_at; index on created_at DESC - Add `circuit_breaker_events` table with trigger_type (daily_loss, single_position, volatility, manual), threshold_value, actual_value, ticker, cooldown_expires, resolved_at, created_at; partial index on active (unresolved) events - Add `trading_decisions` table with recommendation_id FK, decision, skip_reason, ticker, computed_position_size, computed_share_quantity, risk_tier_at_decision, portfolio_heat_at_decision, active_pool_at_decision, reserve_pool_at_decision, circuit_breaker_status, correlation_check_result JSONB, sector_exposure_check_result JSONB, earnings_proximity_flag, is_micro_trade, decision_trace JSONB, created_at; indexes on ticker, recommendation_id, decision - Add `position_stop_levels` table with ticker, entry_price, stop_loss_price, take_profit_price, trailing_stop_active, atr_value, atr_multiplier, reward_risk_ratio, signal_confidence, is_micro_trade, active, timestamps; partial index on active positions - Add `portfolio_snapshots` table with snapshot_date (UNIQUE), portfolio_value, active_pool, reserve_pool, daily_return, cumulative_return, unrealized_pnl, realized_pnl, win/loss counts, win_rate, sharpe_ratio, max_drawdown, current_drawdown_pct, portfolio_heat, risk_tier, positions JSONB, metrics JSONB, created_at; index on snapshot_date DESC - Add `backtest_runs` table with start_date, end_date, initial_capital, risk_tier, config JSONB, result metrics, equity_curve JSONB, status, completed_at, created_at - Add `backtest_trades` table with backtest_id FK (CASCADE), ticker, side, entry/exit prices, quantity, pnl, dates, hold_duration_days, recommendation_id; index on backtest_id - Add `tax_lots` table with ticker, quantity, cost_basis_per_share, acquisition_date, status (open/closed/washed), closed_date, exit_price, realized_pnl, wash_sale_flag, wash_sale_details, order_id FK; indexes on ticker and open status - Add `earnings_calendar` table with ticker, earnings_date, source, confirmed, timestamps; UNIQUE on (ticker, earnings_date); indexes on date and ticker - Add `correlation_matrix_cache` table with ticker_a, ticker_b, correlation_coefficient, lookback_days, computed_at; UNIQUE on (ticker_a, ticker_b) - Add `notifications` table with channel (sms/email), event_type, message, delivery_status (pending/delivered/failed/rate_limited), retry_count, error_message, created_at, delivered_at; indexes on created_at and event_type - Insert default trading_engine_config row with moderate tier defaults - Insert initial reserve_pool_ledger entry with balance 0.0 and trigger_type 'initial' - _Requirements: 18.1, 18.2, 18.3, 18.4, 16.1_ - [x] 1.2 Add new Pydantic schemas and enums to `services/shared/schemas.py` - Add `TradingDecisionType` enum (act, skip) - Add `CircuitBreakerTriggerType` enum (daily_loss, single_position, volatility, manual) - Add `ReservePoolTriggerType` enum (profit_siphon, emergency_liquidation, manual_adjustment, initial) - Add `NotificationChannel` enum (sms, email) - Add `RiskTierName` enum (conservative, moderate, aggressive) - _Requirements: 5.1, 6.1, 3.1, 19.1_ - [x] 1.3 Add trading-related Redis keys to `services/shared/redis_keys.py` - Add `QUEUE_TRADING_DECISIONS = "trading_decisions"` queue name - Add `TRADING_DEDUPE_PREFIX` for recommendation deduplication (`stonks:dedupe:trading`) - Add `TRADING_CB_PREFIX` for circuit breaker state (`stonks:trading:circuit_breaker`) - Add `TRADING_NOTIFICATION_RATE` for notification rate limiting (`stonks:trading:notification_rate`) - _Requirements: 1.5, 6.4, 19.7_ - [x] 1.4 Add `TradingConfig` dataclass to `services/shared/config.py` - Add `TradingConfig` with fields: enabled, risk_tier, reserve_siphon_pct, polling_interval_seconds, stop_loss_check_interval_seconds, fast_stop_loss_interval_seconds, gradual_entry_tranches, gradual_entry_threshold_dollars, absolute_position_cap, active_pool_minimum, emergency_drawdown_threshold_pct, reserve_high_water_pct, micro_trading_enabled, micro_trading_interval_seconds, micro_trading_allocation_cap_pct, micro_trading_max_daily, micro_trading_max_hold_minutes, sns_topic_arn, sns_phone_number, gmail_sender, gmail_recipient - Add `trading: TradingConfig` field to `AppConfig` with env var loading in `load_config()` - _Requirements: 16.1, 20.1, 19.5, 19.6_ - [x] 2. Checkpoint — Ensure migration and shared schemas are consistent - Ensure all tests pass, ask the user if questions arise. - [x] 3. Core data models and risk tier configuration - [x] 3.1 Create `services/trading/__init__.py` and `services/trading/models.py` - Create the `services/trading/` package directory - Define `RiskTierConfig` dataclass with fields: name, min_confidence, max_position_pct, stop_loss_atr_multiplier, reward_risk_ratio, max_sector_pct, max_portfolio_heat - Define `RISK_TIER_DEFAULTS` dict mapping conservative/moderate/aggressive to their default `RiskTierConfig` instances per the design specification - Define `PortfolioState` dataclass with fields: positions (list), total_value, cash, active_pool, reserve_pool, sector_exposure (dict), portfolio_heat, open_position_count - Define `TradingDecision` dataclass with all fields matching the `trading_decisions` table schema - Define `PositionSizeResult` dataclass with dollar_amount, share_quantity, allocation_pct, adjustments list, rejected flag, rejection_reason - Define `StopLevels` dataclass with stop_loss_price, take_profit_price, trailing_stop_active, atr_value, atr_multiplier, reward_risk_ratio, last_updated - Define `OpenPosition` dataclass with ticker, quantity, entry_price, current_price, unrealized_pnl, market_value, sector, stop_loss_price, take_profit_price, signal_confidence, is_micro_trade - Define `ClosedTrade` dataclass with ticker, entry_price, exit_price, quantity, pnl, pnl_pct, hold_duration, recommendation_id, is_micro_trade - Define `PerformanceMetrics` dataclass with all fields from the design (total_portfolio_value through computed_at) - Define `CircuitBreakerState` dataclass with active, trigger_type, triggered_at, cooldown_expires, ticker_cooldowns dict - Define `ReservePoolState` dataclass with balance, total_deposits, total_withdrawals, last_updated - Define `StopTrigger` dataclass with ticker, trigger_type (stop_loss/take_profit), current_price, trigger_price - _Requirements: 5.1, 1.2, 2.1, 4.1, 6.1, 3.1, 13.1, 14.1_ - [x] 3.2 Write property test for risk tier default parameters - **Property 29 (partial): Persistence round-trip for risk tier configs** - Verify all three tier defaults have valid parameter ranges (min_confidence in [0,1], max_position_pct in (0,1], etc.) - Verify conservative < moderate < aggressive for min_confidence thresholds (inverse) and position limits - **Validates: Requirements 5.1** - [x] 4. Position Sizer implementation - [x] 4.1 Implement `services/trading/position_sizer.py` - Implement `PositionSizer` class with `compute()` method accepting confidence, ticker, sector, current_price, active_pool, risk_tier, portfolio_state, correlation_matrix, earnings_calendar - Implement sizing formula: `raw_pct = base_allocation_pct * (confidence / min_confidence) * multiplier`, clamped to max_position_pct, then dollar_amount = active_pool * clamped_pct, clamped to absolute_position_cap - Implement confidence gate: reject when confidence < risk_tier.min_confidence - Implement correlation reduction: compute weighted average correlation with existing positions; reduce proportionally when avg > 0.5; reject entirely when avg > 0.8 - Implement sector exposure reduction: reduce allocation if adding position would push sector above max_sector_pct - Implement diversification bonus: 1.2x multiplier for under-represented sectors when portfolio holds < 3 sectors - Implement earnings proximity: reduce by 50% within 3 trading days; reject within 1 trading day - Implement portfolio heat check: reject if current heat + new position heat exceeds max_portfolio_heat - Implement active pool minimum: reject new entries when Active Pool < configured minimum ($100 default) - Implement absolute cap enforcement and share rounding (round down to whole shares, reject if quantity = 0) - _Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 9.2, 9.3, 9.4, 9.5, 10.2, 10.3, 13.1, 13.2, 3.5_ - [x] 4.2 Write property test for position sizing formula and invariants - **Property 1: Position sizing formula and invariants** - Generate random confidence values, Active Pool balances, stock prices, and RiskTierConfig objects - Verify zero allocation when confidence < min_confidence - Verify allocation never exceeds max_position_pct or absolute_position_cap - Verify share quantity is rounded down to whole shares - Verify rejection when rounded quantity is zero - **Validates: Requirements 2.1, 2.2, 2.3, 2.4, 2.7** - [x] 4.3 Write property test for correlation-based allocation adjustment - **Property 2: Correlation-based allocation adjustment** - Generate random correlation matrices and portfolio positions - Verify allocation reduced when weighted avg correlation > 0.5 - Verify trade rejected when weighted avg correlation > 0.8 - Verify allocation unchanged when weighted avg correlation <= 0.5 - Verify monotonic non-increase: higher correlation → lower or equal allocation - **Validates: Requirements 2.5, 9.2, 9.3** - [x] 4.4 Write property test for sector exposure enforcement - **Property 3: Sector exposure computation and enforcement** - Generate random portfolios with sector labels - Verify sector exposure equals sum of market values per sector - Verify allocation reduced when adding position would exceed max_sector_pct - **Validates: Requirements 2.6, 9.4** - [x] 4.5 Write property test for diversification bonus - **Property 4: Diversification bonus for under-represented sectors** - Generate portfolios with varying sector counts - Verify 1.2x bonus applied when portfolio has < 3 sectors and trade is in new sector - Verify no bonus when portfolio has >= 3 sectors - **Validates: Requirements 9.5** - [x] 4.6 Write property test for Active Pool computation - **Property 5: Active Pool computation invariant** - Generate random total_portfolio_value and reserve_pool_balance - Verify Active Pool = total_portfolio_value - reserve_pool_balance - **Validates: Requirements 3.3** - [x] 4.7 Write property test for earnings proximity adjustments - **Property 19: Earnings proximity adjustments** - Generate random earnings dates relative to current date - Verify 50% reduction within 3 trading days - Verify rejection within 1 trading day - Verify normal sizing outside earnings window - **Validates: Requirements 10.2, 10.3** - [x] 4.8 Write property test for portfolio heat computation and enforcement - **Property 24: Portfolio heat computation and threshold enforcement** - Generate random open positions with entry prices and stop-loss levels - Verify heat = sum of position_value * (entry_price - stop_loss_price) / entry_price - Verify new entries rejected when heat exceeds max_portfolio_heat - **Validates: Requirements 13.1, 13.2** - [x] 4.9 Write property test for Active Pool minimum halts entries - **Property 7: Active Pool minimum halts new entries but allows exits** - Generate portfolio states with Active Pool below and above minimum - Verify buy orders rejected when Active Pool < minimum - Verify sell orders allowed regardless of Active Pool - **Validates: Requirements 3.5** - [x] 5. Checkpoint — Ensure position sizer logic and property tests pass - Ensure all tests pass, ask the user if questions arise. - [x] 6. Stop-Loss Manager implementation - [x] 6.1 Implement `services/trading/stop_loss_manager.py` - Implement `StopLossManager` class with `compute_initial_levels()` method: stop_loss = entry_price - (ATR * stop_loss_atr_multiplier), take_profit = entry_price + (stop_distance * reward_risk_ratio) - Implement `re_evaluate_levels()` method: adjust if ATR changed > 10% or signal conditions changed; respect configurable interval (default 5 min) - Implement `check_price_crossings()` method: return list of StopTrigger for positions where current price <= stop_loss or >= take_profit - Implement trailing stop logic: when price moves favorably by > 50% of take-profit distance, move stop-loss to entry price (breakeven) - Implement earnings proximity tightening: 0.7x ATR multiplier when earnings within 3 trading days - Implement high-severity event tightening: 0.5x normal ATR multiplier during active macro events - Implement proactive heat tightening: tighten stops on lowest-confidence positions when heat > 80% of max - Implement price data unavailability safety: close position if price unavailable > 15 minutes during market hours - Persist all stop levels and adjustments to `position_stop_levels` table - _Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 7.2, 10.2, 13.3_ - [x] 6.2 Write property test for stop-loss and take-profit initial computation - **Property 9: Stop-loss and take-profit initial computation** - Generate random entry prices, ATR values, and RiskTierConfig objects - Verify stop-loss = entry_price - (ATR * multiplier) and always below entry - Verify take-profit = entry_price + (stop_distance * reward_risk_ratio) and always above entry - **Validates: Requirements 4.1, 4.2** - [x] 6.3 Write property test for price crossing triggers - **Property 10: Price crossing triggers immediate sell** - Generate random positions with stop/take-profit levels and current prices - Verify sell triggered when price <= stop_loss or >= take_profit - Verify no trigger when price is between stop_loss and take_profit - **Validates: Requirements 4.4, 4.5** - [x] 6.4 Write property test for trailing stop activation - **Property 11: Trailing stop activation at 50% of take-profit distance** - Generate random positions with varying favorable price moves - Verify trailing stop activates (stop moves to entry) when move > 50% of TP distance - Verify trailing stop does not activate when move <= 50% - **Validates: Requirements 4.6** - [x] 6.5 Write property test for stop tightening during high-severity events - **Property 15: Stop tightening during high-severity events** - Generate random positions and ATR values - Verify tightened stop uses 0.5x normal multiplier - Verify tightened stop is closer to current price than normal stop - **Validates: Requirements 7.2** - [x] 6.6 Write property test for proactive stop tightening at 80% heat - **Property 25: Proactive stop tightening at 80% heat threshold** - Generate portfolios with heat near the threshold - Verify lowest-confidence positions get stops tightened first - **Validates: Requirements 13.3** - [x] 7. Reserve Pool Controller implementation - [x] 7.1 Implement `services/trading/reserve_pool.py` - Implement `ReservePoolController` class with `siphon_profit()` method: transfer configured percentage of realized profit to reserve, persist to `reserve_pool_ledger` - Implement `emergency_liquidate()` method: release entire reserve into active pool, log event, persist to ledger - Implement `compute_active_pool()` method: total_portfolio_value - reserve_pool_balance - Implement `get_state()` method: load current balance and history from PostgreSQL - Implement high-water mark detection: signal when reserve > 30% of total portfolio - _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7_ - [x] 7.2 Write property test for reserve pool siphon computation - **Property 6: Reserve pool siphon computation** - Generate random realized profit amounts and siphon percentages - Verify transferred amount = realized_profit * siphon_pct - Verify balance_after = previous_balance + transferred_amount - **Validates: Requirements 3.1, 3.2** - [x] 7.3 Write property test for emergency drawdown triggers reserve liquidation - **Property 8: Emergency drawdown triggers reserve liquidation** - Generate portfolio states with drawdowns above and below emergency threshold - Verify reserve liquidated into active pool when drawdown exceeds threshold - Verify risk tier set to conservative after emergency liquidation - **Validates: Requirements 3.6** - [x] 8. Circuit Breaker implementation - [x] 8.1 Implement `services/trading/circuit_breaker.py` - Implement `CircuitBreaker` class with `check_daily_loss()`: activate when portfolio drops > configured daily_loss_pct - Implement `check_single_position()`: close position and apply ticker cooldown when loss > configured single_position_loss_pct - Implement `check_volatility()`: pause trading when 3+ positions hit stop-losses within 30-minute window - Implement `is_ticker_cooled_down()`: check per-ticker re-entry cooldowns - Implement `is_active()`: return whether any circuit breaker is currently active - Implement cooldown expiry: auto-resolve when current time > triggered_at + cooldown_duration - Persist all circuit breaker events to `circuit_breaker_events` table - Store active state in Redis for fast lookup - _Requirements: 6.1, 6.2, 6.3, 6.4, 6.5, 6.6_ - [x] 8.2 Write property test for circuit breaker activation - **Property 13: Circuit breaker activation** - Generate random portfolio states with varying daily losses, position losses, and stop-loss hit sequences - Verify daily_loss trigger when loss > threshold - Verify single_position trigger and ticker cooldown when position loss > threshold - Verify volatility trigger when 3+ stop-losses within 30 minutes - Verify all new orders rejected when any circuit breaker is active - **Validates: Requirements 6.1, 6.2, 6.3** - [x] 8.3 Write property test for circuit breaker cooldown expiry - **Property 14: Circuit breaker cooldown expiry** - Generate circuit breaker events with varying cooldown durations and current times - Verify transition from active to resolved when time > triggered_at + cooldown - Verify remains active before expiry - **Validates: Requirements 6.5** - [x] 9. Risk Tier Controller implementation - [x] 9.1 Implement `services/trading/risk_tier_controller.py` - Implement `RiskTierController` class with `evaluate()` method accepting PerformanceMetrics and reserve_pct - Implement downgrade logic: downgrade one level when trailing 30-day win rate < 40% OR current drawdown > 15% - Implement upgrade logic: upgrade one level when win rate > 55% AND reserve > 20% of total AND drawdown < 5% - Implement tier bounds: never go below conservative or above aggressive - Persist tier changes to `risk_tier_history` table with previous tier, new tier, and trigger metrics - _Requirements: 5.2, 5.3, 5.4, 5.5, 5.6_ - [x] 9.2 Write property test for risk tier auto-adjustment conditions - **Property 12: Risk tier auto-adjustment conditions** - Generate random performance metrics (win rate, drawdown, reserve percentage) - Verify downgrade when win rate < 40% OR drawdown > 15% - Verify upgrade when win rate > 55% AND reserve > 20% AND drawdown < 5% - Verify no change when neither condition met - Verify tier never goes below conservative or above aggressive - **Validates: Requirements 5.3, 5.4** - [x] 10. Checkpoint — Ensure core components and property tests pass - Ensure all tests pass, ask the user if questions arise. - [x] 11. Correlation Matrix and Tax Lot Tracker - [x] 11.1 Implement `services/trading/correlation.py` - Implement `CorrelationMatrix` class with `refresh()` method: compute trailing 90-day price correlations from market data tables, persist to `correlation_matrix_cache` table - Implement `get_correlation()` method: return coefficient for a ticker pair, 0.0 if unknown - Implement `get_portfolio_correlation()` method: weighted average correlation between candidate and existing positions - Cache in-memory after refresh; schedule daily refresh - _Requirements: 9.1, 9.2, 9.3_ - [x] 11.2 Implement `services/trading/tax_lots.py` - Implement `TaxLotTracker` class with `record_entry()` method: create tax lot record in PostgreSQL - Implement `close_lots_fifo()` method: close lots in FIFO order, compute realized P&L per lot - Implement `check_wash_sale()` method: check 30-day window before and after for same-ticker purchases - Persist wash sale flags and details to tax_lots table - _Requirements: 12.1, 12.2, 12.3, 12.4_ - [x] 11.3 Write property test for tax lot FIFO ordering - **Property 22: Tax lot FIFO ordering** - Generate random sequences of buy/sell transactions for the same ticker - Verify lots closed in FIFO order (earliest acquired first) - Verify realized P&L = (exit_price - cost_basis_per_share) * quantity per lot - **Validates: Requirements 12.4** - [x] 11.4 Write property test for wash sale detection - **Property 23: Wash sale detection within 30-day window** - Generate random loss-closing dates and purchase dates - Verify wash sale flagged when same ticker purchased within 30 days before or after loss - Verify no flag when purchases are outside the 30-day window - **Validates: Requirements 12.2, 12.3** - [x] 12. Trading Window and Gradual Entry logic - [x] 12.1 Implement `services/trading/trading_window.py` - Implement `is_within_trading_window()` function: return True if timestamp is between 9:45 AM ET and 3:45 PM ET on a market day - Implement `next_window_open()` function: return the next timestamp when the trading window opens - Implement `is_market_open()` function: check if current time is during US market hours (9:30 AM - 4:00 PM ET) - _Requirements: 11.1, 11.2_ - [x] 12.2 Implement gradual entry logic in `services/trading/gradual_entry.py` - Implement `should_use_gradual_entry()`: return True when position size exceeds min($30, 5% of Active Pool) - Implement `split_into_tranches()`: split order into configured number of tranches (default 3) of approximately equal size - Implement `GradualEntryManager` class to track pending tranches, re-evaluate before each submission, cancel remaining if conditions deteriorate - Link all tranches to the same parent trading decision ID - _Requirements: 11.3, 11.4, 11.5_ - [x] 12.3 Write property test for trading window determination - **Property 20: Trading window determination** - Generate random timestamps across US market hours - Verify within-window classification for 9:45 AM - 3:45 PM ET - Verify outside-window classification for all other times - **Validates: Requirements 11.1** - [x] 12.4 Write property test for gradual entry tranche splitting - **Property 21: Gradual entry tranche splitting** - Generate random position sizes above and below the threshold - Verify splitting into configured number of tranches when above threshold - Verify all tranches reference the same parent decision ID - Verify tranche sizes are approximately equal - **Validates: Requirements 11.3, 11.5** - [x] 13. Autonomous Decision Loop (core engine) - [x] 13.1 Implement `services/trading/engine.py` - Implement `TradingEngine` class with `__init__()` accepting asyncpg.Pool, aioredis.Redis, and TradingEngineConfig - Implement `start()` method: load portfolio state from Broker Service (positions, account balance), load active risk tier from PostgreSQL, load reserve pool balance, load circuit breaker status, load open stop levels, enter decision loop - Implement `stop()` method: graceful shutdown — cancel pending tranches, persist state - Implement `decision_loop()` method: poll recommendations at configured interval, evaluate each, size positions, submit orders - Implement `poll_recommendations()` method: fetch from `recommendations` table where action IN (buy, sell) AND mode IN (paper_eligible, live_eligible) AND generated_at > last_poll_timestamp, ordered by confidence DESC - Implement recommendation deduplication: check Redis key `stonks:dedupe:trading:{recommendation_id}` with 24h TTL, mark before evaluation - Implement `evaluate_recommendation()` method: run all pre-trade checks (circuit breaker, trading window, risk tier confidence, portfolio heat, sector exposure, correlation, earnings proximity) and produce a TradingDecision record - Implement `execute_decision()` method: generate order job payload matching existing broker queue schema, push to `stonks:queue:broker_orders`, handle gradual entry for large positions - Persist every decision (act or skip) to `trading_decisions` table with full reasoning chain - Implement adaptive market response: trigger immediate re-evaluation on high-severity macro events, tighten stops during events, increase polling frequency - Implement rapid price move detection: re-evaluate position when price moves > 5% in 15 minutes - Implement multiple declining positions halt: stop new entries when > 50% of positions have > 2% negative unrealized P&L - _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 7.1, 7.2, 7.3, 7.4, 7.5, 17.1, 17.2_ - [x] 13.2 Write property test for recommendation deduplication - **Property 27: Recommendation deduplication (idempotence)** - Generate random recommendation IDs and process them twice - Verify second processing is a no-op (no new decision record, no order submitted) - **Validates: Requirements 1.5** - [x] 13.3 Write property test for trading decision record completeness - **Property 28: Trading decision record completeness and traceability** - Generate random recommendations and evaluate them - Verify all required fields present in the persisted decision record - Verify "act" decisions include order job with trading_decision_id - **Validates: Requirements 1.4, 17.1, 17.2** - [x] 13.4 Write property test for multiple declining positions halts entries - **Property 16: Multiple declining positions halts new entries** - Generate portfolio states with varying percentages of declining positions - Verify new entries halted when > 50% of positions have > 2% negative unrealized P&L - Verify entries allowed when <= 50% are declining - **Validates: Requirements 7.5** - [x] 13.5 Write property test for maximum open positions enforcement - **Property 18: Maximum open positions enforcement** - Generate portfolio states at and below the max position limit - Verify new entries rejected at the limit - Verify portfolio never exceeds the configured maximum - **Validates: Requirements 8.4** - [x] 14. Checkpoint — Ensure decision loop and core engine tests pass - Ensure all tests pass, ask the user if questions arise. - [x] 15. Portfolio Rebalancer and Performance Tracker - [x] 15.1 Implement `services/trading/rebalancer.py` - Implement `PortfolioRebalancer` class with `evaluate()` method accepting positions, risk_tier, and active_pool - Generate partial sell orders when single stock exceeds max_position_pct - Generate sell orders for lowest-confidence positions when sector exceeds max_sector_pct - Enforce maximum open positions limit (default 10) - Submit rebalancing orders through normal broker queue with `rebalance` tag in decision trace - Respect trading window and circuit breaker status - Schedule: weekly at market open on Monday (configurable) - _Requirements: 8.1, 8.2, 8.3, 8.4, 8.5, 8.6_ - [x] 15.2 Write property test for portfolio rebalancing sell orders - **Property 17: Portfolio rebalancing generates correct sell orders** - Generate portfolios with over-concentrated positions and sectors - Verify sell orders generated to bring positions within limits - Verify lowest-confidence positions targeted first for sector rebalancing - **Validates: Requirements 8.2, 8.3** - [x] 15.3 Implement `services/trading/performance_tracker.py` - Implement `PerformanceTracker` class with `compute_metrics()` method: compute all PerformanceMetrics fields (total portfolio value, active/reserve pool, unrealized/realized P&L, daily P&L, win/loss counts, win rate, avg win/loss, profit factor, Sharpe ratio, max drawdown, current drawdown, portfolio heat) - Implement Sharpe ratio: `(mean_daily_return / std_daily_return) * sqrt(252)` using trailing 30-day daily returns - Implement `record_trade()` method: persist per-trade metrics (entry/exit price, hold duration, P&L, recommendation ID) - Implement `persist_daily_snapshot()` method: save end-of-day snapshot to `portfolio_snapshots` table - Compute metrics every 5 minutes during market hours - Track micro-trade metrics separately from standard trade metrics - _Requirements: 14.1, 14.2, 14.3, 20.7_ - [x] 15.4 Write property test for performance metrics computation - **Property 26: Performance metrics computation** - Generate random sets of closed trades with entry/exit prices and hold durations - Verify win_rate = wins / total_trades - Verify profit_factor = gross_profits / gross_losses (infinity if no losses) - Verify Sharpe ratio formula consistency - **Validates: Requirements 14.1, 14.2** - [x] 15.5 Write property test for micro-trade metrics tracked separately - **Property 33: Micro-trade metrics tracked separately** - Generate mixed sets of standard and micro-trades - Verify micro-trade metrics computed independently - Verify standard trade metrics not contaminated by micro-trades - **Validates: Requirements 20.7** - [x] 16. Notification Service implementation - [x] 16.1 Implement `services/trading/notifications.py` - Implement `NotificationService` class with `send_alert()` method: send via all enabled channels (SMS via AWS SNS, email via Gmail API) - Implement `send_daily_summary()` method: format and send daily performance summary at configurable time (default 16:30) - Implement `send_weekly_digest()` method: format and send weekly performance digest - Implement rate limiting: max 10 SMS/hour, 20 emails/hour (configurable), using Redis counters with hourly TTL - Implement retry logic: up to 3 retries with exponential backoff on delivery failure - Persist all notifications to `notifications` table with channel, event_type, message, delivery_status, timestamp - Support event types: circuit_breaker_triggered, circuit_breaker_resumed, risk_tier_changed, emergency_liquidation, large_trade_pnl, daily_summary, weekly_digest - Notifications run in separate asyncio tasks — never block trading operations - _Requirements: 19.1, 19.2, 19.3, 19.4, 19.5, 19.6, 19.7, 19.8, 19.11_ - [x] 16.2 Write property test for notification rate limiting - **Property 30: Notification rate limiting** - Generate random sequences of notification requests within a one-hour window - Verify at most 10 SMS and 20 emails delivered per hour - Verify excess notifications marked as 'rate_limited' - **Validates: Requirements 19.7** - [x] 17. Micro-Trading Module - [x] 17.1 Implement `services/trading/micro_trading.py` - Implement `MicroTradingModule` class with `poll_intraday_signals()` method: fetch intraday and 1d trend window signals from aggregation engine - Implement `evaluate_micro_trade()` method: evaluate signal against risk tier confidence threshold, apply micro-trade allocation cap (3% of Active Pool) - Enforce daily micro-trade limit (default 10) - Use tighter stop-loss (1.0x ATR) and take-profit (1.5x stop distance) - Implement auto-close after max hold duration (default 2 hours) - Respect all existing constraints (trading window, circuit breakers, portfolio heat, correlation, sector exposure, earnings) - Toggleable independently via trading_engine_config - _Requirements: 20.1, 20.2, 20.3, 20.4, 20.5, 20.6, 20.8, 20.10_ - [x] 17.2 Write property test for micro-trade parameter constraints - **Property 31: Micro-trade parameter constraints** - Generate random micro-trade scenarios - Verify allocation does not exceed micro_trading_allocation_cap_pct - Verify stop-loss at 1.0x ATR, take-profit at 1.5x stop distance - Verify daily count does not exceed configured maximum - **Validates: Requirements 20.3, 20.4, 20.5** - [x] 17.3 Write property test for micro-trade auto-close - **Property 32: Micro-trade auto-close after max hold duration** - Generate micro-trade positions with varying hold durations - Verify positions closed at market price when hold exceeds max duration - **Validates: Requirements 20.6** - [x] 17.4 Write property test for micro-trades respect all constraints - **Property 34: Micro-trades respect all existing constraints** - Generate micro-trade evaluations with various constraint violations - Verify trading window, circuit breakers, portfolio heat, correlation, sector exposure, and earnings rules all enforced - **Validates: Requirements 20.10** - [x] 18. Checkpoint — Ensure all trading logic and property tests pass - Ensure all tests pass, ask the user if questions arise. - [x] 19. Backtester implementation - [x] 19.1 Implement `services/trading/backtester.py` - Implement `Backtester` class with `run()` method accepting BacktestConfig (start_date, end_date, initial_capital, risk_tier) - Replay historical recommendations from `recommendations` table within date range - Simulate full decision logic: position sizing, stop-loss/take-profit, circuit breakers, reserve pool, rebalancing - Use historical price data from market data tables for simulation - Produce BacktestResult with total_return, sharpe_ratio, max_drawdown, win_rate, profit_factor, trade_count, trade_log, equity_curve - Persist results to `backtest_runs` and `backtest_trades` tables with unique backtest_id - Handle missing historical data gracefully (skip dates, note gaps) - Persist partial results with status 'failed' on mid-run errors - _Requirements: 15.1, 15.2, 15.3, 15.4_ - [x] 19.2 Write property test for backtester produces equivalent metrics - **Property 36: Backtester produces equivalent metrics** - Generate random sets of historical trades - Verify backtester metric computation matches performance tracker for same trade data - **Validates: Requirements 15.3** - [x] 20. Trading Engine FastAPI HTTP Service - [x] 20.1 Implement `services/trading/app.py` - Create FastAPI application with lifespan handler that starts/stops the TradingEngine - Implement `GET /health` liveness probe endpoint - Implement `GET /ready` readiness probe: return healthy when portfolio loaded and loop active - Implement `GET /api/trading/status` endpoint: return engine state (enabled, risk tier, circuit breaker status, active/reserve pool, portfolio heat, open positions, last decision timestamp) - Implement `PUT /api/trading/config` endpoint: update trading_engine_config, record audit event with previous/new config and change source - Implement `POST /api/trading/pause` and `POST /api/trading/resume` endpoints - Implement `GET /api/trading/decisions` endpoint: paginated, filterable by ticker, decision type, date range - Implement `GET /api/trading/metrics` endpoint: current performance metrics - Implement `GET /api/trading/metrics/history` endpoint: historical daily snapshots - Implement `POST /api/trading/backtest` endpoint: launch backtest, return backtest_id - Implement `GET /api/trading/backtest/{id}` endpoint: retrieve backtest results - Implement `GET /api/trading/notifications/config` and `PUT /api/trading/notifications/config` endpoints - Implement `GET /api/trading/notifications/history` endpoint: recent notifications - _Requirements: 1.7, 5.6, 6.6, 15.5, 16.2, 16.3, 16.4, 17.3, 19.9_ - [x] 20.2 Write property test for configuration change audit trail - **Property 35: Configuration change audit trail** - Generate random configuration changes via API - Verify audit event persisted with previous config, new config, and change source - **Validates: Requirements 16.6** - [x] 20.3 Write property test for persistence round-trip - **Property 29: Persistence round-trip for trading engine state** - Generate random trading engine config, reserve pool entries, risk tier history, circuit breaker events, portfolio snapshots, and backtest results - Verify persist-then-read produces equivalent objects with all fields preserved - **Validates: Requirements 3.2, 4.7, 5.5, 6.4, 14.3, 15.4, 16.1** - [x] 21. Checkpoint — Ensure API endpoints and backtester work correctly - Ensure all tests pass, ask the user if questions arise. - [x] 22. Kubernetes deployment and infrastructure - [x] 22.1 Add trading-engine service to Helm chart `infra/helm/stonks-oracle/values.yaml` - Add `tradingEngine` entry under `services:` with: replicas 1, image trading-engine, command `uvicorn services.trading.app:app --host 0.0.0.0 --port 8000`, tier trading, port 8000, secrets [stonks-core-secrets, stonks-broker-secrets], resources (requests: 100m CPU / 256Mi memory, limits: 500m CPU / 512Mi memory), readiness probe on /ready port 8000, liveness probe on /health port 8000 - _Requirements: 1.7, 16.1_ - [x] 22.2 Add network policy for trading-engine - Allow ingress from query-api, dashboard, and kube-system (Traefik) on port 8000 - Allow egress to PostgreSQL, Redis, and external services (SNS, Gmail API) - _Requirements: 16.2_ - [x] 22.3 Add `/trading/` proxy route to dashboard nginx.conf - Add `location /trading/ { proxy_pass http://trading-engine:8000/; }` to `frontend/nginx.conf` - _Requirements: 14.4, 16.5_ - [x] 22.4 Add trading-engine ingress if external access needed - Add ingress host entry for trading engine API (e.g., `stonks-trading.celestium.life`) to values.yaml if direct external access is desired, or rely on dashboard proxy - _Requirements: 16.2_ - [x] 23. Dashboard frontend — Trading Engine panels - [x] 23.1 Add trading API client hooks to `frontend/src/api/` - Add `useTradingStatus()` hook: fetch `GET /trading/api/trading/status` - Add `useTradingDecisions()` hook: fetch `GET /trading/api/trading/decisions` with pagination and filters - Add `useTradingMetrics()` hook: fetch `GET /trading/api/trading/metrics` - Add `useTradingMetricsHistory()` hook: fetch `GET /trading/api/trading/metrics/history` - Add `useTradingConfig()` and `useUpdateTradingConfig()` hooks for config read/write - Add `usePauseTradingEngine()` and `useResumeTradingEngine()` mutation hooks - Add `useBacktestLaunch()` and `useBacktestResult()` hooks - Add `useNotificationConfig()`, `useUpdateNotificationConfig()`, and `useNotificationHistory()` hooks - _Requirements: 14.4, 14.5, 14.6, 14.7, 15.6, 16.5, 17.4, 19.10, 20.9_ - [x] 23.2 Implement Trading Engine overview panel component - Display current Risk Tier, Circuit Breaker status (active/inactive with trigger reason and cooldown remaining), Active Pool and Reserve Pool balances, Portfolio Heat gauge, last 24h P&L summary - Include start/pause/resume controls and Risk Tier selector dropdown - Use TanStack Query for data fetching with auto-refresh - _Requirements: 14.4, 16.5, 6.6_ - [x] 23.3 Implement Portfolio Composition panel component - Display current positions table: ticker, entry price, current price, unrealized P&L, stop-loss level, take-profit level, sector - Display sector allocation pie chart using Recharts - _Requirements: 14.5_ - [x] 23.4 Implement Trade History panel component - Display completed trades table: entry/exit prices, P&L amount and percentage, hold duration, recommendation thesis - Support pagination and filtering by ticker and date range - _Requirements: 14.6, 17.4_ - [x] 23.5 Implement Performance Charts panel component - Display cumulative P&L line chart over time using Recharts - Display daily returns bar chart using Recharts - Display drawdown chart using Recharts - _Requirements: 14.7_ - [x] 23.6 Implement Backtesting panel component - Display backtest configuration form: date range picker, initial capital input, risk tier selector - Display backtest results: equity curve chart, trade log table, summary metrics (total return, Sharpe, max drawdown, win rate, profit factor) - Support launching new backtests and viewing historical results - _Requirements: 15.6_ - [x] 23.7 Implement Micro-Trading panel component - Display micro-trade mode status toggle (enabled/disabled) - Display today's micro-trade count and P&L - Display active micro-trade positions table - Display micro-trade performance metrics over trailing 7 days - _Requirements: 20.9_ - [x] 23.8 Implement Notification Preferences panel component - Display notification channel toggles (SMS, email) with phone number and email address inputs - Display event type selection checkboxes - Display rate limit configuration - Display recent notification history table - _Requirements: 19.10_ - [x] 23.9 Wire trading panels into dashboard routing - Add Trading page route to TanStack Router configuration - Add navigation link to the dashboard sidebar/header - Compose all trading panels (overview, portfolio, trade history, performance, backtesting, micro-trading, notifications) into the Trading page layout - _Requirements: 14.4, 16.5_ - [x] 24. Checkpoint — Ensure frontend builds and all tests pass - Ensure all tests pass, ask the user if questions arise. - [x] 25. Integration wiring and final validation - [x] 25.1 Wire stop-loss price crossing detection into the decision loop - Connect `StopLossManager.check_price_crossings()` to run at the configured interval (5 min default, 60s during high-severity events) - Generate immediate sell orders for triggered positions and submit to broker queue - Handle price data unavailability (close position after 15 min without data) - _Requirements: 4.3, 4.4, 4.5, 4.8, 7.4_ - [x] 25.2 Wire reserve pool siphoning to position close events - Detect profitable position closes from broker service fill events - Call `ReservePoolController.siphon_profit()` with realized profit - Trigger notification for large trade P&L events - _Requirements: 3.1, 19.2_ - [x] 25.3 Wire risk tier evaluation to daily market close schedule - Schedule `RiskTierController.evaluate()` at market close - Trigger notification on tier changes - _Requirements: 5.2, 19.2_ - [x] 25.4 Wire portfolio rebalancer to weekly schedule - Schedule `PortfolioRebalancer.evaluate()` weekly at Monday market open - Submit rebalancing orders through broker queue - _Requirements: 8.1, 8.5_ - [x] 25.5 Wire notification service to all critical events - Connect circuit breaker triggers/resumes, risk tier changes, emergency liquidation, large trade P&L to notification dispatch - Schedule daily summary at configured time (default 16:30) - Schedule weekly digest - _Requirements: 19.2, 19.3, 19.4_ - [x] 25.6 Wire micro-trading module into the decision loop - Start micro-trading polling when enabled in config - Route micro-trade decisions through the same order submission pipeline - Track micro-trade metrics separately in performance tracker - _Requirements: 20.1, 20.2, 20.7_ - [x] 25.7 Write integration tests for end-to-end decision flow - Test full cycle: recommendation → evaluation → position sizing → order submission to broker queue - Test stop-loss crossing → immediate sell order - Test reserve pool siphoning on profitable close - Test circuit breaker trigger → halt → cooldown → resume - Test engine startup state reconstruction from PostgreSQL - _Requirements: 1.1, 1.2, 1.3, 3.1, 4.4, 6.1, 6.5, 18.5_ - [x] 26. Final checkpoint — Ensure all tests pass - Ensure all tests pass, ask the user if questions arise. ## Notes - Tasks marked with `*` are optional and can be skipped for faster MVP - Each task references specific requirements for traceability - Checkpoints ensure incremental validation after each major component - Property tests validate the 36 correctness properties defined in the design document - The trading engine is a NEW service at `services/trading/` — it does not replace existing services - All order submission goes through the existing `stonks:queue:broker_orders` Redis queue consumed by the Broker Service - Migration number 018 is the next available migration slot - Frontend components use the existing React 19 + TypeScript + Tailwind + TanStack + Recharts stack - Dashboard proxy needs `/trading/` → `trading-engine:8000` added to nginx.conf