Quant Trading Systems: Architecture & Infrastructure › Portfolio and Order Management Systems
Quantitative FinanceAll chapters Quant Trading Systems: Architecture & Infrastructure › Portfolio and Order Management SystemsOne section · Sections in this chapter9 / 14 ↓
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Professional trading operations require systems to track positions, monitor P&L, and ensure compliance with trading rules, in real time. These systems provide the ground truth about what the firm owns and owes, which is essential for both trading decisions and regulatory compliance.
The challenge of portfolio and order management is maintaining consistency across distributed systems under concurrent updates. Multiple strategies may be trading the same instrument simultaneously. Orders may be partially filled. Prices change continuously. The systems must provide accurate, up-to-date information despite this complexity.
Order Management System (OMS)
The OMS tracks the complete lifecycle of every order from creation through execution or cancellation. It is the authoritative record of trading activity and gives the data needed for reconciliation, compliance reporting, and strategy evaluation.
An order's lifecycle passes through several states: it is created, submitted to an exchange, potentially partially filled, and eventually either completely filled, cancelled, or rejected. Each transition must be recorded, and the current state must be available instantly for risk checks and strategy decisions.
from enum import Enum
class OrderStatus(Enum):
PENDING = "PENDING" # Created but not sent
SUBMITTED = "SUBMITTED" # Sent to venue
PARTIAL = "PARTIAL" # Partially filled
FILLED = "FILLED" # Completely filled
CANCELLED = "CANCELLED" # Cancelled by user or system
REJECTED = "REJECTED" # Rejected by venue
@dataclass
class OrderRecord:
"""Complete record of an order and its fills."""
order_id: str
symbol: str
side: str
quantity: float
order_type: str
limit_price: Optional[float]
status: OrderStatus
created_time: datetime
submitted_time: Optional[datetime] = None
filled_quantity: float = 0
average_fill_price: float = 0
fills: list = None
def __post_init__(self):
if self.fills is None:
self.fills = []
@property
def remaining_quantity(self) -> float:
return self.quantity - self.filled_quantity
@property
def is_complete(self) -> bool:
return self.status in [
OrderStatus.FILLED,
OrderStatus.CANCELLED,
OrderStatus.REJECTED,
]
class OrderManagementSystem:
"""Tracks orders through their complete lifecycle."""
def __init__(self):
self.orders: dict[str, OrderRecord] = {}
self.order_counter = 0
def create_order(
self,
symbol: str,
side: str,
quantity: float,
order_type: str,
limit_price: Optional[float] = None,
) -> str:
"""Create a new order and return its ID."""
self.order_counter += 1
order_id = f"ORD-{self.order_counter:06d}"
order = OrderRecord(
order_id=order_id,
symbol=symbol,
side=side,
quantity=quantity,
order_type=order_type,
limit_price=limit_price,
status=OrderStatus.PENDING,
created_time=datetime.now(),
)
self.orders[order_id] = order
return order_id
def submit_order(self, order_id: str) -> bool:
"""Mark order as submitted to venue."""
if order_id not in self.orders:
return False
order = self.orders[order_id]
if order.status != OrderStatus.PENDING:
return False
order.status = OrderStatus.SUBMITTED
order.submitted_time = datetime.now()
return True
def on_fill(self, order_id: str, fill_qty: float, fill_price: float):
"""Process a fill notification from the venue."""
if order_id not in self.orders:
return
order = self.orders[order_id]
# Update average price
total_value = (
order.average_fill_price * order.filled_quantity
+ fill_price * fill_qty
)
order.filled_quantity += fill_qty
order.average_fill_price = total_value / order.filled_quantity
# Record fill
order.fills.append(
{"quantity": fill_qty, "price": fill_price, "time": datetime.now()}
)
# Update status
if order.filled_quantity >= order.quantity:
order.status = OrderStatus.FILLED
else:
order.status = OrderStatus.PARTIAL
def get_open_orders(self) -> list[OrderRecord]:
"""Get all non-complete orders."""
return [o for o in self.orders.values() if not o.is_complete]
def get_order_summary(self) -> dict:
"""Get summary statistics of all orders."""
total = len(self.orders)
by_status = {}
for order in self.orders.values():
status = order.status.value
by_status[status] = by_status.get(status, 0) + 1
return {"total": total, "by_status": by_status}# Demonstrate order management
oms = OrderManagementSystem()
# Create and process some orders
order1_id = oms.create_order("AAPL", "BUY", 100, "LIMIT", 185.50)
order2_id = oms.create_order("GOOGL", "BUY", 50, "MARKET")
order3_id = oms.create_order("MSFT", "SELL", 200, "LIMIT", 380.00)
# Submit orders
for oid in [order1_id, order2_id, order3_id]:
oms.submit_order(oid)
# Simulate fills
oms.on_fill(order1_id, 60, 185.48) # Partial fill
oms.on_fill(order1_id, 40, 185.50) # Complete fill
oms.on_fill(order2_id, 50, 141.25) # Complete fill
summary = oms.get_order_summary()Order Management System Status
======================================================================
ORD-000001:
BUY 100 AAPL @ 185.50
Status: FILLED
Filled: 100/100 @ 185.49
ORD-000002:
BUY 50 GOOGL @ MARKET
Status: FILLED
Filled: 50/50 @ 141.25
ORD-000003:
SELL 200 MSFT @ 380.00
Status: SUBMITTED
Filled: 0/200 @ 0.00
Summary: {'total': 3, 'by_status': {'FILLED': 2, 'SUBMITTED': 1}}The OMS tracks the state of each order, updating from PENDING to FILLED as simulated executions occur. Notice that the AAPL order received two partial fills at different prices, and the OMS correctly computes the average fill price across both fills. The MSFT order remains SUBMITTED because no fills have been received, illustrating the difference between orders that are working at the exchange and orders that have been executed.
Portfolio Position Tracking
The portfolio management system maintains real-time position and P&L information. This is the authoritative source of truth about what positions the firm holds and what those positions are worth. Every other component, from risk management to strategy generation, depends on accurate portfolio state.
The core challenge is tracking the cost basis of positions as they are accumulated through multiple trades. When you buy 100 shares at one price and then 50 more at a different price, your average cost must be updated correctly. When you sell, you realize gains or losses based on that average cost.
@dataclass
class Position:
"""Represents a position in a single security."""
symbol: str
quantity: float
average_cost: float
current_price: float = 0
@property
def market_value(self) -> float:
return self.quantity * self.current_price
@property
def cost_basis(self) -> float:
return self.quantity * self.average_cost
@property
def unrealized_pnl(self) -> float:
return self.market_value - self.cost_basis
@property
def unrealized_pnl_pct(self) -> float:
if self.cost_basis == 0:
return 0
return self.unrealized_pnl / abs(self.cost_basis)
class PortfolioManager:
"""Manages portfolio positions and P&L tracking."""
def __init__(self, initial_cash: float):
self.cash = initial_cash
self.positions: dict[str, Position] = {}
self.realized_pnl = 0
def update_position(
self,
symbol: str,
quantity_change: float,
price: float,
is_fill: bool = True,
):
"""Update position from a trade fill or market data update."""
if symbol not in self.positions:
self.positions[symbol] = Position(symbol, 0, 0, price)
pos = self.positions[symbol]
if is_fill:
# This is a trade - update cash and average cost
trade_value = quantity_change * price
self.cash -= trade_value
if quantity_change > 0: # Buying
# Update average cost
total_cost = pos.cost_basis + trade_value
new_quantity = pos.quantity + quantity_change
pos.average_cost = (
total_cost / new_quantity if new_quantity != 0 else 0
)
pos.quantity = new_quantity
else: # Selling
# Realize P&L
sold_qty = abs(quantity_change)
realized = sold_qty * (price - pos.average_cost)
self.realized_pnl += realized
pos.quantity += quantity_change
# Update current price
pos.current_price = price
# Clean up zero positions
if abs(pos.quantity) < 0.001:
del self.positions[symbol]
def update_prices(self, prices: dict[str, float]):
"""Update current prices for all positions."""
for symbol, price in prices.items():
if symbol in self.positions:
self.positions[symbol].current_price = price
def get_portfolio_summary(self) -> dict:
"""Get complete portfolio summary."""
total_market_value = sum(
p.market_value for p in self.positions.values()
)
total_unrealized = sum(
p.unrealized_pnl for p in self.positions.values()
)
nav = self.cash + total_market_value
return {
"cash": self.cash,
"market_value": total_market_value,
"nav": nav,
"unrealized_pnl": total_unrealized,
"realized_pnl": self.realized_pnl,
"total_pnl": total_unrealized + self.realized_pnl,
"num_positions": len(self.positions),
}# Demonstrate portfolio management
portfolio = PortfolioManager(initial_cash=1000000)
# Execute some trades
trades = [
("AAPL", 100, 185.50), # Buy 100 AAPL
("GOOGL", 50, 141.00), # Buy 50 GOOGL
("MSFT", 75, 378.00), # Buy 75 MSFT
("AAPL", 50, 187.25), # Buy 50 more AAPL (average up)
]
trade_log = []
for symbol, qty, price in trades:
portfolio.update_position(symbol, qty, price)
trade_log.append(f"Bought {qty} {symbol} @ {price:.2f}")
# Update with current market prices
current_prices = {"AAPL": 188.50, "GOOGL": 142.75, "MSFT": 375.00}
portfolio.update_prices(current_prices)
# Sell some AAPL to realize profit
portfolio.update_position("AAPL", -75, 188.50)
trade_log.append("\nSold 75 AAPL @ 188.50")
summary = portfolio.get_portfolio_summary()Trade Execution Log: -------------------------------------------------- Bought 100 AAPL @ 185.50 Bought 50 GOOGL @ 141.00 Bought 75 MSFT @ 378.00 Bought 50 AAPL @ 187.25 Sold 75 AAPL @ 188.50 ================================================== Portfolio Summary ================================================== Cash: $ 950,825.00 Market Value: $ 49,400.00 NAV: $1,000,225.00 Unrealized P&L: $ 43.75 Realized P&L: $ 181.25 Total P&L: $ 225.00 -------------------------------------------------- Position Details: AAPL: 75 shares @ avg 186.08, P&L: $181.25 (1.30%) GOOGL: 50 shares @ avg 141.00, P&L: $87.50 (1.24%) MSFT: 75 shares @ avg 378.00, P&L: $-225.00 (-0.79%)
The portfolio summary aggregates cash and positions to calculate the Net Asset Value (NAV) and tracks both realized and unrealized P&L. Notice how the sale of 75 AAPL shares at 188.50 realized a profit because the sale price exceeded the average cost of the position. The remaining 75 AAPL shares continue to show unrealized profit as the current price exceeds the average cost.