Files
foxhunt/migrations/009_dual_provider_configuration.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

428 lines
27 KiB
PL/PgSQL

-- Dual-Provider Configuration Migration for Foxhunt HFT Trading System
-- ======================================================================
-- This migration adds support for dual data providers (Databento + Benzinga)
-- and removes legacy Polygon configurations.
-- === PROVIDER CONFIGURATION CATEGORIES ===
-- Add market data providers category
INSERT INTO config_categories (category_name, parent_id, category_path, description, is_system, display_order)
SELECT 'providers', id, 'trading.providers', 'Market data and news provider configurations', false, 4
FROM config_categories WHERE category_path = 'trading';
-- Add subcategories for each provider
INSERT INTO config_categories (category_name, parent_id, category_path, description, is_system, display_order)
SELECT 'databento', id, 'trading.providers.databento', 'Databento market data provider settings', false, 1
FROM config_categories WHERE category_path = 'trading.providers';
INSERT INTO config_categories (category_name, parent_id, category_path, description, is_system, display_order)
SELECT 'benzinga', id, 'trading.providers.benzinga', 'Benzinga news and data provider settings', false, 2
FROM config_categories WHERE category_path = 'trading.providers';
-- === PROVIDER CONFIGURATION TABLES ===
-- Provider credentials and connection settings
CREATE TABLE IF NOT EXISTS provider_configurations (
id SERIAL PRIMARY KEY,
provider_name VARCHAR(50) NOT NULL,
config_key VARCHAR(200) NOT NULL,
config_value JSONB NOT NULL,
value_type VARCHAR(50) NOT NULL DEFAULT 'string',
environment VARCHAR(50) NOT NULL DEFAULT 'development',
is_sensitive BOOLEAN NOT NULL DEFAULT false,
is_active BOOLEAN NOT NULL DEFAULT true,
description TEXT,
validation_schema JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
created_by VARCHAR(100) NOT NULL DEFAULT CURRENT_USER,
updated_by VARCHAR(100) NOT NULL DEFAULT CURRENT_USER,
-- Constraints
CONSTRAINT uk_provider_config_key_env UNIQUE (provider_name, config_key, environment),
CONSTRAINT chk_provider_name CHECK (provider_name IN ('databento', 'benzinga')),
CONSTRAINT chk_provider_value_type CHECK (value_type IN ('string', 'number', 'boolean', 'object', 'array')),
CONSTRAINT chk_provider_environment CHECK (environment IN ('development', 'staging', 'production', 'test'))
);
-- Provider subscription and feature settings
CREATE TABLE IF NOT EXISTS provider_subscriptions (
id SERIAL PRIMARY KEY,
provider_name VARCHAR(50) NOT NULL,
subscription_type VARCHAR(100) NOT NULL, -- 'equities', 'options', 'crypto', 'news', etc.
dataset VARCHAR(100) NOT NULL, -- Provider-specific dataset identifier
symbols TEXT[], -- Array of symbols/instruments
is_active BOOLEAN NOT NULL DEFAULT true,
environment VARCHAR(50) NOT NULL DEFAULT 'development',
rate_limit_per_second INTEGER,
max_concurrent_connections INTEGER DEFAULT 1,
retry_attempts INTEGER DEFAULT 3,
timeout_seconds INTEGER DEFAULT 30,
metadata JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
-- Constraints
CONSTRAINT chk_subscription_provider CHECK (provider_name IN ('databento', 'benzinga')),
CONSTRAINT chk_subscription_environment CHECK (environment IN ('development', 'staging', 'production', 'test'))
);
-- Provider API endpoint and routing configuration
CREATE TABLE IF NOT EXISTS provider_endpoints (
id SERIAL PRIMARY KEY,
provider_name VARCHAR(50) NOT NULL,
endpoint_type VARCHAR(50) NOT NULL, -- 'live', 'historical', 'news', 'fundamentals'
base_url VARCHAR(500) NOT NULL,
websocket_url VARCHAR(500),
api_version VARCHAR(20),
environment VARCHAR(50) NOT NULL DEFAULT 'development',
is_primary BOOLEAN NOT NULL DEFAULT false, -- Primary endpoint for failover
priority INTEGER DEFAULT 0, -- Lower number = higher priority
health_check_path VARCHAR(200),
auth_method VARCHAR(50) NOT NULL DEFAULT 'api_key', -- 'api_key', 'oauth', 'bearer_token'
connection_pool_size INTEGER DEFAULT 10,
request_timeout_ms INTEGER DEFAULT 5000,
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
-- Constraints
CONSTRAINT chk_endpoint_provider CHECK (provider_name IN ('databento', 'benzinga')),
CONSTRAINT chk_endpoint_type CHECK (endpoint_type IN ('live', 'historical', 'news', 'fundamentals', 'analytics')),
CONSTRAINT chk_endpoint_environment CHECK (environment IN ('development', 'staging', 'production', 'test')),
CONSTRAINT chk_auth_method CHECK (auth_method IN ('api_key', 'oauth', 'bearer_token', 'basic_auth'))
);
-- === INDEXES FOR PERFORMANCE ===
-- Provider configurations indexes
CREATE INDEX idx_provider_configurations_provider ON provider_configurations(provider_name);
CREATE INDEX idx_provider_configurations_environment ON provider_configurations(environment);
CREATE INDEX idx_provider_configurations_active ON provider_configurations(is_active) WHERE is_active = true;
CREATE INDEX idx_provider_configurations_sensitive ON provider_configurations(is_sensitive) WHERE is_sensitive = true;
-- Provider subscriptions indexes
CREATE INDEX idx_provider_subscriptions_provider ON provider_subscriptions(provider_name);
CREATE INDEX idx_provider_subscriptions_type ON provider_subscriptions(subscription_type);
CREATE INDEX idx_provider_subscriptions_active ON provider_subscriptions(is_active) WHERE is_active = true;
CREATE INDEX idx_provider_subscriptions_symbols ON provider_subscriptions USING GIN(symbols);
-- Provider endpoints indexes
CREATE INDEX idx_provider_endpoints_provider ON provider_endpoints(provider_name);
CREATE INDEX idx_provider_endpoints_type ON provider_endpoints(endpoint_type);
CREATE INDEX idx_provider_endpoints_primary ON provider_endpoints(is_primary) WHERE is_primary = true;
CREATE INDEX idx_provider_endpoints_priority ON provider_endpoints(priority);
-- === DATABENTO CONFIGURATION ===
-- Databento API Configuration
INSERT INTO config_settings (config_key, category_id, category_path, config_value, value_type, environment, description, tags, is_sensitive, is_system, hot_reload, restart_required)
SELECT 'databento_api_key', id, 'trading.providers.databento', '""'::jsonb, 'string', 'development', 'Databento API key for authentication', ARRAY['databento', 'api', 'authentication'], true, false, true, false
FROM config_categories WHERE category_path = 'trading.providers.databento';
INSERT INTO config_settings (config_key, category_id, category_path, config_value, value_type, environment, description, tags, is_sensitive, is_system, hot_reload, restart_required)
SELECT 'databento_api_key', id, 'trading.providers.databento', '""'::jsonb, 'string', 'production', 'Production Databento API key', ARRAY['databento', 'api', 'authentication'], true, false, true, false
FROM config_categories WHERE category_path = 'trading.providers.databento';
INSERT INTO config_settings (config_key, category_id, category_path, config_value, value_type, environment, description, tags, is_sensitive, is_system, hot_reload, restart_required)
SELECT 'databento_dataset', id, 'trading.providers.databento', '"XNAS.ITCH"'::jsonb, 'string', 'development', 'Primary Databento dataset for market data', ARRAY['databento', 'dataset', 'market_data'], false, false, true, false
FROM config_categories WHERE category_path = 'trading.providers.databento';
INSERT INTO config_settings (config_key, category_id, category_path, config_value, value_type, environment, description, tags, is_sensitive, is_system, hot_reload, restart_required)
SELECT 'databento_dataset', id, 'trading.providers.databento', '"XNAS.ITCH"'::jsonb, 'string', 'production', 'Production Databento dataset', ARRAY['databento', 'dataset', 'market_data'], false, false, true, false
FROM config_categories WHERE category_path = 'trading.providers.databento';
INSERT INTO config_settings (config_key, category_id, category_path, config_value, value_type, environment, description, tags, is_sensitive, is_system, hot_reload, restart_required)
SELECT 'databento_symbols', id, 'trading.providers.databento', '["AAPL", "MSFT", "GOOGL", "TSLA", "AMZN"]'::jsonb, 'array', 'development', 'List of symbols to subscribe to', ARRAY['databento', 'symbols', 'subscription'], false, false, true, false
FROM config_categories WHERE category_path = 'trading.providers.databento';
INSERT INTO config_settings (config_key, category_id, category_path, config_value, value_type, environment, description, tags, is_sensitive, is_system, hot_reload, restart_required)
SELECT 'databento_connection_timeout_ms', id, 'trading.providers.databento', '30000'::jsonb, 'number', 'development', 'Connection timeout for Databento API', ARRAY['databento', 'timeout', 'connection'], false, false, true, false
FROM config_categories WHERE category_path = 'trading.providers.databento';
INSERT INTO config_settings (config_key, category_id, category_path, config_value, value_type, environment, description, tags, is_sensitive, is_system, hot_reload, restart_required)
SELECT 'databento_request_timeout_ms', id, 'trading.providers.databento', '10000'::jsonb, 'number', 'development', 'Request timeout for Databento API calls', ARRAY['databento', 'timeout', 'request'], false, false, true, false
FROM config_categories WHERE category_path = 'trading.providers.databento';
INSERT INTO config_settings (config_key, category_id, category_path, config_value, value_type, environment, description, tags, is_sensitive, is_system, hot_reload, restart_required)
SELECT 'databento_rate_limit_rps', id, 'trading.providers.databento', '100'::jsonb, 'number', 'development', 'Rate limit for Databento API requests', ARRAY['databento', 'rate_limit'], false, false, true, false
FROM config_categories WHERE category_path = 'trading.providers.databento';
INSERT INTO config_settings (config_key, category_id, category_path, config_value, value_type, environment, description, tags, is_sensitive, is_system, hot_reload, restart_required)
SELECT 'databento_enable_live_data', id, 'trading.providers.databento', 'true'::jsonb, 'boolean', 'development', 'Enable live market data streaming', ARRAY['databento', 'live_data', 'streaming'], false, false, true, false
FROM config_categories WHERE category_path = 'trading.providers.databento';
INSERT INTO config_settings (config_key, category_id, category_path, config_value, value_type, environment, description, tags, is_sensitive, is_system, hot_reload, restart_required)
SELECT 'databento_enable_historical_data', id, 'trading.providers.databento', 'true'::jsonb, 'boolean', 'development', 'Enable historical data retrieval', ARRAY['databento', 'historical_data'], false, false, true, false
FROM config_categories WHERE category_path = 'trading.providers.databento';
-- === BENZINGA CONFIGURATION ===
-- Benzinga API Configuration
INSERT INTO config_settings (config_key, category_id, category_path, config_value, value_type, environment, description, tags, is_sensitive, is_system, hot_reload, restart_required)
SELECT 'benzinga_api_key', id, 'trading.providers.benzinga', '""'::jsonb, 'string', 'development', 'Benzinga API key for authentication', ARRAY['benzinga', 'api', 'authentication'], true, false, true, false
FROM config_categories WHERE category_path = 'trading.providers.benzinga';
INSERT INTO config_settings (config_key, category_id, category_path, config_value, value_type, environment, description, tags, is_sensitive, is_system, hot_reload, restart_required)
SELECT 'benzinga_api_key', id, 'trading.providers.benzinga', '""'::jsonb, 'string', 'production', 'Production Benzinga API key', ARRAY['benzinga', 'api', 'authentication'], true, false, true, false
FROM config_categories WHERE category_path = 'trading.providers.benzinga';
INSERT INTO config_settings (config_key, category_id, category_path, config_value, value_type, environment, description, tags, is_sensitive, is_system, hot_reload, restart_required)
SELECT 'benzinga_subscription_tier', id, 'trading.providers.benzinga', '"pro"'::jsonb, 'string', 'development', 'Benzinga subscription tier (basic, pro, enterprise)', ARRAY['benzinga', 'subscription', 'tier'], false, false, true, false
FROM config_categories WHERE category_path = 'trading.providers.benzinga';
INSERT INTO config_settings (config_key, category_id, category_path, config_value, value_type, environment, description, tags, is_sensitive, is_system, hot_reload, restart_required)
SELECT 'benzinga_subscription_tier', id, 'trading.providers.benzinga', '"enterprise"'::jsonb, 'string', 'production', 'Production Benzinga subscription tier', ARRAY['benzinga', 'subscription', 'tier'], false, false, true, false
FROM config_categories WHERE category_path = 'trading.providers.benzinga';
INSERT INTO config_settings (config_key, category_id, category_path, config_value, value_type, environment, description, tags, is_sensitive, is_system, hot_reload, restart_required)
SELECT 'benzinga_enable_news_feed', id, 'trading.providers.benzinga', 'true'::jsonb, 'boolean', 'development', 'Enable Benzinga news feed', ARRAY['benzinga', 'news', 'feed'], false, false, true, false
FROM config_categories WHERE category_path = 'trading.providers.benzinga';
INSERT INTO config_settings (config_key, category_id, category_path, config_value, value_type, environment, description, tags, is_sensitive, is_system, hot_reload, restart_required)
SELECT 'benzinga_enable_analyst_ratings', id, 'trading.providers.benzinga', 'true'::jsonb, 'boolean', 'development', 'Enable analyst ratings data', ARRAY['benzinga', 'analyst_ratings'], false, false, true, false
FROM config_categories WHERE category_path = 'trading.providers.benzinga';
INSERT INTO config_settings (config_key, category_id, category_path, config_value, value_type, environment, description, tags, is_sensitive, is_system, hot_reload, restart_required)
SELECT 'benzinga_enable_earnings_data', id, 'trading.providers.benzinga', 'true'::jsonb, 'boolean', 'development', 'Enable earnings calendar and data', ARRAY['benzinga', 'earnings'], false, false, true, false
FROM config_categories WHERE category_path = 'trading.providers.benzinga';
INSERT INTO config_settings (config_key, category_id, category_path, config_value, value_type, environment, description, tags, is_sensitive, is_system, hot_reload, restart_required)
SELECT 'benzinga_connection_timeout_ms', id, 'trading.providers.benzinga', '30000'::jsonb, 'number', 'development', 'Connection timeout for Benzinga API', ARRAY['benzinga', 'timeout', 'connection'], false, false, true, false
FROM config_categories WHERE category_path = 'trading.providers.benzinga';
INSERT INTO config_settings (config_key, category_id, category_path, config_value, value_type, environment, description, tags, is_sensitive, is_system, hot_reload, restart_required)
SELECT 'benzinga_rate_limit_rpm', id, 'trading.providers.benzinga', '1000'::jsonb, 'number', 'development', 'Rate limit for Benzinga API requests per minute', ARRAY['benzinga', 'rate_limit'], false, false, true, false
FROM config_categories WHERE category_path = 'trading.providers.benzinga';
INSERT INTO config_settings (config_key, category_id, category_path, config_value, value_type, environment, description, tags, is_sensitive, is_system, hot_reload, restart_required)
SELECT 'benzinga_news_categories', id, 'trading.providers.benzinga', '["earnings", "analyst-ratings", "sec-filings", "mergers-acquisitions"]'::jsonb, 'array', 'development', 'News categories to subscribe to', ARRAY['benzinga', 'news', 'categories'], false, false, true, false
FROM config_categories WHERE category_path = 'trading.providers.benzinga';
-- === PROVIDER ENDPOINT CONFIGURATIONS ===
-- Databento endpoints
INSERT INTO provider_endpoints (provider_name, endpoint_type, base_url, websocket_url, api_version, environment, is_primary, priority, health_check_path, auth_method, connection_pool_size, request_timeout_ms) VALUES
('databento', 'live', 'https://api.databento.com', 'wss://api.databento.com/v0/live', 'v0', 'development', true, 1, '/v0/metadata', 'api_key', 5, 5000),
('databento', 'historical', 'https://api.databento.com', NULL, 'v0', 'development', true, 1, '/v0/metadata', 'api_key', 10, 30000),
('databento', 'live', 'https://api.databento.com', 'wss://api.databento.com/v0/live', 'v0', 'production', true, 1, '/v0/metadata', 'api_key', 20, 5000),
('databento', 'historical', 'https://api.databento.com', NULL, 'v0', 'production', true, 1, '/v0/metadata', 'api_key', 50, 30000);
-- Benzinga endpoints
INSERT INTO provider_endpoints (provider_name, endpoint_type, base_url, websocket_url, api_version, environment, is_primary, priority, health_check_path, auth_method, connection_pool_size, request_timeout_ms) VALUES
('benzinga', 'news', 'https://api.benzinga.com', 'wss://api.benzinga.com/news/stream', 'v2', 'development', true, 1, '/v2/news', 'api_key', 5, 10000),
('benzinga', 'fundamentals', 'https://api.benzinga.com', NULL, 'v2', 'development', true, 1, '/v2/fundamentals', 'api_key', 10, 15000),
('benzinga', 'analytics', 'https://api.benzinga.com', NULL, 'v2', 'development', true, 1, '/v2/analytics', 'api_key', 5, 20000),
('benzinga', 'news', 'https://api.benzinga.com', 'wss://api.benzinga.com/news/stream', 'v2', 'production', true, 1, '/v2/news', 'api_key', 20, 10000),
('benzinga', 'fundamentals', 'https://api.benzinga.com', NULL, 'v2', 'production', true, 1, '/v2/fundamentals', 'api_key', 50, 15000),
('benzinga', 'analytics', 'https://api.benzinga.com', NULL, 'v2', 'production', true, 1, '/v2/analytics', 'api_key', 20, 20000);
-- === PROVIDER SUBSCRIPTION CONFIGURATIONS ===
-- Databento subscriptions
INSERT INTO provider_subscriptions (provider_name, subscription_type, dataset, symbols, environment, rate_limit_per_second, max_concurrent_connections, metadata) VALUES
('databento', 'equities_l1', 'XNAS.ITCH', ARRAY['AAPL', 'MSFT', 'GOOGL', 'TSLA', 'AMZN'], 'development', 100, 2, '{"schema": "mbo", "stype_in": "raw_symbol"}'::jsonb),
('databento', 'equities_l2', 'XNAS.ITCH', ARRAY['AAPL', 'MSFT', 'GOOGL'], 'development', 50, 1, '{"schema": "mbp-1", "stype_in": "raw_symbol"}'::jsonb),
('databento', 'equities_l1', 'XNAS.ITCH', ARRAY['AAPL', 'MSFT', 'GOOGL', 'TSLA', 'AMZN', 'NVDA', 'META'], 'production', 500, 5, '{"schema": "mbo", "stype_in": "raw_symbol"}'::jsonb),
('databento', 'equities_l2', 'XNAS.ITCH', ARRAY['AAPL', 'MSFT', 'GOOGL', 'TSLA', 'AMZN'], 'production', 200, 3, '{"schema": "mbp-1", "stype_in": "raw_symbol"}'::jsonb);
-- Benzinga subscriptions
INSERT INTO provider_subscriptions (provider_name, subscription_type, dataset, symbols, environment, rate_limit_per_second, max_concurrent_connections, metadata) VALUES
('benzinga', 'news_feed', 'general', ARRAY['AAPL', 'MSFT', 'GOOGL', 'TSLA', 'AMZN'], 'development', 10, 1, '{"channels": ["news", "analyst-ratings"]}'::jsonb),
('benzinga', 'earnings_calendar', 'earnings', NULL, 'development', 5, 1, '{"importance": "high"}'::jsonb),
('benzinga', 'analyst_ratings', 'ratings', ARRAY['AAPL', 'MSFT', 'GOOGL', 'TSLA', 'AMZN'], 'development', 5, 1, '{"rating_type": ["Upgrade", "Downgrade", "Initiates", "Reiterates"]}'::jsonb),
('benzinga', 'news_feed', 'general', ARRAY['AAPL', 'MSFT', 'GOOGL', 'TSLA', 'AMZN', 'NVDA', 'META'], 'production', 50, 3, '{"channels": ["news", "analyst-ratings", "sec-filings"]}'::jsonb),
('benzinga', 'earnings_calendar', 'earnings', NULL, 'production', 20, 1, '{"importance": "high"}'::jsonb),
('benzinga', 'analyst_ratings', 'ratings', ARRAY['AAPL', 'MSFT', 'GOOGL', 'TSLA', 'AMZN', 'NVDA', 'META'], 'production', 20, 1, '{"rating_type": ["Upgrade", "Downgrade", "Initiates", "Reiterates"]}'::jsonb);
-- === NOTIFICATION TRIGGERS FOR PROVIDER TABLES ===
-- Provider configurations change trigger
CREATE OR REPLACE FUNCTION notify_provider_config_change()
RETURNS TRIGGER AS $$
DECLARE
payload JSONB;
BEGIN
payload := jsonb_build_object(
'table', 'provider_configurations',
'operation', TG_OP,
'provider', COALESCE(NEW.provider_name, OLD.provider_name),
'config_key', COALESCE(NEW.config_key, OLD.config_key),
'environment', COALESCE(NEW.environment, OLD.environment),
'timestamp', EXTRACT(EPOCH FROM NOW())
);
PERFORM pg_notify('foxhunt_provider_changes', payload::text);
RETURN COALESCE(NEW, OLD);
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER tr_provider_configurations_notify
AFTER INSERT OR UPDATE OR DELETE ON provider_configurations
FOR EACH ROW EXECUTE FUNCTION notify_provider_config_change();
-- Provider subscriptions change trigger
CREATE OR REPLACE FUNCTION notify_provider_subscription_change()
RETURNS TRIGGER AS $$
DECLARE
payload JSONB;
BEGIN
payload := jsonb_build_object(
'table', 'provider_subscriptions',
'operation', TG_OP,
'provider', COALESCE(NEW.provider_name, OLD.provider_name),
'subscription_type', COALESCE(NEW.subscription_type, OLD.subscription_type),
'environment', COALESCE(NEW.environment, OLD.environment),
'timestamp', EXTRACT(EPOCH FROM NOW())
);
PERFORM pg_notify('foxhunt_provider_changes', payload::text);
RETURN COALESCE(NEW, OLD);
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER tr_provider_subscriptions_notify
AFTER INSERT OR UPDATE OR DELETE ON provider_subscriptions
FOR EACH ROW EXECUTE FUNCTION notify_provider_subscription_change();
-- Provider endpoints change trigger
CREATE OR REPLACE FUNCTION notify_provider_endpoint_change()
RETURNS TRIGGER AS $$
DECLARE
payload JSONB;
BEGIN
payload := jsonb_build_object(
'table', 'provider_endpoints',
'operation', TG_OP,
'provider', COALESCE(NEW.provider_name, OLD.provider_name),
'endpoint_type', COALESCE(NEW.endpoint_type, OLD.endpoint_type),
'environment', COALESCE(NEW.environment, OLD.environment),
'timestamp', EXTRACT(EPOCH FROM NOW())
);
PERFORM pg_notify('foxhunt_provider_changes', payload::text);
RETURN COALESCE(NEW, OLD);
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER tr_provider_endpoints_notify
AFTER INSERT OR UPDATE OR DELETE ON provider_endpoints
FOR EACH ROW EXECUTE FUNCTION notify_provider_endpoint_change();
-- === CONFIGURATION SUBSCRIPTIONS FOR PROVIDERS ===
-- Trading service subscribes to provider configuration changes
INSERT INTO config_subscriptions (service_name, config_pattern, category_pattern, environment, subscription_type, is_active) VALUES
('trading_service', 'trading.providers.*', 'trading.providers.*', 'development', 'notify', true),
('trading_service', 'trading.providers.*', 'trading.providers.*', 'production', 'notify', true);
-- Market data service subscribes to provider changes
INSERT INTO config_subscriptions (service_name, config_pattern, category_pattern, environment, subscription_type, is_active) VALUES
('market_data_service', 'trading.providers.*', 'trading.providers.*', 'development', 'notify', true),
('market_data_service', 'trading.providers.*', 'trading.providers.*', 'production', 'notify', true);
-- === UTILITY FUNCTIONS FOR PROVIDER MANAGEMENT ===
-- Function to get active providers for an environment
CREATE OR REPLACE FUNCTION get_active_providers(p_environment VARCHAR(50) DEFAULT 'development')
RETURNS TABLE(provider_name VARCHAR(50), config_count BIGINT) AS $$
BEGIN
RETURN QUERY
SELECT
pc.provider_name,
COUNT(*) as config_count
FROM provider_configurations pc
WHERE pc.environment = p_environment
AND pc.is_active = true
GROUP BY pc.provider_name
ORDER BY pc.provider_name;
END;
$$ LANGUAGE plpgsql;
-- Function to get provider configuration with fallback
CREATE OR REPLACE FUNCTION get_provider_config(
p_provider_name VARCHAR(50),
p_config_key VARCHAR(200),
p_environment VARCHAR(50) DEFAULT 'development'
)
RETURNS JSONB AS $$
DECLARE
result JSONB;
BEGIN
SELECT config_value INTO result
FROM provider_configurations
WHERE provider_name = p_provider_name
AND config_key = p_config_key
AND environment = p_environment
AND is_active = true;
RETURN result;
END;
$$ LANGUAGE plpgsql;
-- Function to update provider configuration
CREATE OR REPLACE FUNCTION set_provider_config(
p_provider_name VARCHAR(50),
p_config_key VARCHAR(200),
p_config_value JSONB,
p_environment VARCHAR(50) DEFAULT 'development',
p_description TEXT DEFAULT NULL
)
RETURNS BOOLEAN AS $$
BEGIN
INSERT INTO provider_configurations (
provider_name, config_key, config_value, environment, description, updated_at
) VALUES (
p_provider_name, p_config_key, p_config_value, p_environment, p_description, NOW()
)
ON CONFLICT (provider_name, config_key, environment)
DO UPDATE SET
config_value = EXCLUDED.config_value,
description = EXCLUDED.description,
updated_at = NOW();
RETURN true;
END;
$$ LANGUAGE plpgsql;
-- === UPDATE STATISTICS ===
ANALYZE provider_configurations;
ANALYZE provider_subscriptions;
ANALYZE provider_endpoints;
ANALYZE config_categories;
ANALYZE config_settings;
-- === COMMENTS FOR DOCUMENTATION ===
COMMENT ON TABLE provider_configurations IS 'Provider-specific configuration settings with environment support';
COMMENT ON TABLE provider_subscriptions IS 'Provider subscription and feature configurations';
COMMENT ON TABLE provider_endpoints IS 'Provider API endpoint configurations with failover support';
COMMENT ON FUNCTION notify_provider_config_change() IS 'Notification trigger for provider configuration changes';
COMMENT ON FUNCTION get_active_providers(VARCHAR) IS 'Returns list of active providers for an environment';
COMMENT ON FUNCTION get_provider_config(VARCHAR, VARCHAR, VARCHAR) IS 'Retrieves provider configuration value';
COMMENT ON FUNCTION set_provider_config(VARCHAR, VARCHAR, JSONB, VARCHAR, TEXT) IS 'Updates provider configuration value';
-- Dual-provider configuration migration completed successfully!
--
-- Created:
-- - Provider-specific configuration tables for Databento and Benzinga
-- - Hot-reload notification system for provider changes
-- - Comprehensive configuration entries for both providers
-- - Endpoint and subscription management
-- - Utility functions for provider configuration management
--
-- Key Features:
-- - Environment-specific provider configurations
-- - Secure API key storage with sensitivity flags
-- - Rate limiting and connection pooling settings
-- - Subscription management with symbol filtering
-- - Endpoint failover and priority configuration
-- - Real-time hot-reload notifications via PostgreSQL NOTIFY/LISTEN
--
-- Usage:
-- 1. Services listen to 'foxhunt_provider_changes' channel for provider updates
-- 2. Use get_provider_config('databento', 'api_key', 'production') for configuration retrieval
-- 3. Use set_provider_config() for runtime configuration updates
-- 4. TLI dashboard can manage provider configurations through PostgreSQL
-- 5. Configuration changes trigger immediate notifications to subscribed services