Quant Trading Systems: Architecture & Infrastructure › Portfolio and Order Management Systems

Michael BrenndoerferJanuary 21, 20267 min read
Quantitative FinanceAll chapters
Part 15 of 15Trading Systems and Production Deployment
Chapter 77Quant Trading Systems: Architecture & Infrastructure › Portfolio and Order Management Systems
Reading section 9 of 14 · Portfolio and Order Management Systems · 7 minSection 9 of 14 · 7 min

Book contents

Section 9 of 14 · 7 min read

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.

In[35]:
Code
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}
In[36]:
Code
# 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()
Out[37]:
Console
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.

In[38]:
Code
@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),
        }
In[39]:
Code
# 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()
Out[40]:
Console
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.