-- ================================================================================================ -- Migration 001: Trading Events Schema -- Comprehensive PostgreSQL schema for HFT trading events with nanosecond precision -- Production-ready with compliance, audit, and performance optimizations -- ================================================================================================ -- Enable required extensions for HFT functionality CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; CREATE EXTENSION IF NOT EXISTS "btree_gin"; CREATE EXTENSION IF NOT EXISTS "pg_stat_statements"; CREATE EXTENSION IF NOT EXISTS "timescaledb" CASCADE; -- For time-series optimization -- ================================================================================================ -- NANOSECOND TIMESTAMP DOMAIN -- Custom domain for nanosecond precision timestamps required for HFT -- ================================================================================================ CREATE DOMAIN ns_timestamp AS BIGINT CHECK (VALUE >= 0 AND VALUE <= 9223372036854775807); -- Max 64-bit signed integer COMMENT ON DOMAIN ns_timestamp IS 'Nanoseconds since Unix epoch for HFT precision timing'; -- ================================================================================================ -- TRADING EVENT TYPES ENUM -- Comprehensive event type classification for all trading activities -- ================================================================================================ CREATE TYPE trading_event_type AS ENUM ( 'order_submitted', 'order_accepted', 'order_rejected', 'order_modified', 'order_cancelled', 'order_expired', 'order_filled', 'order_partially_filled', 'trade_executed', 'trade_settled', 'position_opened', 'position_closed', 'position_modified', 'market_data_received', 'signal_generated', 'risk_breach', 'system_startup', 'system_shutdown', 'heartbeat' ); -- ================================================================================================ -- ORDER SIDE AND STATUS ENUMS -- ================================================================================================ CREATE TYPE order_side AS ENUM ('buy', 'sell', 'short', 'cover'); CREATE TYPE order_type AS ENUM ('market', 'limit', 'stop', 'stop_limit', 'iceberg', 'twap', 'vwap'); CREATE TYPE order_status AS ENUM ('pending', 'accepted', 'rejected', 'partial', 'filled', 'cancelled', 'expired'); CREATE TYPE time_in_force AS ENUM ('day', 'gtc', 'ioc', 'fok', 'gtd'); -- ================================================================================================ -- CORE TRADING EVENTS TABLE -- Immutable event store for all trading activities with nanosecond precision -- ================================================================================================ CREATE TABLE trading_events ( -- Primary identifiers id UUID DEFAULT uuid_generate_v4(), event_id BIGSERIAL NOT NULL, -- Sequential event ID for ordering correlation_id UUID NOT NULL, -- Links related events -- Timing with nanosecond precision event_timestamp ns_timestamp NOT NULL, -- Hardware RDTSC timestamp received_timestamp ns_timestamp NOT NULL, -- When system received the event processing_timestamp ns_timestamp NOT NULL, -- When system began processing -- Event classification event_type trading_event_type NOT NULL, event_source VARCHAR(100) NOT NULL, -- Component that generated event event_version SMALLINT NOT NULL DEFAULT 1, -- Schema version for evolution -- Trading context symbol VARCHAR(32) NOT NULL, account_id VARCHAR(64), strategy_id VARCHAR(100), venue VARCHAR(50), -- Event payload (immutable JSON) event_data JSONB NOT NULL, -- Complete event payload metadata JSONB, -- Additional metadata -- Compliance and audit user_id VARCHAR(64), session_id UUID, request_id UUID, -- Original client request ID trace_id UUID, -- Distributed tracing ID -- System context node_id VARCHAR(50) NOT NULL, -- Which system node processed this process_id INTEGER NOT NULL, -- OS process ID thread_id INTEGER, -- Thread ID for debugging cpu_core SMALLINT, -- CPU core for performance analysis -- Checksums for integrity event_hash VARCHAR(64) NOT NULL, -- SHA-256 of event_data parent_hash VARCHAR(64), -- Hash of previous related event -- Partition key for performance event_date DATE NOT NULL, -- Constraints CONSTRAINT chk_timestamps CHECK ( received_timestamp >= event_timestamp AND processing_timestamp >= received_timestamp ), -- Composite primary key including partition column PRIMARY KEY (id, event_date) ) PARTITION BY RANGE (event_date); -- Create table comment COMMENT ON TABLE trading_events IS 'Immutable event store for all trading activities with nanosecond precision and compliance features'; -- Trigger function to set event_date from event_timestamp (nanoseconds) CREATE OR REPLACE FUNCTION set_trading_event_date() RETURNS TRIGGER AS $$ BEGIN NEW.event_date := DATE(TO_TIMESTAMP(NEW.event_timestamp / 1000000000.0)); RETURN NEW; END; $$ LANGUAGE plpgsql IMMUTABLE; -- Trigger to auto-populate event_date on insert CREATE TRIGGER tg_set_trading_event_date BEFORE INSERT ON trading_events FOR EACH ROW EXECUTE FUNCTION set_trading_event_date(); -- ================================================================================================ -- ORDERS TABLE (CURRENT STATE) -- Mutable state table for current order status (derived from events) -- ================================================================================================ CREATE TABLE orders ( -- Primary identifiers id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), client_order_id VARCHAR(128) UNIQUE, -- Client-provided ID exchange_order_id VARCHAR(128), -- Exchange-provided ID parent_order_id UUID, -- For child orders (iceberg, etc.) -- Order details symbol VARCHAR(32) NOT NULL, side order_side NOT NULL, order_type order_type NOT NULL, time_in_force time_in_force NOT NULL DEFAULT 'day', -- Quantities (in base units, scaled for precision) quantity BIGINT NOT NULL CHECK (quantity > 0), filled_quantity BIGINT NOT NULL DEFAULT 0 CHECK (filled_quantity >= 0), remaining_quantity BIGINT NOT NULL DEFAULT 0, -- Pricing (in cents or smallest currency unit) limit_price BIGINT, -- NULL for market orders stop_price BIGINT, -- For stop orders avg_fill_price BIGINT DEFAULT 0, -- Status and timing status order_status NOT NULL DEFAULT 'pending', created_at ns_timestamp NOT NULL, updated_at ns_timestamp NOT NULL, expires_at ns_timestamp, -- Trading context account_id VARCHAR(64) NOT NULL, strategy_id VARCHAR(100), venue VARCHAR(50) NOT NULL, -- Risk and compliance risk_check_passed BOOLEAN DEFAULT FALSE, compliance_approved BOOLEAN DEFAULT FALSE, estimated_commission BIGINT DEFAULT 0, -- Metadata tags JSONB, -- Flexible tagging system notes TEXT, -- Human-readable notes -- Audit trail created_by VARCHAR(64), last_modified_by VARCHAR(64), -- Constraints CONSTRAINT chk_quantities CHECK (filled_quantity <= quantity), CONSTRAINT chk_limit_price CHECK ( (order_type IN ('market') AND limit_price IS NULL) OR (order_type IN ('limit', 'stop_limit') AND limit_price IS NOT NULL) ), CONSTRAINT chk_stop_price CHECK ( (order_type IN ('stop', 'stop_limit') AND stop_price IS NOT NULL) OR (order_type NOT IN ('stop', 'stop_limit')) ) ); -- Trigger function to calculate remaining_quantity CREATE OR REPLACE FUNCTION set_order_remaining_quantity() RETURNS TRIGGER AS $$ BEGIN NEW.remaining_quantity := NEW.quantity - NEW.filled_quantity; RETURN NEW; END; $$ LANGUAGE plpgsql IMMUTABLE; -- Trigger to auto-populate remaining_quantity on insert/update CREATE TRIGGER tg_set_order_remaining_quantity BEFORE INSERT OR UPDATE OF quantity, filled_quantity ON orders FOR EACH ROW EXECUTE FUNCTION set_order_remaining_quantity(); -- ================================================================================================ -- FILLS TABLE (TRADE EXECUTIONS) -- Immutable record of trade executions -- ================================================================================================ CREATE TABLE fills ( -- Primary identifiers id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), order_id UUID NOT NULL REFERENCES orders(id), execution_id VARCHAR(128) NOT NULL, -- Exchange execution ID trade_id VARCHAR(128), -- Exchange trade ID -- Execution details symbol VARCHAR(32) NOT NULL, side order_side NOT NULL, quantity BIGINT NOT NULL CHECK (quantity > 0), price BIGINT NOT NULL CHECK (price > 0), -- Fees and costs commission BIGINT NOT NULL DEFAULT 0, commission_currency VARCHAR(10) DEFAULT 'USD', sec_fee BIGINT DEFAULT 0, -- SEC fees taf_fee BIGINT DEFAULT 0, -- TAF fees clearing_fee BIGINT DEFAULT 0, -- Execution context venue VARCHAR(50) NOT NULL, execution_timestamp ns_timestamp NOT NULL, settlement_date DATE, -- Market making classification is_maker BOOLEAN, -- True if provided liquidity liquidity_flag CHAR(1), -- Exchange-specific liquidity flag -- Cross-reference and audit contra_broker VARCHAR(50), -- Counterparty broker contra_trader VARCHAR(100), -- Counterparty trader ID -- System timestamps received_at ns_timestamp NOT NULL, processed_at ns_timestamp NOT NULL, reported_at ns_timestamp, -- When reported to external systems -- Metadata execution_details JSONB, -- Exchange-specific details -- Constraints CONSTRAINT uk_fills_execution UNIQUE (venue, execution_id), CONSTRAINT chk_fill_timestamps CHECK ( processed_at >= received_at AND execution_timestamp <= received_at ) ); -- ================================================================================================ -- POSITIONS TABLE (CURRENT HOLDINGS) -- Real-time position tracking with mark-to-market -- ================================================================================================ CREATE TABLE positions ( -- Primary identifiers id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), symbol VARCHAR(32) NOT NULL, account_id VARCHAR(64) NOT NULL, strategy_id VARCHAR(100), -- Position details quantity BIGINT NOT NULL DEFAULT 0, -- Signed: positive=long, negative=short avg_cost BIGINT NOT NULL DEFAULT 0, -- Average cost basis (cents) realized_pnl BIGINT NOT NULL DEFAULT 0, -- Realized P&L (cents) unrealized_pnl BIGINT NOT NULL DEFAULT 0, -- Unrealized P&L (cents) -- Market data last_price BIGINT NOT NULL DEFAULT 0, -- Last known market price market_value BIGINT NOT NULL DEFAULT 0, -- Risk metrics var_1d BIGINT, -- 1-day Value at Risk var_10d BIGINT, -- 10-day Value at Risk beta DECIMAL(8,4), -- Beta vs market -- Timing first_trade_time ns_timestamp, -- When position was opened last_trade_time ns_timestamp, -- Last trade affecting position last_updated ns_timestamp NOT NULL, -- Position limits max_position BIGINT, -- Maximum allowed position size current_exposure BIGINT NOT NULL DEFAULT 0, -- Audit version INTEGER NOT NULL DEFAULT 1, -- Optimistic locking version -- Constraints CONSTRAINT uk_positions_symbol_account UNIQUE (symbol, account_id, strategy_id), CONSTRAINT chk_position_times CHECK ( last_trade_time IS NULL OR first_trade_time IS NULL OR last_trade_time >= first_trade_time ) ); -- Trigger function to calculate market_value and current_exposure CREATE OR REPLACE FUNCTION set_position_calculated_fields() RETURNS TRIGGER AS $$ BEGIN NEW.market_value := ABS(NEW.quantity) * NEW.last_price; NEW.current_exposure := ABS(NEW.quantity * NEW.last_price); RETURN NEW; END; $$ LANGUAGE plpgsql IMMUTABLE; -- Trigger to auto-populate calculated fields on insert/update CREATE TRIGGER tg_set_position_calculated_fields BEFORE INSERT OR UPDATE OF quantity, last_price ON positions FOR EACH ROW EXECUTE FUNCTION set_position_calculated_fields(); -- ================================================================================================ -- HIGH-PERFORMANCE INDEXES -- Optimized for HFT query patterns and real-time operations -- ================================================================================================ -- Trading events indexes (time-series optimized) CREATE INDEX idx_trading_events_timestamp ON trading_events USING BTREE (event_timestamp); CREATE INDEX idx_trading_events_symbol_timestamp ON trading_events USING BTREE (symbol, event_timestamp); CREATE INDEX idx_trading_events_type_timestamp ON trading_events USING BTREE (event_type, event_timestamp); CREATE INDEX idx_trading_events_correlation ON trading_events USING HASH (correlation_id); CREATE INDEX idx_trading_events_account ON trading_events USING BTREE (account_id, event_timestamp); CREATE INDEX idx_trading_events_venue ON trading_events USING BTREE (venue, event_timestamp); CREATE INDEX idx_trading_events_strategy ON trading_events USING BTREE (strategy_id, event_timestamp); -- GIN index for JSONB event_data (flexible querying) CREATE INDEX idx_trading_events_data_gin ON trading_events USING GIN (event_data); CREATE INDEX idx_trading_events_metadata_gin ON trading_events USING GIN (metadata); -- Orders indexes (operational queries) CREATE INDEX idx_orders_symbol_status ON orders USING BTREE (symbol, status); CREATE INDEX idx_orders_account_status ON orders USING BTREE (account_id, status); CREATE INDEX idx_orders_client_order_id ON orders USING HASH (client_order_id) WHERE client_order_id IS NOT NULL; CREATE INDEX idx_orders_exchange_order_id ON orders USING HASH (exchange_order_id) WHERE exchange_order_id IS NOT NULL; CREATE INDEX idx_orders_venue_status ON orders USING BTREE (venue, status); CREATE INDEX idx_orders_strategy ON orders USING BTREE (strategy_id, created_at) WHERE strategy_id IS NOT NULL; CREATE INDEX idx_orders_created_at ON orders USING BTREE (created_at); CREATE INDEX idx_orders_expires_at ON orders USING BTREE (expires_at) WHERE expires_at IS NOT NULL; -- Fills indexes (execution analysis) CREATE INDEX idx_fills_order_id ON fills USING BTREE (order_id); CREATE INDEX idx_fills_symbol_timestamp ON fills USING BTREE (symbol, execution_timestamp); CREATE INDEX idx_fills_venue_timestamp ON fills USING BTREE (venue, execution_timestamp); CREATE INDEX idx_fills_execution_timestamp ON fills USING BTREE (execution_timestamp); CREATE INDEX idx_fills_settlement_date ON fills USING BTREE (settlement_date) WHERE settlement_date IS NOT NULL; -- Positions indexes (real-time position management) CREATE INDEX idx_positions_symbol ON positions USING BTREE (symbol); CREATE INDEX idx_positions_account ON positions USING BTREE (account_id); CREATE INDEX idx_positions_strategy ON positions USING BTREE (strategy_id) WHERE strategy_id IS NOT NULL; CREATE INDEX idx_positions_last_updated ON positions USING BTREE (last_updated); CREATE INDEX idx_positions_nonzero ON positions USING BTREE (symbol, account_id) WHERE quantity != 0; -- ================================================================================================ -- AUTOMATIC PARTITIONING FOR TRADING EVENTS -- Daily partitions for optimal performance and maintenance -- ================================================================================================ -- Function to create daily partitions for trading events CREATE OR REPLACE FUNCTION create_trading_events_partition(target_date DATE) RETURNS VOID AS $$ DECLARE partition_name TEXT; start_date DATE; end_date DATE; BEGIN start_date := target_date; end_date := target_date + INTERVAL '1 day'; partition_name := 'trading_events_' || to_char(start_date, 'YYYY_MM_DD'); -- Create partition if it doesn't exist IF NOT EXISTS ( SELECT 1 FROM information_schema.tables WHERE table_name = partition_name ) THEN EXECUTE format('CREATE TABLE %I PARTITION OF trading_events FOR VALUES FROM (%L) TO (%L)', partition_name, start_date, end_date); -- Add partition-specific indexes for performance EXECUTE format('CREATE INDEX %I ON %I USING BTREE (event_timestamp)', 'idx_' || partition_name || '_timestamp', partition_name); EXECUTE format('CREATE INDEX %I ON %I USING BTREE (symbol, event_timestamp)', 'idx_' || partition_name || '_symbol_ts', partition_name); EXECUTE format('CREATE INDEX %I ON %I USING HASH (correlation_id)', 'idx_' || partition_name || '_correlation', partition_name); END IF; END; $$ LANGUAGE plpgsql; -- Create partitions for current and next 7 days DO $$ DECLARE i INTEGER; BEGIN FOR i IN 0..7 LOOP PERFORM create_trading_events_partition(CURRENT_DATE + i); END LOOP; END $$; -- ================================================================================================ -- TRIGGER FUNCTIONS FOR DATA INTEGRITY AND AUTOMATION -- ================================================================================================ -- Function to update position from fill CREATE OR REPLACE FUNCTION update_position_from_fill() RETURNS TRIGGER AS $$ DECLARE position_delta BIGINT; new_avg_cost BIGINT; existing_quantity BIGINT := 0; existing_avg_cost BIGINT := 0; BEGIN -- Calculate position delta (buy = positive, sell = negative) position_delta := CASE WHEN NEW.side IN ('buy', 'cover') THEN NEW.quantity ELSE -NEW.quantity END; -- Get existing position SELECT quantity, avg_cost INTO existing_quantity, existing_avg_cost FROM positions WHERE symbol = NEW.symbol AND account_id = (SELECT account_id FROM orders WHERE id = NEW.order_id) AND strategy_id = (SELECT strategy_id FROM orders WHERE id = NEW.order_id); -- Calculate new average cost IF existing_quantity = 0 THEN new_avg_cost := NEW.price; ELSIF (existing_quantity > 0 AND position_delta > 0) OR (existing_quantity < 0 AND position_delta < 0) THEN -- Adding to existing position new_avg_cost := (ABS(existing_quantity) * existing_avg_cost + ABS(position_delta) * NEW.price) / (ABS(existing_quantity) + ABS(position_delta)); ELSE -- Reducing or reversing position, keep existing avg cost new_avg_cost := existing_avg_cost; END IF; -- Update or insert position INSERT INTO positions ( symbol, account_id, strategy_id, quantity, avg_cost, last_price, last_trade_time, last_updated ) SELECT NEW.symbol, o.account_id, o.strategy_id, position_delta, NEW.price, NEW.price, NEW.execution_timestamp, NEW.execution_timestamp FROM orders o WHERE o.id = NEW.order_id ON CONFLICT (symbol, account_id, strategy_id) DO UPDATE SET quantity = positions.quantity + position_delta, avg_cost = CASE WHEN positions.quantity = 0 THEN NEW.price ELSE new_avg_cost END, last_price = NEW.price, last_trade_time = NEW.execution_timestamp, last_updated = NEW.execution_timestamp, version = positions.version + 1; RETURN NEW; END; $$ LANGUAGE plpgsql; -- Function to validate order constraints CREATE OR REPLACE FUNCTION validate_order_constraints() RETURNS TRIGGER AS $$ BEGIN -- Validate price requirements IF NEW.order_type = 'limit' AND NEW.limit_price IS NULL THEN RAISE EXCEPTION 'Limit orders must specify limit_price'; END IF; IF NEW.order_type IN ('stop', 'stop_limit') AND NEW.stop_price IS NULL THEN RAISE EXCEPTION 'Stop orders must specify stop_price'; END IF; -- Validate quantity constraints IF NEW.filled_quantity > NEW.quantity THEN RAISE EXCEPTION 'Filled quantity cannot exceed order quantity'; END IF; -- Auto-update timestamps IF TG_OP = 'INSERT' THEN NEW.created_at := EXTRACT(EPOCH FROM NOW()) * 1000000000; NEW.updated_at := NEW.created_at; ELSE NEW.updated_at := EXTRACT(EPOCH FROM NOW()) * 1000000000; END IF; -- Update status based on fill level IF NEW.filled_quantity = 0 THEN NEW.status := 'pending'; ELSIF NEW.filled_quantity = NEW.quantity THEN NEW.status := 'filled'; ELSIF NEW.filled_quantity > 0 THEN NEW.status := 'partial'; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; -- Function to generate trading events from orders CREATE OR REPLACE FUNCTION generate_order_event() RETURNS TRIGGER AS $$ DECLARE event_type_val trading_event_type; event_ts ns_timestamp; BEGIN event_ts := EXTRACT(EPOCH FROM NOW()) * 1000000000; -- Determine event type IF TG_OP = 'INSERT' THEN event_type_val := 'order_submitted'; ELSE -- Update case - determine based on status change CASE WHEN OLD.status != NEW.status THEN CASE NEW.status WHEN 'accepted' THEN event_type_val := 'order_accepted'; WHEN 'rejected' THEN event_type_val := 'order_rejected'; WHEN 'cancelled' THEN event_type_val := 'order_cancelled'; WHEN 'expired' THEN event_type_val := 'order_expired'; WHEN 'filled' THEN event_type_val := 'order_filled'; WHEN 'partial' THEN event_type_val := 'order_partially_filled'; ELSE event_type_val := 'order_modified'; END CASE; ELSE event_type_val := 'order_modified'; END CASE; END IF; -- Insert trading event INSERT INTO trading_events ( correlation_id, event_timestamp, received_timestamp, processing_timestamp, event_type, event_source, symbol, account_id, strategy_id, venue, event_data, node_id, process_id, event_hash ) VALUES ( COALESCE(NEW.id, OLD.id), event_ts, event_ts, event_ts, event_type_val, 'order_management', COALESCE(NEW.symbol, OLD.symbol), COALESCE(NEW.account_id, OLD.account_id), COALESCE(NEW.strategy_id, OLD.strategy_id), COALESCE(NEW.venue, OLD.venue), jsonb_build_object( 'order_id', COALESCE(NEW.id, OLD.id), 'client_order_id', COALESCE(NEW.client_order_id, OLD.client_order_id), 'order_type', COALESCE(NEW.order_type, OLD.order_type), 'side', COALESCE(NEW.side, OLD.side), 'quantity', COALESCE(NEW.quantity, OLD.quantity), 'filled_quantity', COALESCE(NEW.filled_quantity, OLD.filled_quantity), 'limit_price', COALESCE(NEW.limit_price, OLD.limit_price), 'status', COALESCE(NEW.status, OLD.status) ), 'trading-node-01', -- TODO: Get from environment pg_backend_pid(), encode(sha256(COALESCE(NEW.id, OLD.id)::text::bytea), 'hex') ); RETURN COALESCE(NEW, OLD); END; $$ LANGUAGE plpgsql; -- ================================================================================================ -- CREATE TRIGGERS -- ================================================================================================ -- Order validation and event generation CREATE TRIGGER tg_validate_orders BEFORE INSERT OR UPDATE ON orders FOR EACH ROW EXECUTE FUNCTION validate_order_constraints(); CREATE TRIGGER tg_generate_order_events AFTER INSERT OR UPDATE ON orders FOR EACH ROW EXECUTE FUNCTION generate_order_event(); -- Position updates from fills CREATE TRIGGER tg_update_position_from_fill AFTER INSERT ON fills FOR EACH ROW EXECUTE FUNCTION update_position_from_fill(); -- ================================================================================================ -- ANALYTICAL VIEWS FOR REPORTING -- ================================================================================================ -- Real-time order book view CREATE VIEW v_active_orders AS SELECT o.id, o.symbol, o.side, o.order_type, o.quantity, o.filled_quantity, o.remaining_quantity, o.limit_price, o.status, o.created_at, o.account_id, o.venue FROM orders o WHERE o.status IN ('pending', 'accepted', 'partial') ORDER BY o.symbol, o.side, o.limit_price; -- Position summary view CREATE VIEW v_position_summary AS SELECT p.symbol, p.account_id, p.strategy_id, p.quantity, p.avg_cost, p.last_price, p.market_value, p.unrealized_pnl, p.realized_pnl, (p.unrealized_pnl + p.realized_pnl) as total_pnl, p.last_updated FROM positions p WHERE p.quantity != 0 ORDER BY ABS(p.market_value) DESC; -- Daily trading summary view CREATE VIEW v_daily_trading_summary AS SELECT DATE(TO_TIMESTAMP(f.execution_timestamp / 1000000000.0)) as trade_date, f.symbol, o.account_id, COUNT(*) as trade_count, SUM(f.quantity) as total_volume, AVG(f.price) as avg_price, MIN(f.price) as min_price, MAX(f.price) as max_price, SUM(f.commission) as total_commission FROM fills f JOIN orders o ON f.order_id = o.id GROUP BY DATE(TO_TIMESTAMP(f.execution_timestamp / 1000000000.0)), f.symbol, o.account_id ORDER BY trade_date DESC, total_volume DESC; -- ================================================================================================ -- PERFORMANCE MONITORING FUNCTIONS -- ================================================================================================ -- Function to get trading events statistics CREATE OR REPLACE FUNCTION get_trading_events_stats( start_time ns_timestamp DEFAULT NULL, end_time ns_timestamp DEFAULT NULL ) RETURNS TABLE ( event_type trading_event_type, event_count BIGINT, avg_processing_time_ns NUMERIC, max_processing_time_ns BIGINT, events_per_second NUMERIC ) AS $$ DECLARE default_start ns_timestamp := EXTRACT(EPOCH FROM (NOW() - INTERVAL '1 hour')) * 1000000000; default_end ns_timestamp := EXTRACT(EPOCH FROM NOW()) * 1000000000; time_span_seconds NUMERIC; BEGIN start_time := COALESCE(start_time, default_start); end_time := COALESCE(end_time, default_end); time_span_seconds := (end_time - start_time) / 1000000000.0; RETURN QUERY SELECT te.event_type, COUNT(*) as event_count, AVG(te.processing_timestamp - te.received_timestamp) as avg_processing_time_ns, MAX(te.processing_timestamp - te.received_timestamp) as max_processing_time_ns, (COUNT(*) / time_span_seconds) as events_per_second FROM trading_events te WHERE te.event_timestamp BETWEEN start_time AND end_time GROUP BY te.event_type ORDER BY event_count DESC; END; $$ LANGUAGE plpgsql; -- ================================================================================================ -- ARCHIVAL AND RETENTION POLICY -- ================================================================================================ -- Function to archive old trading events (for 7+ year retention) CREATE OR REPLACE FUNCTION archive_old_trading_events(retention_days INTEGER DEFAULT 2555) -- 7 years RETURNS INTEGER AS $$ DECLARE archive_date DATE; archived_count INTEGER := 0; BEGIN archive_date := CURRENT_DATE - INTERVAL '1 day' * retention_days; -- Move old partitions to archive schema (implement as needed) -- This is a placeholder for actual archival implementation RETURN archived_count; END; $$ LANGUAGE plpgsql; -- ================================================================================================ -- GRANTS AND PERMISSIONS -- ================================================================================================ -- Note: Uncomment and modify these grants based on your specific user roles -- Application user permissions -- GRANT SELECT, INSERT ON trading_events TO trading_app_user; -- GRANT SELECT, INSERT, UPDATE ON orders TO trading_app_user; -- GRANT SELECT, INSERT ON fills TO trading_app_user; -- GRANT SELECT, UPDATE ON positions TO trading_app_user; -- Read-only analytics user -- GRANT SELECT ON ALL TABLES IN SCHEMA public TO analytics_user; -- GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO analytics_user; -- Risk management user (read positions and events) -- GRANT SELECT ON trading_events, positions, orders, fills TO risk_user; -- ================================================================================================ -- FINAL COMMENTS AND DOCUMENTATION -- ================================================================================================ COMMENT ON TABLE trading_events IS 'Immutable event store for all trading activities with nanosecond precision. Partitioned by date for optimal performance. Never update or delete records for compliance.'; COMMENT ON TABLE orders IS 'Current state of trading orders. Mutable table derived from trading_events. Primary operational table for order management.'; COMMENT ON TABLE fills IS 'Immutable record of trade executions. Links to orders and triggers position updates. Critical for P&L calculation and reporting.'; COMMENT ON TABLE positions IS 'Current position holdings with real-time mark-to-market. Updated via triggers from fills. Primary table for risk management.'; COMMENT ON DOMAIN ns_timestamp IS 'Nanoseconds since Unix epoch (1970-01-01 00:00:00 UTC). Used for microsecond-precision timing in HFT systems.'; -- Performance notes COMMENT ON INDEX idx_trading_events_timestamp IS 'Primary time-series index for trading events. Critical for chronological queries and event replay.'; COMMENT ON INDEX idx_orders_symbol_status IS 'Composite index for order book queries by symbol and status. Essential for active order management.'; COMMENT ON INDEX idx_positions_nonzero IS 'Partial index for non-zero positions only. Optimizes position management queries by excluding closed positions.';