Core Entities
Account: account_id, user_id, account_type (CASH, MARGIN), buying_power, portfolio_value, status. Order: order_id, account_id, symbol, side (BUY/SELL), type (MARKET, LIMIT, STOP, STOP_LIMIT), quantity, limit_price, stop_price, time_in_force (DAY, GTC, IOC, FOK), status (PENDING, OPEN, PARTIALLY_FILLED, FILLED, CANCELLED, REJECTED), filled_quantity, avg_fill_price, created_at, updated_at. Fill: fill_id, order_id, quantity, price, counterparty_order_id, timestamp. Position: position_id, account_id, symbol, quantity, avg_cost_basis, current_price, unrealized_pnl. OrderBook: in-memory data structure, not persisted — reconstructed from exchange feed. Quote: symbol, bid_price, bid_size, ask_price, ask_size, last_price, last_size, timestamp.
Order Matching Engine
The order book maintains two priority queues per symbol: bids (sorted by price descending, then time ascending) and asks (sorted by price ascending, then time ascending). Price-time priority: the highest bid and lowest ask are matched first; among equal prices, the earlier order is matched first.
import heapq
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class Order:
order_id: str
side: str # BUY or SELL
price: float
quantity: int
timestamp: int
filled: int = 0
def remaining(self) -> int:
return self.quantity - self.filled
class OrderBook:
def __init__(self):
# bids: max-heap by price (negate price for max-heap)
self.bids: list = [] # (-price, timestamp, order)
# asks: min-heap by price
self.asks: list = [] # (price, timestamp, order)
def add_limit_order(self, order: Order) -> list[dict]:
fills = []
if order.side == "BUY":
while self.asks and order.remaining() > 0:
ask_price, ts, ask = self.asks[0]
if ask_price > order.price:
break
fill_qty = min(order.remaining(), ask.remaining())
fill_price = ask_price # ask price (maker sets price)
order.filled += fill_qty
ask.filled += fill_qty
fills.append({"price": fill_price, "qty": fill_qty,
"buyer": order.order_id, "seller": ask.order_id})
if ask.remaining() == 0:
heapq.heappop(self.asks)
if order.remaining() > 0:
heapq.heappush(self.bids, (-order.price, order.timestamp, order))
else: # SELL
while self.bids and order.remaining() > 0:
neg_price, ts, bid = self.bids[0]
bid_price = -neg_price
if bid_price 0:
heapq.heappush(self.asks, (order.price, order.timestamp, order))
return fills
Pre-Trade Risk Checks
Every order must pass risk checks before reaching the order book: Buying power check: for BUY orders, estimated_cost = quantity * (limit_price or last_price * 1.05 for market orders). Reject if estimated_cost > account.buying_power. Position limit check: total position in symbol after this order <= max_position_size (risk-configured per symbol or account type). Day trading buying power: for margin accounts, day trades use 4:1 intraday leverage but 2:1 overnight — check which limit applies. Order rate limit: max N orders per second per account to prevent runaway algorithms. Market order protection: reject market orders when the spread is > X% (protects against flash crashes filling at extreme prices).
Settlement and Position Tracking
On each fill: Update buying power: for BUY, deduct filled_qty * fill_price from buying_power. For SELL, add to buying_power (after T+1 or T+2 settlement period for equities). Update position: for BUY, add filled_qty to position.quantity; recalculate avg_cost_basis = (old_qty * old_avg + fill_qty * fill_price) / new_qty. For SELL, reduce position.quantity; calculate realized PnL = (fill_price – avg_cost_basis) * fill_qty; record in realized_pnl. Portfolio value: recomputed periodically by multiplying all positions by current market prices. All position updates happen in a transaction with the fill record insert — no fill without a corresponding position update.
{“@context”:”https://schema.org”,”@type”:”FAQPage”,”mainEntity”:[{“@type”:”Question”,”name”:”What is price-time priority in an order book and why is it used?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Price-time priority is the standard matching rule for limit order books: orders are ranked first by price (best price gets matched first — highest bid, lowest ask), then by time (among orders at the same price, the earliest-arriving order is matched first). Why: price priority ensures that buyers willing to pay more and sellers willing to accept less are rewarded — it maximizes price efficiency. Time priority within the same price level incentivizes early order placement and rewards market makers who provide liquidity by quoting early. This creates a fair, predictable, deterministic matching system that is used by virtually all regulated exchanges.”}},{“@type”:”Question”,”name”:”What are the pre-trade risk checks required before accepting an order?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Minimum pre-trade checks: (1) Buying power — estimated cost of the order <= available buying power. For market orders, estimate conservatively (last price * 1.05 * quantity). (2) Position limits — the resulting position would not exceed the maximum allowed position size for the symbol or account. (3) Pattern Day Trader rules — for US equities, a cash account cannot day trade; a margin account is restricted after 3 day trades in 5 days without $25K equity. (4) Short sell restrictions — cannot short a stock without borrowable shares confirmed. (5) Order rate limiting — max orders per second per account (protect against runaway algorithms). (6) Market hours — reject orders for symbols not currently in their trading session.”}},{“@type”:”Question”,”name”:”How is average cost basis calculated for a position, and why does it matter?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Average cost basis = total cost of all shares purchased / total shares held. On each buy fill: new_avg = (old_avg * old_qty + fill_price * fill_qty) / (old_qty + fill_qty). This running average is used to calculate realized P&L on each sell: realized_pnl = (sell_price – avg_cost_basis) * sell_qty. It matters for: (1) P&L reporting — accurate profit/loss per trade and portfolio-level. (2) Tax reporting — cost basis determines capital gains (short-term vs. long-term). (3) Risk management — unrealized P&L = (current_price – avg_cost_basis) * current_qty. FIFO (first-in, first-out) and specific lot identification are alternative cost basis methods required for tax purposes in some jurisdictions.”}},{“@type”:”Question”,”name”:”How do you handle partially filled orders in the order management system?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”A limit order may fill in multiple partial fills over time as matching counterparty orders arrive. Order status: OPEN after placement. PARTIALLY_FILLED after each partial fill. FILLED when filled_qty == total_qty. Track: filled_quantity (sum of all partial fills), avg_fill_price (volume-weighted average across all fills), and a Fill records table (one row per fill with quantity, price, timestamp, counterparty_order_id). On each partial fill: update Order record (filled_quantity, avg_fill_price, status), insert Fill record, update buying_power and position. For IOC (Immediate or Cancel) orders: cancel the unfilled portion immediately after the first matching attempt. For FOK (Fill or Kill): only fill if the entire quantity can be filled at once, otherwise reject entirely.”}},{“@type”:”Question”,”name”:”How does T+1 or T+2 settlement affect buying power in a trading system?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Equity trades settle T+2 (trade date + 2 business days) in the US (moving to T+1). This means cash from a sell is not available to withdraw for 2 days, but for trading purposes, brokers typically credit buying power immediately (same-day) or next day depending on account type and regulations. In the system: track settled_cash (available to withdraw) and unsettled_cash (from recent sells, not yet settled) separately. Buying power for new trades = settled_cash + unsettled_cash (brokers allow buying with unsettled proceeds). Withdrawal limit = settled_cash only. Failed settlement (counterparty default) is handled by the clearinghouse (DTCC in the US), which guarantees settlement, so individual brokers do not typically need to model settlement failure risk.”}}]}
See also: Coinbase Interview Prep
See also: Stripe Interview Prep
See also: LinkedIn Interview Prep