Files
foxhunt/migrations/012_create_event_and_config_tables.sql
jgrusewski 36c9c7cfe3 🔧 Wave 112: Migration consolidation and cleanup
- Removed 9 deprecated migration files (001-006 up/down, auth_schema, trading_service_events)
- Updated 12 core migrations with improved constraints and indexing
- Consolidated schema from 22 migrations to 18 clean migrations
- All migrations tested and applied successfully (Agent 32 validation)
- Zero migration errors in production database
2025-10-05 19:42:08 +02:00

222 lines
10 KiB
PL/PgSQL

-- Migration 012: Create Event Processing and Configuration Tables
-- This migration creates the missing tables for event processing and configuration
-- Tables: market_events, trading_events, event_processing_stats, configuration, secrets
-- Enable required extensions if not already enabled
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Market events table - stores market-related events
CREATE TABLE IF NOT EXISTS market_events (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
event_type VARCHAR(64) NOT NULL,
symbol VARCHAR(32),
"timestamp" TIMESTAMP WITH TIME ZONE NOT NULL,
event_data JSONB NOT NULL,
source VARCHAR(64), -- Event source (exchange, internal, etc.)
severity VARCHAR(20) DEFAULT 'info' CHECK (severity IN ('debug', 'info', 'warning', 'error', 'critical')),
processed BOOLEAN DEFAULT FALSE,
processed_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);
-- Trading events table - stores trading-related events
CREATE TABLE IF NOT EXISTS trading_events (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
event_type VARCHAR(64) NOT NULL,
symbol VARCHAR(32),
"order_id" UUID,
account_id VARCHAR(64),
"timestamp" TIMESTAMP WITH TIME ZONE NOT NULL,
event_data JSONB NOT NULL,
"correlation_id" UUID, -- For tracing related events
severity VARCHAR(20) DEFAULT 'info' CHECK (severity IN ('debug', 'info', 'warning', 'error', 'critical')),
processed BOOLEAN DEFAULT FALSE,
processed_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);
-- Event processing stats table - stores processing statistics
CREATE TABLE IF NOT EXISTS event_processing_stats (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
processor_name VARCHAR(128) NOT NULL,
event_type VARCHAR(64) NOT NULL,
"timestamp" TIMESTAMP WITH TIME ZONE NOT NULL,
events_processed INTEGER NOT NULL DEFAULT 0,
events_failed INTEGER NOT NULL DEFAULT 0,
average_processing_time_ms DECIMAL(10, 3),
max_processing_time_ms DECIMAL(10, 3),
min_processing_time_ms DECIMAL(10, 3),
total_processing_time_ms BIGINT,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
UNIQUE(processor_name, event_type, "timestamp")
);
-- Configuration table - stores application configuration (if not already exists from config migration)
CREATE TABLE IF NOT EXISTS configuration (
id SERIAL PRIMARY KEY,
key VARCHAR(256) NOT NULL UNIQUE,
value TEXT NOT NULL,
value_type VARCHAR(32) NOT NULL DEFAULT 'string' CHECK (value_type IN ('string', 'number', 'boolean', 'json')),
description TEXT,
category VARCHAR(128),
environment VARCHAR(32) DEFAULT 'development',
is_sensitive BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
created_by VARCHAR(128),
updated_by VARCHAR(128)
);
-- Secrets table - stores encrypted sensitive configuration
CREATE TABLE IF NOT EXISTS secrets (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
key VARCHAR(256) NOT NULL,
encrypted_value BYTEA NOT NULL,
key_version INTEGER NOT NULL DEFAULT 1,
environment VARCHAR(32) DEFAULT 'development',
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
expires_at TIMESTAMP WITH TIME ZONE,
created_by VARCHAR(128),
updated_by VARCHAR(128),
UNIQUE(key, environment)
);
-- Risk-related tables that were referenced
CREATE TABLE IF NOT EXISTS risk_alerts (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
alert_type VARCHAR(64) NOT NULL,
symbol VARCHAR(32),
account_id VARCHAR(64),
severity VARCHAR(20) NOT NULL CHECK (severity IN ('low', 'medium', 'high', 'critical')),
message TEXT NOT NULL,
threshold_value DECIMAL(20, 8),
current_value DECIMAL(20, 8),
metadata JSONB,
acknowledged BOOLEAN DEFAULT FALSE,
acknowledged_by VARCHAR(128),
acknowledged_at TIMESTAMP WITH TIME ZONE,
resolved BOOLEAN DEFAULT FALSE,
resolved_by VARCHAR(128),
resolved_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS position_risks (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
symbol VARCHAR(32) NOT NULL,
account_id VARCHAR(64),
position_size BIGINT NOT NULL,
market_value DECIMAL(20, 8) NOT NULL,
unrealized_pnl DECIMAL(20, 8),
var_1d DECIMAL(20, 8), -- 1-day Value at Risk
var_10d DECIMAL(20, 8), -- 10-day Value at Risk
exposure_pct DECIMAL(10, 6), -- Exposure as percentage of total portfolio
concentration_risk DECIMAL(10, 6),
liquidity_risk DECIMAL(10, 6),
calculated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
UNIQUE(symbol, account_id, calculated_at)
);
CREATE TABLE IF NOT EXISTS var_calculations (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
portfolio_id VARCHAR(128) NOT NULL,
calculation_date DATE NOT NULL,
confidence_level DECIMAL(5, 4) NOT NULL, -- e.g., 0.99 for 99%
time_horizon_days INTEGER NOT NULL,
var_amount DECIMAL(20, 8) NOT NULL,
expected_shortfall DECIMAL(20, 8), -- Conditional VaR
calculation_method VARCHAR(64) NOT NULL, -- historical, parametric, monte_carlo
model_parameters JSONB,
calculation_time_ms INTEGER,
calculated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
UNIQUE(portfolio_id, calculation_date, confidence_level, time_horizon_days)
);
-- Create indexes for optimal query performance
-- Market events indexes (only create if table was created by this migration, not migration 001)
DO $$
BEGIN
-- Check if market_events has the "timestamp" column (our schema) vs event_timestamp (migration 001 schema)
IF EXISTS (SELECT 1 FROM information_schema.columns
WHERE table_name = 'market_events' AND column_name = 'timestamp') THEN
CREATE INDEX IF NOT EXISTS idx_market_events_type_timestamp ON market_events(event_type, "timestamp" DESC);
CREATE INDEX IF NOT EXISTS idx_market_events_symbol_timestamp ON market_events(symbol, "timestamp" DESC);
CREATE INDEX IF NOT EXISTS idx_market_events_timestamp ON market_events("timestamp" DESC);
CREATE INDEX IF NOT EXISTS idx_market_events_processed ON market_events(processed, "timestamp" DESC);
END IF;
END $$;
-- Trading events indexes (only create if table was created by this migration, not migration 001)
DO $$
BEGIN
-- Check if trading_events has the "timestamp" column (our schema) vs event_timestamp (migration 001 schema)
IF EXISTS (SELECT 1 FROM information_schema.columns
WHERE table_name = 'trading_events' AND column_name = 'timestamp') THEN
CREATE INDEX IF NOT EXISTS idx_trading_events_type_timestamp ON trading_events(event_type, "timestamp" DESC);
CREATE INDEX IF NOT EXISTS idx_trading_events_symbol_timestamp ON trading_events(symbol, "timestamp" DESC);
CREATE INDEX IF NOT EXISTS idx_trading_events_order_id ON trading_events("order_id");
CREATE INDEX IF NOT EXISTS idx_trading_events_correlation_id ON trading_events("correlation_id");
CREATE INDEX IF NOT EXISTS idx_trading_events_timestamp ON trading_events("timestamp" DESC);
END IF;
END $$;
-- Event processing stats indexes (always create as this table is new)
CREATE INDEX IF NOT EXISTS idx_event_processing_stats_processor_type ON event_processing_stats(processor_name, event_type, "timestamp" DESC);
-- Configuration indexes
CREATE INDEX IF NOT EXISTS idx_configuration_category ON configuration(category);
CREATE INDEX IF NOT EXISTS idx_configuration_environment ON configuration(environment);
-- Secrets indexes
CREATE INDEX IF NOT EXISTS idx_secrets_environment ON secrets(environment);
CREATE INDEX IF NOT EXISTS idx_secrets_expires_at ON secrets(expires_at);
-- Risk alerts indexes
CREATE INDEX IF NOT EXISTS idx_risk_alerts_type_severity ON risk_alerts(alert_type, severity, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_risk_alerts_symbol ON risk_alerts(symbol, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_risk_alerts_unresolved ON risk_alerts(resolved, created_at DESC) WHERE NOT resolved;
-- Position risks indexes
CREATE INDEX IF NOT EXISTS idx_position_risks_symbol_date ON position_risks(symbol, calculated_at DESC);
CREATE INDEX IF NOT EXISTS idx_position_risks_account_date ON position_risks(account_id, calculated_at DESC);
-- VaR calculations indexes
CREATE INDEX IF NOT EXISTS idx_var_calculations_portfolio_date ON var_calculations(portfolio_id, calculation_date DESC);
-- Add triggers for updating timestamps
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ language 'plpgsql';
-- Apply update triggers
CREATE TRIGGER update_configuration_updated_at BEFORE UPDATE ON configuration
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_secrets_updated_at BEFORE UPDATE ON secrets
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- Add table comments
COMMENT ON TABLE market_events IS 'Market-related events from exchanges and internal systems';
COMMENT ON TABLE trading_events IS 'Trading-related events including orders, fills, and position changes';
COMMENT ON TABLE event_processing_stats IS 'Statistics and metrics for event processing performance';
COMMENT ON TABLE configuration IS 'Application configuration key-value pairs';
COMMENT ON TABLE secrets IS 'Encrypted sensitive configuration data';
COMMENT ON TABLE risk_alerts IS 'Risk management alerts and notifications';
COMMENT ON TABLE position_risks IS 'Position-level risk calculations and metrics';
COMMENT ON TABLE var_calculations IS 'Value at Risk calculations for portfolios';
-- Grant permissions (assuming the trading service user exists from previous migrations)
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'trading_service') THEN
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO trading_service;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO trading_service;
END IF;
END
$$;