Files
stonks-oracle/.kiro/specs/autonomous-trading-engine/tasks.md
T
Celes Renata 4ffde8cc06 feat: autonomous trading engine — full implementation
- Database migration 018 with 13 tables for trading engine state
- Trading engine service (services/trading/) with 12 pure computation modules:
  position sizer, stop-loss manager, reserve pool, circuit breaker,
  risk tier controller, correlation matrix, tax lots, trading window,
  gradual entry, notifications, micro-trading, backtester
- Core TradingEngine with pre-trade evaluation pipeline and integration wiring
- FastAPI HTTP service with 14 endpoints (health, config, decisions, metrics, backtest)
- Performance tracker with Sharpe ratio, drawdown, profit factor computation
- 194 Python tests (165 property-based + 29 integration)
- Frontend: 13 TanStack Query hooks, 7 dashboard panels, tabbed Trading Engine page
- Helm chart entry, network policy, nginx proxy, ingress for trading-engine
- Shared infrastructure: enums, Redis keys, TradingConfig in AppConfig
2026-04-15 16:12:22 +00:00

657 lines
46 KiB
Markdown

# 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