-- ================================================================================================ -- Migration 045: Wave D Regime Tracking Tables -- Creates tables for regime state, transitions, and adaptive strategy metrics -- ================================================================================================ -- ================================================================================================ -- Table: regime_states -- Stores current regime classification and associated metrics per symbol -- ================================================================================================ CREATE TABLE regime_states ( id BIGSERIAL PRIMARY KEY, symbol TEXT NOT NULL, event_timestamp TIMESTAMPTZ NOT NULL, regime TEXT NOT NULL CHECK (regime IN ('Normal', 'Trending', 'Ranging', 'Volatile', 'Crisis', 'Illiquid', 'Momentum')), confidence DOUBLE PRECISION NOT NULL CHECK (confidence >= 0.0 AND confidence <= 1.0), -- CUSUM metrics (Agent D13 features) cusum_s_plus DOUBLE PRECISION, cusum_s_minus DOUBLE PRECISION, cusum_alert_count INTEGER DEFAULT 0, -- ADX & Directional Indicators (Agent D14 features) adx DOUBLE PRECISION CHECK (adx IS NULL OR (adx >= 0.0 AND adx <= 100.0)), plus_di DOUBLE PRECISION CHECK (plus_di IS NULL OR (plus_di >= 0.0 AND plus_di <= 100.0)), minus_di DOUBLE PRECISION CHECK (minus_di IS NULL OR (minus_di >= 0.0 AND minus_di <= 100.0)), -- Regime stability metrics (Agent D15 features) stability DOUBLE PRECISION CHECK (stability IS NULL OR (stability >= 0.0 AND stability <= 1.0)), entropy DOUBLE PRECISION CHECK (entropy IS NULL OR entropy >= 0.0), created_at TIMESTAMPTZ DEFAULT NOW(), CONSTRAINT unique_regime_state UNIQUE (symbol, event_timestamp) ); -- Index for fast lookups by symbol and time CREATE INDEX idx_regime_states_symbol_timestamp ON regime_states(symbol, event_timestamp DESC); CREATE INDEX idx_regime_states_regime ON regime_states(regime); CREATE INDEX idx_regime_states_confidence ON regime_states(confidence DESC); COMMENT ON TABLE regime_states IS 'Wave D: Stores regime classification and associated metrics per symbol'; COMMENT ON COLUMN regime_states.regime IS 'Current regime: Normal, Trending, Ranging, Volatile, Crisis, Illiquid, Momentum'; COMMENT ON COLUMN regime_states.confidence IS 'Regime classification confidence (0.0-1.0)'; COMMENT ON COLUMN regime_states.cusum_s_plus IS 'CUSUM positive sum (Agent D13)'; COMMENT ON COLUMN regime_states.cusum_s_minus IS 'CUSUM negative sum (Agent D13)'; COMMENT ON COLUMN regime_states.cusum_alert_count IS 'Number of CUSUM alerts detected (Agent D13)'; COMMENT ON COLUMN regime_states.adx IS 'Average Directional Index (0-100, Agent D14)'; COMMENT ON COLUMN regime_states.plus_di IS 'Positive Directional Indicator (+DI, 0-100, Agent D14)'; COMMENT ON COLUMN regime_states.minus_di IS 'Negative Directional Indicator (-DI, 0-100, Agent D14)'; COMMENT ON COLUMN regime_states.stability IS 'Regime stability score (0.0-1.0, Agent D15)'; COMMENT ON COLUMN regime_states.entropy IS 'Regime entropy measure (>0, Agent D15)'; -- ================================================================================================ -- Table: regime_transitions -- Tracks regime changes over time for pattern analysis -- ================================================================================================ CREATE TABLE regime_transitions ( id BIGSERIAL PRIMARY KEY, symbol TEXT NOT NULL, event_timestamp TIMESTAMPTZ NOT NULL, from_regime TEXT NOT NULL CHECK (from_regime IN ('Normal', 'Trending', 'Ranging', 'Volatile', 'Crisis', 'Illiquid', 'Momentum')), to_regime TEXT NOT NULL CHECK (to_regime IN ('Normal', 'Trending', 'Ranging', 'Volatile', 'Crisis', 'Illiquid', 'Momentum')), duration_bars INTEGER CHECK (duration_bars >= 0), -- Transition probability (Agent D15 features) transition_probability DOUBLE PRECISION CHECK (transition_probability IS NULL OR (transition_probability >= 0.0 AND transition_probability <= 1.0)), -- Transition context adx_at_transition DOUBLE PRECISION, cusum_alert_triggered BOOLEAN DEFAULT FALSE, created_at TIMESTAMPTZ DEFAULT NOW(), CONSTRAINT regime_transition_valid CHECK (from_regime != to_regime) ); -- Indexes for fast transition analysis CREATE INDEX idx_regime_transitions_symbol_timestamp ON regime_transitions(symbol, event_timestamp DESC); CREATE INDEX idx_regime_transitions_from_to ON regime_transitions(from_regime, to_regime); CREATE INDEX idx_regime_transitions_symbol_from_to ON regime_transitions(symbol, from_regime, to_regime); COMMENT ON TABLE regime_transitions IS 'Wave D: Tracks regime transitions for pattern analysis'; COMMENT ON COLUMN regime_transitions.from_regime IS 'Source regime before transition'; COMMENT ON COLUMN regime_transitions.to_regime IS 'Destination regime after transition'; COMMENT ON COLUMN regime_transitions.duration_bars IS 'Number of bars in previous regime'; COMMENT ON COLUMN regime_transitions.transition_probability IS 'Transition probability from matrix (Agent D15)'; COMMENT ON COLUMN regime_transitions.adx_at_transition IS 'ADX value at transition point'; COMMENT ON COLUMN regime_transitions.cusum_alert_triggered IS 'Whether CUSUM alert triggered transition'; -- ================================================================================================ -- Table: adaptive_strategy_metrics -- Stores adaptive strategy adjustments and performance per regime -- ================================================================================================ CREATE TABLE adaptive_strategy_metrics ( id BIGSERIAL PRIMARY KEY, symbol TEXT NOT NULL, event_timestamp TIMESTAMPTZ NOT NULL, regime TEXT NOT NULL CHECK (regime IN ('Normal', 'Trending', 'Ranging', 'Volatile', 'Crisis', 'Illiquid', 'Momentum')), -- Adaptive Strategy Metrics (Agent D16 features) position_multiplier DOUBLE PRECISION NOT NULL CHECK (position_multiplier >= 0.0 AND position_multiplier <= 2.0), stop_loss_multiplier DOUBLE PRECISION NOT NULL CHECK (stop_loss_multiplier >= 1.0 AND stop_loss_multiplier <= 5.0), regime_sharpe DOUBLE PRECISION, risk_budget_utilization DOUBLE PRECISION CHECK (risk_budget_utilization IS NULL OR (risk_budget_utilization >= 0.0 AND risk_budget_utilization <= 1.0)), -- Performance tracking total_trades INTEGER DEFAULT 0, winning_trades INTEGER DEFAULT 0, total_pnl BIGINT DEFAULT 0, created_at TIMESTAMPTZ DEFAULT NOW(), CONSTRAINT unique_adaptive_metrics UNIQUE (symbol, event_timestamp, regime) ); -- Indexes for performance analysis CREATE INDEX idx_adaptive_metrics_symbol_timestamp ON adaptive_strategy_metrics(symbol, event_timestamp DESC); CREATE INDEX idx_adaptive_metrics_regime ON adaptive_strategy_metrics(regime); CREATE INDEX idx_adaptive_metrics_sharpe ON adaptive_strategy_metrics(regime_sharpe DESC) WHERE regime_sharpe IS NOT NULL; COMMENT ON TABLE adaptive_strategy_metrics IS 'Wave D: Adaptive strategy adjustments and regime-specific performance'; COMMENT ON COLUMN adaptive_strategy_metrics.position_multiplier IS 'Position size multiplier for regime (0.2-2.0x, Agent D16)'; COMMENT ON COLUMN adaptive_strategy_metrics.stop_loss_multiplier IS 'Stop-loss distance multiplier for regime (1.0-5.0x, Agent D16)'; COMMENT ON COLUMN adaptive_strategy_metrics.regime_sharpe IS 'Sharpe ratio conditioned on regime (Agent D16)'; COMMENT ON COLUMN adaptive_strategy_metrics.risk_budget_utilization IS 'Risk budget usage ratio (0.0-1.0, Agent D16)'; -- ================================================================================================ -- Function: Get latest regime state for symbol -- ================================================================================================ CREATE OR REPLACE FUNCTION get_latest_regime(p_symbol TEXT) RETURNS TABLE ( regime TEXT, confidence DOUBLE PRECISION, event_timestamp TIMESTAMPTZ, cusum_s_plus DOUBLE PRECISION, cusum_s_minus DOUBLE PRECISION, adx DOUBLE PRECISION, stability DOUBLE PRECISION ) AS $$ BEGIN RETURN QUERY SELECT rs.regime, rs.confidence, rs.event_timestamp, rs.cusum_s_plus, rs.cusum_s_minus, rs.adx, rs.stability FROM regime_states rs WHERE rs.symbol = p_symbol ORDER BY rs.event_timestamp DESC LIMIT 1; END; $$ LANGUAGE plpgsql; COMMENT ON FUNCTION get_latest_regime IS 'Get most recent regime classification for a symbol'; -- ================================================================================================ -- Function: Get regime transition matrix -- Calculate transition probabilities between regimes -- ================================================================================================ CREATE OR REPLACE FUNCTION get_regime_transition_matrix( p_symbol TEXT, p_window_hours INTEGER DEFAULT 168 -- 1 week default ) RETURNS TABLE ( from_regime TEXT, to_regime TEXT, transition_count BIGINT, transition_probability DOUBLE PRECISION ) AS $$ BEGIN RETURN QUERY WITH transition_counts AS ( SELECT rt.from_regime, rt.to_regime, COUNT(*) AS count FROM regime_transitions rt WHERE rt.symbol = p_symbol AND rt.event_timestamp >= NOW() - (p_window_hours || ' hours')::INTERVAL GROUP BY rt.from_regime, rt.to_regime ), from_regime_totals AS ( SELECT tc2.from_regime AS regime, SUM(tc2.count) AS total FROM transition_counts tc2 GROUP BY tc2.from_regime ) SELECT tc.from_regime, tc.to_regime, tc.count AS transition_count, (tc.count::DOUBLE PRECISION / frt.total::DOUBLE PRECISION) AS transition_probability FROM transition_counts tc JOIN from_regime_totals frt ON tc.from_regime = frt.regime ORDER BY tc.from_regime, tc.to_regime; END; $$ LANGUAGE plpgsql; COMMENT ON FUNCTION get_regime_transition_matrix IS 'Calculate regime transition probabilities over time window'; -- ================================================================================================ -- Function: Get adaptive strategy performance by regime -- ================================================================================================ CREATE OR REPLACE FUNCTION get_regime_performance( p_symbol TEXT DEFAULT NULL, p_window_hours INTEGER DEFAULT 24 ) RETURNS TABLE ( regime TEXT, total_trades BIGINT, win_rate DOUBLE PRECISION, avg_sharpe DOUBLE PRECISION, avg_position_multiplier DOUBLE PRECISION, avg_stop_loss_multiplier DOUBLE PRECISION, total_pnl NUMERIC, avg_risk_utilization DOUBLE PRECISION ) AS $$ BEGIN RETURN QUERY SELECT asm.regime, SUM(asm.total_trades) AS total_trades, CASE WHEN SUM(asm.total_trades) > 0 THEN SUM(asm.winning_trades)::DOUBLE PRECISION / SUM(asm.total_trades)::DOUBLE PRECISION ELSE 0.0 END AS win_rate, AVG(asm.regime_sharpe) AS avg_sharpe, AVG(asm.position_multiplier) AS avg_position_multiplier, AVG(asm.stop_loss_multiplier) AS avg_stop_loss_multiplier, SUM(asm.total_pnl) AS total_pnl, AVG(asm.risk_budget_utilization) AS avg_risk_utilization FROM adaptive_strategy_metrics asm WHERE asm.event_timestamp >= NOW() - (p_window_hours || ' hours')::INTERVAL AND (p_symbol IS NULL OR asm.symbol = p_symbol) GROUP BY asm.regime ORDER BY asm.regime; END; $$ LANGUAGE plpgsql; COMMENT ON FUNCTION get_regime_performance IS 'Get adaptive strategy performance metrics by regime'; -- ================================================================================================ -- Grant permissions -- ================================================================================================ GRANT SELECT, INSERT, UPDATE ON regime_states TO foxhunt; GRANT SELECT, INSERT ON regime_transitions TO foxhunt; GRANT SELECT, INSERT, UPDATE ON adaptive_strategy_metrics TO foxhunt; GRANT USAGE, SELECT ON SEQUENCE regime_states_id_seq TO foxhunt; GRANT USAGE, SELECT ON SEQUENCE regime_transitions_id_seq TO foxhunt; GRANT USAGE, SELECT ON SEQUENCE adaptive_strategy_metrics_id_seq TO foxhunt; GRANT EXECUTE ON FUNCTION get_latest_regime TO foxhunt; GRANT EXECUTE ON FUNCTION get_regime_transition_matrix TO foxhunt; GRANT EXECUTE ON FUNCTION get_regime_performance TO foxhunt; -- ================================================================================================ -- END MIGRATION 045 -- ================================================================================================