0% found this document useful (0 votes)
8 views24 pages

Trading System Design Complete Notes

The document outlines the design and architecture of a high-performance stock trading platform, focusing on components like the order matching engine, real-time data streaming, and scalability. It includes detailed explanations of system architecture, core components, performance requirements, and database design, along with scenario-based interview questions for practical application. Key features include ultra-low latency transaction handling, a microservices architecture, and strategies for disaster recovery and peak load management.

Uploaded by

khushivp
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views24 pages

Trading System Design Complete Notes

The document outlines the design and architecture of a high-performance stock trading platform, focusing on components like the order matching engine, real-time data streaming, and scalability. It includes detailed explanations of system architecture, core components, performance requirements, and database design, along with scenario-based interview questions for practical application. Key features include ultra-low latency transaction handling, a microservices architecture, and strategies for disaster recovery and peak load management.

Uploaded by

khushivp
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Stock Trading Platform

High-Performance Distributed System Design

Study Notes & Scenario-Based Questions

Generated: March 13, 2026

• System Architecture & Components

• Order Matching Engine Design

• Real-Time Data Streaming

• Scalability & Performance

• 10 Scenario-Based Interview Questions


Table of Contents

1. Trading System Fundamentals

2. High-Level Architecture

3. Core Components Deep Dive

4. Order Matching Engine

5. Real-Time Data Streaming

6. Performance Requirements

7. Database Design

8. Scalability Patterns

9. Scenario-Based Questions (10)

10. Summary & Next Steps


1. Trading System Fundamentals

What is a Trading System?


A trading system is a platform that facilitates the buying and selling of financial instruments (stocks, bonds,
commodities, etc.). It must handle millions of transactions with ultra-low latency while maintaining strict
consistency and audit compliance.

Key Concepts
Concept Definition Example

Order Request to buy/sell securities Buy 100 AAPL @ $150

Market Order Execute immediately at current price Buy NOW

Limit Order Execute only at specified price or better Buy at $150 or less

Order Book List of all pending buy/sell orders Bids and Asks

Matching Pairing compatible buy/sell orders Buy $150 meets Sell $150

Trade Executed transaction 100 shares @ $150

Settlement Transfer of ownership and payment T+2 days

Order Types Explained


• Market Order: Guarantees execution but not price. Used when speed matters more than exact price.

• Limit Order: Guarantees price (or better) but not execution. May sit in order book indefinitely.

• Stop-Loss Order: Becomes market order when stock hits trigger price. Used for risk management.

• Fill-or-Kill (FOK): Execute entire order immediately or cancel. No partial fills.

• Immediate-or-Cancel (IOC): Execute whatever possible immediately, cancel remainder.


2. High-Level System Architecture

The trading platform follows a microservices architecture with event-driven communication. Each component is
independently scalable and fault-tolerant.

System Components

[Clients] → API Gateway → Load Balancer



■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
↓ ↓ ↓ ↓
Order Market Data Portfolio User
Service Service Service Service
↓ ↓
Kafka Queue WebSocket

ORDER MATCHING ENGINE (OME)

■■■■■■■■■■■■■■■■■■■■■
↓ ↓ ↓
Position Settlement Risk
Manager Service Manager

Data Stores (PostgreSQL, Cassandra, Redis)

Component Responsibilities
Component Responsibility Technology

API Gateway Authentication, rate limiting, routing Kong, NGINX

Order Service Validation, enrichment, publishing Java Spring Boot

Matching Engine Core order matching logic Java (single-threaded)

Market Data Real-time price feeds WebSocket servers

Position Manager Track user holdings Java + Redis

Settlement T+2 clearing process Batch processing

Message Queue Async communication Kafka

Cache Session, reference data Redis

Primary DB Accounts, users, orders PostgreSQL

Time-series DB Trade history, analytics Cassandra


3. Core Components Deep Dive

3.1 API Gateway


Acts as the single entry point for all client requests. Handles cross-cutting concerns.

• Authentication & Authorization: JWT token validation, role-based access control (RBAC)

• Rate Limiting: Prevent abuse (e.g., 100 orders/second per user)

• Request Routing: Direct requests to appropriate microservices

• Protocol Translation: HTTP/REST to internal gRPC

• SSL Termination: Handle TLS encryption/decryption

• Logging & Monitoring: Request tracing, metrics collection

3.2 Order Service


Validates incoming orders before they reach the matching engine. Acts as the first line of defense.

• Balance Check: Ensure user has sufficient funds/shares

• Symbol Validation: Verify stock symbol exists and is tradable

• Quantity Limits: Enforce min/max order sizes

• Price Validation: Reject orders with unrealistic prices (e.g., $0.01 for AAPL)

• Market Hours: Only accept orders during trading hours (9:30 AM - 4:00 PM EST)

• Duplicate Detection: Prevent accidental resubmissions

Order Flow:

1. Client submits order via REST API


2. API Gateway validates JWT token
3. Order Service receives request
4. Validate order (balance, symbol, quantity)
5. Enrich with account info (user ID, account ID)
6. Publish to Kafka topic (partition by symbol)
7. Return order ID to client immediately
8. OME consumes from Kafka asynchronously
4. Order Matching Engine (OME)
The heart of the trading system. Responsible for matching buy and sell orders with ultra-low latency while
maintaining fairness and consistency.

4.1 Design Principles


• Single-Threaded per Symbol: Eliminates race conditions, guarantees deterministic execution

• In-Memory Order Books: All data structures in RAM for microsecond access

• Price-Time Priority: Orders matched by best price first, then FIFO within same price

• Event Sourcing: Every state change logged as immutable event

• Lock-Free Data Structures: Use concurrent queues where possible

4.2 Order Book Data Structure

// Java implementation
class OrderBook {
// TreeMap maintains sorted order
TreeMap> bids; // Descending
TreeMap> asks; // Ascending

// Fast lookups by order ID


HashMap orderIndex;

// Price levels for quick access


Price bestBid;
Price bestAsk;
}

// Example Order Book State for AAPL:


Asks (Sell Orders):
$151.00 → [Order(100 shares, userA), Order(50 shares, userB)]
$150.50 → [Order(200 shares, userC)]

Bids (Buy Orders):


$150.00 → [Order(150 shares, userD), Order(75 shares, userE)]
$149.50 → [Order(300 shares, userF)]

4.3 Matching Algorithm

When a BUY order arrives:


1. Check if bestAsk exists and buyPrice >= bestAsk
2. If yes, MATCH:
a. Deduct quantities from both orders
b. Generate Trade event
c. Update order book (remove filled orders)
d. Publish trade to Kafka
3. If partial fill, repeat step 1 with remaining quantity
4. If no match or remainder, add to bid side

Time Complexity:
• Best case (no match): O(1) - just insert
• Worst case (full match): O(log N) - TreeMap operations
• Average: O(log N)

4.4 Why Single-Threaded?


Multi-threading introduces race conditions and requires locks, which hurt latency. Single-threaded execution per
symbol provides:

• Deterministic Execution: Same inputs always produce same outputs

• No Locks: Eliminates contention and context switching

• Simpler Testing: Easier to reproduce bugs and verify correctness

• Cache Efficiency: Data stays in L1/L2 cache

• Predictable Latency: No unpredictable lock wait times

Scalability: Each symbol gets its own thread. AAPL, GOOGL, TSLA all process independently. With 5,000
actively traded symbols, we can utilize 5,000 CPU cores.
5. Real-Time Data Streaming
Trading platforms require real-time distribution of market data to millions of users with sub-10ms latency. This
section covers the streaming architecture.

5.1 Event Flow

OME generates events:


• OrderPlaced
• OrderMatched
• OrderCancelled
• OrderPartiallyFilled

Kafka Topic (partitioned by symbol)

Consumers:
■■ Database Writer (persist to Cassandra)
■■ Market Data Service (fan out to clients)
■■ Position Manager (update holdings)
■■ Analytics Service (real-time metrics)

Market Data Service

WebSocket Connections (per-user)

Client receives:
• Price updates (L1/L2 data)
• Trade confirmations
• Order status changes

5.2 Kafka Configuration


• Topic Partitioning: Partition by symbol (AAPL → partition 1, GOOGL → partition 2)

• Replication Factor: 3 (for fault tolerance)

• Retention: 7 days (for replay and audit)

• Compression: LZ4 (low CPU, good ratio)

• Acks: all (ensure durability before ACK)

5.3 WebSocket Architecture


Persistent bidirectional connections for real-time updates. Each user maintains a single WebSocket connection
for all subscriptions.

• Connection Pooling: Each WS server handles 10K connections

• Subscription Model: Client subscribes to symbols (AAPL, GOOGL)

• Server-Side Filtering: Only send relevant updates to each client

• Heartbeat/Ping-Pong: Keep connection alive, detect disconnects

• Reconnection Logic: Auto-reconnect with exponential backoff


• Message Batching: Bundle multiple updates into single message

Message Formats:

L1 Data (Best Bid/Ask):


{
"symbol": "AAPL",
"bid": 150.00,
"ask": 150.50,
"lastPrice": 150.25,
"volume": 1500000,
"timestamp": 1678901234567
}

L2 Data (Order Book Depth):


{
"symbol": "AAPL",
"bids": [
[150.00, 1000],
[149.95, 500],
[149.90, 2000]
],
"asks": [
[150.50, 800],
[150.55, 1200],
[150.60, 300]
]
}

Trade Event:
{
"symbol": "AAPL",
"price": 150.25,
"quantity": 100,
"timestamp": 1678901234567,
"tradeId": "TRD-12345"
}
6. Performance Requirements
Trading systems have strict performance SLAs. Here are industry-standard targets:

Metric Target Why It Matters

Order Submission < 1ms User experience, competitive advantage

Matching Latency < 100µs Fairness, HFT support

Market Data Latency < 10ms Real-time visibility

DB Write Latency < 5ms Audit trail, recovery

Throughput 100K orders/sec Peak trading hours (market open/close)

Availability 99.99% Financial impact of downtime

Data Consistency 100% Regulatory requirement

Performance Optimization Techniques


• CPU Pinning: Pin OME threads to specific CPU cores (avoid context switching)

• NUMA Awareness: Keep data in same memory node as CPU

• Huge Pages: Reduce TLB misses for large memory allocations

• Kernel Bypass Networking: Use DPDK or Solarflare OpenOnload

• Lock-Free Queues: Disruptor pattern for inter-thread communication

• Memory Pools: Pre-allocate objects to avoid GC pauses

• Zero-Copy Serialization: SBE (Simple Binary Encoding)

• Hardware Timestamping: NIC-level timestamps for accurate latency measurement


7. Database Design
Trading systems use polyglot persistence - different databases for different use cases.

7.1 Database Selection


Database Use Case Why

PostgreSQL Users, accounts, balances ACID transactions, relational

Cassandra Trade history, order history High write throughput, time-series

Redis Sessions, cache, positions In-memory, microsecond latency

Event Store Audit log, event sourcing Immutable append-only log

ClickHouse Analytics, reporting OLAP, column-oriented

S3/Object Store Regulatory archives Long-term retention, cheap

7.2 Schema Design

-- Users Table (PostgreSQL)


CREATE TABLE users (
user_id BIGSERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255),
created_at TIMESTAMP DEFAULT NOW()
);

-- Accounts Table
CREATE TABLE accounts (
account_id BIGSERIAL PRIMARY KEY,
user_id BIGINT REFERENCES users(user_id),
account_type VARCHAR(50), -- 'cash', 'margin'
balance DECIMAL(20, 2),
created_at TIMESTAMP DEFAULT NOW()
);

-- Orders Table (write to PostgreSQL, archive to Cassandra)


CREATE TABLE orders (
order_id BIGSERIAL PRIMARY KEY,
account_id BIGINT,
symbol VARCHAR(10),
side VARCHAR(4), -- 'BUY', 'SELL'
order_type VARCHAR(10), -- 'MARKET', 'LIMIT'
quantity INT,
price DECIMAL(10, 2),
status VARCHAR(20), -- 'PENDING', 'FILLED', 'CANCELLED'
created_at TIMESTAMP DEFAULT NOW()
);

-- Trades Table (Cassandra - time-series)


CREATE TABLE trades (
trade_id UUID,
symbol TEXT,
buy_order_id BIGINT,
sell_order_id BIGINT,
price DECIMAL,
quantity INT,
trade_time TIMESTAMP,
PRIMARY KEY ((symbol), trade_time, trade_id)
) WITH CLUSTERING ORDER BY (trade_time DESC);

7.3 Consistency Guarantees


ACID Transactions for Critical Operations:

BEGIN TRANSACTION;

-- Deduct balance
UPDATE accounts
SET balance = balance - (order_quantity * order_price)
WHERE account_id = ? AND balance >= (order_quantity * order_price);

-- Insert order
INSERT INTO orders (...) VALUES (...);

COMMIT;

If balance insufficient, transaction rolls back automatically. No partial updates.


8. Scalability Patterns

8.1 Horizontal Scaling Strategies


• Symbol-Based Partitioning: Each OME instance handles subset of symbols (AAPL on node1, GOOGL
on node2)

• Stateless Services: Order Service, API Gateway can scale to N instances behind load balancer

• Kafka Consumer Groups: Multiple consumers process different partitions in parallel

• Database Sharding: Shard accounts by account_id, trades by symbol

• Read Replicas: PostgreSQL read replicas for analytics queries

• Geographic Distribution: Deploy in multiple regions (US-East, US-West, EU, Asia)

8.2 Handling Peak Load


Market open (9:30 AM) and close (4:00 PM) see 10x normal traffic. Strategies to handle:

• Pre-warming: Scale up infrastructure 30 minutes before market open

• Queue Buffering: Kafka absorbs spike, OME processes at steady rate

• Rate Limiting: Enforce per-user limits (100 orders/sec)

• Priority Queues: Institutional clients get priority over retail

• Circuit Breakers: Pause trading if volatility exceeds threshold

• Auto-scaling: Add more Order Service instances based on CPU/queue depth

8.3 Disaster Recovery


• Event Sourcing: Replay Kafka events to rebuild OME state from any point in time

• Snapshot + WAL: Periodic snapshots of order book + write-ahead log for fast recovery

• Active-Active OME: Run two OMEs in parallel, fail over in <1 second

• Database Backups: Continuous backup to S3, point-in-time recovery

• Multi-Region Replication: Async replication to DR site

• RTO Target: 30 seconds (Resume Trading Objective)

• RPO Target: 0 seconds (zero data loss)


9. Scenario-Based Interview Questions

These questions test your ability to apply trading system concepts to real-world scenarios. They are commonly
asked in senior/principal engineer interviews at fintech companies.

Q1: Order Matching Engine Crash During Trade Execution


Scenario: Your OME crashes in the middle of matching a large order (10,000 shares). The order was partially
filled (3,000 shares executed, 7,000 remaining). How do you ensure consistency and recover without
double-execution?

Answer:
Recovery Strategy: 1. Event Sourcing Approach: - Every state change (OrderPlaced, OrderMatched,
OrderCancelled) is written to Kafka BEFORE updating in-memory state - Kafka acts as source of truth, OME
state is derived 2. Recovery Process: a. New OME instance starts up b. Load latest snapshot (taken every 10
minutes) c. Replay events from Kafka since snapshot d. Rebuild order book to exact state before crash 3.
Preventing Double Execution: - Each trade event has unique tradeId (UUID) - Downstream systems
(settlement, position manager) are idempotent - If they receive duplicate tradeId, they ignore it 4. Partial Fill
Handling: - The partially filled order (3K filled, 7K remaining) is reconstructed - Remaining 7K shares go back
into order book - User sees accurate status: "Partially Filled - 3000/10000" 5. Time to Recovery: - Snapshot
load: 1 second - Event replay: 10 seconds (for 10 min of events at 100K/sec) - Total downtime: ~11 seconds
Trade-offs: - Snapshot frequency: More frequent = faster recovery but higher I/O cost - Event replay overhead:
Proportional to time since last snapshot

Q2: Flash Crash Detection and Circuit Breakers


Scenario: A stock price drops 15% in 30 seconds due to algorithmic trading gone wrong. How do you detect
this and implement circuit breakers to pause trading?

Answer:
Detection Mechanism: 1. Real-Time Price Monitoring: - OME maintains moving average of last 5 minutes
price - On every trade, calculate: |currentPrice - avgPrice| / avgPrice - If > 10% in 1 minute OR > 15% in 5
minutes → Trigger circuit breaker 2. Circuit Breaker Levels: - Level 1 (5% drop): 5-minute trading pause -
Level 2 (10% drop): 15-minute trading pause - Level 3 (15% drop): Halt trading for rest of day 3.
Implementation: class CircuitBreaker { double basePrice; // Reference price (last close or 5-min avg) double
threshold1 = 0.05; double threshold2 = 0.10; double threshold3 = 0.15; void onTrade(Trade trade) { double drop
= (basePrice - [Link]) / basePrice; if (drop >= threshold3) { haltTradingForDay([Link]);
notifyExchange(); } else if (drop >= threshold2) { pauseTrading([Link], 15_MINUTES); } else if (drop >=
threshold1) { pauseTrading([Link], 5_MINUTES); } } } 4. Communication: - Broadcast circuit breaker
event to all clients via WebSocket - Display prominent warning in UI - Send alerts to risk management team -
Log to regulatory audit trail 5. Resume Trading: - After pause period, publish "TradingResumed" event - Reset
basePrice to current price - Process queued orders in FIFO order Edge Cases: - What if drop is legitimate (bad
earnings)? → Still pause, let humans decide - Multiple symbols crashing simultaneously? → System-wide halt -
After-hours trading? → Different thresholds (wider bands)
Q3: Handling Duplicate Order Submissions
Scenario: A user's mobile app has poor network, causing them to tap 'Buy' button 5 times. You receive 5
identical orders. How do you ensure only 1 executes?

Answer:
Solution: Idempotency Key Pattern 1. Client-Side: - Client generates UUID on button click
(idempotencyKey) - Sends same UUID with every retry // Client code const idempotencyKey = uuid.v4();
fetch('/api/orders', { method: 'POST', headers: { 'Idempotency-Key': idempotencyKey }, body:
[Link](order) }); 2. Server-Side (Order Service): @PostMapping("/orders") public Response
createOrder( @RequestHeader("Idempotency-Key") String key, @RequestBody Order order ) { // Check Redis
for existing order with this key String existingOrderId = [Link]("idempotency:" + key); if (existingOrderId !=
null) { // Already processed, return cached response return [Link](existingOrderId).build(); } // First time
seeing this request String orderId = processOrder(order); // Cache for 24 hours [Link]("idempotency:" +
key, 86400, orderId); return [Link](orderId).build(); } 3. Alternative: Database Unique Constraint: -
Add UNIQUE constraint on (userId, idempotencyKey) - Database rejects duplicate inserts automatically ALTER
TABLE orders ADD CONSTRAINT unique_order UNIQUE (user_id, idempotency_key); 4. TTL for
Idempotency Keys: - Keep in Redis for 24 hours (enough for retries) - After 24h, same key can be reused
(unlikely collision) Benefits: - Prevents double-charging user - Client can safely retry without fear - Works even
if client crashes and restarts

Q4: Real-Time Position Tracking at Scale


Scenario: You have 10 million users. When a trade executes, you need to update the buyer's and seller's
positions in real-time. How do you handle 100K trades/sec without overwhelming the database?

Answer:
Architecture: Event-Driven with Redis Cache 1. Trade Event Flow: OME executes trade ↓ Publish
TradeEvent to Kafka (symbol, buyerId, sellerId, qty, price) ↓ Position Manager consumes event ↓ Update Redis
(in-memory positions) ↓ Async write to PostgreSQL (batched) 2. Redis Data Structure: // Hash per user Key:
"position:user123" Value: { "AAPL": 150, // 150 shares of Apple "GOOGL": 50, // 50 shares of Google "cash":
50000 // $50K cash balance } 3. Position Update Logic: class PositionManager { void
handleTrade(TradeEvent event) { // Update buyer [Link]("position:" + [Link], [Link],
[Link]); [Link]("position:" + [Link], "cash", -[Link] * [Link]); // Update seller
[Link]("position:" + [Link], [Link], -[Link]); [Link]("position:" +
[Link], "cash", [Link] * [Link]); // Queue for DB write (batched) [Link](event); } }
4. Database Batch Writes: - Accumulate 1000 position updates in memory - Write to PostgreSQL every 1
second as batch - Reduces DB load from 100K writes/sec to 100 writes/sec 5. Read Path: - Client requests
portfolio → Read from Redis (sub-ms) - If Redis miss → Read from PostgreSQL, warm Redis - 99.9% cache hit
rate (most users don't refresh constantly) 6. Consistency Guarantees: - Redis is source of truth for real-time
view - PostgreSQL is eventual consistency (1-2 seconds behind) - If Redis crashes → rebuild from PostgreSQL
+ replay Kafka events Scalability: - Redis Cluster: Shard by userId (user1-1M on node1, user1M-2M on node2)
- Each Redis node handles 100K ops/sec - Total capacity: 10 nodes × 100K = 1M ops/sec
Q5: Preventing Insider Trading and Market Manipulation
Scenario: Design a system to detect suspicious trading patterns that could indicate insider trading or
pump-and-dump schemes. What metrics do you track and what are the red flags?

Answer:
Real-Time Surveillance System 1. Suspicious Patterns to Detect: a) Front-Running: - Large institutional
order placed - Immediately followed by related trades from same broker - Detection: Track order timestamps,
look for <100ms correlation b) Wash Trading: - User sells to themselves through different accounts - Creates
false volume - Detection: Match buyer and seller IPs, devices, payment methods c) Pump and Dump: -
Coordinated buying to inflate price - Followed by mass selling - Detection: Unusual volume spike + multiple
accounts + short timeframe d) Spoofing: - Place large fake orders to move price - Cancel before execution -
Detection: High cancel rate (>90%) for large orders 2. Metrics to Track (Real-Time Stream Processing):
Using Apache Flink or Kafka Streams: class SurveillanceEngine { // Window functions void
detectPatterns(TradeStream trades) { // 1. Volume spike detection
[Link]([Link]([Link])) .aggregate(new VolumeAggregator()) .filter(vol -> vol >
10x_average) .alertCompliance(); // 2. Cross-account correlation [Link](t -> [Link])
.window([Link]([Link])) .apply(new WashTradingDetector()); // 3. Price manipulation
[Link](t -> [Link]) .window([Link]([Link])) .apply(new
PriceManipulationDetector()); } } 3. Machine Learning Models: - Train on historical fraud cases - Features: *
Order-to-cancel ratio * Time between order and cancel * User's trading history * Correlation with news events *
Social media sentiment - Flag trades with >80% fraud probability 4. Action on Detection: - Automated: Reject
orders in real-time if confidence >95% - Manual Review: Alert compliance team if 70-95% - Logging: Record
all decisions for regulatory audit 5. Regulatory Reporting: - Generate daily Suspicious Activity Reports (SARs)
- Submit to SEC, FINRA within 24 hours - Provide full audit trail (who, what, when, why) Privacy
Considerations: - Anonymize user data in ML training - Access control: Only compliance team sees PII -
Retention: Keep surveillance logs for 7 years (regulatory requirement)
Q6: Handling After-Hours Trading
Scenario: Regular market hours are 9:30 AM - 4:00 PM. Users want to trade after hours (4:00 PM - 8:00 PM).
How does this impact your system design?

Answer:
After-Hours Trading Modifications 1. Separate Order Books: - Maintain two order books per symbol: *
Regular Session Order Book (9:30 AM - 4:00 PM) * Extended Hours Order Book (4:00 PM - 8:00 PM) - Do NOT
cross-match between sessions 2. Session Transitions: class SessionManager { void onMarketClose() { // 4:00
PM // Cancel all market orders (they don't carry over) [Link](); // Limit orders
can either: // Option A: Automatically cancel (default) // Option B: User opts-in to carry over if
(![Link]) { [Link](user); } // Start extended hours session
[Link](); } void onExtendedHoursClose() { // 8:00 PM // Cancel all remaining orders
[Link](); // Prepare for next day pre-market (4:00 AM - 9:30 AM) } } 3. Key Differences
in Extended Hours: | Aspect | Regular Hours | Extended Hours | |--------|---------------|----------------| | Liquidity |
High | Low (10-20% of regular) | | Spreads | Tight ($0.01) | Wide ($0.05-0.10) | | Volatility | Normal | Higher
(news reactions) | | Order Types | All | Limit orders only (no market) | | Min Quantity | 1 share | 100 shares
(some platforms) | 4. User Experience Considerations: - Display prominent warning: "Extended hours trading
is riskier" - Show wider bid-ask spread - Require explicit opt-in checkbox - Default to "Day" orders (don't carry
over) 5. System Load: - Extended hours: 5-10% of regular volume - Scale down infrastructure (cost savings) -
Keep full OME capacity for regular hours 6. Opening Auction (9:30 AM): - Accumulate all pre-market orders
(4:00 AM - 9:30 AM) - At 9:30 AM sharp, run auction algorithm: * Find price that maximizes volume * Execute all
matching orders at single price * Uncrossing algorithm - Publish opening price to all clients Technical
Challenges: - Clock synchronization: All OME nodes must agree on session transitions - Race conditions at
4:00 PM: Order submitted at 15:59:59.999 vs 16:00:00.001 - Solution: Use centralized time service (AWS Time
Sync, NTP pools)
Q7: Multi-Region Deployment for Global Trading
Scenario: Your trading platform needs to support users in US, Europe, and Asia. How do you handle
multi-region deployment while ensuring consistent order book state?

Answer:
Global Architecture Strategy 1. Problem Statement: - US stocks trade on NYSE (New York) - Cannot have
duplicate OME instances (would cause double-execution) - Network latency: Asia → US = 200-300ms
(unacceptable for trading) 2. Solution: Hybrid Architecture Primary OME: Always runs in region where stock
is listed - US stocks → OME in US-East (NYSE co-location) - European stocks → OME in EU-West (London) -
Asian stocks → OME in Asia-Pacific (Tokyo) Regional Services: Deployed in all regions - API Gateway
(handles auth, routing) - Order Service (validates orders) - Market Data Service (distributes updates) -
WebSocket servers (minimize latency) 3. Data Flow for European User Trading US Stock: User in London ↓
(10ms) EU API Gateway ↓ (validate locally) EU Order Service ↓ (publish to Kafka - cross-region replication)
Kafka EU → Kafka US (50-100ms) ↓ US OME (executes trade) ↓ Publish trade event ↓ Kafka US → Kafka EU
(replicate back) ↓ (10ms) EU WebSocket Server → User Total latency: ~150-200ms (acceptable for retail) 4.
Latency Optimization for Professional Traders: - Institutional clients get direct connection to primary region -
Deploy trading algorithms co-located with OME - Use dedicated fiber lines (not public internet) - Achieve <1ms
latency 5. Kafka Cross-Region Replication: Config: - MirrorMaker 2: Replicate topics between regions -
Replication Lag: 50-100ms (fast fiber connections) - Priority: Orders replicated immediately, analytics data
eventually 6. Disaster Recovery: - If US-East OME crashes, failover to US-West (<5 seconds) - Event replay
from Kafka (all regions have copy) - Never failover to different continent (latency too high) 7. Data Residency
Compliance: - EU users' PII stored in EU databases (GDPR) - Trade data can replicate globally (financial
regulations allow) - Anonymize data before sending to analytics clusters Cost Optimization: - Run full stack in
US-East (primary market) - EU and Asia: Only user-facing services + cache - Share expensive components
(OME, compliance, analytics) in US - Cross-region bandwidth: $0.02/GB (cheaper than duplicate infrastructure)
Q8: Handling Corporate Actions (Stock Splits, Dividends)
Scenario: Apple announces a 4-for-1 stock split. Every share becomes 4 shares, price divides by 4. You have
1M users holding AAPL. How do you process this overnight?

Answer:
Corporate Action Processing Pipeline 1. Timeline: - Announcement Date: Company announces split -
Record Date: Shareholders as of this date are eligible - Ex-Date: Stock trades at new split price - Payment
Date: New shares distributed Example: Announced July 1, Record Date July 15, Ex-Date July 20 2. Data
Sources: - Subscribe to corporate action feeds (Bloomberg, Reuters) - Parse XML/JSON notifications - Store in
corporate_actions table CREATE TABLE corporate_actions ( action_id UUID PRIMARY KEY, symbol
VARCHAR(10), action_type VARCHAR(50), -- 'SPLIT', 'DIVIDEND', 'MERGER' ratio VARCHAR(20), -- '4-for-1',
'2-for-1' ex_date DATE, payment_date DATE, status VARCHAR(20) ); 3. Stock Split Processing (Overnight
Batch Job): class CorporateActionProcessor { void processStockSplit(String symbol, double ratio) { // 1. Pause
trading (after market close) [Link](symbol); // 2. Update all user positions List positions =
[Link]("SELECT * FROM positions WHERE symbol = ?", symbol); for (Position pos : positions) { // Old: 100
shares @ $400/share = $40,000 // New: 400 shares @ $100/share = $40,000 int newQuantity = [Link] *
4; double newAvgPrice = [Link] / 4; [Link]("UPDATE positions SET quantity = ?, avg_price = ?
WHERE user_id = ? AND symbol = ?", newQuantity, newAvgPrice, [Link], symbol); } // 3. Adjust all
pending limit orders List orders = [Link]("SELECT * FROM orders WHERE symbol = ? AND status =
'PENDING'", symbol); for (Order order : orders) { // Order: Buy 100 shares @ $400 // Adjusted: Buy 400 shares
@ $100 [Link] *= 4; [Link] /= 4; [Link](order); } // 4. Update reference data
[Link](symbol, new StockInfo( newPrice = oldPrice / 4, outstandingShares = oldShares * 4 )); // 5.
Publish event for downstream systems [Link](new SplitCompleteEvent(symbol, ratio)); // 6. Resume
trading at market open [Link](symbol); } } 4. Verification: - Run reconciliation job - Total value
before split == total value after split - If mismatch, alert operations team SELECT SUM(quantity * avg_price) AS
total_value_before, SUM(new_quantity * new_avg_price) AS total_value_after FROM position_audit; -- Should
be equal (within rounding errors) 5. User Communication: - Email notification: "Your 100 AAPL shares are
now 400 shares" - In-app banner explaining the split - Update portfolio UI to show new quantities - Show split
history in transaction log 6. Edge Cases: - Fractional shares: 150 shares → 600 shares (no issue) - Odd lots: 1
share → 4 shares (clean division) - Reverse split (1-for-4): 3 shares → 0.75 shares (cash out fractions)
Dividend Processing (Similar Flow): - Cash dividend: Credit user's account balance - Stock dividend:
Increase position quantity - DRIP (Dividend Reinvestment): Auto-buy more shares
Q9: Implementing Market Maker Rebates and Taker Fees
Scenario: You want to incentivize liquidity provision. Market makers (those who place limit orders) get rebates.
Takers (those who execute against existing orders) pay fees. How do you implement this in the OME?

Answer:
Fee Structure Implementation 1. Maker-Taker Model: - Maker: Adds liquidity (places limit order in book) →
Receives $0.0020/share rebate - Taker: Removes liquidity (market order or aggressive limit) → Pays
$0.0030/share fee - Exchange profit: $0.0010/share spread 2. Determining Maker vs Taker: class
FeeCalculator { Fee calculateFee(Order order, Trade trade) { if ([Link]() && [Link]()) { //
This order was in the book, counterparty hit it return new Fee( type = MAKER, amount = -0.0020 *
[Link], // Negative = rebate userId = [Link] ); } else { // This order executed against existing order
(taker) return new Fee( type = TAKER, amount = 0.0030 * [Link], // Positive = fee userId = [Link]
); } } } 3. Integration with OME: class OrderMatchingEngine { void matchOrders(Order incomingOrder) { // ...
matching logic ... Trade trade = executeTrade(buyOrder, sellOrder); // Calculate fees for BOTH sides Fee
buyerFee = [Link](buyOrder, trade); Fee sellerFee = [Link](sellOrder, trade); //
Publish fee events [Link](new FeeEvent(buyerFee)); [Link](new FeeEvent(sellerFee)); // Publish
trade (downstream systems apply fees) [Link](trade); } } 4. Fee Settlement (End of Day): class
FeeSettlement { void settleDaily() { // Aggregate all fees per user Map netFees = new HashMap<>(); for
(FeeEvent fee : [Link]()) { [Link]([Link], [Link], Double::sum); } // Apply to
account balances for (Entry entry : [Link]()) { if ([Link]() > 0) { // User owes fees
[Link]([Link](), [Link]()); } else { // User receives rebate
[Link]([Link](), -[Link]()); } } } } 5. Advanced Fee Tiers (Volume-Based): |
Monthly Volume | Maker Rebate | Taker Fee | |----------------|--------------|-----------| | < 1M shares | $0.0020 |
$0.0030 | | 1M - 10M | $0.0025 | $0.0028 | | > 10M | $0.0030 | $0.0025 | High-volume traders get better rates
(incentivize large players) 6. Displaying Fees to Users: Trade Confirmation: - Bought 100 AAPL @ $150.00 -
Trade value: $15,000.00 - Fee: $3.00 (taker) - Total cost: $15,003.00 Maker Rebate: - Sold 100 GOOGL @
$2,500.00 - Trade value: $250,000.00 - Rebate: $20.00 (maker) - Net proceeds: $250,020.00 7. Regulatory
Reporting: - Report fee revenue to SEC (Form 1) - Itemize maker/taker fees separately - Disclose fee schedule
publicly (transparency requirement) Why This Model Works: - Incentivizes traders to place limit orders
(provide liquidity) - Tightens spreads (more liquidity = better prices) - Exchange earns profit from spread - Retail
traders pay small fees, HFT firms get rebates (fair trade-off)
Q10: Stress Testing and Capacity Planning
Scenario: Your trading platform currently handles 50K orders/sec peak load. Marketing predicts 3x growth in
next year. How do you validate the system can handle 150K orders/sec and identify bottlenecks?

Answer:
Load Testing and Capacity Planning Strategy 1. Establish Baseline Metrics: Current production metrics
(50K orders/sec): - API Gateway CPU: 40% - Order Service CPU: 60% - OME CPU: 80% (bottleneck!) - Kafka
throughput: 30% capacity - PostgreSQL connections: 200/500 - Redis ops/sec: 50K/100K capacity 2. Load
Testing Approach: a) Synthetic Load Generation: class LoadGenerator { void generateLoad(int
ordersPerSec) { ExecutorService pool = [Link](100); for (int i = 0; i < ordersPerSec;
i++) { [Link](() -> { Order order = createRandomOrder(); [Link]("/api/orders", order); }); // Rate
limit to maintain target TPS [Link](1000 / ordersPerSec); } } } b) Realistic Traffic Mix: - 60% limit orders
(go to order book) - 30% market orders (execute immediately) - 10% cancellations - Symbol distribution: 70% in
top 100 stocks, 30% long tail 3. Testing Scenarios: Scenario 1: Steady State - Ramp up to 150K orders/sec
over 10 minutes - Sustain for 1 hour - Measure: latency (p50, p95, p99), error rate, CPU, memory Scenario 2:
Spike Test - Jump from 50K → 200K instantly (market open simulation) - Hold for 5 minutes - Measure: queue
depth, dropped requests, recovery time Scenario 3: Soak Test - 100K orders/sec for 24 hours - Check for:
memory leaks, connection pool exhaustion, disk space 4. Identifying Bottlenecks: Use profiling tools: -
JProfiler: CPU hotspots in Java services - Flame graphs: Visualize where time is spent - DB slow query log:
PostgreSQL queries >10ms - Distributed tracing (Jaeger): End-to-end latency breakdown Example findings: -
OME spends 40% time in TreeMap operations → Use faster structure - Order validation regex takes 2ms →
Precompile patterns - Database connection pool maxed out → Increase from 200 to 500 5. Capacity Planning:
Component Analysis: | Component | Current | Target | Action | |----------------|---------|--------|--------| | API Gateway
| 40% CPU | 80% OK | No change | | Order Service | 60% CPU | Scale horizontal | Add 2 instances | | OME |
80% CPU | 240% (!) | Need 3x instances | | Kafka | 30% usage | 90% OK | No change | | PostgreSQL | 40%
conn | Scale vertical | Upgrade to larger RDS | | Redis | 50% ops | Scale cluster | Add 1 more node | OME
Scaling Plan: - Current: 10 OME instances (each handles 500 symbols) - Target: 30 OME instances (same
500 symbols each) - Cost: $50K/month → $150K/month (acceptable) 6. Auto-Scaling Policies: # AWS Auto
Scaling config ScaleUp: - If CPU > 70% for 2 minutes → Add 1 instance - If queue depth > 10,000 → Add 2
instances ScaleDown: - If CPU < 30% for 10 minutes → Remove 1 instance - Never scale down during market
hours (9:30-4:00) 7. Cost Optimization: - Use spot instances for batch processing (60% savings) - Reserved
instances for baseline capacity (30% savings) - Auto-scale only during peak hours 8. Chaos Engineering: - Kill
random OME instance during load test - Verify: Orders reroute to other instances, no data loss - Introduce
network latency (simulate cross-region) - Verify: Graceful degradation, not cascading failures Success
Criteria: - Sustain 150K orders/sec with p99 latency < 10ms - Zero data loss during instance failures -
Auto-recovery within 30 seconds - Total cost under $200K/month
10. Summary and Next Steps

Key Takeaways
• Single-Threaded OME: Eliminates race conditions, provides deterministic execution

• Event Sourcing: All state changes logged to Kafka for replay and audit

• In-Memory Data Structures: Order books in RAM for microsecond latency

• Polyglot Persistence: Right database for each use case (PostgreSQL, Cassandra, Redis)

• Real-Time Streaming: WebSocket + Kafka for sub-10ms market data delivery

• Horizontal Scalability: Partition by symbol, scale each component independently

• Idempotency: Critical for preventing duplicate orders and ensuring exactly-once semantics

• Circuit Breakers: Protect against flash crashes and cascading failures

• Regulatory Compliance: Surveillance, audit trails, and reporting built-in

• Stress Testing: Validate capacity at 3x current load before scaling

Interview Preparation Checklist


■ Understand order types (market, limit, stop-loss, FOK, IOC)

■ Explain price-time priority matching algorithm

■ Justify single-threaded vs multi-threaded OME design

■ Design event sourcing for crash recovery

■ Calculate latency budget for 100µs matching SLA

■ Design real-time WebSocket architecture for 1M concurrent users

■ Implement idempotency for duplicate order prevention

■ Design circuit breakers for flash crash protection

■ Plan multi-region deployment for global trading

■ Perform capacity planning for 3x growth

Recommended Deep Dives


For your next study session, I recommend focusing on:

1. LMAX Disruptor Pattern: High-performance inter-thread messaging (used by many trading systems)

2. FIX Protocol: Industry standard for order communication (Financial Information eXchange)

3. Matching Engine Algorithms: Pro-rata vs FIFO vs price-time priority

4. Market Microstructure: How exchanges actually work (order routing, dark pools)
5. Risk Management: Pre-trade checks, margin requirements, position limits

6. Settlement and Clearing: T+2 cycle, DTCC, clearing houses

7. Low-Latency Optimization: Kernel bypass, DPDK, FPGA acceleration

Additional Resources
Books:

• 'Trading and Exchanges' by Larry Harris

• 'Flash Boys' by Michael Lewis (market microstructure narrative)

• 'Building Microservices' by Sam Newman

Papers:

• 'LMAX Architecture' (Martin Fowler blog)

• 'TAQ Database' (NYSE trade and quote data)

Open Source:

• Quickfix (FIX protocol implementation)

• Apache Kafka (event streaming)

• LMAX Disruptor (high-performance queues)

Practice:

• Build a simple matching engine in Java

• Implement order book with TreeMap

• Create real-time WebSocket server

■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
■■■■■■■■■■■■■■■■

End of Trading System Design Notes. Good luck with your interviews!

You might also like