Files
foxhunt/migrations/047_broker_gateway_tables.sql
jgrusewski 00ef9e2866 Wave 15: Complete FactoredAction migration to 45-action system
Major Changes:
- Migrated from 3-action TradingAction to 45-action FactoredAction
- 45 actions: 5 exposure × 3 order types × 3 urgency levels
- Absolute exposure model (target positions -1.0 to +1.0)
- Transaction cost differentiation (Market 0.15%, LimitMaker 0.05%, IoC 0.10%)
- Fixed action diversity threshold (1.11% → 0.5% for 45-action space)

Bug Fixes:
- Bug #15: Incomplete FactoredAction integration (code existed but unused)
- Bug #16: Runtime crash in action diversity checking (hardcoded 3-action match)

Code Changes (13 files, ~464 lines):
- ml/src/dqn/action_space.rs: Core FactoredAction + 4 helper methods
- ml/src/trainers/dqn.rs: Action diversity refactored (3→45 dynamic)
- ml/src/dqn/reward.rs: calculate_reward() signature updated
- ml/src/dqn/portfolio_tracker.rs: execute_action() absolute exposure
- ml/src/dqn/dqn.rs: WorkingDQN action selection migrated
- ml/tests/*.rs: 9 test files updated with FactoredAction assertions

Test Results:
- 1-epoch smoke test: 100% action diversity (45/45 actions, 80.2s)
- 10-epoch production: 87.8% readiness (79/90 scorecard, 14.0 min)
- Loss convergence: 96.9% reduction (119K → 3.6K)
- Action diversity: 100% → 44% (healthy specialization)
- Checkpoint reliability: 12/12 files saved (100%)
- DQN tests: 195/195 passing (100%)
- ML baseline: 1,514/1,515 passing (99.93%)

Production Status:  CERTIFIED (87.8% readiness)
Go/No-Go:  GO FOR 100-EPOCH PRODUCTION TRAINING

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 23:27:02 +01:00

559 lines
28 KiB
PL/PgSQL

-- ================================================================================================
-- 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;