-- Symbol Configuration Tables Migration -- Comprehensive symbol classification and configuration management -- Created: 2025-09-29 -- Purpose: Support symbol-specific trading parameters, volatility profiles, and market hours -- Enable required extensions CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- Asset Classification enumeration CREATE TYPE asset_classification AS ENUM ( 'EQUITY', 'FUTURE', 'FOREX', 'CRYPTO', 'COMMODITY', 'FIXED_INCOME', 'OPTION', 'ETF', 'INDEX', 'DERIVATIVE' ); -- Volatility regime classification CREATE TYPE volatility_regime AS ENUM ( 'LOW', 'NORMAL', 'ELEVATED', 'HIGH' ); -- Main symbol configuration table CREATE TABLE symbol_config ( -- Primary identification id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), symbol VARCHAR(50) NOT NULL UNIQUE, description TEXT NOT NULL, classification asset_classification NOT NULL, -- Market parameters primary_exchange VARCHAR(50) NOT NULL, currency VARCHAR(3) NOT NULL DEFAULT 'USD', tick_size DECIMAL(18,8) NOT NULL DEFAULT 0.01, lot_size DECIMAL(18,8) NOT NULL DEFAULT 1.0, min_order_size DECIMAL(18,8) NOT NULL DEFAULT 1.0, max_order_size DECIMAL(18,8) NOT NULL DEFAULT 1000000.0, -- Financial metrics sector VARCHAR(100), industry VARCHAR(100), market_cap DECIMAL(20,2), avg_daily_volume DECIMAL(20,2) DEFAULT 0.0, margin_requirement DECIMAL(5,4) NOT NULL DEFAULT 0.25, -- Risk parameters position_limit DECIMAL(18,8), risk_multiplier DECIMAL(8,4) NOT NULL DEFAULT 1.0, -- Status and metadata is_active BOOLEAN NOT NULL DEFAULT true, data_source VARCHAR(50) NOT NULL DEFAULT 'manual', -- Timestamps created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), last_validated TIMESTAMP WITH TIME ZONE, -- Constraints CONSTRAINT symbol_config_tick_size_positive CHECK (tick_size > 0), CONSTRAINT symbol_config_lot_size_positive CHECK (lot_size > 0), CONSTRAINT symbol_config_min_order_positive CHECK (min_order_size > 0), CONSTRAINT symbol_config_max_order_valid CHECK (max_order_size >= min_order_size), CONSTRAINT symbol_config_margin_valid CHECK (margin_requirement >= 0 AND margin_requirement <= 1), CONSTRAINT symbol_config_risk_multiplier_positive CHECK (risk_multiplier > 0) ); -- Volatility profile table CREATE TABLE volatility_profile ( -- Primary identification id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), symbol_config_id UUID NOT NULL REFERENCES symbol_config(id) ON DELETE CASCADE, -- Volatility metrics average_volatility DECIMAL(8,6) NOT NULL DEFAULT 0.20, max_volatility DECIMAL(8,6) NOT NULL DEFAULT 1.00, min_volatility DECIMAL(8,6) NOT NULL DEFAULT 0.05, beta DECIMAL(8,4) NOT NULL DEFAULT 1.0, atr DECIMAL(18,8) NOT NULL DEFAULT 0.0, market_correlation DECIMAL(6,4) NOT NULL DEFAULT 0.0, volatility_regime volatility_regime NOT NULL DEFAULT 'NORMAL', -- Metadata sample_size INTEGER NOT NULL DEFAULT 0, last_updated TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), -- Constraints CONSTRAINT volatility_profile_avg_positive CHECK (average_volatility >= 0), CONSTRAINT volatility_profile_max_positive CHECK (max_volatility >= 0), CONSTRAINT volatility_profile_min_positive CHECK (min_volatility >= 0), CONSTRAINT volatility_profile_range_valid CHECK (max_volatility >= average_volatility AND average_volatility >= min_volatility), CONSTRAINT volatility_profile_correlation_valid CHECK (market_correlation >= -1 AND market_correlation <= 1), CONSTRAINT volatility_profile_sample_size_positive CHECK (sample_size >= 0), -- Unique constraint UNIQUE(symbol_config_id) ); -- Trading hours table CREATE TABLE trading_hours ( -- Primary identification id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), symbol_config_id UUID NOT NULL REFERENCES symbol_config(id) ON DELETE CASCADE, -- Time zone and hours timezone VARCHAR(50) NOT NULL DEFAULT 'America/New_York', market_open TIME NOT NULL DEFAULT '09:30:00', market_close TIME NOT NULL DEFAULT '16:00:00', pre_market_open TIME, after_hours_close TIME, -- Trading days (stored as bit flags: Mon=1, Tue=2, Wed=4, Thu=8, Fri=16, Sat=32, Sun=64) trading_days_mask INTEGER NOT NULL DEFAULT 31, -- Mon-Fri = 1+2+4+8+16 = 31 -- Metadata created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), -- Constraints CONSTRAINT trading_hours_open_before_close CHECK (market_open < market_close), CONSTRAINT trading_hours_days_valid CHECK (trading_days_mask > 0 AND trading_days_mask < 128), -- Unique constraint UNIQUE(symbol_config_id) ); -- Market holidays table CREATE TABLE market_holidays ( -- Primary identification id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), trading_hours_id UUID NOT NULL REFERENCES trading_hours(id) ON DELETE CASCADE, -- Holiday information holiday_date DATE NOT NULL, holiday_name VARCHAR(100) NOT NULL, is_half_day BOOLEAN NOT NULL DEFAULT false, early_close_time TIME, -- Only applicable if is_half_day = true -- Metadata created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), -- Constraints CONSTRAINT market_holidays_early_close_valid CHECK ( (is_half_day = false AND early_close_time IS NULL) OR (is_half_day = true AND early_close_time IS NOT NULL) ), -- Unique constraint UNIQUE(trading_hours_id, holiday_date) ); -- Symbol configuration tags table (for flexible categorization) CREATE TABLE symbol_config_tags ( -- Primary identification id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), symbol_config_id UUID NOT NULL REFERENCES symbol_config(id) ON DELETE CASCADE, -- Tag information tag_key VARCHAR(50) NOT NULL, tag_value VARCHAR(100) NOT NULL, -- Metadata created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), -- Unique constraint UNIQUE(symbol_config_id, tag_key) ); -- Indexes for performance CREATE INDEX idx_symbol_config_symbol ON symbol_config(symbol); CREATE INDEX idx_symbol_config_classification ON symbol_config(classification); CREATE INDEX idx_symbol_config_exchange ON symbol_config(primary_exchange); CREATE INDEX idx_symbol_config_active ON symbol_config(is_active); CREATE INDEX idx_symbol_config_updated ON symbol_config(updated_at); CREATE INDEX idx_volatility_profile_regime ON volatility_profile(volatility_regime); CREATE INDEX idx_volatility_profile_updated ON volatility_profile(last_updated); CREATE INDEX idx_trading_hours_timezone ON trading_hours(timezone); CREATE INDEX idx_market_holidays_date ON market_holidays(holiday_date); CREATE INDEX idx_symbol_tags_key ON symbol_config_tags(tag_key); CREATE INDEX idx_symbol_tags_value ON symbol_config_tags(tag_value); -- Function to update updated_at timestamp CREATE OR REPLACE FUNCTION update_symbol_config_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql; -- Trigger to automatically update updated_at CREATE TRIGGER symbol_config_update_trigger BEFORE UPDATE ON symbol_config FOR EACH ROW EXECUTE FUNCTION update_symbol_config_updated_at(); CREATE TRIGGER trading_hours_update_trigger BEFORE UPDATE ON trading_hours FOR EACH ROW EXECUTE FUNCTION update_symbol_config_updated_at(); -- Function to automatically create default volatility profile and trading hours CREATE OR REPLACE FUNCTION create_default_symbol_components() RETURNS TRIGGER AS $$ BEGIN -- Create default volatility profile INSERT INTO volatility_profile (symbol_config_id) VALUES (NEW.id); -- Create default trading hours based on asset classification INSERT INTO trading_hours ( symbol_config_id, timezone, market_open, market_close, pre_market_open, after_hours_close, trading_days_mask ) VALUES ( NEW.id, CASE WHEN NEW.classification = 'CRYPTO' THEN 'UTC' WHEN NEW.classification = 'FOREX' THEN 'America/New_York' ELSE 'America/New_York' END, CASE WHEN NEW.classification = 'CRYPTO' THEN '00:00:00'::TIME WHEN NEW.classification = 'FOREX' THEN '17:00:00'::TIME -- Sunday 5 PM EST (Forex market opens) ELSE '09:30:00'::TIME END, CASE WHEN NEW.classification = 'CRYPTO' THEN '23:59:59'::TIME WHEN NEW.classification = 'FOREX' THEN '17:00:01'::TIME -- Friday 5:00:01 PM EST (Forex market closes, 1 second after open for weekly cycle) ELSE '16:00:00'::TIME END, CASE WHEN NEW.classification IN ('EQUITY', 'ETF') THEN '04:00:00'::TIME ELSE NULL END, CASE WHEN NEW.classification IN ('EQUITY', 'ETF') THEN '20:00:00'::TIME ELSE NULL END, CASE WHEN NEW.classification = 'CRYPTO' THEN 127 -- All days WHEN NEW.classification = 'FOREX' THEN 95 -- Sun-Fri (64+1+2+4+8+16) ELSE 31 -- Mon-Fri (1+2+4+8+16) END ); RETURN NEW; END; $$ LANGUAGE plpgsql; -- Trigger to create default components CREATE TRIGGER symbol_config_create_defaults_trigger AFTER INSERT ON symbol_config FOR EACH ROW EXECUTE FUNCTION create_default_symbol_components(); -- Views for easy access to complete symbol configuration -- Complete symbol configuration view CREATE VIEW v_symbol_config_complete AS SELECT sc.*, vp.average_volatility, vp.max_volatility, vp.min_volatility, vp.beta, vp.atr, vp.market_correlation, vp.volatility_regime, vp.sample_size, vp.last_updated as volatility_last_updated, th.timezone, th.market_open, th.market_close, th.pre_market_open, th.after_hours_close, th.trading_days_mask FROM symbol_config sc LEFT JOIN volatility_profile vp ON sc.id = vp.symbol_config_id LEFT JOIN trading_hours th ON sc.id = th.symbol_config_id; -- Active symbols view CREATE VIEW v_active_symbols AS SELECT * FROM v_symbol_config_complete WHERE is_active = true; -- Symbol configuration by classification view CREATE VIEW v_symbols_by_classification AS SELECT classification, COUNT(*) as symbol_count, COUNT(CASE WHEN is_active THEN 1 END) as active_count, AVG(avg_daily_volume) as avg_volume, AVG(margin_requirement) as avg_margin_requirement FROM symbol_config GROUP BY classification; -- Insert some example symbol configurations INSERT INTO symbol_config (symbol, description, classification, primary_exchange, currency, sector, industry) VALUES ('AAPL', 'Apple Inc.', 'EQUITY', 'NASDAQ', 'USD', 'Technology', 'Consumer Electronics'), ('MSFT', 'Microsoft Corporation', 'EQUITY', 'NASDAQ', 'USD', 'Technology', 'Software'), ('SPY', 'SPDR S&P 500 ETF Trust', 'ETF', 'NYSE Arca', 'USD', NULL, NULL), ('EURUSD', 'Euro/US Dollar', 'FOREX', 'OTC', 'USD', NULL, NULL), ('BTCUSD', 'Bitcoin/US Dollar', 'CRYPTO', 'Coinbase', 'USD', NULL, NULL), ('ESH5', 'E-mini S&P 500 Future March 2025', 'FUTURE', 'CME', 'USD', NULL, NULL); -- Update volatility profiles with example data UPDATE volatility_profile SET average_volatility = 0.25, max_volatility = 0.80, min_volatility = 0.10, beta = 1.2, volatility_regime = 'NORMAL' WHERE symbol_config_id = (SELECT id FROM symbol_config WHERE symbol = 'AAPL'); UPDATE volatility_profile SET average_volatility = 0.22, max_volatility = 0.65, min_volatility = 0.12, beta = 0.9, volatility_regime = 'NORMAL' WHERE symbol_config_id = (SELECT id FROM symbol_config WHERE symbol = 'MSFT'); UPDATE volatility_profile SET average_volatility = 0.15, max_volatility = 0.45, min_volatility = 0.08, beta = 1.0, volatility_regime = 'NORMAL' WHERE symbol_config_id = (SELECT id FROM symbol_config WHERE symbol = 'SPY'); UPDATE volatility_profile SET average_volatility = 0.45, max_volatility = 1.20, min_volatility = 0.20, beta = 0.1, volatility_regime = 'ELEVATED' WHERE symbol_config_id = (SELECT id FROM symbol_config WHERE symbol = 'BTCUSD'); -- Add some configuration tags INSERT INTO symbol_config_tags (symbol_config_id, tag_key, tag_value) VALUES ((SELECT id FROM symbol_config WHERE symbol = 'AAPL'), 'sector', 'technology'), ((SELECT id FROM symbol_config WHERE symbol = 'AAPL'), 'market_cap', 'large'), ((SELECT id FROM symbol_config WHERE symbol = 'MSFT'), 'sector', 'technology'), ((SELECT id FROM symbol_config WHERE symbol = 'MSFT'), 'market_cap', 'large'), ((SELECT id FROM symbol_config WHERE symbol = 'SPY'), 'type', 'index_etf'), ((SELECT id FROM symbol_config WHERE symbol = 'BTCUSD'), 'type', 'digital_asset'), ((SELECT id FROM symbol_config WHERE symbol = 'ESH5'), 'type', 'equity_index_future'); -- Add some market holidays for US equity markets INSERT INTO market_holidays (trading_hours_id, holiday_date, holiday_name, is_half_day, early_close_time) SELECT th.id, '2025-01-01'::DATE, 'New Year''s Day', false, NULL FROM trading_hours th JOIN symbol_config sc ON th.symbol_config_id = sc.id WHERE sc.classification IN ('EQUITY', 'ETF'); INSERT INTO market_holidays (trading_hours_id, holiday_date, holiday_name, is_half_day, early_close_time) SELECT th.id, '2025-07-04'::DATE, 'Independence Day', false, NULL FROM trading_hours th JOIN symbol_config sc ON th.symbol_config_id = sc.id WHERE sc.classification IN ('EQUITY', 'ETF'); INSERT INTO market_holidays (trading_hours_id, holiday_date, holiday_name, is_half_day, early_close_time) SELECT th.id, '2025-11-27'::DATE, 'Thanksgiving Day (Half Day)', true, '13:00:00'::TIME FROM trading_hours th JOIN symbol_config sc ON th.symbol_config_id = sc.id WHERE sc.classification IN ('EQUITY', 'ETF'); -- Create configuration change notification function CREATE OR REPLACE FUNCTION notify_symbol_config_change() RETURNS TRIGGER AS $$ BEGIN -- Send notification for configuration changes PERFORM pg_notify('symbol_config_changed', json_build_object( 'action', TG_OP, 'symbol', COALESCE(NEW.symbol, OLD.symbol), 'id', COALESCE(NEW.id, OLD.id), 'timestamp', NOW() )::text ); RETURN COALESCE(NEW, OLD); END; $$ LANGUAGE plpgsql; -- Triggers for configuration change notifications CREATE TRIGGER symbol_config_notify_trigger AFTER INSERT OR UPDATE OR DELETE ON symbol_config FOR EACH ROW EXECUTE FUNCTION notify_symbol_config_change(); CREATE TRIGGER volatility_profile_notify_trigger AFTER INSERT OR UPDATE OR DELETE ON volatility_profile FOR EACH ROW EXECUTE FUNCTION notify_symbol_config_change(); -- Grant permissions (adjust as needed for your environment) -- GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO trading_user; -- GRANT SELECT, UPDATE ON ALL SEQUENCES IN SCHEMA public TO trading_user; -- GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO trading_user; -- Performance optimization: Create partial indexes for common queries CREATE INDEX idx_symbol_config_active_equity ON symbol_config(symbol) WHERE is_active = true AND classification = 'EQUITY'; CREATE INDEX idx_symbol_config_active_crypto ON symbol_config(symbol) WHERE is_active = true AND classification = 'CRYPTO'; CREATE INDEX idx_volatility_profile_high_vol ON volatility_profile(symbol_config_id, average_volatility) WHERE volatility_regime IN ('ELEVATED', 'HIGH'); -- Add comments for documentation COMMENT ON TABLE symbol_config IS 'Core symbol configuration with trading parameters and metadata'; COMMENT ON TABLE volatility_profile IS 'Volatility metrics and risk characteristics for each symbol'; COMMENT ON TABLE trading_hours IS 'Market operating hours and trading session definitions'; COMMENT ON TABLE market_holidays IS 'Market holidays and half-day sessions'; COMMENT ON TABLE symbol_config_tags IS 'Flexible key-value tags for symbol categorization'; COMMENT ON COLUMN symbol_config.symbol IS 'Unique symbol identifier (e.g., AAPL, EURUSD, BTCUSD)'; COMMENT ON COLUMN symbol_config.classification IS 'Asset class for regulatory and risk management purposes'; COMMENT ON COLUMN symbol_config.tick_size IS 'Minimum price increment for the instrument'; COMMENT ON COLUMN symbol_config.margin_requirement IS 'Initial margin requirement as decimal (0.25 = 25%)'; COMMENT ON COLUMN volatility_profile.volatility_regime IS 'Current volatility classification for risk management'; COMMENT ON COLUMN trading_hours.trading_days_mask IS 'Bit mask for trading days (Mon=1, Tue=2, Wed=4, Thu=8, Fri=16, Sat=32, Sun=64)';