Case Study: Design a Stock Exchange
“Trong thế giới tài chính, 1 micro-giây chậm hơn đối thủ có thể mất hàng triệu đô. Sàn chứng khoán là nơi mà engineering phải đạt đến mức extreme nhất.”
Tags: system-design stock-exchange matching-engine low-latency event-sourcing alex-xu-vol2 Student: Hieu Source: Alex Xu — System Design Interview Volume 2, Chapter 8 Prerequisite: Tuan-01-Scale-From-Zero-To-Millions · Tuan-02-Back-of-the-envelope · Tuan-08-Message-Queue Lien quan: Tuan-13-Monitoring-Observability · Case-Design-Payment-System · Tuan-14-AuthN-AuthZ-Security · Tuan-15-Data-Security-Encryption
1. Context & Why — Tại sao Stock Exchange quan trọng?
1.1 Analogy: Sàn giao dịch chứng khoán Hồ Chí Minh (HOSE)
Bạn tưởng tượng mình đang xây hệ thống cho HOSE (Ho Chi Minh Stock Exchange). Mỗi ngày có hàng triệu lệnh mua bán từ các nhà đầu tư khắp cả nước. Khi bạn đặt lệnh mua 100 cổ phiếu VNM giá 80,000 VND, hệ thống phải:
- Nhận lệnh từ broker (VNDirect, SSI, TCBS…)
- Kiểm tra lệnh có hợp lệ không (đủ tiền? giá nằm trong biên độ? tài khoản bị khóa không?)
- Khớp lệnh — tìm người bán phù hợp với giá và số lượng
- Phát dữ liệu — thông báo giá mới nhất cho toàn bộ thị trường
- Thanh toán bù trừ — chuyển cổ phiếu và tiền giữa hai bên (T+2)
Tất cả những bước trên phải xảy ra trong micro-giây (không phải milli-giây). Tại các sàn lớn như NYSE, NASDAQ, latency trung bình là ~10-50 microseconds cho matching. Chậm hơn đối thủ 1ms = mất lợi thế cạnh tranh.
1.2 Tại sao đây là bài toán khó?
| Thách thức | Giải thích |
|---|---|
| Ultra-low latency | Matching phải xong trong micro-giây, không phải milli-giây |
| Determinism | Cùng input phải cho cùng output — mọi lần, mọi lúc |
| Fairness | Lệnh đến trước phải được xử lý trước (FIFO) — pháp luật yêu cầu |
| Reliability | Không được mất lệnh — một lệnh mất = kiện tụng pháp lý |
| Throughput | 10K-100K lệnh/giây trong giờ cao điểm |
| Regulatory compliance | SEC (Mỹ), FCA (Anh), UBCKNN (Việt Nam) yêu cầu audit trail cho mọi lệnh |
| Market data | Phát giá real-time cho hàng triệu subscriber |
Aha Moment: Stock exchange khác hoàn toàn với web service thông thường. Web service tối ưu cho throughput (bao nhiêu request/giây). Stock exchange tối ưu cho latency (mỗi request nhanh bao nhiêu). Đây là hai triết lý hoàn toàn khác nhau, dẫn đến kiến trúc khác biệt.
1.3 Stock Exchange vs Web Application — Sự khác biệt cơ bản
| Khía cạnh | Web Application | Stock Exchange |
|---|---|---|
| Latency target | 100-500ms | 1-100 microseconds |
| Threading model | Multi-threaded, async | Single-threaded, synchronous |
| Network | TCP/HTTP | Kernel bypass, DPDK, multicast UDP |
| Storage | Database (disk) | In-memory, memory-mapped files |
| Scaling strategy | Horizontal (thêm server) | Vertical (tăng tốc 1 server) |
| Consistency | Eventual consistency ok | Strict ordering bắt buộc |
| Garbage collection | Chấp nhận GC pause | GC pause = mất tiền |
| Programming language | Java, Python, Node.js | C++, Rust, FPGA |
2. Deep Dive — Alex Xu 4-Step Framework
Step 1: Requirements — Hiểu và giới hạn bài toán
2.1.1 Functional Requirements
| Chức năng | Mô tả chi tiết |
|---|---|
| Place Order | Nhà đầu tư gửi lệnh mua/bán với loại lệnh (limit, market), giá, số lượng, mã chứng khoán |
| Match Orders | Hệ thống tự động khớp lệnh mua và bán khi giá phù hợp |
| Cancel Order | Nhà đầu tư hủy lệnh chưa khớp (hoặc chưa khớp hết) |
| Amend Order | Sửa lệnh (thay đổi giá hoặc số lượng) — thực tế là cancel + new order |
| Market Data Streaming | Phát dữ liệu giá real-time (best bid/ask, order book depth, trades) |
| Order Status | Truy vấn trạng thái lệnh: pending, partially filled, filled, cancelled, rejected |
2.1.2 Non-Functional Requirements
| Yêu cầu | Mục tiêu | Lý do |
|---|---|---|
| Latency | < 10ms end-to-end, < 100 microseconds matching | Cạnh tranh với các sàn khác |
| Throughput | 10,000 orders/sec (peak: 50,000) | Giờ cao điểm, sự kiện bất thường (earnings, IPO) |
| Availability | 99.99% trong giờ giao dịch | Sàn đóng cửa = mất tin tưởng thị trường |
| Durability | Zero order loss | Pháp luật yêu cầu — mất lệnh = kiện |
| Determinism | Replay cho kết quả giống hệt | Audit, dispute resolution, disaster recovery |
| Fairness | FIFO — lệnh đến trước xử lý trước | Regulatory requirement |
| Auditability | Mọi event được log | SEC Rule 613 (Consolidated Audit Trail) |
2.1.3 Các loại lệnh (Order Types)
| Loại lệnh | Mô tả | Ví dụ |
|---|---|---|
| Limit Order | Mua/bán tại một giá cụ thể hoặc tốt hơn | ”Mua 100 VNM tại giá 80,000 hoặc thấp hơn” |
| Market Order | Mua/bán ngay tại giá tốt nhất hiện có | ”Mua 100 VNM ngay — bất kể giá nào” |
| Stop Order | Kích hoạt khi giá chạm mức nhất định | ”Bán VNM khi giá giảm xuống 75,000” |
| Stop-Limit Order | Stop + Limit kết hợp | ”Khi VNM chạm 75,000, đặt lệnh bán limit tại 74,500” |
| IOC (Immediate or Cancel) | Khớp ngay phần có thể, hủy phần còn lại | Dùng cho trader muốn vào nhanh |
| FOK (Fill or Kill) | Khớp toàn bộ hoặc hủy toàn bộ | Không chấp nhận khớp một phần |
| GTC (Good Till Cancel) | Lệnh tồn tại cho đến khi khớp hoặc bị hủy | Lệnh dài hạn |
Step 2: High-Level Design
2.2.1 Kiến trúc tổng quan
flowchart TB subgraph Clients B1["Broker A<br/>(VNDirect)"] B2["Broker B<br/>(SSI)"] B3["Broker C<br/>(TCBS)"] end subgraph "Trading Gateway" GW["Gateway<br/>Authentication<br/>Rate Limiting<br/>Protocol Translation"] end subgraph "Pre-Trade Risk" RISK["Risk Engine<br/>Position Limits<br/>Price Bands<br/>Fat Finger Check"] end subgraph "Core Trading" SEQ["Sequencer<br/>Assign Sequence #<br/>Event Log"] OM["Order Manager<br/>Order State Machine<br/>Order Book Management"] ME["Matching Engine<br/>Price-Time Priority<br/>Single-Threaded"] end subgraph "Post-Trade" CS["Clearing &<br/>Settlement"] RPT["Reporting &<br/>Audit Trail"] end subgraph "Market Data" MDP["Market Data<br/>Publisher"] L1["L1 Feed<br/>Best Bid/Ask"] L2["L2 Feed<br/>Order Book Depth"] L3["L3 Feed<br/>Individual Orders"] end B1 & B2 & B3 --> GW GW --> RISK RISK --> SEQ SEQ --> OM OM --> ME ME --> OM ME --> MDP ME --> CS ME --> RPT MDP --> L1 & L2 & L3 SEQ -.->|"Event Log<br/>(Write-Ahead)"| RPT style ME fill:#e53935,color:#fff style SEQ fill:#1e88e5,color:#fff style MDP fill:#43a047,color:#fff
2.2.2 Luồng xử lý lệnh (Order Flow)
sequenceDiagram participant Broker participant Gateway participant Risk as Risk Engine participant Seq as Sequencer participant OM as Order Manager participant ME as Matching Engine participant MDP as Market Data Publisher participant CS as Clearing & Settlement Broker->>Gateway: New Order (FIX Protocol) Gateway->>Gateway: Authenticate & Validate Format Gateway->>Risk: Pre-trade Risk Check alt Risk Check Failed Risk-->>Gateway: Reject Gateway-->>Broker: Order Rejected else Risk Check Passed Risk->>Seq: Forward Order Seq->>Seq: Assign Sequence Number Seq->>OM: Sequenced Order OM->>OM: Create Order State (NEW) OM->>ME: Send to Matching Engine alt Match Found ME->>ME: Execute Trade ME->>OM: Execution Report (FILLED) ME->>MDP: Trade Event ME->>CS: Trade for Clearing OM-->>Gateway: Execution Report Gateway-->>Broker: Order Filled MDP->>MDP: Update L1/L2/L3 Feeds else No Match ME->>OM: Order Added to Book ME->>MDP: Order Book Update OM-->>Gateway: Order Acknowledged Gateway-->>Broker: Order Accepted (in book) end end
2.2.3 Các component chính và vai trò
| Component | Vai trò | Đặc điểm |
|---|---|---|
| Trading Gateway | Điểm vào của hệ thống — nhận lệnh từ broker | FIX protocol, authentication, rate limiting, protocol translation |
| Risk Engine | Kiểm tra risk trước khi lệnh vào matching | Pre-trade checks: position limit, price band, fat finger |
| Sequencer | Gán sequence number cho mọi event | Single point of ordering, event sourcing, write-ahead log |
| Order Manager | Quản lý trạng thái lệnh | State machine: New → Accepted → Partially Filled → Filled / Cancelled |
| Matching Engine | Trái tim của sàn — khớp lệnh | Single-threaded, price-time priority, in-memory order book |
| Market Data Publisher | Phát dữ liệu giá cho thị trường | Multicast UDP, L1/L2/L3 feeds |
| Clearing & Settlement | Thanh toán bù trừ sau giao dịch | T+2 settlement, netting, CCP (Central Counterparty) |
Step 3: Deep Dive — Chi tiết từng component
2.3.1 Matching Engine — Trái tim của sàn giao dịch
Order Book là gì?
Order book là cấu trúc dữ liệu trung tâm của matching engine. Nó lưu trữ tất cả các lệnh chưa khớp, chia thành hai phía:
| Phía | Tên gọi | Sắp xếp | Ý nghĩa |
|---|---|---|---|
| Bid Side (Bên mua) | Bids | Giảm dần theo giá | Người mua muốn mua giá cao nhất trước |
| Ask Side (Bên bán) | Asks / Offers | Tăng dần theo giá | Người bán muốn bán giá thấp nhất trước |
Ví dụ Order Book của VNM:
| Bid (Mua) | Ask (Bán) | |||
|---|---|---|---|---|
| Số lượng | Giá | Giá | Số lượng | |
| 500 | 80,500 | 80,600 | 300 | |
| 1,200 | 80,400 | 80,700 | 800 | |
| 300 | 80,300 | 80,800 | 1,500 | |
| 2,000 | 80,200 | 80,900 | 400 | |
| 150 | 80,100 | 81,000 | 2,200 |
- Best Bid: 80,500 (giá cao nhất người mua sẵn sàng trả)
- Best Ask: 80,600 (giá thấp nhất người bán sẵn sàng bán)
- Bid-Ask Spread: 80,600 - 80,500 = 100 VND (độ chênh lệch giữa mua và bán)
Aha Moment: Spread càng hẹp = thanh khoản càng tốt. Cổ phiếu blue-chip như VNM có spread rất hẹp (100-200 VND). Cổ phiếu penny stock có spread lớn (hàng nghìn VND). Spread là chi phí ẩn của giao dịch.
Price-Time Priority (FIFO Matching Algorithm)
Đây là thuật toán khớp lệnh phổ biến nhất, được dùng tại hầu hết các sàn lớn (NYSE, NASDAQ, HOSE). Quy tắc:
- Price Priority (Ưu tiên giá): Lệnh mua giá cao hơn được ưu tiên. Lệnh bán giá thấp hơn được ưu tiên.
- Time Priority (Ưu tiên thời gian): Nếu cùng giá, lệnh đến trước được khớp trước (FIFO).
Ví dụ khớp lệnh chi tiết:
Giả sử order book hiện tại:
| Thứ tự | Phía | Giá | Số lượng | Thời gian |
|---|---|---|---|---|
| #1 | BID | 80,500 | 300 | 09:00:01.001 |
| #2 | BID | 80,500 | 200 | 09:00:01.005 |
| #3 | BID | 80,400 | 500 | 09:00:00.990 |
| #4 | ASK | 80,600 | 400 | 09:00:01.002 |
| #5 | ASK | 80,700 | 600 | 09:00:00.995 |
Bây giờ có lệnh bán mới: SELL 400 VNM tại Market Order (bán ngay bất kể giá).
Quá trình khớp:
- Tìm best bid = 80,500
- Khớp với #1 (300 cổ, 09:00:01.001) — do #1 đến trước #2 tại cùng giá → 300 cổ khớp tại 80,500
- Còn lại 100 cổ, khớp với #2 (200 cổ, 09:00:01.005) → 100 cổ khớp tại 80,500
- Lệnh #2 còn lại 100 cổ chưa khớp, vẫn nằm trong order book
Kết quả:
- Trade 1: 300 cổ tại 80,500
- Trade 2: 100 cổ tại 80,500
- Order #1: Filled (khớp hết)
- Order #2: Partially Filled (còn 100)
- Order #3, #4, #5: Không thay đổi
Limit Order vs Market Order — Xử lý khác nhau
| Tình huống | Limit Order | Market Order |
|---|---|---|
| Cách khớp | Chỉ khớp tại giá chỉ định hoặc tốt hơn | Khớp tại giá tốt nhất hiện có |
| Nếu không có đối tác | Thêm vào order book, chờ | Reject hoặc khớp phần có thể (tùy IOC/FOK) |
| Rủi ro giá | Biết trước giá tối đa/tối thiểu | Có thể khớp tại giá rất xấu (slippage) |
| Sử dụng | 90%+ lệnh trên sàn | Khi cần vào/ra nhanh |
Quan trọng: Market order trên thị trường kém thanh khoản cực kỳ nguy hiểm. Ví dụ: đặt market order mua 10,000 cổ phiếu penny stock có thể đẩy giá lên 50% vì “ăn hết” các lệnh bán ở nhiều mức giá. Đây gọi là slippage.
Cấu trúc dữ liệu của Order Book
Order book cần 2 thao tác nhanh:
- Insert/Delete lệnh: O(log n) — khi có lệnh mới hoặc hủy lệnh
- Find best bid/ask: O(1) hoặc O(log n) — để khớp lệnh
Các cấu trúc dữ liệu phù hợp:
| Cấu trúc | Insert/Delete | Find Best | Ưu điểm | Nhược điểm |
|---|---|---|---|---|
| Red-Black Tree | O(log n) | O(log n) | Balanced, deterministic | Complex implementation |
| Skip List | O(log n) avg | O(1) với pointer | Đơn giản hơn, cache-friendly | Probabilistic balancing |
| Sorted Array | O(n) | O(1) | Cache-friendly, đơn giản | Insert chậm (shift elements) |
| Hash Map + Sorted Structure | O(1) lookup by ID + O(log n) insert | O(1) | Tìm lệnh theo ID nhanh | Phức tạp implementation |
Thực tế: Hầu hết các sàn dùng kép: một sorted structure (red-black tree hoặc skip list) cho price levels, kết hợp với doubly linked list tại mỗi price level cho FIFO ordering.
Order Book (VNM)
│
├── Bid Side (Red-Black Tree, descending by price)
│ ├── Price Level 80,500
│ │ └── Queue: [Order#1(300)] → [Order#2(200)] (FIFO linked list)
│ ├── Price Level 80,400
│ │ └── Queue: [Order#3(500)]
│ └── Price Level 80,200
│ └── Queue: [Order#7(2000)]
│
└── Ask Side (Red-Black Tree, ascending by price)
├── Price Level 80,600
│ └── Queue: [Order#4(400)]
├── Price Level 80,700
│ └── Queue: [Order#5(600)]
└── Price Level 80,800
└── Queue: [Order#6(1500)]
Aha Moment: Mỗi price level là một FIFO queue (doubly linked list). Khi khớp lệnh, engine lấy lệnh đầu tiên trong queue. Khi lệnh mới đến cùng giá, thêm vào cuối queue. Đây là lý do tại sao gọi là price-time priority.
Tại sao Matching Engine phải Single-Threaded?
Đây là điều phản trực giác nhất trong thiết kế stock exchange:
Multi-threaded matching engine có vấn đề gì?
| Vấn đề | Giải thích |
|---|---|
| Lock contention | Nhiều thread tranh nhau truy cập order book → lock wait time lớn hơn thời gian xử lý |
| Non-determinism | Thứ tự thực thi của thread không xác định → replay cho kết quả khác nhau |
| Context switching | OS chuyển đổi giữa các thread tốn thời gian (microseconds) |
| Cache invalidation | Nhiều thread trên nhiều CPU core → cache line bouncing |
| Complexity | Race condition, deadlock, priority inversion — debug cực khó |
Single-threaded matching engine giải quyết tất cả:
| Lợi ích | Giải thích |
|---|---|
| Zero lock overhead | Không cần lock vì chỉ có 1 thread |
| Deterministic | Cùng input, cùng output — mọi lần |
| No context switching | Thread luôn chạy, không bị OS chuyển |
| Cache-friendly | Dữ liệu luôn trong L1/L2 cache của 1 CPU core |
| Simple code | Dễ debug, dễ test, dễ verify |
Benchmark thực tế: LMAX Exchange (London) xử lý 6 triệu lệnh/giây với single-threaded matching engine. Latency trung bình: ~1 microsecond. Nhanh hơn bất kỳ multi-threaded implementation nào.
Aha Moment: Single-threaded không có nghĩa là chậm. Ngược lại — với stock exchange, single-threaded nhanh hơn multi-threaded vì loại bỏ toàn bộ overhead của synchronization. Đây là bài học lớn nhất của bài toán này: determinism quan trọng hơn raw speed.
2.3.2 Sequencer — Đảm bảo thứ tự tuyệt đối
Vai trò của Sequencer
Sequencer là component gán sequence number duy nhất, tăng dần cho mọi event trong hệ thống. Đây là nền tảng của event sourcing.
| Tính chất | Mô tả |
|---|---|
| Monotonically increasing | Sequence number luôn tăng: 1, 2, 3, … không bao giờ nhảy |
| Gap-free | Không được có khoảng trống: 1, 2, 4 (thiếu 3) = lỗi |
| Single point of assignment | Chỉ có 1 sequencer gán số — đảm bảo toàn cục duy nhất |
| Persistent | Sequence number được ghi vào disk trước khi xử lý (write-ahead) |
Event Sourcing Pattern
Thay vì lưu trữ trạng thái hiện tại của order book (state), hệ thống lưu chuỗi các event đã xảy ra. Trạng thái hiện tại = replay tất cả event từ đầu.
flowchart LR subgraph "Event Log (Sequencer)" E1["#1: NewOrder<br/>BUY 100 VNM@80500"] E2["#2: NewOrder<br/>SELL 50 VNM@80500"] E3["#3: Trade<br/>50 VNM@80500"] E4["#4: NewOrder<br/>SELL 80 VNM@80400"] E5["#5: CancelOrder<br/>Order #1 (50 remaining)"] E6["#6: Trade<br/>N/A (order cancelled)"] end E1 --> E2 --> E3 --> E4 --> E5 --> E6 subgraph "Replay" R["Replay events 1→6<br/>= Current Order Book State"] end E6 --> R style E3 fill:#43a047,color:#fff style E5 fill:#e53935,color:#fff
Lợi ích của Event Sourcing:
| Lợi ích | Giải thích |
|---|---|
| Perfect replay | Replay từ event #1 sẽ cho kết quả giống hệt trạng thái hiện tại |
| Audit trail | Mọi event được lưu — cơ quan quản lý xem được toàn bộ lịch sử |
| Disaster recovery | Node chết → khởi động lại → replay event log → phục hồi trạng thái |
| Debugging | Lỗi xảy ra tại event #47,382? Replay đến đó và xem |
| Hot-warm failover | Warm standby liên tục replay event từ primary → sẵn sàng thay thế |
| Time travel | Muốn biết trạng thái order book lúc 14:30:05? Replay đến thời điểm đó |
Aha Moment: Event sourcing biến stock exchange từ hệ thống phải “không bao giờ chết” thành hệ thống “có thể chết và phục hồi hoàn hảo”. Đây là insight quan trọng nhất về high availability trong bài toán này.
Deterministic Replay
Để replay cho kết quả giống hệt, cần đảm bảo:
| Điều kiện | Giải thích |
|---|---|
| Cùng thứ tự event | Sequencer đảm bảo — mỗi event có sequence number |
| Cùng logic xử lý | Matching engine là deterministic (cùng input → cùng output) |
| Không phụ thuộc thời gian | Dùng sequence number, không dùng wall clock time |
| Không phụ thuộc random | Không có random trong matching logic |
| Single-threaded | Không có race condition — thứ tự xử lý là duy nhất |
2.3.3 Market Data — Phát dữ liệu giá cho thị trường
3 cấp độ Market Data
| Level | Tên | Nội dung | Ai dùng | Tần suất |
|---|---|---|---|---|
| L1 | Top of Book | Best bid, best ask, last trade price, volume | Nhà đầu tư cá nhân, ứng dụng mobile | Mỗi thay đổi (tick-by-tick) |
| L2 | Market Depth | Top 5-10 price levels mỗi phía (giá + tổng số lượng) | Trader chuyên nghiệp, algo trading | Mỗi thay đổi |
| L3 | Full Order Book | Từng lệnh riêng lẻ (order ID, size, time) | Market maker, HFT firms | Mỗi thay đổi |
Ví dụ L1 data cho VNM:
| Trường | Giá trị |
|---|---|
| Symbol | VNM |
| Best Bid | 80,500 x 500 |
| Best Ask | 80,600 x 300 |
| Last Trade | 80,500 x 100 |
| Volume | 1,234,567 |
| High | 81,200 |
| Low | 79,800 |
| Open | 80,000 |
Ví dụ L2 data cho VNM:
| Bid Qty | Bid Price | Ask Price | Ask Qty | |
|---|---|---|---|---|
| 500 | 80,500 | 80,600 | 300 | |
| 1,200 | 80,400 | 80,700 | 800 | |
| 300 | 80,300 | 80,800 | 1,500 | |
| 2,000 | 80,200 | 80,900 | 400 | |
| 150 | 80,100 | 81,000 | 2,200 |
Market Data Distribution — Multicast UDP
| Phương pháp | Mô tả | Ưu điểm | Nhược điểm |
|---|---|---|---|
| TCP Unicast | Gửi riêng cho từng subscriber | Reliable, ordered | Không scale — 10,000 subscriber = 10,000 connections |
| Multicast UDP | Gửi 1 lần, nhiều subscriber nhận | Scale tốt, latency thấp | Unreliable — có thể mất packet |
| WebSocket | Full-duplex TCP | Phù hợp web client | Latency cao hơn multicast |
Sàn chứng khoán dùng Multicast UDP cho institutional client (broker, HFT firm) và WebSocket/TCP cho retail client.
Xử lý mất packet trong Multicast UDP:
| Vấn đề | Giải pháp |
|---|---|
| Mất packet | Mỗi message có sequence number → subscriber phát hiện gap → request retransmission |
| Thứ tự sai | Buffer messages, sort theo sequence number, chỉ process khi liên tục |
| Subscriber chậm | Snapshot + incremental update. Subscriber chậm sẽ nhận snapshot mới |
Ghi nhớ: Market data tạo ra traffic khổng lồ. Mỗi lệnh mới tạo ra ít nhất 1 order book update event. Mỗi trade tạo ra nhiều event (trade, order update, L1 update, L2 update). Một sàn 10K orders/sec có thể tạo ra 50K-100K market data events/sec.
2.3.4 Risk Management — Bảo vệ hệ thống và thị trường
Pre-Trade Risk Checks
Mỗi lệnh phải qua risk engine trước khi vào matching engine. Các kiểm tra:
| Kiểm tra | Mô tả | Ví dụ |
|---|---|---|
| Position Limit | Nhà đầu tư không được nắm giữ quá nhiều cổ phiếu một loại | Max 5% cổ phiếu lưu hành của 1 mã |
| Order Size Limit | Lệnh không được quá lớn | Max 100,000 cổ phiếu mỗi lệnh |
| Price Band | Giá phải nằm trong biên độ cho phép | HOSE: +/- 7% so với giá tham chiếu |
| Fat Finger Check | Phát hiện lệnh sai do nhập nhầm | Giá lệnh chênh > 5% so với giá hiện tại |
| Credit/Buying Power | Tài khoản có đủ tiền/cổ phiếu không | Mua 1 triệu cổ phải có đủ tiền ký quỹ |
| Duplicate Check | Cùng lệnh gửi 2 lần | Idempotency check theo client order ID |
| Trading Halt Check | Cổ phiếu có bị tạm ngừng giao dịch không | Đang chờ thông tin quan trọng, circuit breaker |
| Account Status | Tài khoản có bị khóa/đình chỉ không | Black-listed accounts |
Real-Time Exposure Calculation
| Metric | Công thức | Mục đích |
|---|---|---|
| Net Exposure | Long Value - Short Value | Tổng giá trị ròng vị thế |
| Gross Exposure | Long Value + Short Value | Tổng giá trị tuyệt đối vị thế |
| Margin Utilization | Used Margin / Total Margin | Phần trăm ký quỹ đã dùng |
| Concentration Risk | Position in Symbol / Total Portfolio | Tỷ trọng 1 mã trong danh mục |
Circuit Breaker — Cơ chế ngắt mạch
Khi thị trường biến động quá mạnh, sàn tự động tạm ngừng giao dịch:
| Cấp độ | Điều kiện (ví dụ NYSE) | Hành động |
|---|---|---|
| Level 1 | Index giảm 7% | Tạm ngừng 15 phút |
| Level 2 | Index giảm 13% | Tạm ngừng 15 phút |
| Level 3 | Index giảm 20% | Đóng sàn cả ngày |
HOSE có cơ chế tương tự: biên độ +/- 7% cho mỗi cổ phiếu. Nếu giá chạm trần/sàn, lệnh chỉ có thể khớp tại giá trần/sàn.
2.3.5 Performance Optimization — Tối ưu hiệu năng đến microsecond
Kernel Bypass — Bỏ qua kernel của OS
Trong network stack bình thường: Application → System Call → Kernel Network Stack → NIC Driver → NIC → Wire
Vấn đề: Mỗi system call + kernel processing mất 5-10 microseconds. Quá chậm cho stock exchange.
Giải pháp — DPDK (Data Plane Development Kit): Application → DPDK (user space) → NIC → Wire
| Kỹ thuật | Mô tả | Lợi ích |
|---|---|---|
| DPDK | Intel framework, xử lý packet trong user space | Loại bỏ system call overhead, latency < 1 microsecond |
| Kernel bypass | NIC gửi packet thẳng vào user space memory | Không qua kernel network stack |
| Busy polling | Thread liên tục kiểm tra NIC có packet mới | Không có interrupt latency |
| Huge pages | Dùng memory page 2MB/1GB thay vì 4KB | Giảm TLB miss, tăng cache efficiency |
Lock-Free Data Structures
| Kỹ thuật | Mô tả | Dùng cho |
|---|---|---|
| Lock-free queue (Disruptor pattern) | Ring buffer với atomic CAS operations | Message passing giữa components |
| Single-writer principle | Chỉ 1 thread ghi vào một vùng memory | Matching engine, sequencer |
| Memory barriers | Đảm bảo thứ tự ghi/đọc giữa CPU cores | Communication giữa threads |
LMAX Disruptor Pattern: Ring buffer với single producer, multiple consumers. Không cần lock, throughput lên đến 100 triệu events/giây.
Memory-Mapped Files
| Kỹ thuật | Mô tả | Lợi ích |
|---|---|---|
| mmap() | Map file trên disk vào virtual memory | Ghi event log nhanh như ghi vào memory |
| Write-ahead | Ghi event vào mmap file trước khi xử lý | Durability mà không mất speed |
| OS page cache | OS tự động sync mmap data xuống disk | Application không cần quản lý disk I/O |
CPU Pinning & NUMA Awareness
| Kỹ thuật | Mô tả | Lợi ích |
|---|---|---|
| CPU pinning (thread affinity) | Gán 1 thread vào 1 CPU core cố định | Không bị migrate giữa cores, cache luôn warm |
| NUMA awareness | Allocate memory gần CPU core đang dùng | Giảm memory access latency |
| Isolcpus | Dành riêng CPU core cho application, OS không dùng | Không bị interrupt bởi OS tasks |
| Disable hyperthreading | Tắt SMT trên core chạy matching engine | Core dedicated, không share resources |
FPGA — Phần cứng chuyên dụng
| Đặc điểm | Mô tả |
|---|---|
| FPGA (Field-Programmable Gate Array) | Vi mạch có thể lập trình lại, xử lý logic trong hardware |
| Latency | Sub-microsecond (~100-500 nanoseconds) |
| Dùng cho | Market data parsing, pre-trade risk checks, order routing |
| Ai dùng | HFT firms (Citadel, Jump Trading, Virtu Financial) |
| Nhược điểm | Khó lập trình (VHDL/Verilog), khó debug, đắt tiền |
Aha Moment: Các tầng tối ưu (từ dễ đến khó):
- Thuật toán tốt (O(log n) thay vì O(n))
- Single-threaded design (loại bỏ lock)
- Lock-free data structures (Disruptor)
- Memory-mapped files (loại bỏ disk I/O)
- CPU pinning + NUMA (tối ưu hardware utilization)
- Kernel bypass / DPDK (loại bỏ OS overhead)
- FPGA (xử lý trong hardware)
Mỗi tầng giảm latency từ milliseconds → microseconds → nanoseconds.
2.3.6 High Availability — Sàn không được ngừng
Hot-Warm Standby Architecture
flowchart TB subgraph "Primary (Hot)" P_SEQ["Sequencer<br/>(Primary)"] P_ME["Matching Engine<br/>(Primary)"] P_LOG["Event Log<br/>(Primary)"] end subgraph "Standby (Warm)" W_SEQ["Sequencer<br/>(Warm)"] W_ME["Matching Engine<br/>(Warm)"] W_LOG["Event Log<br/>(Warm)"] end subgraph "Arbiter" ARB["Heartbeat Monitor<br/>Failover Decision"] end P_SEQ --> P_LOG P_LOG -->|"Replicate events<br/>(sync or async)"| W_LOG W_LOG --> W_ME ARB -->|"Monitor"| P_SEQ ARB -->|"Monitor"| W_SEQ ARB -.->|"Failover trigger"| W_SEQ style P_ME fill:#43a047,color:#fff style W_ME fill:#ff9800,color:#fff style ARB fill:#1e88e5,color:#fff
| Mode | Mô tả | Khi nào dùng |
|---|---|---|
| Hot (Primary) | Đang xử lý lệnh, active | Bình thường |
| Warm (Standby) | Nhận event log từ primary, replay liên tục, sẵn sàng thay thế | Luôn chạy |
| Cold (Backup) | Có event log nhưng không replay, cần thời gian khởi động | Disaster recovery |
Failover Process
| Bước | Hành động | Thời gian |
|---|---|---|
| 1 | Arbiter phát hiện primary mất heartbeat | ~100ms (configurable) |
| 2 | Arbiter xác nhận primary thật sự chết (không phải network glitch) | ~200ms |
| 3 | Arbiter gửi lệnh failover cho warm standby | ~10ms |
| 4 | Warm standby hoàn thành replay các event chưa xử lý | ~50ms |
| 5 | Warm standby chuyển thành primary, bắt đầu nhận lệnh mới | ~10ms |
| Tổng | ~370ms |
Mục tiêu: failover trong < 1 giây. Trong thời gian failover, gateway buffer các lệnh mới và gửi lại sau khi standby sẵn sàng.
Deterministic Replay cho Recovery
Khi primary chết và warm standby lên thay:
- Warm standby đã replay event #1 → #999,990 (trễ 10 events so với primary)
- Primary chết tại event #1,000,000
- Warm standby tiếp tục replay #999,991 → #1,000,000 từ event log (đã được replicate)
- Trạng thái của warm standby giống hệt trạng thái của primary trước khi chết
- Warm standby bắt đầu nhận lệnh mới, tiếp tục từ event #1,000,001
Aha Moment: Nhờ event sourcing + deterministic replay, ta có thể đảm bảo rằng standby có chính xác cùng trạng thái như primary. Không mất 1 lệnh nào. Đây là lý do tại sao event sourcing là nền tảng của high availability trong stock exchange.
2.3.7 Clearing & Settlement — Thanh toán bù trừ
T+2 Settlement
| Thuật ngữ | Mô tả |
|---|---|
| T | Trade date — ngày giao dịch xảy ra |
| T+2 | Settlement date — 2 ngày làm việc sau T |
| Settlement | Chuyển cổ phiếu từ người bán sang người mua, chuyển tiền từ người mua sang người bán |
| Clearing | Xác nhận giao dịch, tính toán nghĩa vụ thanh toán |
Netting — Giảm số lượng giao dịch thanh toán
Ví dụ: Trong 1 ngày, giữa Broker A và Broker B:
- Trade 1: A mua 100 VNM từ B
- Trade 2: A bán 60 VNM cho B
- Trade 3: A mua 40 VNM từ B
Không có netting: 3 giao dịch thanh toán riêng. Có netting: A mua net 80 VNM từ B (100 - 60 + 40). Chỉ cần 1 giao dịch thanh toán.
Central Counterparty (CCP) — Trung tâm bù trừ
| Vai trò | Mô tả |
|---|---|
| Đối tác trung tâm | CCP đứng giữa người mua và người bán. A mua từ CCP, CCP mua từ B |
| Giảm counterparty risk | Nếu B phá sản, CCP vẫn đảm bảo A nhận cổ phiếu |
| Margin requirement | CCP yêu cầu ký quỹ từ cả hai bên |
| Default fund | Quỹ dự phòng khi một thành viên mất khả năng thanh toán |
Tại Việt Nam, VSDC (Vietnam Securities Depository and Clearing Corporation) đóng vai trò CCP.
3. Estimation — Ước lượng hệ thống
3.1 Throughput Estimation
Assumptions:
| Thông số | Giá trị | Giải thích |
|---|---|---|
| Phiên giao dịch | 6 giờ/ngày | 9:00 - 15:00 |
| Số mã chứng khoán | 1,500 | Tương đương HOSE + HNX |
| Số lệnh trung bình/ngày | 10,000,000 | 10 triệu lệnh |
| Phần trăm cancel | 60% | HFT và algo trading hủy lệnh nhiều |
| Peak/Average ratio | 5x | Giờ mở cửa và đóng cửa cao điểm |
Với sàn lớn hơn (NYSE, NASDAQ), peak có thể lên 100K-500K orders/sec.
3.2 Market Data Events Estimation
Mỗi order tạo ra nhiều market data event:
| Event type | Số event trung bình mỗi order |
|---|---|
| Order acknowledgment | 1 |
| Order book update (L2) | 1 |
| L1 update (best bid/ask) | 0.3 (chỉ khi best thay đổi) |
| Trade event (khi khớp) | 0.4 (40% lệnh được khớp) |
| Execution report | 0.4 |
3.3 Storage Estimation — Audit Trail
Mỗi event cần lưu cho audit:
| Trường | Kích thước |
|---|---|
| Sequence number | 8 bytes |
| Timestamp (nanosecond) | 8 bytes |
| Event type | 4 bytes |
| Order ID | 16 bytes |
| Symbol | 8 bytes |
| Side (buy/sell) | 1 byte |
| Price | 8 bytes |
| Quantity | 8 bytes |
| Account ID | 16 bytes |
| Metadata | ~50 bytes |
Với compression (LZ4, ~3x ratio):
Regulatory yêu cầu lưu audit trail 7 năm (SEC) hoặc 5 năm (UBCKNN):
3.4 Network Bandwidth Estimation
Inbound (orders từ broker):
Rất nhỏ — orders là message nhỏ.
Outbound (market data):
Với multicast, bandwidth phụ thuộc vào số multicast group, không phải số subscriber:
Nếu dùng unicast TCP cho retail: — không khả thi. Đây là lý do phải dùng CDN hoặc market data vendor (Bloomberg, Reuters) làm trung gian cho retail.
3.5 Tóm tắt Estimation
| Metric | Giá trị |
|---|---|
| Peak orders/sec | 10,000 (design target) |
| Peak market data events/sec | 50,000 |
| Matching latency target | < 100 microseconds |
| End-to-end latency target | < 10 milliseconds |
| Audit storage/year | ~1 TB (raw), ~333 GB (compressed) |
| Inbound bandwidth | ~16 Mbps |
| Outbound bandwidth (multicast) | ~220 Mbps |
| Outbound bandwidth (unicast retail) | Dùng CDN / market data vendor |
4. Security — Bảo vệ sàn giao dịch
4.1 Order Authentication & Authorization
| Tầng bảo vệ | Mô tả |
|---|---|
| Broker authentication | Mỗi broker có certificate (mutual TLS) để kết nối với gateway |
| Session management | FIX session với logon/logout, heartbeat, sequence number |
| Order authorization | Kiểm tra broker có quyền giao dịch mã chứng khoán này không |
| Account verification | Kiểm tra tài khoản nhà đầu tư có hợp lệ và không bị khóa |
| Digital signature | Mỗi lệnh có thể được ký số để chống chối từ (non-repudiation) |
| IP whitelist | Chỉ cho phép kết nối từ IP đã đăng ký của broker |
4.2 Market Manipulation Detection
Các hình thức thao túng thị trường
| Hình thức | Mô tả | Cách phát hiện |
|---|---|---|
| Spoofing | Đặt lệnh lớn để tạo ảo tưởng cung/cầu, rồi hủy trước khi khớp | Tỷ lệ hủy lệnh cao bất thường (> 90%), đặt lệnh lớn rồi hủy trong < 1 giây |
| Layering | Đặt nhiều lệnh ở nhiều mức giá để tạo “bức tường” ảo | Nhiều lệnh cùng phía, cùng tài khoản, ở nhiều mức giá, hủy đồng loạt |
| Wash Trading | Tự mua tự bán để tạo thanh khoản ảo | Cùng beneficial owner ở cả hai phía của giao dịch |
| Front Running | Broker biết lệnh lớn của khách, giao dịch trước | Lệnh của broker xuất hiện trước lệnh lớn của khách |
| Pump and Dump | Đẩy giá lên bằng tin giả, rồi bán ra | Giá tăng đột biến + volume cao bất thường + tin tức trên social media |
| Quote Stuffing | Gửi hàng nghìn lệnh trong microseconds để gây nghẽn hệ thống | Số lệnh/giây từ 1 nguồn vượt ngưỡng bất thường |
Hệ thống giám sát (Market Surveillance)
| Component | Vai trò |
|---|---|
| Real-time alert engine | Phát hiện pattern bất thường trong stream lệnh |
| Pattern matching | So sánh hành vi giao dịch với các pattern thao túng đã biết |
| Cross-market surveillance | Theo dõi giao dịch trên nhiều sàn (cổ phiếu, phái sinh, trái phiếu) |
| Beneficial owner tracking | Xác định người thực sự sở hữu tài khoản để phát hiện wash trading |
| Post-trade analysis | Phân tích sau phiên giao dịch để phát hiện pattern khó thấy real-time |
4.3 Audit Trail — Không được mất một event nào
| Yêu cầu | Chi tiết |
|---|---|
| Completeness | Mọi event phải được log: order, cancel, amend, trade, rejection |
| Immutability | Event log không được sửa/xóa — append-only |
| Timestamps | Nanosecond precision, đồng bộ qua NTP/PTP |
| Retention | SEC Rule 17a-4: lưu 6 năm. UBCKNN: lưu 5 năm |
| Accessibility | Cơ quan quản lý có thể truy xuất bất kỳ lúc nào |
| Integrity | Hash chain (tương tự blockchain) để chứng minh log không bị tampering |
4.4 Regulatory Compliance
| Quy định | Khu vực | Yêu cầu chính |
|---|---|---|
| SEC Rule 613 (CAT) | Mỹ | Consolidated Audit Trail — mọi lệnh phải có unique ID theo dõi từ đầu đến cuối |
| MiFID II | Châu Âu | Best execution, transaction reporting, algo trading controls |
| Reg NMS | Mỹ | Best price execution, order protection rule |
| FCA MAR | Anh | Market abuse detection and reporting |
| UBCKNN | Việt Nam | Biên độ giá, ký quỹ, báo cáo giao dịch |
4.5 DDoS Protection tại Gateway
| Tầng bảo vệ | Mô tả |
|---|---|
| Rate limiting per broker | Mỗi broker có quota lệnh/giây (ví dụ: 1,000 orders/sec) |
| Message validation | Reject message không đúng format FIX trước khi xử lý |
| Connection limit | Giới hạn số kết nối đồng thời từ 1 IP |
| Throttling | Khi tải cao, giảm tốc độ xử lý thay vì reject tất cả |
| Kill switch | Có thể ngắt kết nối từ 1 broker cụ thể ngay lập tức |
| Network firewall | Chỉ cho phép FIX protocol trên port đã định, block tất cả port khác |
5. DevOps — Vận hành sàn giao dịch
5.1 Latency Monitoring — Đo chính xác đến microsecond
Các điểm đo latency (Measurement Points)
| Điểm đo | Vị trí | Mục tiêu |
|---|---|---|
| Wire-to-wire | Từ NIC nhận packet đến NIC gửi response | < 50 microseconds |
| Gateway processing | Parse FIX message, validate, forward | < 5 microseconds |
| Risk check | Pre-trade risk engine processing | < 10 microseconds |
| Sequencer | Assign sequence number, write event log | < 2 microseconds |
| Matching | Order matching trong matching engine | < 5 microseconds |
| Market data publish | Từ trade xảy ra đến market data gửi đi | < 10 microseconds |
Công cụ đo latency
| Công cụ | Mô tả |
|---|---|
| Hardware timestamping | NIC gán timestamp tại hardware level — chính xác nanosecond |
| PTP (Precision Time Protocol) | Đồng bộ thời gian giữa các server với độ chính xác < 1 microsecond |
| Kernel bypass probes | Đo latency trong DPDK pipeline không qua kernel |
| Custom latency framework | Mỗi component ghi timestamp vào message header, tính delta tại cuối |
Latency Percentiles
| Percentile | Mục tiêu | Ý nghĩa |
|---|---|---|
| p50 | < 10 microseconds | Phần nửa lệnh nhanh hơn mức này |
| p99 | < 100 microseconds | 99% lệnh nhanh hơn mức này |
| p99.9 | < 1 millisecond | Chỉ 1/1000 lệnh chậm hơn mức này |
| p99.99 | < 10 milliseconds | Tail latency — GC pause, page fault |
Quan trọng: Trong stock exchange, tail latency (p99.9, p99.99) quan trọng không kém median. Một GC pause 50ms có thể làm mất hàng nghìn lệnh trong giờ cao điểm.
5.2 Matching Engine Metrics
| Metric | Mô tả | Alert threshold |
|---|---|---|
| Orders/sec | Số lệnh xử lý mỗi giây | < expected → có vấn đề |
| Trades/sec | Số giao dịch khớp mỗi giây | |
| Order book depth | Số lệnh trong order book (mỗi mã) | Quá sâu → thanh khoản kém |
| Spread | Chênh lệch bid-ask | Spread rộng bất thường → cảnh báo |
| Queue depth | Số lệnh chờ xử lý trong input queue | > 100 → bottleneck |
| Event log write latency | Thời gian ghi event ra disk | > 1ms → disk vấn đề |
5.3 Order Rejection Rate
| Metric | Ngưỡng bình thường | Cảnh báo |
|---|---|---|
| Total rejection rate | < 5% | > 10% → kiểm tra risk engine |
| Risk rejection rate | < 3% | > 5% → risk params quá chặt? |
| Validation rejection rate | < 1% | > 2% → broker gửi sai format? |
| Duplicate rejection rate | < 0.5% | > 1% → broker có bug retry? |
5.4 Event Log Integrity Verification
| Kiểm tra | Tần suất | Mô tả |
|---|---|---|
| Sequence gap check | Liên tục (real-time) | Kiểm tra không có gap trong sequence number |
| Hash chain verification | Mỗi 1 phút | Verify hash của mỗi event liên kết với event trước |
| Primary-standby comparison | Mỗi 1 giây | So sánh event log trên primary và standby |
| Checksum verification | Mỗi phiên | Tính checksum toàn bộ event log của ngày |
| Replay verification | Hàng tuần | Replay event log và so sánh kết quả với trạng thái thực tế |
5.5 Disaster Recovery Drills
| Loại drill | Tần suất | Mô tả |
|---|---|---|
| Failover drill | Hàng tháng | Tắt primary, kiểm tra warm standby lên thay trong < 1 giây |
| Full replay drill | Hàng quý | Replay toàn bộ event log của 1 ngày, xác nhận kết quả khớp |
| Network partition drill | Hàng quý | Mô phỏng mất kết nối giữa components |
| Data center failover | Hàng năm | Chuyển toàn bộ hoạt động sang data center dự phòng |
| Capacity test | Hàng quý | Stress test 2x-3x peak load |
Runbook cho sự cố
| Sự cố | Bước xử lý |
|---|---|
| Matching engine hang | 1. Gateway buffer lệnh. 2. Failover sang warm standby. 3. Thông báo broker. 4. Investigate root cause. |
| Event log gap detected | 1. Halt matching. 2. Sync event log từ standby. 3. Verify integrity. 4. Resume. |
| Market data delay > 1s | 1. Kiểm tra MDP process. 2. Kiểm tra network. 3. Restart MDP nếu cần. 4. Subscribers request snapshot. |
| Risk engine chậm | 1. Tăng timeout. 2. Kiểm tra cause (CPU, memory, dependency). 3. Restart risk engine (orders bị reject trong thời gian restart). |
| DDoS từ 1 broker | 1. Rate limit broker đó. 2. Kill switch nếu cần. 3. Thông báo broker. 4. Report cho cơ quan quản lý. |
6. Mermaid Diagrams — Tổng hợp kiến trúc
6.1 Overall Architecture (Chi tiết)
flowchart TB subgraph "External" B["Brokers<br/>(FIX Protocol)"] MD_SUB["Market Data<br/>Subscribers"] REG["Regulators<br/>(SEC, UBCKNN)"] end subgraph "Gateway Layer" GW1["Gateway 1"] GW2["Gateway 2"] GW_N["Gateway N"] end subgraph "Risk Layer" RE1["Risk Engine 1<br/>(Symbols A-F)"] RE2["Risk Engine 2<br/>(Symbols G-M)"] RE3["Risk Engine 3<br/>(Symbols N-Z)"] end subgraph "Core Engine" SEQ["Sequencer"] OM["Order Manager"] ME["Matching Engine<br/>(Single-Threaded)"] OB["Order Book<br/>(In-Memory)"] end subgraph "Event Store" EL["Event Log<br/>(Append-Only)"] EL_R["Event Log<br/>(Replica)"] end subgraph "Market Data" MDP["Market Data<br/>Publisher"] MC["Multicast<br/>(UDP)"] WS["WebSocket<br/>(Retail)"] end subgraph "Post-Trade" CL["Clearing"] ST["Settlement"] RPT["Reporting"] end subgraph "Standby" W_ME["Warm Standby<br/>Matching Engine"] W_OB["Warm Standby<br/>Order Book"] end B --> GW1 & GW2 & GW_N GW1 & GW2 & GW_N --> RE1 & RE2 & RE3 RE1 & RE2 & RE3 --> SEQ SEQ --> EL EL --> EL_R SEQ --> OM OM --> ME ME <--> OB ME --> MDP ME --> CL --> ST ME --> RPT --> REG MDP --> MC --> MD_SUB MDP --> WS --> MD_SUB EL_R --> W_ME W_ME <--> W_OB style ME fill:#e53935,color:#fff style SEQ fill:#1e88e5,color:#fff style MDP fill:#43a047,color:#fff style EL fill:#ff9800,color:#fff
6.2 Order Matching Flow (Chi tiết)
flowchart TD START["New Order Received<br/>BUY 200 VNM @ MARKET"] CHECK_TYPE{"Order Type?"} LIMIT["Limit Order"] MARKET["Market Order"] FIND_MATCH{"Best Ask exists<br/>and price matches?"} FULL_MATCH{"Order fully<br/>filled?"} PARTIAL["Partially Filled<br/>Reduce remaining qty"] ADD_BOOK["Add remaining<br/>to Order Book<br/>(Bid Side)"] TRADE["Generate Trade<br/>Execution Report"] UPDATE_BOOK["Update Order Book<br/>Remove/Reduce Ask"] PUBLISH["Publish Events:<br/>1. Trade (L1/L2/L3)<br/>2. Order Book Update<br/>3. Execution Report"] NO_MATCH_LIMIT["Add to Order Book<br/>Wait for match"] NO_MATCH_MARKET{"IOC or FOK?"} CANCEL_REMAINING["Cancel remaining qty<br/>(IOC)"] CANCEL_ALL["Cancel entire order<br/>(FOK)"] START --> CHECK_TYPE CHECK_TYPE -->|Limit| LIMIT CHECK_TYPE -->|Market| MARKET LIMIT --> FIND_MATCH MARKET --> FIND_MATCH FIND_MATCH -->|Yes| TRADE FIND_MATCH -->|No, Limit| NO_MATCH_LIMIT FIND_MATCH -->|No, Market| NO_MATCH_MARKET TRADE --> UPDATE_BOOK UPDATE_BOOK --> FULL_MATCH FULL_MATCH -->|Yes| PUBLISH FULL_MATCH -->|No| PARTIAL PARTIAL --> FIND_MATCH NO_MATCH_MARKET -->|IOC| CANCEL_REMAINING NO_MATCH_MARKET -->|FOK| CANCEL_ALL NO_MATCH_LIMIT --> PUBLISH style TRADE fill:#43a047,color:#fff style CANCEL_ALL fill:#e53935,color:#fff style CANCEL_REMAINING fill:#ff9800,color:#fff
6.3 Event Sourcing & Replay
flowchart LR subgraph "Normal Operation" direction TB IN["Incoming Orders"] SEQ["Sequencer"] EL["Event Log<br/>#1 #2 #3 ... #N"] ME["Matching Engine"] STATE["Order Book State<br/>(In-Memory)"] IN --> SEQ SEQ --> EL SEQ --> ME ME --> STATE end subgraph "Disaster Recovery" direction TB EL2["Event Log<br/>(Replicated)"] ME2["New Matching<br/>Engine Instance"] STATE2["Rebuilt Order Book<br/>(Identical State)"] EL2 -->|"Replay #1 → #N"| ME2 ME2 --> STATE2 end subgraph "Debugging" direction TB EL3["Event Log"] ME3["Replay Engine"] BUG["State at event #47382<br/>(moment of bug)"] EL3 -->|"Replay #1 → #47382"| ME3 ME3 --> BUG end EL -.->|"Replicate"| EL2 EL -.->|"Copy"| EL3 style EL fill:#ff9800,color:#fff style STATE fill:#43a047,color:#fff style STATE2 fill:#43a047,color:#fff style BUG fill:#e53935,color:#fff
6.4 Market Data Distribution
flowchart TB ME["Matching Engine"] MDP["Market Data Publisher"] subgraph "L1 Feed (Top of Book)" L1_MC["Multicast Group A<br/>(UDP)"] L1_WS["WebSocket Gateway"] L1_S1["HFT Firm 1"] L1_S2["HFT Firm 2"] L1_S3["Broker App"] L1_S4["Retail App 1"] L1_S5["Retail App N"] end subgraph "L2 Feed (Market Depth)" L2_MC["Multicast Group B<br/>(UDP)"] L2_S1["Pro Trader 1"] L2_S2["Pro Trader 2"] L2_S3["Algo Engine"] end subgraph "L3 Feed (Full Book)" L3_MC["Multicast Group C<br/>(UDP)"] L3_S1["Market Maker 1"] L3_S2["Market Maker 2"] end ME --> MDP MDP --> L1_MC MDP --> L1_WS MDP --> L2_MC MDP --> L3_MC L1_MC --> L1_S1 & L1_S2 & L1_S3 L1_WS --> L1_S4 & L1_S5 L2_MC --> L2_S1 & L2_S2 & L2_S3 L3_MC --> L3_S1 & L3_S2 style MDP fill:#43a047,color:#fff style L1_MC fill:#1e88e5,color:#fff style L2_MC fill:#1e88e5,color:#fff style L3_MC fill:#1e88e5,color:#fff style L1_WS fill:#ff9800,color:#fff
7. Aha Moments & Pitfalls
7.1 Aha Moments — Những insight quan trọng nhất
Insight #1: Single-Threaded > Multi-Threaded
“Lock overhead lớn hơn thời gian xử lý lệnh. Bỏ lock đi = nhanh hơn gấp bội.”
Đây là insight phản trực giác nhất. Trong web development, multi-threading là mặc định. Nhưng trong stock exchange, single-threaded matching engine nhanh hơn vì:
- Loại bỏ lock contention (hàng microseconds mỗi lock)
- Loại bỏ context switching (hàng microseconds mỗi switch)
- Data luôn trong L1 cache của 1 CPU core
- LMAX chứng minh: 6 triệu ops/sec với 1 thread
Bài học: Không phải lúc nào “thêm thread” cũng là cách tối ưu performance. Đôi khi, giảm complexity mới là cách nhanh nhất.
Insight #2: Event Sourcing thay đổi mọi thứ
“Không lưu trạng thái — lưu lịch sử. Trạng thái chỉ là kết quả của replay lịch sử.”
Event sourcing cho phép:
- Perfect disaster recovery — replay event log = phục hồi trạng thái chính xác
- Time travel debugging — replay đến bất kỳ thời điểm nào
- Audit trail miễn phí — event log chính là audit trail
- Hot-warm failover — warm standby liên tục replay = luôn sẵn sàng
Bài học: Event sourcing không chỉ dùng cho stock exchange. Nó áp dụng cho bất kỳ hệ thống nào cần audit trail, replay, hoặc deterministic recovery: payment system, banking, inventory management.
Insight #3: Latency đo bằng Microseconds, không phải Milliseconds
“Trong thế giới stock exchange, 1 millisecond = vĩnh cửu.”
| Hệ thống | Latency đơn vị |
|---|---|
| Web application | Milliseconds (100-500ms) |
| Database query | Milliseconds (1-50ms) |
| Stock exchange matching | Microseconds (1-100us) |
| HFT trading | Nanoseconds (100-500ns) |
Khi latency ở mức microsecond, mọi thứ thay đổi:
- Garbage collection = không chấp nhận được
- System call = quá chậm
- Disk I/O = chỉ dùng memory-mapped files
- TCP = quá chậm, dùng kernel bypass
Bài học: Hiểu được latency scale giúp bạn chọn đúng công cụ cho đúng bài toán. Web app dùng Java/Spring Boot là ok. Stock exchange cần C++/Rust + DPDK.
Insight #4: Determinism quan trọng hơn Raw Speed
“Nhanh nhưng không reproduce được = vô dụng. Chậm hơn 1 microsecond nhưng deterministic = vô giá.”
Tại sao determinism quan trọng:
- Regulatory: Cơ quan quản lý yêu cầu chứng minh mọi giao dịch là công bằng
- Dispute resolution: Khi có tranh chấp, cần replay chính xác những gì đã xảy ra
- Testing: Deterministic system có thể test bằng replay — không cần mock
- Recovery: Replay event log phải cho kết quả giống hệt
Bài học: Khi thiết kế hệ thống, tự hỏi: “Nếu replay input, output có giống không?” Nếu không, bạn sẽ gặp vấn đề khi debug, audit, hoặc recover.
Insight #5: Vertical Scaling có vị trí của nó
“Không phải bài toán nào cũng giải bằng horizontal scaling.”
Stock exchange là ví dụ điển hình của vertical scaling:
- Matching engine chạy trên 1 server mạnh nhất có thể
- Tối ưu đến mức hardware: CPU pinning, NUMA, kernel bypass, FPGA
- Horizontal scaling (nhiều matching engine) gây ra ordering problem
Bài học: Phân biệt stateless (horizontal scale dễ — web server, API gateway) và stateful + ordering-sensitive (vertical scale — matching engine, sequencer). Mỗi loại cần strategy khác nhau.
7.2 Pitfalls — Những cái bẫy thường gặp
Pitfall #1: Dùng database cho order book
Sai: Lưu order book trong PostgreSQL/MySQL và query mỗi lần cần khớp lệnh. Đúng: Order book phải hoàn toàn trong memory. Database chỉ dùng cho audit trail (append-only).
Lý do: Một disk I/O mất ~100 microseconds (SSD). Budget latency của matching engine là 5-10 microseconds. Một lần đọc disk = vi phạm latency budget.
Pitfall #2: Dùng message queue giữa Gateway và Matching Engine
Sai: Gửi lệnh qua Kafka/RabbitMQ để “decouple” components. Đúng: Gửi trực tiếp qua shared memory hoặc lock-free ring buffer.
Lý do: Kafka latency ~1-5ms. Ring buffer latency ~100 nanoseconds. Chênh lệch 10,000 lần.
Ngoại lệ: Message queue có thể dùng cho non-critical path: gửi execution report cho broker, gửi trade cho clearing system. Chỉ không dùng cho critical path (order → matching).
Pitfall #3: Dùng JSON/XML cho message format
Sai: Parse JSON message từ broker. Đúng: Dùng FIX protocol (binary-optimized) hoặc FlatBuffers/SBE (zero-copy deserialization).
Lý do: JSON parsing mất hàng microseconds (allocate memory, parse string). FIX/SBE parsing có thể zero-copy — chỉ di chuyển pointer, không allocate memory.
Pitfall #4: Không test tail latency
Sai: Chỉ đo average latency và thấy 5 microseconds — tuyệt vời! Đúng: Đo p99, p99.9, p99.99. Nếu p99.99 = 50ms vì GC pause — đó là vấn đề.
Lý do: 1 GC pause 50ms ở 10K orders/sec = 500 lệnh bị delay. Trong số đó có thể có lệnh của khách hàng VIP hoặc lệnh ảnh hưởng giá thị trường.
Pitfall #5: Multi-master matching engine
Sai: Chạy 2 matching engine active-active để tăng throughput. Đúng: Single matching engine (active) + warm standby.
Lý do: 2 matching engine khớp lệnh đồng thời = ordering conflict. Lệnh A và lệnh B đến 2 engine theo thứ tự khác nhau → kết quả khớp khác nhau → thị trường inconsistent.
Pitfall #6: Đánh giá thấp độ phức tạp của clock synchronization
Sai: Dùng NTP (độ chính xác ~1-10ms) để timestamp lệnh. Đúng: Dùng PTP (Precision Time Protocol) với hardware timestamping (độ chính xác < 1 microsecond).
Lý do: Khi latency đo bằng microseconds, sai số đồng hồ 1ms = hoàn toàn vô nghĩa. Hai lệnh cách nhau 5 microseconds sẽ có timestamp giống nhau nếu đồng hồ sai 1ms.
8. Internal Links — Liên kết với các tuần khác
| Tuần | Liên kết | Áp dụng trong Stock Exchange |
|---|---|---|
| Tuan-08-Message-Queue | Message queue pattern | Event log chính là durable message queue. Disruptor pattern là lock-free ring buffer. Market data distribution tương tự pub/sub. |
| Tuan-13-Monitoring-Observability | Monitoring & Observability | Latency monitoring microsecond precision. Matching engine metrics. Event log integrity verification. Alerting cho order rejection rate. |
| Case-Design-Payment-System | Payment system design | Nhiều điểm tương đồng: event sourcing, idempotency, audit trail, regulatory compliance. Khác biệt: payment tối ưu cho correctness, exchange tối ưu cho speed. |
| Tuan-01-Scale-From-Zero-To-Millions | Scaling fundamentals | Stock exchange là ví dụ của vertical scaling (matching engine) kết hợp horizontal scaling (gateway, market data). |
| Tuan-02-Back-of-the-envelope | Estimation | Estimation cho orders/sec, events/sec, storage, bandwidth — tất cả đều quan trọng để sizing hệ thống. |
| Tuan-14-AuthN-AuthZ-Security | Authentication & Authorization | Mutual TLS cho broker authentication. FIX session management. Order authorization. |
| Tuan-15-Data-Security-Encryption | Data Security | Encryption cho order data in transit. Audit trail integrity (hash chain). |
9. Glossary — Từ điển thuật ngữ
| Thuật ngữ | Tiếng Việt | Mô tả |
|---|---|---|
| Order Book | Sổ lệnh | Cấu trúc dữ liệu chứa tất cả lệnh chưa khớp |
| Bid | Giá mua | Giá người mua sẵn sàng trả |
| Ask (Offer) | Giá bán | Giá người bán sẵn sàng bán |
| Spread | Độ chênh lệch | Chênh lệch giữa best ask và best bid |
| Matching Engine | Máy khớp lệnh | Component khớp lệnh mua và bán |
| Limit Order | Lệnh giới hạn | Lệnh chỉ định giá cụ thể |
| Market Order | Lệnh thị trường | Lệnh khớp ngay tại giá tốt nhất |
| Fill | Khớp lệnh | Lệnh được khớp thành công |
| Partial Fill | Khớp một phần | Lệnh chỉ khớp được một phần số lượng |
| FIFO | Vào trước ra trước | First In, First Out — lệnh đến trước xử lý trước |
| FIX Protocol | Giao thức FIX | Financial Information eXchange — giao thức chuẩn của ngành tài chính |
| Sequencer | Bộ sắp xếp | Component gán số thứ tự cho event |
| Event Sourcing | Lưu sự kiện | Pattern lưu chuỗi event thay vì trạng thái |
| T+2 | Thanh toán sau 2 ngày | Settlement xảy ra 2 ngày làm việc sau giao dịch |
| Netting | Bù trừ | Gộp các giao dịch lại để giảm số lượng thanh toán |
| CCP | Đối tác trung tâm | Central Counterparty — trung gian đảm bảo thanh toán |
| DPDK | — | Data Plane Development Kit — xử lý network trong user space |
| FPGA | — | Field-Programmable Gate Array — vi mạch lập trình được |
| Spoofing | Đặt lệnh ảo | Đặt lệnh lớn rồi hủy để thao túng giá |
| Wash Trading | Giao dịch giả | Tự mua tự bán để tạo thanh khoản ảo |
| Circuit Breaker | Cơ chế ngắt mạch | Tạm ngừng giao dịch khi thị trường biến động quá mạnh |
| PTP | — | Precision Time Protocol — đồng bộ thời gian microsecond precision |
| Kernel Bypass | Bỏ qua kernel | Xử lý network packet trong user space, không qua OS kernel |
| Disruptor | — | LMAX pattern — lock-free ring buffer cho inter-thread communication |
| SBE | — | Simple Binary Encoding — serialization format tối ưu cho latency |
10. Tổng kết — Những điều Hieu cần nhớ
Top 5 Takeaways
-
Single-threaded matching engine nhanh hơn multi-threaded — Loại bỏ lock, context switching, cache invalidation. Determinism là bonus.
-
Event sourcing là backbone — Mọi event có sequence number, append-only log. Cho phép perfect replay, audit trail, disaster recovery, time travel debugging.
-
Latency budget tính bằng microseconds — Mỗi component chỉ được dùng vài microseconds. Không có chỗ cho disk I/O, GC pause, hoặc lock wait.
-
Vertical scaling cho critical path, horizontal scaling cho non-critical — Matching engine = 1 máy mạnh nhất. Gateway, market data = nhiều máy.
-
Determinism > Raw speed — Có thể reproduce kết quả = có thể audit, debug, recover, và chứng minh fairness.
So sánh Stock Exchange với các hệ thống khác
| Khía cạnh | Stock Exchange | Payment System | Chat System | Key-Value Store |
|---|---|---|---|---|
| Ưu tiên #1 | Latency | Correctness | Availability | Scalability |
| Consistency | Strict ordering | Strong | Eventual | Tunable |
| Scaling | Vertical (matching) | Horizontal | Horizontal | Horizontal |
| Data model | Event log + in-memory | Transaction log | Message log | Key-value pairs |
| Latency | Microseconds | Milliseconds | Milliseconds | Milliseconds |
| Failure handling | Deterministic replay | Retry + idempotency | Last-write-wins | Quorum read/write |
Câu hỏi tự kiểm tra cho Hieu
- Tại sao matching engine phải single-threaded? Multi-threaded có lợi gì?
- Event sourcing giúp gì cho disaster recovery? Giải thích bước replay.
- Tại sao dùng multicast UDP cho market data thay vì TCP?
- Spoofing là gì? Hệ thống phát hiện bằng cách nào?
- Tại sao không dùng Kafka giữa gateway và matching engine?
- T+2 settlement là gì? Netting giúp gì?
- Kernel bypass (DPDK) giải quyết vấn đề gì? Tại sao cần nó?
- Nếu matching engine chết, warm standby lên thay như thế nào? Mất bao lâu?
- L1, L2, L3 market data khác nhau thế nào? Ai dùng loại nào?
- Fat finger check là gì? Tại sao quan trọng?
“Stock exchange là nơi mà mọi microsecond đều có giá. Bạn không cần xây sàn chứng khoán, nhưng hiểu cách nó hoạt động sẽ giúp bạn thiết kế bất kỳ hệ thống nào cần low latency, determinism, và reliability.”
Next: Case-Design-Payment-System — Hệ thống thanh toán: cùng event sourcing, khác latency requirement. Related: Tuan-08-Message-Queue · Tuan-13-Monitoring-Observability