-- ================================================================================================ -- Ensemble Audit Analysis Queries -- Production-ready SQL queries for ensemble performance analysis and debugging -- ================================================================================================ -- ================================================================================================ -- TOP PERFORMING MODELS (LAST 24 HOURS) -- ================================================================================================ -- Query 1: Get top 5 models by Sharpe ratio (last 24 hours) SELECT * FROM get_top_models_24h(NULL, 5); -- Query 2: Get top performers for specific symbol SELECT * FROM get_top_models_24h('ES.FUT', 10); -- Query 3: Manual calculation for custom time windows SELECT model_id, symbol, COUNT(*) as predictions, AVG(accuracy) as avg_accuracy, AVG(sharpe_ratio) as avg_sharpe, SUM(total_pnl) as cumulative_pnl, AVG(avg_weight) as avg_ensemble_weight FROM model_performance_attribution WHERE timestamp >= NOW() - INTERVAL '7 days' AND window_hours = 24 GROUP BY model_id, symbol ORDER BY avg_sharpe DESC NULLS LAST LIMIT 10; -- ================================================================================================ -- MODEL CORRELATION MATRIX (LAST 7 DAYS) -- ================================================================================================ -- Query 4: Calculate pairwise model correlations SELECT * FROM calculate_model_correlation_7d(NULL); -- Query 5: Model correlation for specific symbol SELECT * FROM calculate_model_correlation_7d('NQ.FUT'); -- Query 6: Visual correlation matrix (pivot format) WITH correlation_data AS ( SELECT * FROM calculate_model_correlation_7d(NULL) ) SELECT model_a, MAX(CASE WHEN model_b = 'PPO' THEN correlation END) as ppo_corr, MAX(CASE WHEN model_b = 'MAMBA2' THEN correlation END) as mamba2_corr, MAX(CASE WHEN model_b = 'TFT' THEN correlation END) as tft_corr FROM correlation_data WHERE model_a IN ('DQN', 'PPO', 'MAMBA2') GROUP BY model_a ORDER BY model_a; -- ================================================================================================ -- ENSEMBLE VS INDIVIDUAL MODEL PERFORMANCE (LAST 30 DAYS) -- ================================================================================================ -- Query 7: Compare ensemble Sharpe ratio to individual models WITH ensemble_performance AS ( SELECT symbol, COUNT(*) as total_predictions, AVG(ensemble_confidence) as avg_confidence, SUM(pnl) as total_pnl, STDDEV(pnl::DOUBLE PRECISION) as pnl_std, AVG(pnl::DOUBLE PRECISION) / NULLIF(STDDEV(pnl::DOUBLE PRECISION), 0) as sharpe_approx FROM ensemble_predictions WHERE timestamp >= NOW() - INTERVAL '30 days' AND pnl IS NOT NULL GROUP BY symbol ), model_performance AS ( SELECT model_id, symbol, AVG(sharpe_ratio) as avg_sharpe, SUM(total_pnl) as total_pnl FROM model_performance_attribution WHERE timestamp >= NOW() - INTERVAL '30 days' AND window_hours = 24 GROUP BY model_id, symbol ) SELECT ep.symbol, ep.total_predictions as ensemble_predictions, ep.sharpe_approx as ensemble_sharpe, ep.total_pnl as ensemble_pnl, mp_dqn.avg_sharpe as dqn_sharpe, mp_ppo.avg_sharpe as ppo_sharpe, mp_mamba2.avg_sharpe as mamba2_sharpe, mp_tft.avg_sharpe as tft_sharpe, GREATEST( COALESCE(mp_dqn.avg_sharpe, 0), COALESCE(mp_ppo.avg_sharpe, 0), COALESCE(mp_mamba2.avg_sharpe, 0), COALESCE(mp_tft.avg_sharpe, 0) ) as best_individual_sharpe, (ep.sharpe_approx - GREATEST( COALESCE(mp_dqn.avg_sharpe, 0), COALESCE(mp_ppo.avg_sharpe, 0), COALESCE(mp_mamba2.avg_sharpe, 0), COALESCE(mp_tft.avg_sharpe, 0) )) as ensemble_lift FROM ensemble_performance ep LEFT JOIN model_performance mp_dqn ON ep.symbol = mp_dqn.symbol AND mp_dqn.model_id = 'DQN' LEFT JOIN model_performance mp_ppo ON ep.symbol = mp_ppo.symbol AND mp_ppo.model_id = 'PPO' LEFT JOIN model_performance mp_mamba2 ON ep.symbol = mp_mamba2.symbol AND mp_mamba2.model_id = 'MAMBA2' LEFT JOIN model_performance mp_tft ON ep.symbol = mp_tft.symbol AND mp_tft.model_id = 'TFT' ORDER BY ep.sharpe_approx DESC NULLS LAST; -- ================================================================================================ -- HIGH DISAGREEMENT EVENTS (LAST 24 HOURS) -- ================================================================================================ -- Query 8: Get high disagreement events (threshold = 50%) SELECT * FROM get_high_disagreement_events_24h(NULL, 0.5, 100); -- Query 9: Very high disagreement (threshold = 70%) SELECT * FROM get_high_disagreement_events_24h(NULL, 0.7, 50); -- Query 10: Disagreement with market context (price volatility) SELECT ep.timestamp, ep.symbol, ep.ensemble_action, ep.disagreement_rate, ep.dqn_vote, ep.ppo_vote, ep.mamba2_vote, ep.tft_vote, ep.executed_price, ep.pnl, -- Categorize disagreement level CASE WHEN ep.disagreement_rate >= 0.75 THEN 'CRITICAL' WHEN ep.disagreement_rate >= 0.5 THEN 'HIGH' WHEN ep.disagreement_rate >= 0.25 THEN 'MODERATE' ELSE 'LOW' END as disagreement_level FROM ensemble_predictions ep WHERE ep.timestamp >= NOW() - INTERVAL '24 hours' AND ep.disagreement_rate >= 0.25 ORDER BY ep.disagreement_rate DESC, ep.timestamp DESC LIMIT 100; -- ================================================================================================ -- P&L ATTRIBUTION BY MODEL (LAST 7 DAYS) -- ================================================================================================ -- Query 11: Total P&L contribution per model SELECT model_id, symbol, COUNT(*) as trades_with_contribution, SUM(total_pnl) / 100.0 as total_pnl_dollars, -- Convert cents to dollars AVG(total_pnl) / 100.0 as avg_pnl_per_trade_dollars, AVG(avg_weight) as avg_ensemble_weight, SUM(total_pnl) / NULLIF(AVG(avg_weight), 0) / 100.0 as pnl_per_unit_weight FROM model_performance_attribution WHERE timestamp >= NOW() - INTERVAL '7 days' AND window_hours = 24 GROUP BY model_id, symbol ORDER BY total_pnl_dollars DESC; -- Query 12: Daily P&L attribution breakdown SELECT DATE_TRUNC('day', timestamp) as trading_day, model_id, SUM(total_pnl) / 100.0 as daily_pnl_dollars, AVG(accuracy) as avg_accuracy, AVG(sharpe_ratio) as avg_sharpe FROM model_performance_attribution WHERE timestamp >= NOW() - INTERVAL '7 days' AND window_hours = 24 GROUP BY trading_day, model_id ORDER BY trading_day DESC, daily_pnl_dollars DESC; -- Query 13: Per-prediction P&L attribution (detailed view) SELECT ep.timestamp, ep.symbol, ep.ensemble_action, ep.order_id, ep.pnl / 100.0 as ensemble_pnl_dollars, -- Approximate per-model P&L based on weights (ep.pnl * ep.dqn_weight) / 100.0 as dqn_attributed_pnl_dollars, (ep.pnl * ep.ppo_weight) / 100.0 as ppo_attributed_pnl_dollars, (ep.pnl * ep.mamba2_weight) / 100.0 as mamba2_attributed_pnl_dollars, (ep.pnl * ep.tft_weight) / 100.0 as tft_attributed_pnl_dollars, ep.commission / 100.0 as commission_dollars, ep.slippage_bps as slippage_basis_points FROM ensemble_predictions ep WHERE ep.timestamp >= NOW() - INTERVAL '7 days' AND ep.pnl IS NOT NULL ORDER BY ep.timestamp DESC LIMIT 1000; -- ================================================================================================ -- A/B TEST ANALYSIS -- ================================================================================================ -- Query 14: A/B test performance comparison (active tests) WITH ab_metrics AS ( SELECT ab_test_id, ab_group, COUNT(*) as predictions, AVG(ensemble_confidence) as avg_confidence, AVG(disagreement_rate) as avg_disagreement, SUM(pnl) / 100.0 as total_pnl_dollars, STDDEV(pnl::DOUBLE PRECISION) as pnl_std, AVG(pnl::DOUBLE PRECISION) / NULLIF(STDDEV(pnl::DOUBLE PRECISION), 0) as sharpe_approx, COUNT(CASE WHEN pnl > 0 THEN 1 END) as winning_trades, COUNT(CASE WHEN pnl IS NOT NULL THEN 1 END) as total_trades FROM ensemble_predictions WHERE ab_test_id IS NOT NULL AND timestamp >= NOW() - INTERVAL '7 days' GROUP BY ab_test_id, ab_group ) SELECT test.test_name, control.predictions as control_predictions, treatment.predictions as treatment_predictions, control.sharpe_approx as control_sharpe, treatment.sharpe_approx as treatment_sharpe, (treatment.sharpe_approx - control.sharpe_approx) as sharpe_diff, (treatment.sharpe_approx - control.sharpe_approx) / NULLIF(control.sharpe_approx, 0) * 100 as sharpe_lift_percent, control.total_pnl_dollars as control_pnl, treatment.total_pnl_dollars as treatment_pnl, (treatment.total_pnl_dollars - control.total_pnl_dollars) as pnl_diff, control.winning_trades::FLOAT / NULLIF(control.total_trades, 0) as control_win_rate, treatment.winning_trades::FLOAT / NULLIF(treatment.total_trades, 0) as treatment_win_rate FROM ab_test_experiments test LEFT JOIN ab_metrics control ON test.test_id = control.ab_test_id AND control.ab_group = 'control' LEFT JOIN ab_metrics treatment ON test.test_id = treatment.ab_test_id AND treatment.ab_group = 'treatment' WHERE test.status = 'running' ORDER BY test.started_at DESC; -- Query 15: Statistical significance test (Welch's t-test approximation) -- NOTE: For production, use actual statistical libraries; this is a simplified version WITH ab_pnl_samples AS ( SELECT ab_test_id, ab_group, pnl::DOUBLE PRECISION as pnl_sample FROM ensemble_predictions WHERE ab_test_id IS NOT NULL AND pnl IS NOT NULL AND timestamp >= NOW() - INTERVAL '7 days' ), ab_stats AS ( SELECT ab_test_id, ab_group, COUNT(*) as n, AVG(pnl_sample) as mean_pnl, STDDEV(pnl_sample) as std_pnl FROM ab_pnl_samples GROUP BY ab_test_id, ab_group ) SELECT test.test_name, control.n as control_n, treatment.n as treatment_n, control.mean_pnl / 100.0 as control_mean_pnl_dollars, treatment.mean_pnl / 100.0 as treatment_mean_pnl_dollars, (treatment.mean_pnl - control.mean_pnl) / 100.0 as mean_pnl_diff_dollars, control.std_pnl / 100.0 as control_std_dollars, treatment.std_pnl / 100.0 as treatment_std_dollars, -- Welch's t-statistic approximation (treatment.mean_pnl - control.mean_pnl) / SQRT((control.std_pnl * control.std_pnl) / control.n + (treatment.std_pnl * treatment.std_pnl) / treatment.n) as t_statistic, CASE WHEN ABS((treatment.mean_pnl - control.mean_pnl) / SQRT((control.std_pnl * control.std_pnl) / control.n + (treatment.std_pnl * treatment.std_pnl) / treatment.n)) > 1.96 THEN 'SIGNIFICANT (p < 0.05)' ELSE 'NOT SIGNIFICANT' END as significance_95pct FROM ab_test_experiments test LEFT JOIN ab_stats control ON test.test_id = control.ab_test_id AND control.ab_group = 'control' LEFT JOIN ab_stats treatment ON test.test_id = treatment.ab_test_id AND treatment.ab_group = 'treatment' WHERE test.status = 'running' ORDER BY test.started_at DESC; -- ================================================================================================ -- LATENCY ANALYSIS -- ================================================================================================ -- Query 16: P50/P95/P99 inference latency by symbol SELECT symbol, COUNT(*) as predictions, PERCENTILE_CONT(0.50) WITHIN GROUP (ORDER BY inference_latency_us) as p50_latency_us, PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY inference_latency_us) as p95_latency_us, PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY inference_latency_us) as p99_latency_us, AVG(inference_latency_us) as avg_latency_us, MAX(inference_latency_us) as max_latency_us FROM ensemble_predictions WHERE timestamp >= NOW() - INTERVAL '24 hours' AND inference_latency_us IS NOT NULL GROUP BY symbol ORDER BY p99_latency_us DESC; -- Query 17: Aggregation latency distribution SELECT symbol, COUNT(*) as predictions, PERCENTILE_CONT(0.50) WITHIN GROUP (ORDER BY aggregation_latency_us) as p50_agg_us, PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY aggregation_latency_us) as p95_agg_us, PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY aggregation_latency_us) as p99_agg_us, AVG(aggregation_latency_us) as avg_agg_us FROM ensemble_predictions WHERE timestamp >= NOW() - INTERVAL '24 hours' AND aggregation_latency_us IS NOT NULL GROUP BY symbol ORDER BY p99_agg_us DESC; -- ================================================================================================ -- CHECKPOINT TRACKING -- ================================================================================================ -- Query 18: Active checkpoints by model SELECT DISTINCT ON (model_id, symbol) CASE WHEN dqn_checkpoint_id IS NOT NULL THEN 'DQN' WHEN ppo_checkpoint_id IS NOT NULL THEN 'PPO' WHEN mamba2_checkpoint_id IS NOT NULL THEN 'MAMBA2' WHEN tft_checkpoint_id IS NOT NULL THEN 'TFT' END as model_id, symbol, COALESCE(dqn_checkpoint_id, ppo_checkpoint_id, mamba2_checkpoint_id, tft_checkpoint_id) as checkpoint_id, timestamp as last_used_at FROM ensemble_predictions WHERE timestamp >= NOW() - INTERVAL '24 hours' ORDER BY model_id, symbol, timestamp DESC; -- Query 19: Checkpoint performance comparison WITH checkpoint_metrics AS ( SELECT CASE WHEN dqn_checkpoint_id IS NOT NULL THEN 'DQN' WHEN ppo_checkpoint_id IS NOT NULL THEN 'PPO' WHEN mamba2_checkpoint_id IS NOT NULL THEN 'MAMBA2' WHEN tft_checkpoint_id IS NOT NULL THEN 'TFT' END as model_id, COALESCE(dqn_checkpoint_id, ppo_checkpoint_id, mamba2_checkpoint_id, tft_checkpoint_id) as checkpoint_id, COUNT(*) as predictions, AVG(ensemble_confidence) as avg_confidence, SUM(pnl) / 100.0 as total_pnl_dollars FROM ensemble_predictions WHERE timestamp >= NOW() - INTERVAL '7 days' AND pnl IS NOT NULL GROUP BY model_id, checkpoint_id ) SELECT * FROM checkpoint_metrics ORDER BY model_id, total_pnl_dollars DESC; -- ================================================================================================ -- CONTINUOUS AGGREGATE VIEWS (PRE-COMPUTED) -- ================================================================================================ -- Query 20: Hourly ensemble performance (pre-aggregated) SELECT bucket, symbol, ensemble_action, prediction_count, avg_confidence, avg_disagreement, avg_latency_us, total_pnl / 100.0 as total_pnl_dollars, CASE WHEN total_trades > 0 THEN winning_trades::FLOAT / total_trades ELSE 0 END as win_rate FROM ensemble_performance_hourly WHERE bucket >= NOW() - INTERVAL '24 hours' ORDER BY bucket DESC, symbol; -- Query 21: Daily model performance (pre-aggregated) SELECT bucket, model_id, symbol, total_predictions, avg_accuracy, total_pnl / 100.0 as total_pnl_dollars, avg_sharpe_ratio, avg_weight, avg_disagreement_rate FROM model_performance_daily WHERE bucket >= NOW() - INTERVAL '7 days' ORDER BY bucket DESC, avg_sharpe_ratio DESC NULLS LAST; -- ================================================================================================ -- DEBUGGING QUERIES -- ================================================================================================ -- Query 22: Failed predictions (high disagreement + negative P&L) SELECT timestamp, symbol, ensemble_action, ensemble_confidence, disagreement_rate, pnl / 100.0 as pnl_dollars, dqn_vote, ppo_vote, mamba2_vote, tft_vote, feature_snapshot FROM ensemble_predictions WHERE timestamp >= NOW() - INTERVAL '7 days' AND pnl IS NOT NULL AND pnl < 0 AND disagreement_rate > 0.5 ORDER BY pnl ASC LIMIT 50; -- Query 23: Model weight evolution over time SELECT DATE_TRUNC('hour', timestamp) as hour, model_id, AVG(avg_weight) as avg_weight FROM model_performance_attribution WHERE timestamp >= NOW() - INTERVAL '7 days' AND window_hours = 1 GROUP BY hour, model_id ORDER BY hour DESC, model_id; -- Query 24: Data quality checks (missing model votes) SELECT COUNT(*) as total_predictions, COUNT(dqn_signal) as dqn_present, COUNT(ppo_signal) as ppo_present, COUNT(mamba2_signal) as mamba2_present, COUNT(tft_signal) as tft_present, COUNT(*) - COUNT(dqn_signal) as dqn_missing, COUNT(*) - COUNT(ppo_signal) as ppo_missing, COUNT(*) - COUNT(mamba2_signal) as mamba2_missing, COUNT(*) - COUNT(tft_signal) as tft_missing FROM ensemble_predictions WHERE timestamp >= NOW() - INTERVAL '24 hours'; -- ================================================================================================ -- PERFORMANCE OPTIMIZATION QUERIES -- ================================================================================================ -- Query 25: Index usage statistics SELECT schemaname, tablename, indexname, idx_scan as index_scans, idx_tup_read as tuples_read, idx_tup_fetch as tuples_fetched FROM pg_stat_user_indexes WHERE schemaname = 'public' AND tablename IN ('ensemble_predictions', 'model_performance_attribution') ORDER BY idx_scan DESC; -- Query 26: Table size and compression stats SELECT hypertable_name, table_bytes, index_bytes, toast_bytes, total_bytes, pg_size_pretty(total_bytes) as total_size, compression_enabled, compressed_total_bytes, pg_size_pretty(compressed_total_bytes) as compressed_size, CASE WHEN total_bytes > 0 THEN ROUND(100.0 * (1 - compressed_total_bytes::FLOAT / total_bytes), 2) ELSE 0 END as compression_ratio_percent FROM timescaledb_information.hypertable WHERE hypertable_name IN ('ensemble_predictions', 'model_performance_attribution'); -- ================================================================================================ -- END OF ANALYSIS QUERIES -- ================================================================================================