← Quay lại danh sách bài viết
May 26, 2026
5 phút đọc

DeepLOB: Học sâu trên sổ lệnh giới hạn

DeepLOB: Học sâu trên sổ lệnh giới hạn
#deep-learning
#order-book
#LOB
#microstructure
#HFT
#market-making
📖
Part 5 of 6 · Collection
Order Book & Market Microstructure

Sổ lệnh giới hạn là cấu trúc dữ liệu trung tâm của các thị trường điện tử hiện đại. Mỗi lệnh mua, mỗi lệnh bán, mỗi lần hủy lệnh — tất cả đều nằm trong LOB. Trong nhiều thập kỷ, các nhà nghiên cứu định lượng đã thiết kế thủ công các đặc trưng từ dữ liệu này: tỷ lệ mất cân bằng lệnh, giá trung bình có trọng số, tín hiệu vị trí trong hàng đợi. DeepLOB đi theo một hướng khác — nó học các đặc trưng này trực tiếp từ các ảnh chụp sổ lệnh thô bằng kiến trúc lai CNN-LSTM.

Được Zhang, Zohren và Roberts công bố tại Oxford-Man Institute vào năm 2019, DeepLOB cho thấy rằng một mạng học sâu duy nhất, được huấn luyện đầu-cuối trên dữ liệu sổ lệnh thô, có khả năng cạnh tranh hoặc tốt hơn so với các phương pháp cơ sở cổ điển mạnh trong việc dự đoán biến động giá trung bình. Tuy nhiên, chúng ta cần chính xác về bằng chứng. Bộ dữ liệu chuẩn FI-2010 là bộ được hầu hết các bài báo trích dẫn, nhưng chính các tác giả gọi nó là chưa đủ — "quá ngắn, bị lấy mẫu xuống và lấy từ một thị trường kém thanh khoản hơn" — và trình bày một đánh giá thứ hai, lớn hơn nhiều, trên dữ liệu của Sở Giao dịch Chứng khoán London (LSE) như là kết quả mạnh hơn của họ. Nghiên cứu LSE đó kéo dài khoảng một năm với năm cổ phiếu huấn luyện và năm cổ phiếu kiểm tra, theo bậc 100 triệu+ mẫu, và chứng minh khả năng học chuyển giao: một mô hình được huấn luyện trên một tập cổ phiếu vẫn dự đoán được trên các cổ phiếu chưa từng thấy. Kết quả chuyển giao đó, chứ không phải bảng xếp hạng FI-2010, mới là điểm nhấn thực sự của bài báo. Bài viết này mổ xẻ kiến trúc, toán học, các con số thực tế và một bản tái hiện PyTorch hoạt động được.

Sổ lệnh giới hạn như một cấu trúc dữ liệu

Góc nhìn đẳng cự của sổ lệnh giới hạn: các mức khối lượng mua và bán xếp chồng tạo thành một tensor độ sâu quanh giá trung bình

Một sổ lệnh giới hạn duy trì hai danh sách đã sắp xếp — lệnh mua (buy orders) và lệnh bán (sell orders) — mỗi danh sách được tổ chức theo mức giá. Ở mỗi mức, sổ lệnh ghi lại giá và tổng khối lượng các lệnh đang chờ.

Với một sổ lệnh có LL mức, một ảnh chụp duy nhất tại thời điểm tt là một vector gồm 4L4L giá trị:

xt=[p1a,v1a,p1b,v1b,p2a,v2a,p2b,v2b,,pLa,vLa,pLb,vLb]\mathbf{x}_t = [p_1^{a}, v_1^{a}, p_1^{b}, v_1^{b}, p_2^{a}, v_2^{a}, p_2^{b}, v_2^{b}, \ldots, p_L^{a}, v_L^{a}, p_L^{b}, v_L^{b}]

trong đó piap_i^{a}viav_i^{a} là giá bán và khối lượng bán ở mức ii, còn pibp_i^{b}, vibv_i^{b} là các giá trị tương ứng phía mua. Mức 1 chứa giá mua tốt nhất và giá bán tốt nhất (BBO); chênh lệch giữa chúng là chênh lệch mua-bán (bid-ask spread).

Trong bộ dữ liệu chuẩn FI-2010, L=10L = 10, cho 40 đặc trưng mỗi ảnh chụp. FI-2010 được xây dựng từ khoảng 4 triệu thông điệp lệnh giới hạn thô của năm cổ phiếu Phần Lan trên NASDAQ Nordic (Kesko, Outokumpu, Sampo, Rautaruukki, Wartsila) trong mười ngày giao dịch liên tiếp, từ 1-14 tháng 6 năm 2010. Điều quan trọng là DeepLOB không huấn luyện trên 4 triệu ảnh chụp: bộ chuẩn lấy mẫu xuống các thông điệp đó (mỗi 10 sự kiện) thành một biểu diễn chuẩn hóa gồm 394.337 mẫu. Dạng đã lấy mẫu xuống, đã chuẩn hóa đó mới là thứ mà mô hình thực sự nhìn thấy — một trong những lý do khiến các tác giả cảnh báo không nên coi FI-2010 là lời cuối cùng.

Giá trung bình và các biến động của nó

Giá trung bình tại thời điểm tt là trung bình của giá mua tốt nhất và giá bán tốt nhất:

ptmid=p1a(t)+p1b(t)2p_t^{mid} = \frac{p_1^{a}(t) + p_1^{b}(t)}{2}

DeepLOB dự đoán hướng biến động của giá trung bình trong một khoảng tương lai kk sự kiện. Nhãn là một lợi suất được làm mượt, nhưng cần lưu ý rằng có hai quy ước làm mượt trong tài liệu, và chúng không thể thay thế cho nhau:

  • Trung bình tương lai so với giá hiện tại (Tsantekidis và cộng sự): so sánh giá trung bình trung bình trong kk sự kiện tiếp theo với giá trung bình hiện tại thô.
  • Trung bình tương lai so với trung bình trước đó (Ntakaris và cộng sự, nhãn FI-2010 chuẩn mực, cũng là nhãn mà DeepLOB sử dụng cho dữ liệu LSE của mình): cả mốc tham chiếu lẫn mục tiêu đều được làm mượt.
lt(k)=m+(t)m(t)m(t),m(t)=1ki=0k1ptimid,m+(t)=1ki=1kpt+imidl_t(k) = \frac{m_+(t) - m_-(t)}{m_-(t)}, \qquad m_-(t) = \frac{1}{k}\sum_{i=0}^{k-1} p_{t-i}^{mid}, \quad m_+(t) = \frac{1}{k}\sum_{i=1}^{k} p_{t+i}^{mid}

Việc làm mượt mốc tham chiếu (chứ không chỉ tương lai) giảm đáng kể nhiễu nhãn từ những dao động một-tick đơn lẻ, đó là lý do dạng trung-bình-trước/trung-bình-sau được ưa chuộng hơn. Dù bạn dùng dạng nào, lợi suất liên tục được rời rạc hóa thành ba lớp — lên, xuống, đứng yên — bằng các ngưỡng cố định ±α\pm\alpha:

yt={1if lt(k)>α0if lt(k)α1if lt(k)<αy_t = \begin{cases} 1 & \text{if } l_t(k) > \alpha \\ 0 & \text{if } |l_t(k)| \leq \alpha \\ -1 & \text{if } l_t(k) < -\alpha \end{cases}

Bộ dữ liệu FI-2010 cung cấp nhãn cho năm khoảng dự đoán: k{10,20,30,50,100}k \in \{10, 20, 30, 50, 100\} sự kiện.

Các đặc trưng LOB truyền thống

Trước khi có học sâu, các nhà nghiên cứu dựa vào các đặc trưng thiết kế thủ công. Hiểu các đặc trưng này là điều thiết yếu, bởi vì DeepLOB được cấu trúc một cách có chủ đích để giống nhiều trong số chúng — kiến trúc của nó lấy cảm hứng từ các đại lượng này ngay cả khi nó không tái tạo chúng một cách chính xác.

Mất cân bằng sổ lệnh (OBI)

Tín hiệu vi cấu trúc được sử dụng rộng rãi nhất. Ở đỉnh sổ lệnh:

OBI1=v1bv1av1b+v1a\text{OBI}_1 = \frac{v_1^{b} - v_1^{a}}{v_1^{b} + v_1^{a}}

Khối lượng mua nhiều hơn so với khối lượng bán gợi ý áp lực tăng. Mất cân bằng đa mức tổng hợp qua LL mức:

OBIL=i=1Lvibi=1Lviai=1Lvib+i=1Lvia\text{OBI}_L = \frac{\sum_{i=1}^{L} v_i^{b} - \sum_{i=1}^{L} v_i^{a}}{\sum_{i=1}^{L} v_i^{b} + \sum_{i=1}^{L} v_i^{a}}

Giá trung bình có trọng số khối lượng

Giá trung bình tiêu chuẩn cân nhắc cả hai bên ngang nhau. Giá trung bình có trọng số khối lượng (còn gọi là weighted mid, hay VAMP) nghiêng về phía nặng hơn của sổ lệnh. Viết mất cân bằng ở đỉnh sổ lệnh là I=v1b/(v1b+v1a)I = v_1^{b}/(v_1^{b}+v_1^{a}), nó là trung bình có trọng số theo mất cân bằng của hai mức giá:

pwmid=Ip1a+(1I)p1b=v1bp1a+v1ap1bv1b+v1ap^{wmid} = I\, p_1^{a} + (1 - I)\, p_1^{b} = \frac{v_1^{b}\, p_1^{a} + v_1^{a}\, p_1^{b}}{v_1^{b} + v_1^{a}}

Lưu ý cách gán trọng số chéo: giá bán được gán trọng số bởi khối lượng mua và ngược lại. Khi khối lượng mua chiếm ưu thế (I1I \to 1), giá trung bình có trọng số dịch chuyển về phía giá bán — phản ánh kỳ vọng rằng một sổ lệnh mất cân bằng sẽ được giải quyết bằng cách di chuyển về phía nặng hơn.

Một lời cảnh báo về cách đặt tên. Giá trung bình có trọng số khối lượng tĩnh này không phải là microprice của Stoikov. Micro-price của Stoikov (2018) là một ước lượng đã điều chỉnh martingale Mt+g(I,S)M_t + g(I, S) thu được từ một hiệu chỉnh đệ quy về cách chênh lệch và mất cân bằng tiến triển — toàn bộ luận điểm của ông là giá trung bình có trọng số ngây thơ bị thiên lệch và không phải là một martingale. Chúng là hai đối tượng khác biệt; chúng tôi dùng giá trung bình có trọng số ở đây vì tính đơn giản của nó, chứ không phải để thay thế cho microprice.

Mất cân bằng độ sâu

Đo lường hình dạng của sổ lệnh vượt ra ngoài BBO:

DIi=vibviavib+via,i=1,,L\text{DI}_i = \frac{v_i^{b} - v_i^{a}}{v_i^{b} + v_i^{a}}, \quad i = 1, \ldots, L

Chênh lệch và chênh lệch tương đối

st=p1a(t)p1b(t),strel=stptmids_t = p_1^{a}(t) - p_1^{b}(t), \qquad s_t^{rel} = \frac{s_t}{p_t^{mid}}

Các đặc trưng này mạnh nhưng có giới hạn. Chúng đòi hỏi chuyên môn lĩnh vực để thiết kế, không nắm bắt được các tương tác phi tuyến phức tạp, và bỏ qua động lực học theo thời gian giữa các ảnh chụp liên tiếp. DeepLOB giải quyết cả ba hạn chế này.

Kiến trúc DeepLOB

Kiến trúc DeepLOB: một tensor sổ lệnh thô chảy qua các lớp tích chập, một mô-đun inception, và một LSTM

Kiến trúc có ba khối, mỗi khối đảm nhận một vai trò riêng:

  1. Khối tích chập — trích xuất các đặc trưng không gian từ ảnh chụp sổ lệnh
  2. Mô-đun inception — nắm bắt các mẫu thời gian đa thang đo
  3. Khối LSTM — mô hình hóa các phụ thuộc tuần tự theo thời gian

Đầu vào của mạng là một tensor có hình dạng (T,L×4)(T, L \times 4), trong đó TT là cửa sổ nhìn lại (100 bước thời gian trong bài báo) và L×4=40L \times 4 = 40 cho 10 mức giá và khối lượng ở cả hai bên.

Khối 1: Trích xuất đặc trưng tích chập

Các lớp tích chập đầu tiên hoạt động dọc theo chiều đặc trưng để học các tương tác giữa giá và khối lượng ở mỗi mức.

Lớp 1: Một phép tích chập với kích thước bộ lọc 1×21 \times 2 và bước nhảy (1,2)(1, 2) áp dụng cho từng cặp giá-khối lượng. Phép này tóm tắt mỗi cặp (pi,vi)(p_i, v_i) thành một đặc trưng duy nhất — tương tự như việc tính một "tương tác giá-khối lượng" ở mỗi mức. Chiều rộng co lại từ 40 xuống 20.

Lớp 2: Một phép tích chập 1×21 \times 2, bước nhảy (1,2)(1, 2) hoạt động qua các cặp mua-bán tương ứng tại cùng một mức. Phép này nắm bắt thông tin chênh lệch và mất cân bằng theo từng mức. Chiều rộng co lại từ 20 xuống 10.

Lớp 3: Một phép tích chập 1×101 \times 10 tích hợp qua tất cả 10 mức, thu gọn chiều độ sâu xuống chiều rộng 1. Phép này tạo ra một đặc trưng tổng hợp duy nhất cho mỗi bước thời gian, mã hóa toàn bộ hồ sơ độ sâu.

Mỗi bước 1×21 \times 2 / 1×101 \times 10 được theo sau bởi hai phép tích chập (4,1)(4, 1) dọc theo trục thời gian, mỗi phép với hàm kích hoạt LeakyReLU và chuẩn hóa theo batch. Lưu ý rằng các phép tích chập (4,1)(4, 1) với padding=(1, 0) này không bảo toàn chiều thời gian: mỗi phép rút ngắn nó đi một bước (Tout=Tin1T_{\text{out}} = T_{\text{in}} - 1). Qua sáu lớp như vậy trong các khối 1-3, trục thời gian co lại từ 100 xuống 94 trước khi vào mô-đun inception — điều mà các chú thích về hình dạng trong mã bên dưới làm rõ.

Thiết kế cố ý tránh các bộ lọc không gian lớn. Các bộ lọc 1×21 \times 2 tôn trọng cấu trúc ghép cặp tự nhiên của dữ liệu LOB — giá với khối lượng, mua với bán. Thiên kiến quy nạp này rất quan trọng: một bộ lọc 3×33 \times 3 sẽ trộn lẫn các mức và các bên kề nhau theo cách không có ý nghĩa tài chính.

Khối 2: Mô-đun inception

Sau khi khối tích chập rút gọn 40 đặc trưng thành một biểu diễn không gian gọn (chiều rộng 1), mô-đun inception hoạt động dọc theo chiều thời gian để nắm bắt các mẫu ở nhiều thang đo thời gian cùng một lúc.

Mô-đun inception của DeepLOB có bốn đường song song, mỗi đường tạo ra 32 kênh, tất cả được nối lại:

  • Cổ chai 1×11 \times 1 \to tích chập thời gian 3×13 \times 1: các mẫu thời gian ngắn hạn (3 bước thời gian)
  • Cổ chai 1×11 \times 1 \to tích chập thời gian 5×15 \times 1: các mẫu thời gian trung hạn (5 bước thời gian)
  • Max-pool 3×13 \times 1 \to tích chập 1×11 \times 1: một đường gộp giữ lại một góc nhìn đã làm mượt, đã lấy mẫu xuống
  • (các phép rút gọn 1×11 \times 1 đóng vai trò cổ chai tại-một-thời-điểm trước các phép tích chập thời gian)

Mỗi đường thời gian sử dụng đệm đối xứng để chiều thời gian được duy trì bên trong khối inception (không như các khối tích chập ở trên). Các đầu ra được nối lại dọc theo chiều kênh, cho mạng tiếp cận các đặc trưng thời gian đa thang đo cùng một lúc.

Điều này lấy cảm hứng từ kiến trúc inception của GoogLeNet, nhưng được điều chỉnh cho dữ liệu thời gian 1D thay vì hình ảnh 2D. Nhận thức then chốt là động lực học LOB hoạt động ở nhiều thang đo thời gian: nhiễu tick-by-tick, đà ngắn hạn, và hồi quy về trung bình ở khoảng dài hơn đều cùng tồn tại.

Bản tái hiện PyTorch bên dưới trình bày một biến thể ba-đường được đơn giản hóa (hai phép tích chập thời gian cộng với một đường 1×11\times1) để mã giữ được tính dễ đọc; nó bỏ qua đường max-pool. Chúng tôi nêu rõ điều này vì đây là một sai lệch so với bài báo, không phải mô-đun gốc.

Khối 3: Mô hình hóa chuỗi bằng LSTM

Đầu ra của mô-đun inception được đưa vào một lớp LSTM xử lý toàn bộ chuỗi dài TT. LSTM nắm bắt các phụ thuộc thời gian tầm xa mà chỉ riêng tích chập sẽ bỏ lỡ — thay đổi chế độ, hiệu ứng theo thời điểm trong ngày, và điều kiện thị trường đang biến đổi.

Trạng thái ẩn cuối cùng của LSTM được đưa qua một lớp kết nối đầy đủ với hàm kích hoạt softmax để tạo ra một phân bố xác suất trên ba lớp (lên, xuống, đứng yên).

Hàm mất mát

Mô hình được huấn luyện với entropy chéo phân loại:

L=1Ni=1Nc=13yi,clog(y^i,c)\mathcal{L} = -\frac{1}{N} \sum_{i=1}^{N} \sum_{c=1}^{3} y_{i,c} \log(\hat{y}_{i,c})

trong đó yi,cy_{i,c} là nhãn thật được mã hóa one-hot và y^i,c\hat{y}_{i,c} là xác suất dự đoán cho lớp cc.

Bản tái hiện PyTorch

DeepLOB gốc được xây dựng bằng Keras trên backend TensorFlow; kho mã của các tác giả (zcakhaa) sau đó đã bổ sung một bản chuyển sang PyTorch. Mã bên dưới là bản tái hiện PyTorch mang tính minh họa của riêng chúng tôi, không phải bản chính thức. Nó sai lệch so với bài báo theo hai cách mà chúng tôi đánh dấu ngay trong mã: nó bỏ qua đường max-pool của inception (ba đường thay vì bốn), và nó tăng quy mô số lượng kênh. Bài báo sử dụng 16 bộ lọc xuyên suốt các khối tích chập và 32 bộ lọc mỗi đường inception (đầu vào LSTM 3×32=963 \times 32 = 96), cho một mô hình gọn nhẹ khoảng 60 nghìn tham số. Đoạn mã bên dưới dùng 32 bộ lọc tích chập / 64 bộ lọc inception (đầu vào LSTM 192) — nhất quán nội bộ và chạy được, nhưng nặng hơn bản gốc. Để khớp với bài báo, hãy đặt số kênh tích chập thành 16, số kênh inception thành 32, và input_size=96.

import torch
import torch.nn as nn


class DeepLOB(nn.Module):
    """
    DeepLOB-style CNN-Inception-LSTM for limit order books.
    Reimplementation inspired by Zhang, Zohren, Roberts (2019),
    IEEE Transactions on Signal Processing, arXiv:1808.03668.

    NOTE: this is an illustrative reimplementation, NOT the official model.
    Differences from the paper:
      - inception has 3 paths here vs 4 in the paper (max-pool path omitted);
      - channel counts are scaled up (paper: 16 conv / 32 inception, input 96).

    Input shape: (batch_size, 1, T, 40)
        T = lookback window (e.g. 100)
        40 = 10 levels x 4 features (ask_price, ask_vol, bid_price, bid_vol)
    """

    def __init__(self, num_classes: int = 3, T: int = 100):
        super().__init__()
        self.num_classes = num_classes

        self.conv1 = nn.Sequential(
            nn.Conv2d(1, 32, kernel_size=(1, 2), stride=(1, 2)),
            nn.LeakyReLU(negative_slope=0.01),
            nn.BatchNorm2d(32),
            nn.Conv2d(32, 32, kernel_size=(4, 1), padding=(1, 0)),  # time -1
            nn.LeakyReLU(negative_slope=0.01),
            nn.BatchNorm2d(32),
            nn.Conv2d(32, 32, kernel_size=(4, 1), padding=(1, 0)),  # time -1
            nn.LeakyReLU(negative_slope=0.01),
            nn.BatchNorm2d(32),
        )

        self.conv2 = nn.Sequential(
            nn.Conv2d(32, 32, kernel_size=(1, 2), stride=(1, 2)),
            nn.LeakyReLU(negative_slope=0.01),
            nn.BatchNorm2d(32),
            nn.Conv2d(32, 32, kernel_size=(4, 1), padding=(1, 0)),  # time -1
            nn.LeakyReLU(negative_slope=0.01),
            nn.BatchNorm2d(32),
            nn.Conv2d(32, 32, kernel_size=(4, 1), padding=(1, 0)),  # time -1
            nn.LeakyReLU(negative_slope=0.01),
            nn.BatchNorm2d(32),
        )

        self.conv3 = nn.Sequential(
            nn.Conv2d(32, 32, kernel_size=(1, 10)),
            nn.LeakyReLU(negative_slope=0.01),
            nn.BatchNorm2d(32),
            nn.Conv2d(32, 32, kernel_size=(4, 1), padding=(1, 0)),  # time -1
            nn.LeakyReLU(negative_slope=0.01),
            nn.BatchNorm2d(32),
            nn.Conv2d(32, 32, kernel_size=(4, 1), padding=(1, 0)),  # time -1
            nn.LeakyReLU(negative_slope=0.01),
            nn.BatchNorm2d(32),
        )

        self.inp1 = nn.Sequential(
            nn.Conv2d(32, 64, kernel_size=(1, 1), padding=(0, 0)),
            nn.LeakyReLU(negative_slope=0.01),
            nn.BatchNorm2d(64),
        )
        self.inp2 = nn.Sequential(
            nn.Conv2d(32, 64, kernel_size=(1, 1), padding=(0, 0)),
            nn.LeakyReLU(negative_slope=0.01),
            nn.BatchNorm2d(64),
            nn.Conv2d(64, 64, kernel_size=(3, 1), padding=(1, 0)),
            nn.LeakyReLU(negative_slope=0.01),
            nn.BatchNorm2d(64),
        )
        self.inp3 = nn.Sequential(
            nn.Conv2d(32, 64, kernel_size=(1, 1), padding=(0, 0)),
            nn.LeakyReLU(negative_slope=0.01),
            nn.BatchNorm2d(64),
            nn.Conv2d(64, 64, kernel_size=(5, 1), padding=(2, 0)),
            nn.LeakyReLU(negative_slope=0.01),
            nn.BatchNorm2d(64),
        )

        self.lstm = nn.LSTM(
            input_size=192,  # 64 * 3 paths from the simplified inception
            hidden_size=64,
            num_layers=1,
            batch_first=True,
        )

        self.fc = nn.Linear(64, num_classes)

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        """
        Args:
            x: Tensor of shape (batch, 1, T, 40)
        Returns:
            Tensor of shape (batch, num_classes)
        """
        h = self.conv1(x)   # (batch, 32, T-2, 20)
        h = self.conv2(h)   # (batch, 32, T-4, 10)
        h = self.conv3(h)   # (batch, 32, T-6, 1)   e.g. T=100 -> 94

        h_inp1 = self.inp1(h)  # (batch, 64, T-6, 1)
        h_inp2 = self.inp2(h)  # (batch, 64, T-6, 1)
        h_inp3 = self.inp3(h)  # (batch, 64, T-6, 1)
        h = torch.cat([h_inp1, h_inp2, h_inp3], dim=1)  # (batch, 192, T-6, 1)

        h = h.squeeze(-1)          # (batch, 192, T-6)
        h = h.permute(0, 2, 1)     # (batch, T-6, 192)

        h, _ = self.lstm(h)
        h = h[:, -1, :]  # take last hidden state

        out = self.fc(h)
        return out

Tiền xử lý dữ liệu

Bộ dữ liệu FI-2010 được phân phối đã chuẩn hóa z-score. Đối với dữ liệu sổ lệnh trực tiếp, bạn cần chuẩn hóa tương tự:

import numpy as np
from torch.utils.data import Dataset


class LOBDataset(Dataset):
    """Dataset for limit order book snapshots."""

    def __init__(
        self,
        data: np.ndarray,
        labels: np.ndarray,
        T: int = 100,
    ):
        """
        Args:
            data: shape (num_snapshots, 40), raw LOB features
            labels: shape (num_snapshots,), class labels {0, 1, 2}
            T: lookback window length
        """
        self.data = data
        self.labels = labels
        self.T = T

        self.mean = data[:len(data) // 2].mean(axis=0)
        self.std = data[:len(data) // 2].std(axis=0)
        self.std[self.std == 0] = 1.0
        self.data = (self.data - self.mean) / self.std

    def __len__(self) -> int:
        return len(self.data) - self.T

    def __getitem__(self, idx: int):
        x = self.data[idx : idx + self.T].reshape(1, self.T, 40)
        y = self.labels[idx + self.T - 1]
        return torch.tensor(x, dtype=torch.float32), torch.tensor(
            y, dtype=torch.long
        )

Vòng lặp huấn luyện

def train_deeplob(
    model: DeepLOB,
    train_loader,
    val_loader,
    epochs: int = 50,
    lr: float = 0.01,
    device: str = "cuda",
):
    model = model.to(device)
    criterion = nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=lr, eps=1)

    best_val_f1 = 0.0
    for epoch in range(epochs):
        model.train()
        train_loss = 0.0
        for x_batch, y_batch in train_loader:
            x_batch = x_batch.to(device)
            y_batch = y_batch.to(device)

            optimizer.zero_grad()
            logits = model(x_batch)
            loss = criterion(logits, y_batch)
            loss.backward()
            optimizer.step()
            train_loss += loss.item()

        model.eval()
        all_preds, all_labels = [], []
        with torch.no_grad():
            for x_val, y_val in val_loader:
                x_val = x_val.to(device)
                preds = model(x_val).argmax(dim=1).cpu()
                all_preds.extend(preds.numpy())
                all_labels.extend(y_val.numpy())

        from sklearn.metrics import f1_score
        val_f1 = f1_score(all_labels, all_preds, average="weighted")

        print(
            f"Epoch {epoch+1}/{epochs} | "
            f"Train Loss: {train_loss/len(train_loader):.4f} | "
            f"Val F1: {val_f1:.4f}"
        )

        if val_f1 > best_val_f1:
            best_val_f1 = val_f1
            torch.save(model.state_dict(), "deeplob_best.pt")

Các siêu tham số huấn luyện then chốt từ bài báo gốc:

  • Bộ tối ưu: Adam với ϵ=1\epsilon = 1 (lớn bất thường — giúp ổn định các bước cập nhật trên dữ liệu tài chính nhiễu)
  • Tốc độ học: 0.01
  • Kích thước batch: 32
  • Cửa sổ nhìn lại: T=100T = 100 bước thời gian

Chúng tôi theo dõi F1 có trọng số thay vì độ chính xác trong quá trình kiểm định, bởi vì ba lớp bị mất cân bằng — và, như phần tiếp theo cho thấy, F1 là chỉ số mà bài báo gốc nhấn mạnh chính vì lý do đó.

DeepLOB được xây dựng để nắm bắt điều gì

Các tín hiệu vi cấu trúc mà DeepLOB học: mất cân bằng dòng lệnh, động lực hàng đợi, và áp lực giá trung bình có trọng số khối lượng

Một khía cạnh hấp dẫn của DeepLOB là các khối của nó lấy cảm hứng sát đến mức nào từ các đặc trưng thiết kế thủ công ở trên. Chúng tôi muốn thận trọng ở đây: bài báo trình bày những điều này như những trực giác thiết kế, chứ không phải như những thuộc tính đã được chứng minh của mạng đã huấn luyện. Bài báo không công bố hình ảnh hóa bộ lọc, tương quan giữa kích hoạt và mất cân bằng, hay một phân tích về điều mà LSTM tập trung vào. Vì vậy hãy đọc những điều sau như ý đồ kiến trúc, chứ không phải những phát hiện đã đo lường.

Khối tích chập đầu tiên. Các bộ lọc 1×21 \times 2 ghép cặp giá với khối lượng ở mỗi mức. Về mặt cấu trúc, đây là cùng phép toán tạo nên một giá có trọng số khối lượng — bài báo lấy cảm hứng cho lớp đầu tiên bằng cách lưu ý rằng các bản đồ đặc trưng của nó tạo thành một đại lượng giống micro-price. Mạng được xây dựng để phục hồi một thứ gì đó trong họ đó; chúng tôi không khẳng định rằng nó tái khám phá một cách chứng minh được bất kỳ ước lượng cụ thể nào.

Khối tích chập thứ hai. Bằng cách hoạt động qua các cặp mua-bán, các bộ lọc này được đặt vào vị trí để học các đặc trưng giống mất cân bằng ở nhiều mức — một lần nữa, theo thiết kế chứ không phải theo tương quan đã được chứng minh.

Mô-đun inception. Các phép tích chập thời gian đa thang đo nhằm nắm bắt đà ở các tần số khác nhau: đường 1×11\times1 phản ứng với cấu trúc tại-một-thời-điểm, đường 3×13\times1 với các xu hướng ngắn hạn, đường 5×15\times1 với các xu hướng hơi dài hơn, và (trong mô-đun đầy đủ) đường max-pool với một góc nhìn đã làm mượt.

Lớp LSTM. Thành phần hồi quy bổ sung bộ nhớ về các phụ thuộc tầm xa mà tích chập không thể biểu diễn — sự dịch chuyển chế độ và các hiệu ứng theo thời điểm trong ngày. Chúng tôi tránh những khẳng định mạnh hơn (chẳng hạn như việc LSTM triển khai một "sự chú ý thích ứng" tái-gán-trọng-số dữ liệu gần đây trong các chế độ biến động cao); bài báo không cho thấy điều này.

Hiệu năng trên FI-2010

Bài báo gốc đánh giá hai giao thức. Setup 1 sử dụng phân chia chuẩn hóa sớm hơn của bộ dữ liệu; Setup 2 là setup học sâu mà hầu hết các công trình sau này so sánh với. Vì FI-2010 mất cân bằng lớp, bài báo nhấn mạnh F1, không phải độ chính xác, và báo cáo các con số dựa trên precision/recall cho các phương pháp cơ sở phi-mạng-nơ-ron (SVM, MLP). Dưới đây là các con số Setup 2 thực tế (F1, %):

Khoảng (k) SVM MLP CNN-I LSTM DeepLOB
10 35.88 48.27 55.21 66.33 83.40
20 72.82
50 80.35

Tại k=10k = 10, DeepLOB cũng báo cáo độ chính xác 84.47% bên cạnh F1 83.40 của nó. Setup 2 chỉ báo cáo k{10,20,50}k \in \{10, 20, 50\} — không có hàng k=100k = 100 ở đây. (Setup 1 có báo cáo k{10,50,100}k \in \{10, 50, 100\}, với F1 của DeepLOB lần lượt là 77.66 / 74.96 / 76.58.) Các dấu gạch ngang ở trên đánh dấu các ô cơ sở mà bài báo không lập bảng ở những khoảng đó trong giao thức này.

Một số quan sát nổi bật:

  1. DeepLOB vượt các phương pháp cơ sở với biên độ rộng ở khoảng ngắn. Tại k=10k = 10, F1 của nó (83.40) cao hơn hẳn LSTM (66.33), CNN-I (55.21), MLP (48.27), và SVM (35.88). Thứ tự định tính — các mô hình tích chập-hồi quy sâu đứng trên các CNN thuần, MLP và SVM — là điều rút ra vững chắc.
  2. Xu hướng theo khoảng là không đơn điệu, không phải "dài hơn thì dễ hơn". Xuyên suốt Setup 2, F1 của DeepLOB đi 83.4072.8280.3583.40 \to 72.82 \to 80.35 khi kk đi 10205010 \to 20 \to 50 — một mẫu hình chữ U, với k=20k=20 là khó nhất, chứ không phải một sự đi lên gọn gàng. Việc một khoảng "dễ hơn" hay không phụ thuộc rất nhiều vào setup, ngưỡng α\alpha, và sơ đồ làm mượt nhãn; chúng ta không nên đọc ra một quy luật phổ quát từ những con số này.
  3. DeepLOB không thống trị mọi phương pháp cổ điển trên mọi ô. So với các phương pháp cơ sở mạnh như C(TABL), các biên độ thu hẹp lại ở một số khoảng, nên "vượt trội tất cả các phương pháp cổ điển ở mọi nơi" là nói quá. Khẳng định bảo vệ được là nó cạnh tranh-đến-tốt nhất, và rõ ràng tốt nhất ở khoảng ngắn.

Và một lần nữa: quan điểm của chính bài báo là FI-2010 quá nhỏ và bị lấy mẫu xuống quá nhiều để giải quyết câu hỏi. Bằng chứng mạnh hơn của nó là nghiên cứu LSE, nơi DeepLOB huấn luyện trên một tập cổ phiếu và vẫn dự đoán được trên các cổ phiếu được giữ lại — một kết quả học chuyển giao mà FI-2010 không thể cho thấy.

LOBFrame và khủng hoảng tái lập

Các đường cong trong-mẫu và ngoài-mẫu phân kỳ minh họa khủng hoảng tái lập của học sâu trên sổ lệnh

Khung chuẩn LOBFrame (Briola, Bartolucci, Aste, 2024) cung cấp một bổ sung tỉnh táo cho các kết quả này. Trong khi kiến trúc của DeepLOB là vững chắc, khung này làm nổi bật một số lưu ý quan trọng:

Phụ thuộc vào vi cấu trúc. Hiệu năng mô hình thay đổi đáng kể giữa các cổ phiếu có đặc tính vi cấu trúc khác nhau. Cổ phiếu thanh khoản cao, tick lớn dễ dự đoán hơn các công cụ kém thanh khoản, tick nhỏ. Một mô hình trông mạnh trên một cổ phiếu có thể suy giảm đáng kể trên cổ phiếu khác.

Dự đoán so với lợi nhuận. Độ chính xác phân loại cao không tự động chuyển thành lợi nhuận giao dịch. LOBFrame nhấn mạnh các chỉ số gắn với việc liệu một biến động được dự đoán có đủ lớn để vượt qua chênh lệch mua-bán hay không. Một mô hình cần đúng đủ thường xuyên, và trên các biến động đủ lớn, để vượt qua chênh lệch — và đối với các cổ phiếu chênh lệch hẹp, rào cản đó là cao.

Độ nhạy của nhãn. Việc lựa chọn ngưỡng α\alpha và khoảng kk ảnh hưởng mạnh mẽ đến cả độ chính xác được báo cáo lẫn giá trị thực tế của các dự đoán. Nhãn quá chặt tạo ra các mục tiêu nhiễu; nhãn quá rộng tạo ra các dự đoán "chính xác" một cách tầm thường nhưng vô dụng.

Vượt ra ngoài DeepLOB: Bối cảnh hiện tại

Chòm sao các phương pháp dự báo sổ lệnh hiện đại xoay quanh mô hình DeepLOB gốc

Kể từ khi DeepLOB được công bố, một số mở rộng đã xuất hiện:

Transformers cho sổ lệnh giới hạn (Wallbridge, 2020). Thường được gọi một cách lỏng lẻo là "TransLOB" trong tài liệu sau này, bản thân mô hình là một bộ trích xuất đặc trưng tích chập nhân quả/giãn nở theo sau bởi tự-chú-ý đa-đầu có mặt nạ — không đơn giản là DeepLOB với LSTM được thay bằng Transformer; nó không tái sử dụng ngăn xếp inception+tích chập. Nó đã báo cáo một trạng thái nghệ thuật FI-2010 mới ở thời điểm đó.

Các biến thể trong lĩnh vực crypto. Một số nhóm áp dụng các kiến trúc kiểu DeepLOB cho thị trường tiền mã hóa, nơi động lực học sổ lệnh khác đáng kể so với cổ phiếu — chênh lệch rộng hơn, giao dịch 24/7, và một hỗn hợp người tham gia khác biệt, tất cả đều đòi hỏi huấn luyện lại.

Các biến thể tăng cường chú ý. Một hướng công trình chèn chú ý đa-đầu giữa khối inception và khối LSTM, để mạng tập trung vào các mức hoặc bước thời gian giàu thông tin nhất cho dự đoán hiện tại.

LOB-Bench (Nagy, Frey, Li, Sarkar, Vyetrenko, Zohren, Calinescu, Foerster, 2025). Khung chuẩn này chuyển sự chú ý từ dự đoán sang sinh, chấm điểm mức độ thực tế của các mô hình sinh dữ liệu sổ lệnh — liên quan đến backtest và huấn luyện tác tử hơn là dự báo định hướng.

Các cân nhắc thực tế cho môi trường sản xuất

Nếu bạn đang cân nhắc triển khai một mô hình kiểu DeepLOB trong một hệ thống giao dịch trực tiếp, một số mối quan tâm kỹ thuật sẽ phát sinh:

Độ trễ

Mô hình phải tạo ra dự đoán trong ngân sách độ trễ của hệ thống giao dịch của bạn. Đối với các hệ thống HFT hoạt động ở độ trễ dưới mili giây, ngay cả một suy luận PyTorch đã tối ưu cũng có thể quá chậm. Các lựa chọn bao gồm:

  • Xuất ONNX với tối ưu hóa TensorRT
  • Lượng tử hóa về INT8
  • Triển khai FPGA cho các ứng dụng nhạy cảm nhất với độ trễ

Đường ống dữ liệu

Mô hình kỳ vọng các ảnh chụp sổ lệnh đã chuẩn hóa, đã căn chỉnh ở một tần số cố định. Trên thực tế, các cập nhật LOB đến không đồng bộ. Bạn cần:

  • Một bộ máy tái dựng ảnh chụp duy trì trạng thái sổ lệnh hiện tại
  • Một bộ lấy mẫu tần số cố định tạo ra tensor đầu vào (T,40)(T, 40)
  • Chuẩn hóa trực tuyến sử dụng thống kê trượt

Tính ổn định của đặc trưng

Các tham số chuẩn hóa z-score được tính trên dữ liệu huấn luyện trôi dạt theo thời gian. Giá thay đổi, các chế độ biến động dịch chuyển, cấu trúc thị trường tiến hóa. Một hệ thống sản xuất cần:

  • Các cửa sổ chuẩn hóa trượt (ví dụ, tính lại thống kê hàng ngày)
  • Phát hiện chế độ để kích hoạt việc huấn luyện lại mô hình
  • Giám sát sự dịch chuyển phân bố đầu vào

Quá khớp với vi cấu trúc

Các mẫu LOB đặc thù theo sàn và đặc thù theo công cụ. Một mô hình được huấn luyện trên cổ phiếu NASDAQ sẽ không hoạt động trên BTC/USDT của Binance nếu không huấn luyện lại. Ngay cả trong cùng một sàn, các mô hình có thể quá khớp với:

  • Chế độ kích thước tick (cổ phiếu tick-lớn so với tick-nhỏ)
  • Các mẫu theo thời điểm trong ngày (đấu giá mở cửa, lúc nghỉ trưa, phiên đóng cửa)
  • Hành vi của nhà tạo lập thị trường thay đổi theo thời gian

Kết luận

DeepLOB đại diện cho một ứng dụng sạch sẽ, có cơ sở vững chắc của học sâu vào vi cấu trúc thị trường. Kiến trúc ba khối của nó — CNN cho các đặc trưng không gian, inception cho các mẫu thời gian đa thang đo, LSTM cho các phụ thuộc tuần tự — ánh xạ một cách tự nhiên lên cấu trúc của dữ liệu sổ lệnh giới hạn.

Nhận thức then chốt không chỉ là một mô hình sâu có thể sánh ngang hoặc vượt các đặc trưng thiết kế thủ công, mà là các thiên kiến quy nạp của kiến trúc — các bộ lọc 1×21 \times 2, tính song song của inception, bộ nhớ hồi quy — mã hóa kiến thức lĩnh vực thực sự về cách sổ lệnh hoạt động. Đây không phải là một mạng học sâu chung chung ném vào dữ liệu tài chính; nó là một kiến trúc được thiết kế quanh hình học cụ thể của sổ lệnh giới hạn. Và bằng chứng thuyết phục nhất của nó không phải là bảng xếp hạng FI-2010 mà chính các tác giả không tin tưởng, mà là kết quả học chuyển giao trên một năm dữ liệu LSE.

Đối với người thực hành, DeepLOB là một phương pháp cơ sở mạnh và một khối xây dựng hữu ích. Đối với nhà nghiên cứu, nó chứng minh rằng thiết kế kiến trúc chu đáo — chứ không chỉ quy mô mô hình — thúc đẩy hiệu năng trên dữ liệu tài chính có cấu trúc. Chỉ cần nhớ trích dẫn các con số thực tế, để mắt đến sơ đồ nhãn, và coi độ chính xác phân loại còn cách rất xa so với lợi nhuận giao dịch.


Tài liệu tham khảo

  1. Zhang, Z., Zohren, S., & Roberts, S. (2019). DeepLOB: Deep Convolutional Neural Networks for Limit Order Books. IEEE Transactions on Signal Processing, 67(11), 3001-3012. arXiv:1808.03668

  2. Ntakaris, A., Magris, M., Kanniainen, J., Gabbouj, M., & Iosifidis, A. (2018). Benchmark Dataset for Mid-Price Forecasting of Limit Order Book Data with Machine Learning Methods. Journal of Forecasting, 37(8), 852-866. arXiv:1705.03233

  3. Briola, A., Bartolucci, S., & Aste, T. (2024). Deep Limit Order Book Forecasting (LOBFrame). arXiv:2403.09267

  4. Wallbridge, J. (2020). Transformers for Limit Order Books. arXiv:2003.00130

  5. Cont, R., Kukanov, A., & Stoikov, S. (2014). The Price Impact of Order Book Events. Journal of Financial Econometrics, 12(1), 47-88.

  6. Stoikov, S. (2018). The Micro-Price: A High-Frequency Estimator of Future Prices. Quantitative Finance, 18(12), 1959-1966.

  7. Nagy, P., Frey, S., Li, K., Sarkar, B., Vyetrenko, S., Zohren, S., Calinescu, A., & Foerster, J. (2025). LOB-Bench: Benchmarking Generative AI for Finance — an Application to Limit Order Book Data. ICML 2025. arXiv:2502.09172

  8. DeepLOB code (original in Keras/TensorFlow; the repository also provides a PyTorch port): github.com/zcakhaa/DeepLOB-Deep-Convolutional-Neural-Networks-for-Limit-Order-Books

Tuyên bố miễn trừ trách nhiệm: Thông tin được cung cấp trong bài viết này chỉ nhằm mục đích giáo dục và thông tin, không cấu thành lời khuyên về tài chính, đầu tư hoặc giao dịch. Giao dịch tiền mã hóa tiềm ẩn rủi ro thua lỗ đáng kể.

Tác Giả

Eugen Soloviov
Eugen Soloviov

Trading-systems engineer

Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.

Newsletter

Đi Trước Thị Trường

Đăng ký nhận bản tin của chúng tôi để có những thông tin chuyên sâu độc quyền về AI trading, phân tích thị trường và các cập nhật nền tảng.

Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất kỳ lúc nào.