-- Migration 046: Broker Integration (AMP Futures / CQG FIX API) -- Description: Database schema for FIX order routing, execution tracking, and session management -- Date: 2025-11-09 BEGIN; -- ============================================================================ -- broker_orders: Audit trail for all broker orders -- ============================================================================ CREATE TABLE IF NOT EXISTS broker_orders ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), client_order_id VARCHAR(64) NOT NULL UNIQUE, -- Our ClOrdID (Tag 11) broker_order_id VARCHAR(64), -- Broker OrderID (Tag 37, nullable until ACK) account_id VARCHAR(64) NOT NULL, -- AMP account identifier symbol VARCHAR(32) NOT NULL, -- ES, NQ, etc. side VARCHAR(4) NOT NULL, -- BUY, SELL order_type VARCHAR(16) NOT NULL, -- MARKET, LIMIT, STOP, STOP_LIMIT quantity NUMERIC(18, 8) NOT NULL, -- Contracts price NUMERIC(18, 8), -- NULL for market orders stop_price NUMERIC(18, 8), -- For stop orders time_in_force VARCHAR(8) NOT NULL DEFAULT 'DAY', -- DAY, IOC, GTC status VARCHAR(32) NOT NULL, -- PENDING_SUBMIT, SUBMITTED, FILLED, etc. filled_quantity NUMERIC(18, 8) DEFAULT 0, -- Cumulative filled quantity avg_fill_price NUMERIC(18, 8), -- Average fill price metadata JSONB, -- Strategy, model_name, etc. submitted_at TIMESTAMPTZ, -- When submitted to broker filled_at TIMESTAMPTZ, -- When fully filled created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); -- Indexes for broker_orders CREATE INDEX idx_broker_orders_client_order_id ON broker_orders(client_order_id); CREATE INDEX idx_broker_orders_broker_order_id ON broker_orders(broker_order_id) WHERE broker_order_id IS NOT NULL; CREATE INDEX idx_broker_orders_account ON broker_orders(account_id); CREATE INDEX idx_broker_orders_status ON broker_orders(status); CREATE INDEX idx_broker_orders_symbol ON broker_orders(symbol); CREATE INDEX idx_broker_orders_created_at ON broker_orders(created_at DESC); -- ============================================================================ -- broker_executions: Execution reports from broker -- ============================================================================ CREATE TABLE IF NOT EXISTS broker_executions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), execution_id VARCHAR(64) NOT NULL UNIQUE, -- ExecID (Tag 17) broker_order_id VARCHAR(64) NOT NULL, -- OrderID (Tag 37) client_order_id VARCHAR(64) NOT NULL, -- ClOrdID (Tag 11) symbol VARCHAR(32) NOT NULL, side VARCHAR(4) NOT NULL, -- BUY, SELL exec_type VARCHAR(16) NOT NULL, -- NEW, TRADE, CANCELED, REJECTED order_status VARCHAR(32) NOT NULL, -- Order status after this execution last_qty NUMERIC(18, 8), -- Quantity filled in this report (Tag 32) last_price NUMERIC(18, 8), -- Fill price (Tag 31) cum_qty NUMERIC(18, 8), -- Total filled quantity (Tag 14) avg_price NUMERIC(18, 8), -- Average fill price (Tag 6) commission NUMERIC(18, 8), -- Commission charged transact_time TIMESTAMPTZ NOT NULL, -- Exchange execution time text TEXT, -- Reject reason or notes raw_fix_message TEXT, -- Full FIX message for audit received_at TIMESTAMPTZ DEFAULT NOW(), CONSTRAINT fk_broker_executions_order FOREIGN KEY (client_order_id) REFERENCES broker_orders(client_order_id) ON DELETE CASCADE ); -- Indexes for broker_executions CREATE INDEX idx_broker_executions_execution_id ON broker_executions(execution_id); CREATE INDEX idx_broker_executions_broker_order_id ON broker_executions(broker_order_id); CREATE INDEX idx_broker_executions_client_order_id ON broker_executions(client_order_id); CREATE INDEX idx_broker_executions_symbol ON broker_executions(symbol); CREATE INDEX idx_broker_executions_transact_time ON broker_executions(transact_time DESC); CREATE INDEX idx_broker_executions_exec_type ON broker_executions(exec_type); -- ============================================================================ -- fix_sessions: FIX session state for recovery -- ============================================================================ CREATE TABLE IF NOT EXISTS fix_sessions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), session_id VARCHAR(128) NOT NULL UNIQUE, -- "FOXHUNT_CLIENT-CQG" sender_comp_id VARCHAR(64) NOT NULL, -- Tag 49 (SenderCompID) target_comp_id VARCHAR(64) NOT NULL, -- Tag 56 (TargetCompID) sender_seq_num BIGINT NOT NULL DEFAULT 1, -- Our outgoing sequence number target_seq_num BIGINT NOT NULL DEFAULT 1, -- Expected incoming sequence number session_state VARCHAR(32) NOT NULL, -- DISCONNECTED, CONNECTED, ACTIVE, etc. last_heartbeat_sent TIMESTAMPTZ, last_heartbeat_received TIMESTAMPTZ, connected_at TIMESTAMPTZ, disconnected_at TIMESTAMPTZ, updated_at TIMESTAMPTZ DEFAULT NOW() ); -- Indexes for fix_sessions CREATE INDEX idx_fix_sessions_session_id ON fix_sessions(session_id); CREATE INDEX idx_fix_sessions_state ON fix_sessions(session_state); -- ============================================================================ -- broker_account_state: Real-time account balances and positions -- ============================================================================ CREATE TABLE IF NOT EXISTS broker_account_state ( account_id VARCHAR(64) PRIMARY KEY, cash_balance NUMERIC(18, 2) NOT NULL, equity NUMERIC(18, 2) NOT NULL, margin_used NUMERIC(18, 2) NOT NULL, margin_available NUMERIC(18, 2) NOT NULL, buying_power NUMERIC(18, 2) NOT NULL, unrealized_pnl NUMERIC(18, 2) DEFAULT 0, realized_pnl NUMERIC(18, 2) DEFAULT 0, last_updated TIMESTAMPTZ DEFAULT NOW() ); -- Index for broker_account_state CREATE INDEX idx_broker_account_state_last_updated ON broker_account_state(last_updated DESC); -- ============================================================================ -- broker_positions: Real-time position tracking per symbol -- ============================================================================ CREATE TABLE IF NOT EXISTS broker_positions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), account_id VARCHAR(64) NOT NULL, symbol VARCHAR(32) NOT NULL, quantity NUMERIC(18, 8) NOT NULL DEFAULT 0, -- Positive = long, negative = short average_price NUMERIC(18, 8), market_value NUMERIC(18, 2), unrealized_pnl NUMERIC(18, 2) DEFAULT 0, last_updated TIMESTAMPTZ DEFAULT NOW(), CONSTRAINT uq_broker_positions_account_symbol UNIQUE (account_id, symbol) ); -- Indexes for broker_positions CREATE INDEX idx_broker_positions_account ON broker_positions(account_id); CREATE INDEX idx_broker_positions_symbol ON broker_positions(symbol); CREATE INDEX idx_broker_positions_last_updated ON broker_positions(last_updated DESC); -- ============================================================================ -- Grant permissions -- ============================================================================ GRANT SELECT, INSERT, UPDATE, DELETE ON broker_orders TO foxhunt; GRANT SELECT, INSERT ON broker_executions TO foxhunt; GRANT SELECT, INSERT, UPDATE ON fix_sessions TO foxhunt; GRANT SELECT, INSERT, UPDATE ON broker_account_state TO foxhunt; GRANT SELECT, INSERT, UPDATE, DELETE ON broker_positions TO foxhunt; -- ============================================================================ -- Commit migration -- ============================================================================ COMMIT;