-- ================================================================================================ -- Migration 047: Broker Gateway Tables for AMP Futures FIX Integration -- Purpose: Database schema for FIX protocol broker integration with session management, -- order tracking, execution reports, and position reconciliation -- Date: 2025-11-09 -- Agent: 1 (Broker Gateway Database Schema) -- ================================================================================================ -- ================================================================================================ -- TABLE 1: BROKER_SESSIONS -- Tracks FIX session state for recovery after disconnects -- ================================================================================================ CREATE TABLE broker_sessions ( -- Primary identifier id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- FIX session identifiers session_id VARCHAR(128) NOT NULL UNIQUE, -- Composite: "FOXHUNT_CLIENT-CQG" sender_comp_id VARCHAR(64) NOT NULL, -- Tag 49 (SenderCompID) target_comp_id VARCHAR(64) NOT NULL, -- Tag 56 (TargetCompID) -- FIX sequence numbers (critical for message ordering) 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 session_state VARCHAR(32) NOT NULL, -- DISCONNECTED, CONNECTED, LOGGING_IN, ACTIVE, LOGGING_OUT, RECONNECTING -- Heartbeat monitoring last_heartbeat_sent TIMESTAMPTZ, -- Last heartbeat we sent (Tag 35=0) last_heartbeat_received TIMESTAMPTZ, -- Last heartbeat we received -- Connection lifecycle connected_at TIMESTAMPTZ, -- TCP connection established disconnected_at TIMESTAMPTZ, -- TCP connection closed -- Audit timestamps created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), -- Session first created updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), -- Last state update -- Constraints CONSTRAINT chk_broker_session_state CHECK ( session_state IN ('DISCONNECTED', 'CONNECTED', 'LOGGING_IN', 'ACTIVE', 'LOGGING_OUT', 'RECONNECTING') ), CONSTRAINT chk_broker_session_sequences CHECK ( sender_seq_num >= 1 AND target_seq_num >= 1 ) ); -- Add table comment COMMENT ON TABLE broker_sessions IS 'FIX session state tracking for broker gateway - manages sequence numbers and connection lifecycle'; -- Add column comments COMMENT ON COLUMN broker_sessions.session_id IS 'Unique FIX session identifier (SenderCompID-TargetCompID)'; COMMENT ON COLUMN broker_sessions.sender_comp_id IS 'FIX SenderCompID (Tag 49) - our client identifier'; COMMENT ON COLUMN broker_sessions.target_comp_id IS 'FIX TargetCompID (Tag 56) - broker gateway identifier'; COMMENT ON COLUMN broker_sessions.sender_seq_num IS 'Outgoing FIX message sequence number (Tag 34) - persisted for recovery'; COMMENT ON COLUMN broker_sessions.target_seq_num IS 'Expected incoming FIX message sequence number - persisted for gap detection'; COMMENT ON COLUMN broker_sessions.session_state IS 'Current FIX session state (DISCONNECTED, ACTIVE, etc.)'; COMMENT ON COLUMN broker_sessions.last_heartbeat_sent IS 'Timestamp of last Heartbeat message sent (Tag 35=0)'; COMMENT ON COLUMN broker_sessions.last_heartbeat_received IS 'Timestamp of last Heartbeat message received - used for timeout detection'; -- ================================================================================================ -- TABLE 2: BROKER_ORDERS -- Audit trail for all orders routed to broker via FIX protocol -- ================================================================================================ CREATE TABLE broker_orders ( -- Primary identifier id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- FIX order identifiers client_order_id VARCHAR(64) NOT NULL UNIQUE, -- Our ClOrdID (Tag 11) - UUID format broker_order_id VARCHAR(64), -- Broker OrderID (Tag 37) - filled after ExecutionReport -- Integration with internal order tracking internal_order_id UUID, -- Optional FK to orders table -- Trading context account_id VARCHAR(64) NOT NULL, -- AMP account identifier symbol VARCHAR(32) NOT NULL, -- Instrument symbol (ES, NQ, etc.) side VARCHAR(4) NOT NULL, -- BUY, SELL order_type VARCHAR(16) NOT NULL, -- MARKET, LIMIT, STOP, STOP_LIMIT -- Order quantities quantity NUMERIC(18, 8) NOT NULL, -- Order quantity in contracts filled_quantity NUMERIC(18, 8) NOT NULL DEFAULT 0, -- Quantity filled so far -- Pricing (NULL for market orders) price NUMERIC(18, 8), -- Limit price (Tag 44) stop_price NUMERIC(18, 8), -- Stop price (Tag 99) for stop orders avg_fill_price NUMERIC(18, 8), -- Average fill price (Tag 6) -- Order parameters time_in_force VARCHAR(8) NOT NULL DEFAULT 'DAY', -- DAY, GTC, IOC, FOK, GTD -- Order lifecycle status status VARCHAR(32) NOT NULL, -- PENDING_SUBMIT, SUBMITTED, PARTIALLY_FILLED, FILLED, REJECTED, CANCELLED, EXPIRED -- Financial tracking commission NUMERIC(18, 8), -- Commission charged by broker -- Metadata metadata JSONB DEFAULT '{}'::jsonb, -- Strategy, model, additional context -- Timing submitted_at TIMESTAMPTZ, -- When order was submitted to broker filled_at TIMESTAMPTZ, -- When order was fully filled created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), -- When record was created updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), -- Last update -- Constraints CONSTRAINT chk_broker_order_side CHECK (side IN ('BUY', 'SELL')), CONSTRAINT chk_broker_order_type CHECK ( order_type IN ('MARKET', 'LIMIT', 'STOP', 'STOP_LIMIT') ), CONSTRAINT chk_broker_order_tif CHECK ( time_in_force IN ('DAY', 'GTC', 'IOC', 'FOK', 'GTD') ), CONSTRAINT chk_broker_order_status CHECK ( status IN ('PENDING_SUBMIT', 'SUBMITTED', 'PARTIALLY_FILLED', 'FILLED', 'REJECTED', 'CANCELLED', 'EXPIRED', 'CANCEL_PENDING') ), CONSTRAINT chk_broker_order_quantity CHECK (quantity > 0), CONSTRAINT chk_broker_order_filled CHECK ( filled_quantity >= 0 AND filled_quantity <= quantity ), CONSTRAINT chk_broker_order_price CHECK ( (order_type = 'MARKET' AND price IS NULL) OR (order_type IN ('LIMIT', 'STOP_LIMIT') AND price IS NOT NULL) ), CONSTRAINT chk_broker_order_stop_price CHECK ( (order_type IN ('STOP', 'STOP_LIMIT') AND stop_price IS NOT NULL) OR (order_type IN ('MARKET', 'LIMIT') AND stop_price IS NULL) ) ); -- Add table comment COMMENT ON TABLE broker_orders IS 'Broker order audit trail - tracks all orders routed to broker via FIX protocol'; -- Add column comments COMMENT ON COLUMN broker_orders.client_order_id IS 'FIX ClOrdID (Tag 11) - unique client order identifier (UUID format)'; COMMENT ON COLUMN broker_orders.broker_order_id IS 'FIX OrderID (Tag 37) - broker-assigned order identifier (filled after ExecutionReport)'; COMMENT ON COLUMN broker_orders.internal_order_id IS 'Optional foreign key to internal orders table for tracking'; COMMENT ON COLUMN broker_orders.account_id IS 'AMP Futures account identifier'; COMMENT ON COLUMN broker_orders.symbol IS 'Trading symbol (ES, NQ, CL, etc.)'; COMMENT ON COLUMN broker_orders.side IS 'Order side: BUY or SELL (FIX Tag 54: 1=Buy, 2=Sell)'; COMMENT ON COLUMN broker_orders.order_type IS 'Order type: MARKET, LIMIT, STOP, STOP_LIMIT (FIX Tag 40)'; COMMENT ON COLUMN broker_orders.quantity IS 'Order quantity in contracts (FIX Tag 38)'; COMMENT ON COLUMN broker_orders.filled_quantity IS 'Cumulative filled quantity (FIX Tag 14)'; COMMENT ON COLUMN broker_orders.price IS 'Limit price for LIMIT/STOP_LIMIT orders (FIX Tag 44)'; COMMENT ON COLUMN broker_orders.stop_price IS 'Stop price for STOP/STOP_LIMIT orders (FIX Tag 99)'; COMMENT ON COLUMN broker_orders.avg_fill_price IS 'Average fill price across all executions (FIX Tag 6)'; COMMENT ON COLUMN broker_orders.time_in_force IS 'Time in force: DAY, GTC, IOC, FOK (FIX Tag 59)'; COMMENT ON COLUMN broker_orders.status IS 'Order status in lifecycle (mapped from FIX Tag 39)'; COMMENT ON COLUMN broker_orders.metadata IS 'Additional order metadata (strategy, model, risk checks, etc.)'; -- ================================================================================================ -- TABLE 3: BROKER_FILLS -- Stores every ExecutionReport (FIX Tag 35=8) received from broker -- ================================================================================================ CREATE TABLE broker_fills ( -- Primary identifier id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- FIX execution identifiers execution_id VARCHAR(64) NOT NULL UNIQUE, -- ExecID (Tag 17) - unique per fill broker_order_id VARCHAR(64) NOT NULL, -- OrderID (Tag 37) client_order_id VARCHAR(64) NOT NULL, -- ClOrdID (Tag 11) - links to broker_orders -- Integration with internal execution tracking internal_execution_id UUID, -- Optional FK to executions table -- Trade details symbol VARCHAR(32) NOT NULL, -- Instrument symbol side VARCHAR(4) NOT NULL, -- BUY, SELL -- Execution type and status exec_type VARCHAR(16) NOT NULL, -- NEW, TRADE, CANCELED, REJECTED (Tag 150) order_status VARCHAR(32) NOT NULL, -- Order status after this execution (Tag 39) -- Fill quantities and pricing last_qty NUMERIC(18, 8), -- Quantity filled in this report (Tag 32) last_price NUMERIC(18, 8), -- Fill price for this report (Tag 31) cum_qty NUMERIC(18, 8), -- Total filled quantity (Tag 14) avg_price NUMERIC(18, 8), -- Average fill price (Tag 6) leaves_qty NUMERIC(18, 8), -- Remaining unfilled quantity (Tag 151) -- Financial tracking commission NUMERIC(18, 8), -- Commission charged (if provided) -- Timing transact_time TIMESTAMPTZ NOT NULL, -- Exchange execution time (Tag 60) received_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), -- When we received the ExecutionReport -- Audit trail text TEXT, -- Reject reason or notes (Tag 58) raw_fix_message TEXT, -- Full FIX message for regulatory compliance created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), -- Record creation time -- Constraints CONSTRAINT chk_broker_fill_side CHECK (side IN ('BUY', 'SELL')), CONSTRAINT chk_broker_fill_exec_type CHECK ( exec_type IN ('NEW', 'TRADE', 'CANCELED', 'REJECTED', 'PENDING_CANCEL', 'REPLACED', 'PENDING_REPLACE', 'STOPPED', 'SUSPENDED', 'RESTATED', 'EXPIRED') ), CONSTRAINT chk_broker_fill_order_status CHECK ( order_status IN ('NEW', 'PARTIALLY_FILLED', 'FILLED', 'CANCELED', 'REJECTED', 'EXPIRED', 'PENDING_CANCEL', 'PENDING_REPLACE') ), CONSTRAINT chk_broker_fill_quantities CHECK ( (last_qty IS NULL OR last_qty >= 0) AND (cum_qty IS NULL OR cum_qty >= 0) AND (leaves_qty IS NULL OR leaves_qty >= 0) ) ); -- Add table comment COMMENT ON TABLE broker_fills IS 'Broker execution reports - stores every ExecutionReport (FIX Tag 35=8) for audit and compliance'; -- Add column comments COMMENT ON COLUMN broker_fills.execution_id IS 'FIX ExecID (Tag 17) - unique execution identifier'; COMMENT ON COLUMN broker_fills.broker_order_id IS 'FIX OrderID (Tag 37) - broker-assigned order identifier'; COMMENT ON COLUMN broker_fills.client_order_id IS 'FIX ClOrdID (Tag 11) - links to broker_orders table'; COMMENT ON COLUMN broker_fills.exec_type IS 'FIX ExecType (Tag 150) - execution type (NEW, TRADE, CANCELED, REJECTED)'; COMMENT ON COLUMN broker_fills.order_status IS 'FIX OrdStatus (Tag 39) - order status after this execution'; COMMENT ON COLUMN broker_fills.last_qty IS 'FIX LastQty (Tag 32) - quantity filled in this report'; COMMENT ON COLUMN broker_fills.last_price IS 'FIX LastPx (Tag 31) - fill price for this report'; COMMENT ON COLUMN broker_fills.cum_qty IS 'FIX CumQty (Tag 14) - total filled quantity across all fills'; COMMENT ON COLUMN broker_fills.avg_price IS 'FIX AvgPx (Tag 6) - average fill price'; COMMENT ON COLUMN broker_fills.leaves_qty IS 'FIX LeavesQty (Tag 151) - remaining unfilled quantity'; COMMENT ON COLUMN broker_fills.transact_time IS 'FIX TransactTime (Tag 60) - exchange execution timestamp'; COMMENT ON COLUMN broker_fills.text IS 'FIX Text (Tag 58) - reject reason or notes'; COMMENT ON COLUMN broker_fills.raw_fix_message IS 'Complete FIX message for regulatory compliance and debugging'; -- ================================================================================================ -- TABLE 4: BROKER_POSITIONS -- Real-time position reconciliation with broker -- ================================================================================================ CREATE TABLE broker_positions ( -- Primary identifier id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- Position identifiers account_id VARCHAR(64) NOT NULL, -- AMP account identifier symbol VARCHAR(32) NOT NULL, -- Instrument symbol -- Position details quantity NUMERIC(18, 8) NOT NULL DEFAULT 0, -- Signed: positive=long, negative=short avg_entry_price NUMERIC(18, 8), -- Average entry price -- Market value and P&L market_value NUMERIC(18, 8), -- Current market value unrealized_pnl NUMERIC(18, 8), -- Unrealized profit/loss realized_pnl NUMERIC(18, 8), -- Realized profit/loss (closed trades) -- Timing last_updated TIMESTAMPTZ NOT NULL DEFAULT NOW(), -- Last position update from broker created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), -- Position first opened -- Constraints CONSTRAINT uk_broker_positions_account_symbol UNIQUE (account_id, symbol) ); -- Add table comment COMMENT ON TABLE broker_positions IS 'Real-time position cache from broker - used for reconciliation and risk checks'; -- Add column comments COMMENT ON COLUMN broker_positions.account_id IS 'AMP Futures account identifier'; COMMENT ON COLUMN broker_positions.symbol IS 'Trading symbol (ES, NQ, CL, etc.)'; COMMENT ON COLUMN broker_positions.quantity IS 'Position quantity (signed: positive=long, negative=short)'; COMMENT ON COLUMN broker_positions.avg_entry_price IS 'Average entry price across all trades'; COMMENT ON COLUMN broker_positions.market_value IS 'Current market value of position'; COMMENT ON COLUMN broker_positions.unrealized_pnl IS 'Unrealized profit/loss on open position'; COMMENT ON COLUMN broker_positions.realized_pnl IS 'Realized profit/loss from closed trades'; COMMENT ON COLUMN broker_positions.last_updated IS 'Last update timestamp from broker'; -- ================================================================================================ -- HIGH-PERFORMANCE INDEXES -- ================================================================================================ -- Broker sessions indexes CREATE INDEX idx_broker_sessions_state ON broker_sessions(session_state); CREATE INDEX idx_broker_sessions_updated_at ON broker_sessions(updated_at DESC); COMMENT ON INDEX idx_broker_sessions_state IS 'Fast lookups for active FIX sessions'; COMMENT ON INDEX idx_broker_sessions_updated_at IS 'Time-series queries for session lifecycle'; -- Broker orders indexes CREATE INDEX idx_broker_orders_broker_order_id ON broker_orders(broker_order_id); CREATE INDEX idx_broker_orders_account_id ON broker_orders(account_id); CREATE INDEX idx_broker_orders_symbol ON broker_orders(symbol); CREATE INDEX idx_broker_orders_status ON broker_orders(status); CREATE INDEX idx_broker_orders_created_at ON broker_orders(created_at DESC); CREATE INDEX idx_broker_orders_internal_order_id ON broker_orders(internal_order_id); -- Composite indexes for common queries CREATE INDEX idx_broker_orders_symbol_status ON broker_orders(symbol, status); CREATE INDEX idx_broker_orders_account_status ON broker_orders(account_id, status); COMMENT ON INDEX idx_broker_orders_broker_order_id IS 'Fast lookups by broker-assigned OrderID (Tag 37)'; COMMENT ON INDEX idx_broker_orders_account_id IS 'Account-based queries for order history'; COMMENT ON INDEX idx_broker_orders_symbol_status IS 'Composite index for active orders per symbol'; COMMENT ON INDEX idx_broker_orders_internal_order_id IS 'Link to internal orders table'; -- Broker fills indexes CREATE INDEX idx_broker_fills_broker_order_id ON broker_fills(broker_order_id); CREATE INDEX idx_broker_fills_client_order_id ON broker_fills(client_order_id); CREATE INDEX idx_broker_fills_transact_time ON broker_fills(transact_time DESC); CREATE INDEX idx_broker_fills_symbol ON broker_fills(symbol); CREATE INDEX idx_broker_fills_internal_execution_id ON broker_fills(internal_execution_id); COMMENT ON INDEX idx_broker_fills_broker_order_id IS 'Fast lookups of fills by broker OrderID'; COMMENT ON INDEX idx_broker_fills_client_order_id IS 'Fast lookups of fills by client ClOrdID'; COMMENT ON INDEX idx_broker_fills_transact_time IS 'Time-series queries for execution history'; COMMENT ON INDEX idx_broker_fills_internal_execution_id IS 'Link to internal executions table'; -- Broker positions indexes CREATE INDEX idx_broker_positions_account_id 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); COMMENT ON INDEX idx_broker_positions_account_id IS 'Fast lookups of all positions for an account'; COMMENT ON INDEX idx_broker_positions_symbol IS 'Fast lookups of positions by symbol'; COMMENT ON INDEX idx_broker_positions_last_updated IS 'Detect stale position data'; -- ================================================================================================ -- FOREIGN KEY CONSTRAINTS -- ================================================================================================ -- broker_fills → broker_orders (required relationship) ALTER TABLE broker_fills ADD CONSTRAINT fk_broker_fills_client_order_id FOREIGN KEY (client_order_id) REFERENCES broker_orders(client_order_id) ON DELETE CASCADE; COMMENT ON CONSTRAINT fk_broker_fills_client_order_id ON broker_fills IS 'Links execution reports to parent orders - CASCADE delete removes fills when order is deleted'; -- broker_orders → orders (optional relationship, only if orders table exists) DO $$ BEGIN IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'orders') THEN ALTER TABLE broker_orders ADD CONSTRAINT fk_broker_orders_internal_order_id FOREIGN KEY (internal_order_id) REFERENCES orders(id) ON DELETE SET NULL; RAISE NOTICE 'Created foreign key: broker_orders.internal_order_id → orders.id'; ELSE RAISE NOTICE 'Skipped foreign key: orders table does not exist (will be added later)'; END IF; END $$; -- broker_fills → executions (optional relationship, only if executions table exists) DO $$ BEGIN IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'executions') THEN ALTER TABLE broker_fills ADD CONSTRAINT fk_broker_fills_internal_execution_id FOREIGN KEY (internal_execution_id) REFERENCES executions(id) ON DELETE SET NULL; RAISE NOTICE 'Created foreign key: broker_fills.internal_execution_id → executions.id'; ELSE RAISE NOTICE 'Skipped foreign key: executions table does not exist (will be added later)'; END IF; END $$; -- ================================================================================================ -- TRIGGER FUNCTIONS FOR DATA INTEGRITY -- ================================================================================================ -- Function to auto-update updated_at timestamp CREATE OR REPLACE FUNCTION update_broker_orders_timestamp() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at := NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql; -- Trigger to auto-update broker_orders.updated_at CREATE TRIGGER tg_broker_orders_update_timestamp BEFORE UPDATE ON broker_orders FOR EACH ROW EXECUTE FUNCTION update_broker_orders_timestamp(); -- Function to auto-update broker_sessions.updated_at CREATE OR REPLACE FUNCTION update_broker_sessions_timestamp() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at := NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql; -- Trigger to auto-update broker_sessions.updated_at CREATE TRIGGER tg_broker_sessions_update_timestamp BEFORE UPDATE ON broker_sessions FOR EACH ROW EXECUTE FUNCTION update_broker_sessions_timestamp(); COMMENT ON FUNCTION update_broker_orders_timestamp() IS 'Auto-updates broker_orders.updated_at on modification'; COMMENT ON FUNCTION update_broker_sessions_timestamp() IS 'Auto-updates broker_sessions.updated_at on modification'; -- ================================================================================================ -- ANALYTICAL VIEWS FOR REPORTING -- ================================================================================================ -- View for active broker orders CREATE OR REPLACE VIEW v_active_broker_orders AS SELECT bo.id, bo.client_order_id, bo.broker_order_id, bo.account_id, bo.symbol, bo.side, bo.order_type, bo.quantity, bo.filled_quantity, bo.price, bo.avg_fill_price, bo.status, bo.time_in_force, bo.submitted_at, bo.created_at, EXTRACT(EPOCH FROM (NOW() - bo.created_at)) AS age_seconds, (bo.quantity - bo.filled_quantity) AS remaining_quantity FROM broker_orders bo WHERE bo.status IN ('PENDING_SUBMIT', 'SUBMITTED', 'PARTIALLY_FILLED', 'CANCEL_PENDING') ORDER BY bo.created_at DESC; COMMENT ON VIEW v_active_broker_orders IS 'Active broker orders (not yet fully filled, cancelled, or rejected)'; -- View for broker order fill summary CREATE OR REPLACE VIEW v_broker_order_fill_summary AS SELECT bo.client_order_id, bo.broker_order_id, bo.symbol, bo.side, bo.quantity AS order_qty, bo.filled_quantity AS total_filled, bo.avg_fill_price, bo.status, bo.created_at, COUNT(bf.id) AS num_fills, MIN(bf.transact_time) AS first_fill_time, MAX(bf.transact_time) AS last_fill_time, SUM(bf.commission) AS total_commission FROM broker_orders bo LEFT JOIN broker_fills bf ON bo.client_order_id = bf.client_order_id AND bf.exec_type = 'TRADE' GROUP BY bo.client_order_id, bo.broker_order_id, bo.symbol, bo.side, bo.quantity, bo.filled_quantity, bo.avg_fill_price, bo.status, bo.created_at ORDER BY bo.created_at DESC; COMMENT ON VIEW v_broker_order_fill_summary IS 'Summary of fills per order - useful for fill rate analysis'; -- View for position reconciliation CREATE OR REPLACE VIEW v_broker_position_reconciliation AS SELECT bp.account_id, bp.symbol, bp.quantity AS broker_qty, COALESCE(p.quantity / 100000000, 0) AS internal_qty, -- Convert from base units (cents) (bp.quantity - COALESCE(p.quantity / 100000000, 0)) AS delta, bp.unrealized_pnl AS broker_unrealized_pnl, p.unrealized_pnl / 100 AS internal_unrealized_pnl, -- Convert from cents bp.last_updated AS broker_last_updated, p.last_updated AS internal_last_updated FROM broker_positions bp LEFT JOIN positions p ON bp.symbol = p.symbol AND bp.account_id = p.account_id ORDER BY ABS(bp.quantity - COALESCE(p.quantity / 100000000, 0)) DESC; COMMENT ON VIEW v_broker_position_reconciliation IS 'Position reconciliation between broker and internal tracking - highlights mismatches'; -- ================================================================================================ -- GRANT PERMISSIONS -- ================================================================================================ -- Grant permissions to foxhunt role GRANT SELECT, INSERT, UPDATE ON broker_sessions TO foxhunt; GRANT SELECT, INSERT, UPDATE ON broker_orders TO foxhunt; GRANT SELECT, INSERT ON broker_fills TO foxhunt; GRANT SELECT, INSERT, UPDATE, DELETE ON broker_positions TO foxhunt; -- Grant view access GRANT SELECT ON v_active_broker_orders TO foxhunt; GRANT SELECT ON v_broker_order_fill_summary TO foxhunt; GRANT SELECT ON v_broker_position_reconciliation TO foxhunt; -- ================================================================================================ -- FINAL VALIDATION -- ================================================================================================ DO $$ BEGIN -- Verify tables exist IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'broker_sessions') THEN RAISE EXCEPTION 'Migration 047 failed: broker_sessions table not created'; END IF; IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'broker_orders') THEN RAISE EXCEPTION 'Migration 047 failed: broker_orders table not created'; END IF; IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'broker_fills') THEN RAISE EXCEPTION 'Migration 047 failed: broker_fills table not created'; END IF; IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'broker_positions') THEN RAISE EXCEPTION 'Migration 047 failed: broker_positions table not created'; END IF; -- Verify views exist IF NOT EXISTS (SELECT 1 FROM information_schema.views WHERE table_name = 'v_active_broker_orders') THEN RAISE EXCEPTION 'Migration 047 failed: v_active_broker_orders view not created'; END IF; IF NOT EXISTS (SELECT 1 FROM information_schema.views WHERE table_name = 'v_broker_order_fill_summary') THEN RAISE EXCEPTION 'Migration 047 failed: v_broker_order_fill_summary view not created'; END IF; IF NOT EXISTS (SELECT 1 FROM information_schema.views WHERE table_name = 'v_broker_position_reconciliation') THEN RAISE EXCEPTION 'Migration 047 failed: v_broker_position_reconciliation view not created'; END IF; -- Success message RAISE NOTICE '========================================'; RAISE NOTICE 'Migration 047 completed successfully!'; RAISE NOTICE 'Created 4 tables: broker_sessions, broker_orders, broker_fills, broker_positions'; RAISE NOTICE 'Created 3 views: v_active_broker_orders, v_broker_order_fill_summary, v_broker_position_reconciliation'; RAISE NOTICE 'Created 14 indexes for high-performance queries'; RAISE NOTICE 'Created 2 trigger functions for automatic timestamp updates'; RAISE NOTICE 'Broker Gateway database schema ready for AMP Futures FIX integration'; RAISE NOTICE '========================================'; END $$; -- ================================================================================================ -- ROLLBACK INSTRUCTIONS -- ================================================================================================ -- To rollback this migration, run the following commands in order: -- DROP VIEW IF EXISTS v_broker_position_reconciliation CASCADE; -- DROP VIEW IF EXISTS v_broker_order_fill_summary CASCADE; -- DROP VIEW IF EXISTS v_active_broker_orders CASCADE; -- DROP TRIGGER IF EXISTS tg_broker_sessions_update_timestamp ON broker_sessions; -- DROP TRIGGER IF EXISTS tg_broker_orders_update_timestamp ON broker_orders; -- DROP FUNCTION IF EXISTS update_broker_sessions_timestamp() CASCADE; -- DROP FUNCTION IF EXISTS update_broker_orders_timestamp() CASCADE; -- DROP TABLE IF EXISTS broker_fills CASCADE; -- DROP TABLE IF EXISTS broker_positions CASCADE; -- DROP TABLE IF EXISTS broker_orders CASCADE; -- DROP TABLE IF EXISTS broker_sessions CASCADE;