BEGIN; ALTER TABLE app_user ADD COLUMN IF NOT EXISTS is_admin boolean NOT NULL DEFAULT false; CREATE INDEX IF NOT EXISTS idx_app_user_is_admin ON app_user (is_admin); CREATE INDEX IF NOT EXISTS idx_strategy_run_user_status ON strategy_run (user_id, status); CREATE INDEX IF NOT EXISTS idx_strategy_run_user_created ON strategy_run (user_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_engine_event_user_run_ts ON engine_event (user_id, run_id, ts DESC); CREATE INDEX IF NOT EXISTS idx_strategy_log_user_run_ts ON strategy_log (user_id, run_id, ts DESC); CREATE INDEX IF NOT EXISTS idx_event_ledger_user_run_logical ON event_ledger (user_id, run_id, logical_time); CREATE INDEX IF NOT EXISTS idx_paper_order_user_run_ts ON paper_order (user_id, run_id, "timestamp" DESC); CREATE INDEX IF NOT EXISTS idx_paper_trade_user_run_ts ON paper_trade (user_id, run_id, "timestamp" DESC); CREATE INDEX IF NOT EXISTS idx_mtm_ledger_user_run_ts ON mtm_ledger (user_id, run_id, "timestamp" DESC); CREATE INDEX IF NOT EXISTS idx_paper_equity_curve_user_run_ts ON paper_equity_curve (user_id, run_id, "timestamp" DESC); CREATE INDEX IF NOT EXISTS idx_engine_status_user_run ON engine_status (user_id, run_id); CREATE INDEX IF NOT EXISTS idx_engine_state_paper_user_run ON engine_state_paper (user_id, run_id); CREATE OR REPLACE VIEW admin_user_metrics AS WITH session_stats AS ( SELECT user_id, MIN(created_at) AS first_session_at, MAX(COALESCE(last_seen_at, created_at)) AS last_login_at FROM app_session GROUP BY user_id ), run_stats AS ( SELECT user_id, COUNT(*) AS runs_count, MAX(CASE WHEN status = 'RUNNING' THEN run_id END) AS active_run_id, MAX(CASE WHEN status = 'RUNNING' THEN status END) AS active_run_status, MIN(created_at) AS first_run_at FROM strategy_run GROUP BY user_id ), broker_stats AS ( SELECT user_id, BOOL_OR(connected) AS broker_connected FROM user_broker GROUP BY user_id ) SELECT u.id AS user_id, u.username, u.is_admin, COALESCE(session_stats.first_session_at, run_stats.first_run_at) AS created_at, session_stats.last_login_at, COALESCE(run_stats.runs_count, 0) AS runs_count, run_stats.active_run_id, run_stats.active_run_status, COALESCE(broker_stats.broker_connected, FALSE) AS broker_connected FROM app_user u LEFT JOIN session_stats ON session_stats.user_id = u.id LEFT JOIN run_stats ON run_stats.user_id = u.id LEFT JOIN broker_stats ON broker_stats.user_id = u.id; CREATE OR REPLACE VIEW admin_run_metrics AS WITH order_stats AS ( SELECT user_id, run_id, COUNT(*) AS order_count, MAX("timestamp") AS last_order_time FROM paper_order GROUP BY user_id, run_id ), trade_stats AS ( SELECT user_id, run_id, COUNT(*) AS trade_count, MAX("timestamp") AS last_trade_time FROM paper_trade GROUP BY user_id, run_id ), event_stats AS ( SELECT user_id, run_id, MAX("timestamp") AS last_event_time, MAX(CASE WHEN event = 'SIP_EXECUTED' THEN "timestamp" END) AS last_sip_time FROM event_ledger GROUP BY user_id, run_id ), equity_latest AS ( SELECT DISTINCT ON (user_id, run_id) user_id, run_id, equity AS equity_latest, pnl AS pnl_latest, "timestamp" AS equity_ts FROM paper_equity_curve ORDER BY user_id, run_id, "timestamp" DESC ), mtm_latest AS ( SELECT DISTINCT ON (user_id, run_id) user_id, run_id, "timestamp" AS mtm_ts FROM mtm_ledger ORDER BY user_id, run_id, "timestamp" DESC ), log_latest AS ( SELECT user_id, run_id, MAX(ts) AS last_log_time FROM strategy_log GROUP BY user_id, run_id ), engine_latest AS ( SELECT user_id, run_id, MAX(ts) AS last_engine_time FROM engine_event GROUP BY user_id, run_id ), activity AS ( SELECT user_id, run_id, MAX(ts) AS last_event_time FROM ( SELECT user_id, run_id, ts FROM engine_event UNION ALL SELECT user_id, run_id, ts FROM strategy_log UNION ALL SELECT user_id, run_id, "timestamp" AS ts FROM paper_order UNION ALL SELECT user_id, run_id, "timestamp" AS ts FROM paper_trade UNION ALL SELECT user_id, run_id, "timestamp" AS ts FROM mtm_ledger UNION ALL SELECT user_id, run_id, "timestamp" AS ts FROM paper_equity_curve UNION ALL SELECT user_id, run_id, "timestamp" AS ts FROM event_ledger ) t GROUP BY user_id, run_id ) SELECT sr.run_id, sr.user_id, sr.status, sr.created_at, sr.started_at, sr.stopped_at, sr.strategy, sr.mode, sr.broker, sc.sip_amount, sc.sip_frequency_value, sc.sip_frequency_unit, sc.next_run AS next_sip_time, activity.last_event_time, event_stats.last_sip_time, COALESCE(order_stats.order_count, 0) AS order_count, COALESCE(trade_stats.trade_count, 0) AS trade_count, equity_latest.equity_latest, equity_latest.pnl_latest FROM strategy_run sr LEFT JOIN strategy_config sc ON sc.user_id = sr.user_id AND sc.run_id = sr.run_id LEFT JOIN order_stats ON order_stats.user_id = sr.user_id AND order_stats.run_id = sr.run_id LEFT JOIN trade_stats ON trade_stats.user_id = sr.user_id AND trade_stats.run_id = sr.run_id LEFT JOIN event_stats ON event_stats.user_id = sr.user_id AND event_stats.run_id = sr.run_id LEFT JOIN equity_latest ON equity_latest.user_id = sr.user_id AND equity_latest.run_id = sr.run_id LEFT JOIN mtm_latest ON mtm_latest.user_id = sr.user_id AND mtm_latest.run_id = sr.run_id LEFT JOIN log_latest ON log_latest.user_id = sr.user_id AND log_latest.run_id = sr.run_id LEFT JOIN engine_latest ON engine_latest.user_id = sr.user_id AND engine_latest.run_id = sr.run_id LEFT JOIN activity ON activity.user_id = sr.user_id AND activity.run_id = sr.run_id; CREATE OR REPLACE VIEW admin_engine_health AS WITH activity AS ( SELECT user_id, run_id, MAX(ts) AS last_event_time FROM ( SELECT user_id, run_id, ts FROM engine_event UNION ALL SELECT user_id, run_id, ts FROM strategy_log UNION ALL SELECT user_id, run_id, "timestamp" AS ts FROM event_ledger ) t GROUP BY user_id, run_id ) SELECT sr.run_id, sr.user_id, sr.status, activity.last_event_time, es.status AS engine_status, es.last_updated AS engine_status_ts FROM strategy_run sr LEFT JOIN activity ON activity.user_id = sr.user_id AND activity.run_id = sr.run_id LEFT JOIN engine_status es ON es.user_id = sr.user_id AND es.run_id = sr.run_id; CREATE OR REPLACE VIEW admin_order_stats AS SELECT user_id, run_id, COUNT(*) AS total_orders, COUNT(*) FILTER (WHERE "timestamp" >= now() - interval '24 hours') AS orders_last_24h, COUNT(*) FILTER (WHERE status = 'FILLED') AS filled_orders FROM paper_order GROUP BY user_id, run_id; CREATE OR REPLACE VIEW admin_ledger_stats AS WITH mtm_latest AS ( SELECT DISTINCT ON (user_id, run_id) user_id, run_id, portfolio_value, pnl, "timestamp" AS mtm_ts FROM mtm_ledger ORDER BY user_id, run_id, "timestamp" DESC ), equity_latest AS ( SELECT DISTINCT ON (user_id, run_id) user_id, run_id, equity, pnl, "timestamp" AS equity_ts FROM paper_equity_curve ORDER BY user_id, run_id, "timestamp" DESC ) SELECT sr.user_id, sr.run_id, mtm_latest.portfolio_value AS mtm_value, mtm_latest.pnl AS mtm_pnl, mtm_latest.mtm_ts, equity_latest.equity AS equity_value, equity_latest.pnl AS equity_pnl, equity_latest.equity_ts FROM strategy_run sr LEFT JOIN mtm_latest ON mtm_latest.user_id = sr.user_id AND mtm_latest.run_id = sr.run_id LEFT JOIN equity_latest ON equity_latest.user_id = sr.user_id AND equity_latest.run_id = sr.run_id; COMMIT;