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
This commit is contained in:
@@ -0,0 +1,365 @@
|
||||
-- Autonomous Trading Engine
|
||||
-- Adds tables for trading engine configuration, reserve pool management,
|
||||
-- risk tier history, circuit breakers, trading decisions, stop levels,
|
||||
-- portfolio snapshots, backtesting, tax lots, earnings calendar,
|
||||
-- correlation matrix cache, and notifications.
|
||||
|
||||
-- ============================================================
|
||||
-- Trading Engine Configuration
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE trading_engine_config (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
paused BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
risk_tier VARCHAR(20) NOT NULL DEFAULT 'moderate',
|
||||
reserve_siphon_pct FLOAT NOT NULL DEFAULT 0.20,
|
||||
polling_interval_seconds INTEGER NOT NULL DEFAULT 60,
|
||||
gradual_entry_tranches INTEGER NOT NULL DEFAULT 3,
|
||||
gradual_entry_threshold_dollars FLOAT NOT NULL DEFAULT 30.0,
|
||||
gradual_entry_interval_minutes INTEGER NOT NULL DEFAULT 15,
|
||||
trading_window_start_minutes INTEGER NOT NULL DEFAULT 15,
|
||||
trading_window_end_minutes INTEGER NOT NULL DEFAULT 15,
|
||||
max_open_positions INTEGER NOT NULL DEFAULT 10,
|
||||
circuit_breaker_daily_loss_pct FLOAT NOT NULL DEFAULT 0.05,
|
||||
circuit_breaker_single_position_loss_pct FLOAT NOT NULL DEFAULT 0.15,
|
||||
circuit_breaker_ticker_cooldown_hours INTEGER NOT NULL DEFAULT 48,
|
||||
circuit_breaker_volatility_pause_hours INTEGER NOT NULL DEFAULT 2,
|
||||
circuit_breaker_stop_loss_hits_threshold INTEGER NOT NULL DEFAULT 3,
|
||||
circuit_breaker_stop_loss_window_minutes INTEGER NOT NULL DEFAULT 30,
|
||||
active_pool_minimum FLOAT NOT NULL DEFAULT 100.0,
|
||||
emergency_drawdown_threshold_pct FLOAT NOT NULL DEFAULT 0.40,
|
||||
reserve_high_water_pct FLOAT NOT NULL DEFAULT 0.30,
|
||||
absolute_position_cap FLOAT NOT NULL DEFAULT 50.0,
|
||||
correlation_reduction_threshold FLOAT NOT NULL DEFAULT 0.5,
|
||||
correlation_rejection_threshold FLOAT NOT NULL DEFAULT 0.8,
|
||||
earnings_pre_window_days INTEGER NOT NULL DEFAULT 3,
|
||||
earnings_post_cooldown_days INTEGER NOT NULL DEFAULT 1,
|
||||
micro_trading_enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
micro_trading_interval_seconds INTEGER NOT NULL DEFAULT 300,
|
||||
micro_trading_allocation_cap_pct FLOAT NOT NULL DEFAULT 0.03,
|
||||
micro_trading_max_daily INTEGER NOT NULL DEFAULT 10,
|
||||
micro_trading_max_hold_minutes INTEGER NOT NULL DEFAULT 120,
|
||||
notification_sms_enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
notification_email_enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
notification_phone_number VARCHAR(20),
|
||||
notification_email_recipient VARCHAR(200),
|
||||
notification_sns_topic_arn VARCHAR(300),
|
||||
notification_rate_limit_sms_per_hour INTEGER NOT NULL DEFAULT 10,
|
||||
notification_rate_limit_email_per_hour INTEGER NOT NULL DEFAULT 20,
|
||||
notification_daily_summary_time TIME NOT NULL DEFAULT '16:30',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- Reserve Pool Ledger
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE reserve_pool_ledger (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
amount FLOAT NOT NULL,
|
||||
balance_after FLOAT NOT NULL,
|
||||
trigger_type VARCHAR(30) NOT NULL,
|
||||
reference_id UUID,
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT chk_trigger_type CHECK (
|
||||
trigger_type IN ('profit_siphon', 'emergency_liquidation', 'manual_adjustment', 'initial')
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_reserve_pool_ledger_created ON reserve_pool_ledger(created_at DESC);
|
||||
|
||||
-- ============================================================
|
||||
-- Risk Tier History
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE risk_tier_history (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
previous_tier VARCHAR(20) NOT NULL,
|
||||
new_tier VARCHAR(20) NOT NULL,
|
||||
trigger_source VARCHAR(30) NOT NULL,
|
||||
trigger_metrics JSONB NOT NULL DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_risk_tier_history_created ON risk_tier_history(created_at DESC);
|
||||
|
||||
-- ============================================================
|
||||
-- Circuit Breaker Events
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE circuit_breaker_events (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
trigger_type VARCHAR(30) NOT NULL,
|
||||
threshold_value FLOAT,
|
||||
actual_value FLOAT,
|
||||
ticker VARCHAR(20),
|
||||
cooldown_expires TIMESTAMPTZ,
|
||||
resolved_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT chk_cb_trigger_type CHECK (
|
||||
trigger_type IN ('daily_loss', 'single_position', 'volatility', 'manual')
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_circuit_breaker_events_active ON circuit_breaker_events(created_at DESC)
|
||||
WHERE resolved_at IS NULL;
|
||||
|
||||
-- ============================================================
|
||||
-- Trading Decisions
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE trading_decisions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
recommendation_id UUID REFERENCES recommendations(id),
|
||||
decision VARCHAR(10) NOT NULL,
|
||||
skip_reason TEXT,
|
||||
ticker VARCHAR(20) NOT NULL,
|
||||
computed_position_size FLOAT,
|
||||
computed_share_quantity INTEGER,
|
||||
risk_tier_at_decision VARCHAR(20) NOT NULL,
|
||||
portfolio_heat_at_decision FLOAT,
|
||||
active_pool_at_decision FLOAT,
|
||||
reserve_pool_at_decision FLOAT,
|
||||
circuit_breaker_status VARCHAR(20) NOT NULL DEFAULT 'inactive',
|
||||
correlation_check_result JSONB DEFAULT '{}',
|
||||
sector_exposure_check_result JSONB DEFAULT '{}',
|
||||
earnings_proximity_flag BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
is_micro_trade BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
decision_trace JSONB NOT NULL DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_trading_decisions_ticker ON trading_decisions(ticker, created_at DESC);
|
||||
CREATE INDEX idx_trading_decisions_rec ON trading_decisions(recommendation_id);
|
||||
CREATE INDEX idx_trading_decisions_decision ON trading_decisions(decision, created_at DESC);
|
||||
|
||||
-- ============================================================
|
||||
-- Position Stop Levels
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE position_stop_levels (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
ticker VARCHAR(20) NOT NULL,
|
||||
entry_price FLOAT NOT NULL,
|
||||
stop_loss_price FLOAT NOT NULL,
|
||||
take_profit_price FLOAT NOT NULL,
|
||||
trailing_stop_active BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
atr_value FLOAT NOT NULL,
|
||||
atr_multiplier FLOAT NOT NULL,
|
||||
reward_risk_ratio FLOAT NOT NULL,
|
||||
signal_confidence FLOAT,
|
||||
is_micro_trade BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_position_stop_levels_ticker ON position_stop_levels(ticker) WHERE active = TRUE;
|
||||
|
||||
-- ============================================================
|
||||
-- Portfolio Snapshots
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE portfolio_snapshots (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
snapshot_date DATE NOT NULL UNIQUE,
|
||||
portfolio_value FLOAT NOT NULL,
|
||||
active_pool FLOAT NOT NULL,
|
||||
reserve_pool FLOAT NOT NULL,
|
||||
daily_return FLOAT,
|
||||
cumulative_return FLOAT,
|
||||
unrealized_pnl FLOAT,
|
||||
realized_pnl FLOAT,
|
||||
win_count INTEGER NOT NULL DEFAULT 0,
|
||||
loss_count INTEGER NOT NULL DEFAULT 0,
|
||||
win_rate FLOAT,
|
||||
sharpe_ratio FLOAT,
|
||||
max_drawdown FLOAT,
|
||||
current_drawdown_pct FLOAT,
|
||||
portfolio_heat FLOAT,
|
||||
risk_tier VARCHAR(20),
|
||||
positions JSONB NOT NULL DEFAULT '[]',
|
||||
metrics JSONB NOT NULL DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_portfolio_snapshots_date ON portfolio_snapshots(snapshot_date DESC);
|
||||
|
||||
-- ============================================================
|
||||
-- Backtest Runs
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE backtest_runs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
start_date DATE NOT NULL,
|
||||
end_date DATE NOT NULL,
|
||||
initial_capital FLOAT NOT NULL,
|
||||
risk_tier VARCHAR(20) NOT NULL,
|
||||
config JSONB NOT NULL DEFAULT '{}',
|
||||
total_return FLOAT,
|
||||
sharpe_ratio FLOAT,
|
||||
max_drawdown FLOAT,
|
||||
win_rate FLOAT,
|
||||
profit_factor FLOAT,
|
||||
trade_count INTEGER,
|
||||
equity_curve JSONB DEFAULT '[]',
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'running',
|
||||
completed_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- Backtest Trades
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE backtest_trades (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
backtest_id UUID NOT NULL REFERENCES backtest_runs(id) ON DELETE CASCADE,
|
||||
ticker VARCHAR(20) NOT NULL,
|
||||
side VARCHAR(10) NOT NULL,
|
||||
entry_price FLOAT NOT NULL,
|
||||
exit_price FLOAT,
|
||||
quantity INTEGER NOT NULL,
|
||||
pnl FLOAT,
|
||||
pnl_pct FLOAT,
|
||||
entry_date DATE NOT NULL,
|
||||
exit_date DATE,
|
||||
hold_duration_days INTEGER,
|
||||
recommendation_id UUID,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_backtest_trades_run ON backtest_trades(backtest_id);
|
||||
|
||||
-- ============================================================
|
||||
-- Tax Lots
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE tax_lots (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
ticker VARCHAR(20) NOT NULL,
|
||||
quantity INTEGER NOT NULL,
|
||||
cost_basis_per_share FLOAT NOT NULL,
|
||||
acquisition_date DATE NOT NULL,
|
||||
status VARCHAR(10) NOT NULL DEFAULT 'open',
|
||||
closed_date DATE,
|
||||
exit_price FLOAT,
|
||||
realized_pnl FLOAT,
|
||||
wash_sale_flag BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
wash_sale_details TEXT,
|
||||
order_id UUID REFERENCES orders(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT chk_tax_lot_status CHECK (status IN ('open', 'closed', 'washed'))
|
||||
);
|
||||
|
||||
CREATE INDEX idx_tax_lots_ticker ON tax_lots(ticker, acquisition_date DESC);
|
||||
CREATE INDEX idx_tax_lots_status ON tax_lots(status) WHERE status = 'open';
|
||||
|
||||
-- ============================================================
|
||||
-- Earnings Calendar
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE earnings_calendar (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
ticker VARCHAR(20) NOT NULL,
|
||||
earnings_date DATE NOT NULL,
|
||||
source VARCHAR(30) NOT NULL DEFAULT 'manual',
|
||||
confirmed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(ticker, earnings_date)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_earnings_calendar_date ON earnings_calendar(earnings_date);
|
||||
CREATE INDEX idx_earnings_calendar_ticker ON earnings_calendar(ticker);
|
||||
|
||||
-- ============================================================
|
||||
-- Correlation Matrix Cache
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE correlation_matrix_cache (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
ticker_a VARCHAR(20) NOT NULL,
|
||||
ticker_b VARCHAR(20) NOT NULL,
|
||||
correlation_coefficient FLOAT NOT NULL,
|
||||
lookback_days INTEGER NOT NULL DEFAULT 90,
|
||||
computed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(ticker_a, ticker_b)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_correlation_cache_tickers ON correlation_matrix_cache(ticker_a, ticker_b);
|
||||
|
||||
-- ============================================================
|
||||
-- Notifications
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE notifications (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
channel VARCHAR(10) NOT NULL,
|
||||
event_type VARCHAR(50) NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
delivery_status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
||||
retry_count INTEGER NOT NULL DEFAULT 0,
|
||||
error_message TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
delivered_at TIMESTAMPTZ,
|
||||
CONSTRAINT chk_channel CHECK (channel IN ('sms', 'email')),
|
||||
CONSTRAINT chk_delivery_status CHECK (
|
||||
delivery_status IN ('pending', 'delivered', 'failed', 'rate_limited')
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_notifications_created ON notifications(created_at DESC);
|
||||
CREATE INDEX idx_notifications_event ON notifications(event_type, created_at DESC);
|
||||
|
||||
-- ============================================================
|
||||
-- Default Data
|
||||
-- ============================================================
|
||||
|
||||
-- Insert default trading engine configuration (moderate tier defaults)
|
||||
INSERT INTO trading_engine_config (
|
||||
enabled, paused, risk_tier, reserve_siphon_pct,
|
||||
polling_interval_seconds, gradual_entry_tranches,
|
||||
gradual_entry_threshold_dollars, gradual_entry_interval_minutes,
|
||||
trading_window_start_minutes, trading_window_end_minutes,
|
||||
max_open_positions,
|
||||
circuit_breaker_daily_loss_pct, circuit_breaker_single_position_loss_pct,
|
||||
circuit_breaker_ticker_cooldown_hours, circuit_breaker_volatility_pause_hours,
|
||||
circuit_breaker_stop_loss_hits_threshold, circuit_breaker_stop_loss_window_minutes,
|
||||
active_pool_minimum, emergency_drawdown_threshold_pct,
|
||||
reserve_high_water_pct, absolute_position_cap,
|
||||
correlation_reduction_threshold, correlation_rejection_threshold,
|
||||
earnings_pre_window_days, earnings_post_cooldown_days,
|
||||
micro_trading_enabled, micro_trading_interval_seconds,
|
||||
micro_trading_allocation_cap_pct, micro_trading_max_daily,
|
||||
micro_trading_max_hold_minutes,
|
||||
notification_sms_enabled, notification_email_enabled,
|
||||
notification_rate_limit_sms_per_hour, notification_rate_limit_email_per_hour,
|
||||
notification_daily_summary_time
|
||||
) VALUES (
|
||||
FALSE, FALSE, 'moderate', 0.20,
|
||||
60, 3,
|
||||
30.0, 15,
|
||||
15, 15,
|
||||
10,
|
||||
0.05, 0.15,
|
||||
48, 2,
|
||||
3, 30,
|
||||
100.0, 0.40,
|
||||
0.30, 50.0,
|
||||
0.5, 0.8,
|
||||
3, 1,
|
||||
FALSE, 300,
|
||||
0.03, 10,
|
||||
120,
|
||||
FALSE, FALSE,
|
||||
10, 20,
|
||||
'16:30'
|
||||
);
|
||||
|
||||
-- Insert initial reserve pool ledger entry with zero balance
|
||||
INSERT INTO reserve_pool_ledger (amount, balance_after, trigger_type, notes)
|
||||
VALUES (0.0, 0.0, 'initial', 'Initial reserve pool entry');
|
||||
Reference in New Issue
Block a user