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:

  1. Nhận lệnh từ broker (VNDirect, SSI, TCBS…)
  2. 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?)
  3. Khớp lệnh — tìm người bán phù hợp với giá và số lượng
  4. Phát dữ liệu — thông báo giá mới nhất cho toàn bộ thị trường
  5. 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ứcGiải thích
Ultra-low latencyMatching phải xong trong micro-giây, không phải milli-giây
DeterminismCùng input phải cho cùng output — mọi lần, mọi lúc
FairnessLệnh đến trước phải được xử lý trước (FIFO) — pháp luật yêu cầu
ReliabilityKhông được mất lệnh — một lệnh mất = kiện tụng pháp lý
Throughput10K-100K lệnh/giây trong giờ cao điểm
Regulatory complianceSEC (Mỹ), FCA (Anh), UBCKNN (Việt Nam) yêu cầu audit trail cho mọi lệnh
Market dataPhá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ạnhWeb ApplicationStock Exchange
Latency target100-500ms1-100 microseconds
Threading modelMulti-threaded, asyncSingle-threaded, synchronous
NetworkTCP/HTTPKernel bypass, DPDK, multicast UDP
StorageDatabase (disk)In-memory, memory-mapped files
Scaling strategyHorizontal (thêm server)Vertical (tăng tốc 1 server)
ConsistencyEventual consistency okStrict ordering bắt buộc
Garbage collectionChấp nhận GC pauseGC pause = mất tiền
Programming languageJava, Python, Node.jsC++, 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ăngMô tả chi tiết
Place OrderNhà đầ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 OrdersHệ thống tự động khớp lệnh mua và bán khi giá phù hợp
Cancel OrderNhà đầu tư hủy lệnh chưa khớp (hoặc chưa khớp hết)
Amend OrderSửa lệnh (thay đổi giá hoặc số lượng) — thực tế là cancel + new order
Market Data StreamingPhát dữ liệu giá real-time (best bid/ask, order book depth, trades)
Order StatusTruy vấn trạng thái lệnh: pending, partially filled, filled, cancelled, rejected

2.1.2 Non-Functional Requirements

Yêu cầuMục tiêuLý do
Latency< 10ms end-to-end, < 100 microseconds matchingCạnh tranh với các sàn khác
Throughput10,000 orders/sec (peak: 50,000)Giờ cao điểm, sự kiện bất thường (earnings, IPO)
Availability99.99% trong giờ giao dịchSàn đóng cửa = mất tin tưởng thị trường
DurabilityZero order lossPháp luật yêu cầu — mất lệnh = kiện
DeterminismReplay cho kết quả giống hệtAudit, dispute resolution, disaster recovery
FairnessFIFO — lệnh đến trước xử lý trướcRegulatory requirement
AuditabilityMọi event được logSEC Rule 613 (Consolidated Audit Trail)

2.1.3 Các loại lệnh (Order Types)

Loại lệnhMô tảVí dụ
Limit OrderMua/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 OrderMua/bán ngay tại giá tốt nhất hiện có”Mua 100 VNM ngay — bất kể giá nào”
Stop OrderKí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 OrderStop + 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ạiDù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ủyLệ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ò

ComponentVai tròĐặc điểm
Trading GatewayĐiểm vào của hệ thống — nhận lệnh từ brokerFIX protocol, authentication, rate limiting, protocol translation
Risk EngineKiểm tra risk trước khi lệnh vào matchingPre-trade checks: position limit, price band, fat finger
SequencerGán sequence number cho mọi eventSingle point of ordering, event sourcing, write-ahead log
Order ManagerQuản lý trạng thái lệnhState machine: New → Accepted → Partially Filled → Filled / Cancelled
Matching EngineTrái tim của sàn — khớp lệnhSingle-threaded, price-time priority, in-memory order book
Market Data PublisherPhát dữ liệu giá cho thị trườngMulticast UDP, L1/L2/L3 feeds
Clearing & SettlementThanh toán bù trừ sau giao dịchT+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íaTên gọiSắp xếpÝ nghĩa
Bid Side (Bên mua)BidsGiảm dần theo giáNgười mua muốn mua giá cao nhất trước
Ask Side (Bên bán)Asks / OffersTă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ượngGiáGiáSố lượng
50080,50080,600300
1,20080,40080,700800
30080,30080,8001,500
2,00080,20080,900400
15080,10081,0002,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:

  1. 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.
  2. 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íaGiáSố lượngThời gian
#1BID80,50030009:00:01.001
#2BID80,50020009:00:01.005
#3BID80,40050009:00:00.990
#4ASK80,60040009:00:01.002
#5ASK80,70060009: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:

  1. Tìm best bid = 80,500
  2. 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
  3. 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
  4. 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ốngLimit OrderMarket Order
Cách khớpChỉ khớp tại giá chỉ định hoặc tốt hơnKhớp tại giá tốt nhất hiện có
Nếu không có đối tácThê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ểuCó thể khớp tại giá rất xấu (slippage)
Sử dụng90%+ lệnh trên sànKhi 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:

  1. Insert/Delete lệnh: O(log n) — khi có lệnh mới hoặc hủy lệnh
  2. 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úcInsert/DeleteFind BestƯu điểmNhược điểm
Red-Black TreeO(log n)O(log n)Balanced, deterministicComplex implementation
Skip ListO(log n) avgO(1) với pointerĐơn giản hơn, cache-friendlyProbabilistic balancing
Sorted ArrayO(n)O(1)Cache-friendly, đơn giảnInsert chậm (shift elements)
Hash Map + Sorted StructureO(1) lookup by ID + O(log n) insertO(1)Tìm lệnh theo ID nhanhPhứ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 contentionNhiều thread tranh nhau truy cập order book → lock wait time lớn hơn thời gian xử lý
Non-determinismThứ tự thực thi của thread không xác định → replay cho kết quả khác nhau
Context switchingOS chuyển đổi giữa các thread tốn thời gian (microseconds)
Cache invalidationNhiều thread trên nhiều CPU core → cache line bouncing
ComplexityRace condition, deadlock, priority inversion — debug cực khó

Single-threaded matching engine giải quyết tất cả:

Lợi íchGiải thích
Zero lock overheadKhông cần lock vì chỉ có 1 thread
DeterministicCùng input, cùng output — mọi lần
No context switchingThread luôn chạy, không bị OS chuyển
Cache-friendlyDữ liệu luôn trong L1/L2 cache của 1 CPU core
Simple codeDễ 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ấtMô tả
Monotonically increasingSequence number luôn tăng: 1, 2, 3, … không bao giờ nhảy
Gap-freeKhông được có khoảng trống: 1, 2, 4 (thiếu 3) = lỗi
Single point of assignmentChỉ có 1 sequencer gán số — đảm bảo toàn cục duy nhất
PersistentSequence 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 íchGiải thích
Perfect replayReplay từ event #1 sẽ cho kết quả giống hệt trạng thái hiện tại
Audit trailMọi event được lưu — cơ quan quản lý xem được toàn bộ lịch sử
Disaster recoveryNode chết → khởi động lại → replay event log → phục hồi trạng thái
DebuggingLỗi xảy ra tại event #47,382? Replay đến đó và xem
Hot-warm failoverWarm standby liên tục replay event từ primary → sẵn sàng thay thế
Time travelMuố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ệnGiải thích
Cùng thứ tự eventSequencer đả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 gianDùng sequence number, không dùng wall clock time
Không phụ thuộc randomKhông có random trong matching logic
Single-threadedKhô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
LevelTênNội dungAi dùngTần suất
L1Top of BookBest bid, best ask, last trade price, volumeNhà đầu tư cá nhân, ứng dụng mobileMỗi thay đổi (tick-by-tick)
L2Market DepthTop 5-10 price levels mỗi phía (giá + tổng số lượng)Trader chuyên nghiệp, algo tradingMỗi thay đổi
L3Full Order BookTừng lệnh riêng lẻ (order ID, size, time)Market maker, HFT firmsMỗi thay đổi

Ví dụ L1 data cho VNM:

TrườngGiá trị
SymbolVNM
Best Bid80,500 x 500
Best Ask80,600 x 300
Last Trade80,500 x 100
Volume1,234,567
High81,200
Low79,800
Open80,000

Ví dụ L2 data cho VNM:

Bid QtyBid PriceAsk PriceAsk Qty
50080,50080,600300
1,20080,40080,700800
30080,30080,8001,500
2,00080,20080,900400
15080,10081,0002,200
Market Data Distribution — Multicast UDP
Phương phápMô tảƯu điểmNhược điểm
TCP UnicastGửi riêng cho từng subscriberReliable, orderedKhông scale — 10,000 subscriber = 10,000 connections
Multicast UDPGửi 1 lần, nhiều subscriber nhậnScale tốt, latency thấpUnreliable — có thể mất packet
WebSocketFull-duplex TCPPhù hợp web clientLatency 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 packetMỗi message có sequence number → subscriber phát hiện gap → request retransmission
Thứ tự saiBuffer messages, sort theo sequence number, chỉ process khi liên tục
Subscriber chậmSnapshot + 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 traMô tảVí dụ
Position LimitNhà đầu tư không được nắm giữ quá nhiều cổ phiếu một loạiMax 5% cổ phiếu lưu hành của 1 mã
Order Size LimitLệnh không được quá lớnMax 100,000 cổ phiếu mỗi lệnh
Price BandGiá phải nằm trong biên độ cho phépHOSE: +/- 7% so với giá tham chiếu
Fat Finger CheckPhát hiện lệnh sai do nhập nhầmGiá lệnh chênh > 5% so với giá hiện tại
Credit/Buying PowerTài khoản có đủ tiền/cổ phiếu khôngMua 1 triệu cổ phải có đủ tiền ký quỹ
Duplicate CheckCùng lệnh gửi 2 lầnIdempotency check theo client order ID
Trading Halt CheckCổ phiếu có bị tạm ngừng giao dịch khôngĐang chờ thông tin quan trọng, circuit breaker
Account StatusTài khoản có bị khóa/đình chỉ khôngBlack-listed accounts
Real-Time Exposure Calculation
MetricCông thứcMục đích
Net ExposureLong Value - Short ValueTổng giá trị ròng vị thế
Gross ExposureLong Value + Short ValueTổng giá trị tuyệt đối vị thế
Margin UtilizationUsed Margin / Total MarginPhần trăm ký quỹ đã dùng
Concentration RiskPosition in Symbol / Total PortfolioTỷ 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 1Index giảm 7%Tạm ngừng 15 phút
Level 2Index giảm 13%Tạm ngừng 15 phút
Level 3Index 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ậtMô tảLợi ích
DPDKIntel framework, xử lý packet trong user spaceLoại bỏ system call overhead, latency < 1 microsecond
Kernel bypassNIC gửi packet thẳng vào user space memoryKhông qua kernel network stack
Busy pollingThread liên tục kiểm tra NIC có packet mớiKhông có interrupt latency
Huge pagesDùng memory page 2MB/1GB thay vì 4KBGiảm TLB miss, tăng cache efficiency
Lock-Free Data Structures
Kỹ thuậtMô tảDùng cho
Lock-free queue (Disruptor pattern)Ring buffer với atomic CAS operationsMessage passing giữa components
Single-writer principleChỉ 1 thread ghi vào một vùng memoryMatching engine, sequencer
Memory barriersĐảm bảo thứ tự ghi/đọc giữa CPU coresCommunication 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ậtMô tảLợi ích
mmap()Map file trên disk vào virtual memoryGhi event log nhanh như ghi vào memory
Write-aheadGhi event vào mmap file trước khi xử lýDurability mà không mất speed
OS page cacheOS tự động sync mmap data xuống diskApplication không cần quản lý disk I/O
CPU Pinning & NUMA Awareness
Kỹ thuậtMô tảLợi ích
CPU pinning (thread affinity)Gán 1 thread vào 1 CPU core cố địnhKhông bị migrate giữa cores, cache luôn warm
NUMA awarenessAllocate memory gần CPU core đang dùngGiảm memory access latency
IsolcpusDành riêng CPU core cho application, OS không dùngKhông bị interrupt bởi OS tasks
Disable hyperthreadingTắt SMT trên core chạy matching engineCore dedicated, không share resources
FPGA — Phần cứng chuyên dụng
Đặc điểmMô tả
FPGA (Field-Programmable Gate Array)Vi mạch có thể lập trình lại, xử lý logic trong hardware
LatencySub-microsecond (~100-500 nanoseconds)
Dùng choMarket data parsing, pre-trade risk checks, order routing
Ai dùngHFT firms (Citadel, Jump Trading, Virtu Financial)
Nhược điểmKhó lập trình (VHDL/Verilog), khó debug, đắt tiền

Aha Moment: Các tầng tối ưu (từ dễ đến khó):

  1. Thuật toán tốt (O(log n) thay vì O(n))
  2. Single-threaded design (loại bỏ lock)
  3. Lock-free data structures (Disruptor)
  4. Memory-mapped files (loại bỏ disk I/O)
  5. CPU pinning + NUMA (tối ưu hardware utilization)
  6. Kernel bypass / DPDK (loại bỏ OS overhead)
  7. 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
ModeMô tảKhi nào dùng
Hot (Primary)Đang xử lý lệnh, activeBì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 độngDisaster recovery
Failover Process
BướcHành độngThời gian
1Arbiter phát hiện primary mất heartbeat~100ms (configurable)
2Arbiter xác nhận primary thật sự chết (không phải network glitch)~200ms
3Arbiter gửi lệnh failover cho warm standby~10ms
4Warm standby hoàn thành replay các event chưa xử lý~50ms
5Warm 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:

  1. Warm standby đã replay event #1 → #999,990 (trễ 10 events so với primary)
  2. Primary chết tại event #1,000,000
  3. Warm standby tiếp tục replay #999,991 → #1,000,000 từ event log (đã được replicate)
  4. Trạng thái của warm standby giống hệt trạng thái của primary trước khi chết
  5. 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ả
TTrade date — ngày giao dịch xảy ra
T+2Settlement date — 2 ngày làm việc sau T
SettlementChuyể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
ClearingXá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âmCCP đứng giữa người mua và người bán. A mua từ CCP, CCP mua từ B
Giảm counterparty riskNếu B phá sản, CCP vẫn đảm bảo A nhận cổ phiếu
Margin requirementCCP yêu cầu ký quỹ từ cả hai bên
Default fundQuỹ 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ịch6 giờ/ngày9:00 - 15:00
Số mã chứng khoán1,500Tương đương HOSE + HNX
Số lệnh trung bình/ngày10,000,00010 triệu lệnh
Phần trăm cancel60%HFT và algo trading hủy lệnh nhiều
Peak/Average ratio5xGiờ 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 typeSố event trung bình mỗi order
Order acknowledgment1
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 report0.4

3.3 Storage Estimation — Audit Trail

Mỗi event cần lưu cho audit:

TrườngKích thước
Sequence number8 bytes
Timestamp (nanosecond)8 bytes
Event type4 bytes
Order ID16 bytes
Symbol8 bytes
Side (buy/sell)1 byte
Price8 bytes
Quantity8 bytes
Account ID16 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

MetricGiá trị
Peak orders/sec10,000 (design target)
Peak market data events/sec50,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 authenticationMỗi broker có certificate (mutual TLS) để kết nối với gateway
Session managementFIX session với logon/logout, heartbeat, sequence number
Order authorizationKiểm tra broker có quyền giao dịch mã chứng khoán này không
Account verificationKiểm tra tài khoản nhà đầu tư có hợp lệ và không bị khóa
Digital signatureMỗi lệnh có thể được ký số để chống chối từ (non-repudiation)
IP whitelistChỉ 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ứcMô 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ớpTỷ 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” ảoNhiều lệnh cùng phía, cùng tài khoản, ở nhiều mức giá, hủy đồng loạt
Wash TradingTự mua tự bán để tạo thanh khoản ảoCùng beneficial owner ở cả hai phía của giao dịch
Front RunningBroker biết lệnh lớn của khách, giao dịch trướcLệ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 raGiá tăng đột biến + volume cao bất thường + tin tức trên social media
Quote StuffingGửi hàng nghìn lệnh trong microseconds để gây nghẽn hệ thốngSố 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)

ComponentVai trò
Real-time alert enginePhát hiện pattern bất thường trong stream lệnh
Pattern matchingSo sánh hành vi giao dịch với các pattern thao túng đã biết
Cross-market surveillanceTheo dõi giao dịch trên nhiều sàn (cổ phiếu, phái sinh, trái phiếu)
Beneficial owner trackingXác định người thực sự sở hữu tài khoản để phát hiện wash trading
Post-trade analysisPhâ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ầuChi tiết
CompletenessMọi event phải được log: order, cancel, amend, trade, rejection
ImmutabilityEvent log không được sửa/xóa — append-only
TimestampsNanosecond precision, đồng bộ qua NTP/PTP
RetentionSEC Rule 17a-4: lưu 6 năm. UBCKNN: lưu 5 năm
AccessibilityCơ quan quản lý có thể truy xuất bất kỳ lúc nào
IntegrityHash chain (tương tự blockchain) để chứng minh log không bị tampering

4.4 Regulatory Compliance

Quy địnhKhu vựcYê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 IIChâu ÂuBest execution, transaction reporting, algo trading controls
Reg NMSMỹBest price execution, order protection rule
FCA MARAnhMarket abuse detection and reporting
UBCKNNViệt NamBiê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 brokerMỗi broker có quota lệnh/giây (ví dụ: 1,000 orders/sec)
Message validationReject message không đúng format FIX trước khi xử lý
Connection limitGiới hạn số kết nối đồng thời từ 1 IP
ThrottlingKhi tải cao, giảm tốc độ xử lý thay vì reject tất cả
Kill switchCó thể ngắt kết nối từ 1 broker cụ thể ngay lập tức
Network firewallChỉ 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 đoVị tríMục tiêu
Wire-to-wireTừ NIC nhận packet đến NIC gửi response< 50 microseconds
Gateway processingParse FIX message, validate, forward< 5 microseconds
Risk checkPre-trade risk engine processing< 10 microseconds
SequencerAssign sequence number, write event log< 2 microseconds
MatchingOrder matching trong matching engine< 5 microseconds
Market data publishTừ trade xảy ra đến market data gửi đi< 10 microseconds

Công cụ đo latency

Công cụMô tả
Hardware timestampingNIC 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 frameworkMỗi component ghi timestamp vào message header, tính delta tại cuối

Latency Percentiles

PercentileMục tiêuÝ nghĩa
p50< 10 microsecondsPhần nửa lệnh nhanh hơn mức này
p99< 100 microseconds99% lệnh nhanh hơn mức này
p99.9< 1 millisecondChỉ 1/1000 lệnh chậm hơn mức này
p99.99< 10 millisecondsTail 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

MetricMô tảAlert threshold
Orders/secSố lệnh xử lý mỗi giây< expected → có vấn đề
Trades/secSố giao dịch khớp mỗi giây
Order book depthSố lệnh trong order book (mỗi mã)Quá sâu → thanh khoản kém
SpreadChênh lệch bid-askSpread rộng bất thường → cảnh báo
Queue depthSố lệnh chờ xử lý trong input queue> 100 → bottleneck
Event log write latencyThời gian ghi event ra disk> 1ms → disk vấn đề

5.3 Order Rejection Rate

MetricNgưỡng bình thườngCả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 traTần suấtMô tả
Sequence gap checkLiên tục (real-time)Kiểm tra không có gap trong sequence number
Hash chain verificationMỗi 1 phútVerify hash của mỗi event liên kết với event trước
Primary-standby comparisonMỗi 1 giâySo sánh event log trên primary và standby
Checksum verificationMỗi phiênTính checksum toàn bộ event log của ngày
Replay verificationHàng tuầnReplay 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 drillTần suấtMô tả
Failover drillHàng thángTắt primary, kiểm tra warm standby lên thay trong < 1 giây
Full replay drillHàng quýReplay toàn bộ event log của 1 ngày, xác nhận kết quả khớp
Network partition drillHàng quýMô phỏng mất kết nối giữa components
Data center failoverHàng nămChuyển toàn bộ hoạt động sang data center dự phòng
Capacity testHàng quýStress test 2x-3x peak load

Runbook cho sự cố

Sự cốBước xử lý
Matching engine hang1. Gateway buffer lệnh. 2. Failover sang warm standby. 3. Thông báo broker. 4. Investigate root cause.
Event log gap detected1. Halt matching. 2. Sync event log từ standby. 3. Verify integrity. 4. Resume.
Market data delay > 1s1. Kiểm tra MDP process. 2. Kiểm tra network. 3. Restart MDP nếu cần. 4. Subscribers request snapshot.
Risk engine chậm1. 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 broker1. 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ốngLatency đơn vị
Web applicationMilliseconds (100-500ms)
Database queryMilliseconds (1-50ms)
Stock exchange matchingMicroseconds (1-100us)
HFT tradingNanoseconds (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.


TuầnLiên kếtÁp dụng trong Stock Exchange
Tuan-08-Message-QueueMessage queue patternEvent 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-ObservabilityMonitoring & ObservabilityLatency monitoring microsecond precision. Matching engine metrics. Event log integrity verification. Alerting cho order rejection rate.
Case-Design-Payment-SystemPayment system designNhiề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-MillionsScaling fundamentalsStock exchange là ví dụ của vertical scaling (matching engine) kết hợp horizontal scaling (gateway, market data).
Tuan-02-Back-of-the-envelopeEstimationEstimation cho orders/sec, events/sec, storage, bandwidth — tất cả đều quan trọng để sizing hệ thống.
Tuan-14-AuthN-AuthZ-SecurityAuthentication & AuthorizationMutual TLS cho broker authentication. FIX session management. Order authorization.
Tuan-15-Data-Security-EncryptionData SecurityEncryption cho order data in transit. Audit trail integrity (hash chain).

9. Glossary — Từ điển thuật ngữ

Thuật ngữTiếng ViệtMô tả
Order BookSổ lệnhCấu trúc dữ liệu chứa tất cả lệnh chưa khớp
BidGiá muaGiá người mua sẵn sàng trả
Ask (Offer)Giá bánGiá người bán sẵn sàng bán
SpreadĐộ chênh lệchChênh lệch giữa best ask và best bid
Matching EngineMáy khớp lệnhComponent khớp lệnh mua và bán
Limit OrderLệnh giới hạnLệnh chỉ định giá cụ thể
Market OrderLệnh thị trườngLệnh khớp ngay tại giá tốt nhất
FillKhớp lệnhLệnh được khớp thành công
Partial FillKhớp một phầnLệnh chỉ khớp được một phần số lượng
FIFOVào trước ra trướcFirst In, First Out — lệnh đến trước xử lý trước
FIX ProtocolGiao thức FIXFinancial Information eXchange — giao thức chuẩn của ngành tài chính
SequencerBộ sắp xếpComponent gán số thứ tự cho event
Event SourcingLưu sự kiệnPattern lưu chuỗi event thay vì trạng thái
T+2Thanh toán sau 2 ngàySettlement xảy ra 2 ngày làm việc sau giao dịch
NettingBù trừGộp các giao dịch lại để giảm số lượng thanh toán
CCPĐối tác trung tâmCentral Counterparty — trung gian đảm bảo thanh toán
DPDKData Plane Development Kit — xử lý network trong user space
FPGAField-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 TradingGiao dịch giảTự mua tự bán để tạo thanh khoản ảo
Circuit BreakerCơ chế ngắt mạchTạm ngừng giao dịch khi thị trường biến động quá mạnh
PTPPrecision Time Protocol — đồng bộ thời gian microsecond precision
Kernel BypassBỏ qua kernelXử lý network packet trong user space, không qua OS kernel
DisruptorLMAX pattern — lock-free ring buffer cho inter-thread communication
SBESimple 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

  1. Single-threaded matching engine nhanh hơn multi-threaded — Loại bỏ lock, context switching, cache invalidation. Determinism là bonus.

  2. 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.

  3. 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.

  4. 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.

  5. 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ạnhStock ExchangePayment SystemChat SystemKey-Value Store
Ưu tiên #1LatencyCorrectnessAvailabilityScalability
ConsistencyStrict orderingStrongEventualTunable
ScalingVertical (matching)HorizontalHorizontalHorizontal
Data modelEvent log + in-memoryTransaction logMessage logKey-value pairs
LatencyMicrosecondsMillisecondsMillisecondsMilliseconds
Failure handlingDeterministic replayRetry + idempotencyLast-write-winsQuorum read/write

Câu hỏi tự kiểm tra cho Hieu

  1. Tại sao matching engine phải single-threaded? Multi-threaded có lợi gì?
  2. Event sourcing giúp gì cho disaster recovery? Giải thích bước replay.
  3. Tại sao dùng multicast UDP cho market data thay vì TCP?
  4. Spoofing là gì? Hệ thống phát hiện bằng cách nào?
  5. Tại sao không dùng Kafka giữa gateway và matching engine?
  6. T+2 settlement là gì? Netting giúp gì?
  7. Kernel bypass (DPDK) giải quyết vấn đề gì? Tại sao cần nó?
  8. Nếu matching engine chết, warm standby lên thay như thế nào? Mất bao lâu?
  9. L1, L2, L3 market data khác nhau thế nào? Ai dùng loại nào?
  10. 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