Trading System Design Complete Notes
Trading System Design Complete Notes
2. High-Level Architecture
6. Performance Requirements
7. Database Design
8. Scalability Patterns
Key Concepts
Concept Definition Example
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
• 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.
The trading platform follows a microservices architecture with event-driven communication. Each component is
independently scalable and fault-tolerant.
System Components
Component Responsibilities
Component Responsibility Technology
• Authentication & Authorization: JWT token validation, role-based access control (RBAC)
• 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)
Order Flow:
• 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
// Java implementation
class OrderBook {
// TreeMap maintains sorted order
TreeMap> bids; // Descending
TreeMap> asks; // Ascending
Time Complexity:
• Best case (no match): O(1) - just insert
• Worst case (full match): O(log N) - TreeMap operations
• Average: O(log N)
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.
Message Formats:
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:
-- 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()
);
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;
• Stateless Services: Order Service, API Gateway can scale to N instances behind load balancer
• 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
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.
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
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
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
• Polyglot Persistence: Right database for each use case (PostgreSQL, Cassandra, Redis)
• Idempotency: Critical for preventing duplicate orders and ensuring exactly-once semantics
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)
4. Market Microstructure: How exchanges actually work (order routing, dark pools)
5. Risk Management: Pre-trade checks, margin requirements, position limits
Additional Resources
Books:
Papers:
Open Source:
Practice:
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
■■■■■■■■■■■■■■■■
End of Trading System Design Notes. Good luck with your interviews!