- 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
222 lines
10 KiB
PL/PgSQL
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
|
|
$$; |