← Terug naar artikelen
May 26, 2026
5 min leestijd

DeepLOB: Deep Learning op Limit Order Books

DeepLOB: Deep Learning op Limit Order Books
#deep-learning
#order-book
#LOB
#microstructure
#HFT
#market-making
📖
Part 5 of 6 · Collection
Order Book & Market Microstructure

Het limit order book is de centrale datastructuur van moderne elektronische markten. Elk bod, elke aanbieding, elke annulering — het leeft allemaal in het LOB. Al decennialang ontwerpen kwantitatieve onderzoekers handmatig features uit deze data: order-imbalance-ratio's, gewogen mid-prices, wachtrijpositie-signalen. DeepLOB koos een andere weg — het leert deze features direct uit ruwe orderboek-snapshots met behulp van een hybride CNN-LSTM-architectuur.

Gepubliceerd door Zhang, Zohren en Roberts aan het Oxford-Man Institute in 2019, toonde DeepLOB aan dat één diep netwerk, end-to-end getraind op ruwe boekdata, concurrerend is met — of beter dan — sterke klassieke baselines bij het voorspellen van mid-price-bewegingen. We moeten echter precies zijn over het bewijs. De benchmark-dataset FI-2010 is degene die de meeste papers aanhalen, maar de auteurs zelf noemen die onvoldoende — "veel te kort, ondersampled en afkomstig van een minder liquide markt" — en presenteren een tweede, veel grotere evaluatie op data van de London Stock Exchange (LSE) als hun sterkere resultaat. Die LSE-studie beslaat ongeveer een jaar op vijf trainingsaandelen en vijf testaandelen, in de orde van 100M+ samples, en toont transfer learning aan: een model getraind op één set aandelen voorspelt nog steeds op ongeziene aandelen. Dat transferresultaat, niet het FI-2010-klassement, is de echte kern van het paper. Deze post ontleedt de architectuur, de wiskunde, de echte cijfers en een werkende PyTorch-herimplementatie.

Het Limit Order Book als Datastructuur

Isometrisch beeld van een limit order book: gestapelde bied- en laatvolumeniveaus vormen een dieptetensor rond de mid-price

Een limit order book onderhoudt twee gesorteerde lijsten — bids (koopopdrachten) en asks (verkoopopdrachten) — elk georganiseerd per prijsniveau. Op elk niveau registreert het boek de prijs en het geaggregeerde volume van openstaande orders.

Voor een orderboek met LL niveaus is een enkele snapshot op tijdstip tt een vector van 4L4L waarden:

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}]

waarbij piap_i^{a} en viav_i^{a} de laatprijs en het volume op niveau ii zijn, en pibp_i^{b}, vibv_i^{b} de overeenkomstige biedzijde-waarden. Niveau 1 bevat het beste bod en de beste laatprijs (de BBO); hun verschil is de bid-ask spread.

In de benchmark-dataset FI-2010 is L=10L = 10, wat 40 features per snapshot geeft. FI-2010 is opgebouwd uit ongeveer 4 miljoen ruwe limit-order-berichten van vijf Finse aandelen op NASDAQ Nordic (Kesko, Outokumpu, Sampo, Rautaruukki, Wartsila) over tien opeenvolgende handelsdagen, 1-14 juni 2010. Cruciaal is dat DeepLOB niet traint op 4M snapshots: de benchmark ondersamplet die berichten (elke 10 events) tot een genormaliseerde representatie van 394.337 samples. Die ondersamplede, genormaliseerde vorm is wat het model daadwerkelijk ziet — één van de redenen waarom de auteurs waarschuwen om FI-2010 niet als het laatste woord te behandelen.

Mid-Price en Zijn Bewegingen

De mid-price op tijdstip tt is het gemiddelde van het beste bod en de beste laatprijs:

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

DeepLOB voorspelt de richting van de mid-price-beweging over een toekomstige horizon van kk events. Het label is een gladgestreken rendement, maar wees u ervan bewust dat er twee smoothing-conventies in de literatuur bestaan, en die zijn niet uitwisselbaar:

  • Toekomstig gemiddelde versus huidige prijs (Tsantekidis et al.): vergelijkt de gemiddelde mid-price over de volgende kk events met de ruwe huidige mid-price.
  • Toekomstig gemiddelde versus vorig gemiddelde (Ntakaris et al., het canonieke FI-2010-label, ook degene die DeepLOB gebruikte voor zijn LSE-data): zowel de referentie als het doel worden gladgestreken.
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}

Het smoothen van de referentie (niet alleen de toekomst) vermindert de labelruis van enkele-tick-fluctuaties aanzienlijk, wat de reden is waarom de vorm met vorig/volgend gemiddelde de voorkeur krijgt. Welke u ook gebruikt, het continue rendement wordt gediscretiseerd in drie klassen — omhoog, omlaag, stationair — met vaste drempels ±α\pm\alpha:

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

De FI-2010-dataset biedt labels voor vijf voorspellingshorizonten: k{10,20,30,50,100}k \in \{10, 20, 30, 50, 100\} events.

Traditionele LOB-Features

Vóór deep learning vertrouwden onderzoekers op handmatig ontworpen features. Deze begrijpen is essentieel, omdat DeepLOB doelbewust zo gestructureerd is dat het op veel ervan lijkt — de architectuur is gemotiveerd door deze grootheden, zelfs als het ze niet exact reproduceert.

Order Book Imbalance (OBI)

Het meest gebruikte microstructuursignaal. Aan de top van het boek:

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

Meer biedvolume ten opzichte van laatvolume suggereert opwaartse druk. Multi-niveau imbalance aggregeert over LL niveaus:

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}}

Volume-Gewogen Mid-Price

De standaard mid-price weegt beide zijden gelijk. De volume-gewogen mid-price (ook wel gewogen mid, of VAMP genoemd) helt naar de zwaardere kant van het boek. Als we de top-of-book imbalance schrijven als I=v1b/(v1b+v1a)I = v_1^{b}/(v_1^{b}+v_1^{a}), is dit het imbalance-gewogen gemiddelde van de twee prijzen:

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}}

Let op de kruisweging: de laatprijs wordt gewogen door biedvolume en vice versa. Wanneer biedvolume domineert (I1I \to 1), verschuift de gewogen mid richting de laatprijs — wat de verwachting weerspiegelt dat een onevenwichtig boek zich zal oplossen door naar de zwaardere kant te bewegen.

Een woord van waarschuwing over de naamgeving. Deze statische, volume-gewogen mid is niet Stoikovs microprice. Stoikovs micro-price (2018) is een martingale-gecorrigeerde schatter Mt+g(I,S)M_t + g(I, S) verkregen uit een recursieve correctie voor hoe spread en imbalance evolueren — zijn hele punt is dat de naïeve gewogen mid vertekend is en geen martingale is. Het zijn twee afzonderlijke objecten; we gebruiken hier de gewogen mid vanwege de eenvoud, niet als vervanging voor de microprice.

Depth Imbalance

Meet de vorm van het boek voorbij de 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

Spread en Relatieve Spread

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}}

Deze features zijn krachtig maar beperkt. Ze vereisen domeinkennis om te ontwerpen, vangen geen complexe niet-lineaire interacties op en negeren temporele dynamiek tussen opeenvolgende snapshots. DeepLOB pakt alle drie beperkingen aan.

DeepLOB-Architectuur

DeepLOB-architectuur: een ruwe orderboek-tensor stroomt door convolutionele lagen, een inception-module en een LSTM

De architectuur bestaat uit drie blokken, elk met een eigen rol:

  1. Convolutioneel blok — extraheert ruimtelijke features uit de orderboek-snapshot
  2. Inception-module — vangt multi-schaal temporele patronen
  3. LSTM-blok — modelleert sequentiële afhankelijkheden over de tijd

De input voor het netwerk is een tensor met vorm (T,L×4)(T, L \times 4), waarbij TT het terugkijkvenster is (100 tijdstappen in het paper) en L×4=40L \times 4 = 40 voor 10 niveaus van prijs en volume aan beide zijden.

Blok 1: Convolutionele Feature-Extractie

De eerste convolutionele lagen opereren langs de feature-dimensie om interacties tussen prijs en volume op elk niveau te leren.

Laag 1: Een convolutie met filtergrootte 1×21 \times 2 en stride (1,2)(1, 2) toegepast op elk prijs-volumepaar. Dit vat elk (pi,vi)(p_i, v_i)-paar samen in één enkele feature — analoog aan het berekenen van een "prijs-volume-interactie" op elk niveau. De breedte krimpt van 40 naar 20.

Laag 2: Een 1×21 \times 2, stride (1,2)(1, 2) convolutie die opereert over gematchte bied-laat-paren op hetzelfde niveau. Dit vangt spread- en imbalance-informatie per niveau. De breedte krimpt van 20 naar 10.

Laag 3: Een 1×101 \times 10 convolutie die integreert over alle 10 niveaus, waarbij de diepte-dimensie wordt gereduceerd tot breedte 1. Dit produceert één geaggregeerde feature per tijdstap die het volledige diepteprofiel codeert.

Elke 1×21 \times 2 / 1×101 \times 10 stap wordt gevolgd door twee (4,1)(4, 1) convoluties langs de tijdas, elk met LeakyReLU-activatie en batch-normalisatie. Merk op dat deze (4,1)(4, 1) convoluties met padding=(1, 0) de tijdsdimensie niet behouden: elke verkort deze met één stap (Tout=Tin1T_{\text{out}} = T_{\text{in}} - 1). Over de zes van dergelijke lagen in blokken 1-3 krimpt de tijdas van 100 naar 94 vóór de inception-module — iets wat de vorm-commentaren in de code hieronder expliciet maken.

Het ontwerp vermijdt bewust grote ruimtelijke filters. De 1×21 \times 2 filters respecteren de natuurlijke paringsstructuur van LOB-data — prijs met volume, bod met laatprijs. Deze inductieve bias is cruciaal: een 3×33 \times 3 filter zou aangrenzende niveaus en zijden mengen op een manier die geen financiële betekenis heeft.

Blok 2: Inception-Module

Nadat het convolutionele blok de 40 features reduceert tot een compacte ruimtelijke representatie (breedte 1), opereert de inception-module langs de tijdsdimensie om patronen op meerdere temporele schalen tegelijkertijd te vangen.

De DeepLOB-inception-module heeft vier parallelle paden, elk producerend 32 kanalen, allemaal geconcateneerd:

  • 1×11 \times 1 bottleneck \to 3×13 \times 1 temporele conv: kortetermijn temporele patronen (3 tijdstappen)
  • 1×11 \times 1 bottleneck \to 5×15 \times 1 temporele conv: middellangetermijn temporele patronen (5 tijdstappen)
  • 3×13 \times 1 max-pool \to 1×11 \times 1 conv: een pooling-pad dat een gladgestreken, ondersamplede weergave behoudt
  • (de 1×11 \times 1 reducties fungeren als point-in-time bottlenecks vóór de temporele convoluties)

Elk temporeel pad gebruikt symmetrische padding zodat de tijdsdimensie behouden blijft binnen het inception-blok (in tegenstelling tot de conv-blokken hierboven). De outputs worden geconcateneerd langs de kanaaldimensie, wat het netwerk toegang geeft tot multi-schaal temporele features tegelijk.

Dit is geïnspireerd op de GoogLeNet-inception-architectuur, maar aangepast voor 1D-temporele data in plaats van 2D-afbeeldingen. Het kerninzicht is dat LOB-dynamiek op meerdere tijdschalen opereert: tick-voor-tick-ruis, kortetermijn-momentum en langere-horizon mean reversion bestaan allemaal naast elkaar.

De PyTorch-herimplementatie hieronder presenteert een vereenvoudigde drie-padvariant (de twee temporele convs plus een 1×11\times1 pad) zodat de code leesbaar blijft; het max-pool-pad wordt weggelaten. We benoemen dit expliciet omdat het een afwijking van het paper is, niet de originele module.

Blok 3: LSTM-Sequentiemodellering

De output van de inception-module wordt gevoed naar een LSTM-laag die de volledige sequentie van lengte TT verwerkt. De LSTM vangt langeafstands-temporele afhankelijkheden die convoluties alleen zouden missen — regimewisselingen, tijd-van-de-dag-effecten en evoluerende marktomstandigheden.

De uiteindelijke verborgen toestand van de LSTM wordt doorgegeven aan een volledig verbonden laag met softmax-activatie om een kansverdeling over de drie klassen te produceren (omhoog, omlaag, stationair).

Verliesfunctie

Het model wordt getraind met categorische cross-entropy:

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})

waarbij yi,cy_{i,c} het one-hot gecodeerde ware label is en y^i,c\hat{y}_{i,c} de voorspelde kans voor klasse cc.

PyTorch-Herimplementatie

De originele DeepLOB is gebouwd in Keras op de TensorFlow-backend; de repository van de auteurs (zcakhaa) voegde later een PyTorch-poort toe. De code hieronder is onze eigen illustratieve PyTorch-herimplementatie, niet de officiële. Het wijkt op twee manieren van het paper af, die we inline markeren: het laat het inception-max-pool-pad weg (drie paden in plaats van vier) en het schaalt de kanaalaantallen op. Het paper gebruikt 16 filters door de conv-blokken heen en 32 filters per inception-pad (LSTM-input 3×32=963 \times 32 = 96), wat een compact model van ongeveer 60k parameters oplevert. Het fragment hieronder gebruikt 32 conv- / 64 inception-filters (LSTM-input 192) — intern consistent en uitvoerbaar, maar zwaarder dan het origineel. Om het paper te evenaren, stelt u de conv-kanalen in op 16, de inception-kanalen op 32, en 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

Data-Voorverwerking

De FI-2010-dataset wordt geleverd met z-score-normalisatie. Voor live orderboekdata moet u op vergelijkbare wijze normaliseren:

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
        )

Trainingslus

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")

Belangrijke trainingshyperparameters uit het originele paper:

  • Optimizer: Adam met ϵ=1\epsilon = 1 (ongewoon groot — helpt updates te stabiliseren op ruizige financiële data)
  • Leersnelheid: 0.01
  • Batchgrootte: 32
  • Terugkijkvenster: T=100T = 100 tijdstappen

We volgen gewogen F1 in plaats van accuracy bij validatie, omdat de drie klassen onevenwichtig zijn — en, zoals de volgende sectie laat zien, is F1 de metriek die het originele paper precies om die reden benadrukt.

Wat DeepLOB Gebouwd Is om te Vangen

Microstructuursignalen die DeepLOB leert: order-flow imbalance, wachtrijdynamiek en volume-gewogen mid-price-druk

Een aantrekkelijk aspect van DeepLOB is hoe nauw de blokken gemotiveerd zijn door de handmatig ontworpen features hierboven. We moeten hier voorzichtig zijn: het paper presenteert deze als ontwerpintuïties, niet als bewezen eigenschappen van het getrainde netwerk. Het publiceert geen filtervisualisaties, activatie-imbalance-correlaties, of een analyse van waar de LSTM op let. Lees het volgende dus als architecturale intentie, niet als gemeten bevindingen.

Eerste convolutionele blok. De 1×21 \times 2 filters koppelen prijs aan volume op elk niveau. Structureel is dit dezelfde operatie die een volume-gewogen prijs vormt — het paper motiveert de eerste laag door op te merken dat de feature maps een microprice-achtige grootheid vormen. Het netwerk is gebouwd om iets in die familie te herstellen; we beweren niet dat het aantoonbaar een specifieke schatter herontdekt.

Tweede convolutionele blok. Door te opereren over bied-laat-paren zijn deze filters gepositioneerd om imbalance-achtige features op meerdere niveaus te leren — wederom door constructie eerder dan door aangetoonde correlatie.

Inception-module. De multi-schaal temporele convoluties zijn bedoeld om momentum op verschillende frequenties te vangen: het 1×11\times1 pad reageert op point-in-time-structuur, het 3×13\times1 pad op kortetermijntrends, het 5×15\times1 pad op iets langere trends, en (in de volledige module) het max-pool-pad op een gladgestreken weergave.

LSTM-laag. De recurrente component voegt geheugen toe van langeafstandsafhankelijkheden die de convoluties niet kunnen representeren — regimewisselingen en tijd-van-de-dag-effecten. We vermijden sterkere beweringen (zoals dat de LSTM een "adaptieve aandacht" implementeert die recente data herweegt in volatiele regimes); het paper toont dit niet aan.

Prestaties op FI-2010

Het originele paper evalueert twee protocollen. Setup 1 gebruikt de eerdere normalisatie-split van de dataset; Setup 2 is de deep-learning-setup waarmee het meeste vervolgwerk zich vergelijkt. Omdat FI-2010 klasse-onevenwichtig is, benadrukt het paper F1, niet accuracy, en rapporteert precision/recall-gebaseerde cijfers voor de niet-neurale baselines (SVM, MLP). Hier zijn de echte Setup 2-cijfers (F1, %):

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

Bij k=10k = 10 rapporteert DeepLOB ook 84,47% accuracy naast zijn F1 van 83,40. Setup 2 rapporteert alleen k{10,20,50}k \in \{10, 20, 50\} — er is hier geen k=100k = 100 rij. (Setup 1 rapporteert wel k{10,50,100}k \in \{10, 50, 100\}, met DeepLOB F1 van respectievelijk 77,66 / 74,96 / 76,58.) De streepjes hierboven markeren baseline-cellen die het paper in dit protocol niet bij die horizonten tabelleert.

Verschillende observaties vallen op:

  1. DeepLOB verslaat de baselines met een ruime marge op de korte horizon. Bij k=10k = 10 ligt zijn F1 (83,40) ruim boven LSTM (66,33), CNN-I (55,21), MLP (48,27) en SVM (35,88). De kwalitatieve volgorde — diepe recurrent-convolutionele modellen boven simpele CNN's, MLP's en SVM's — is de robuuste conclusie.
  2. De horizontrend is niet-monotoon, niet "langer is makkelijker". Over Setup 2 gaat DeepLOB's F1 83,4072,8280,3583,40 \to 72,82 \to 80,35 terwijl kk van 10205010 \to 20 \to 50 gaat — een U-vormig patroon, met k=20k=20 het moeilijkst, geen nette opwaartse mars. Of een horizon "makkelijker" is, hangt sterk af van de setup, de drempel α\alpha en het label-smoothingschema; we moeten hier geen universele wet in lezen.
  3. DeepLOB domineert niet elke klassieke methode in elke cel. Tegen sterke baselines zoals C(TABL) versmallen de marges op sommige horizonten, dus "presteert overal beter dan alle klassieke benaderingen" overdrijft de zaak. De verdedigbare bewering is dat het competitief tot best is, en duidelijk het beste op de korte horizon.

En nogmaals: het paper zelf vindt dat FI-2010 te klein en te ondersampled is om de vraag te beslechten. Het sterkere bewijs is de LSE-studie, waar DeepLOB traint op één set aandelen en nog steeds voorspelt op ongeziene aandelen — een transfer-learning-resultaat dat FI-2010 niet kan tonen.

LOBFrame en de Replicatiecrisis

Divergerende in-sample en out-of-sample curves die de replicatiecrisis van deep learning op orderboeken illustreren

Het LOBFrame-benchmarkframework (Briola, Bartolucci, Aste, 2024) biedt een ontnuchterend aanvullend perspectief op deze resultaten. Hoewel de architectuur van DeepLOB solide is, belicht het framework verschillende belangrijke voorbehouden:

Microstructuur-afhankelijkheid. De modelprestaties variëren aanzienlijk tussen aandelen met verschillende microstructurele kenmerken. Liquide aandelen met grote tick zijn gemakkelijker te voorspellen dan illiquide instrumenten met kleine tick. Een model dat er sterk uitziet op één aandeel kan aanzienlijk verslechteren op een ander.

Voorspelling versus winst. Hoge classificatienauwkeurigheid vertaalt zich niet automatisch naar handelswinst. LOBFrame benadrukt metrieken die gekoppeld zijn aan of een voorspelde beweging groot genoeg is om de bid-ask spread te overwinnen. Een model moet vaak genoeg gelijk hebben, en bij voldoende grote bewegingen, om de spread te overwinnen — en voor aandelen met een krappe spread ligt die lat hoog.

Labelgevoeligheid. De keuze van drempel α\alpha en horizon kk beïnvloedt zowel de gerapporteerde nauwkeurigheid als de praktische waarde van voorspellingen dramatisch. Labels die te krap zijn produceren ruizige doelen; labels die te breed zijn produceren triviaal "nauwkeurige" maar nutteloze voorspellingen.

Voorbij DeepLOB: Het Huidige Landschap

Constellatie van moderne orderboek-voorspellingsbenaderingen die om het originele DeepLOB-model draaien

Sinds de publicatie van DeepLOB zijn er verschillende uitbreidingen verschenen:

Transformers voor limit order books (Wallbridge, 2020). Vaak losjes aangeduid als "TransLOB" in latere literatuur, is het model zelf een causale/gedilateerde convolutionele feature-extractor gevolgd door gemaskeerde multi-head self-attention — niet simpelweg DeepLOB met de LSTM vervangen door een Transformer; het hergebruikt de inception+conv-stack niet. Het rapporteerde destijds een nieuwe state-of-the-art voor FI-2010.

Crypto-domeinvarianten. Verschillende groepen passen DeepLOB-achtige architecturen toe op cryptomarkten, waar orderboekdynamiek substantieel verschilt van aandelen — bredere spreads, 24/7-handel en een andere deelnemersmix die allemaal herscholing vereisen.

Attention-versterkte varianten. Een onderzoekslijn voegt multi-head attention toe tussen de inception- en LSTM-blokken, waardoor het netwerk zich kan concentreren op de niveaus of tijdstappen die het meest informatief zijn voor de huidige voorspelling.

LOB-Bench (Nagy, Frey, Li, Sarkar, Vyetrenko, Zohren, Calinescu, Foerster, 2025). Deze benchmark verlegt de aandacht van voorspelling naar generatie, en scoort hoe realistisch generatieve modellen van orderboekdata zijn — relevant voor backtesting en agent-training in plaats van directionele voorspelling.

Praktische Overwegingen voor Productie

Als u overweegt een DeepLOB-achtig model in een live handelssysteem in te zetten, doen zich verschillende technische overwegingen voor:

Latentie

Het model moet voorspellingen produceren binnen het latentiebudget van uw handelssysteem. Voor HFT-systemen die op sub-milliseconde-latenties opereren, kan zelfs een geoptimaliseerde PyTorch-inferentie te traag zijn. Opties zijn onder meer:

  • ONNX-export met TensorRT-optimalisatie
  • Kwantisatie naar INT8
  • FPGA-implementatie voor de meest latentiegevoelige toepassingen

Datapijplijn

Het model verwacht genormaliseerde, uitgelijnde orderboek-snapshots met een vaste frequentie. In de praktijk komen LOB-updates asynchroon binnen. U heeft nodig:

  • Een snapshot-reconstructie-engine die de huidige boektoestand bijhoudt
  • Een sampler met vaste frequentie die de (T,40)(T, 40) input-tensor produceert
  • Online normalisatie met behulp van rollende statistieken

Feature-Stabiliteit

Z-score-normalisatieparameters berekend op trainingsdata verschuiven mettertijd. Prijzen veranderen, volatiliteitsregimes verschuiven, marktstructuur evolueert. Een productiesysteem heeft nodig:

  • Rollende normalisatievensters (bijv. dagelijks statistieken herberekenen)
  • Regimedetectie om herscholing van het model te triggeren
  • Monitoring van verschuiving in de inputdistributie

Overfitting op Microstructuur

LOB-patronen zijn platform-specifiek en instrument-specifiek. Een model getraind op NASDAQ-aandelen zal niet werken op Binance BTC/USDT zonder herscholing. Zelfs binnen hetzelfde platform kunnen modellen overfitten op:

  • Tickgrootte-regime (grote-tick versus kleine-tick-aandelen)
  • Tijd-van-de-dag-patronen (openingsveiling, lunchdip, sluitingskruising)
  • Market-maker-gedrag dat in de loop van de tijd verandert

Conclusie

DeepLOB vertegenwoordigt een schone, goed gemotiveerde toepassing van deep learning op marktmicrostructuur. De architectuur met drie blokken — CNN voor ruimtelijke features, inception voor multi-schaal temporele patronen, LSTM voor sequentiële afhankelijkheden — mapt natuurlijk op de structuur van limit-order-book-data.

Het kerninzicht is niet alleen dat een diep model handmatig ontworpen features kan evenaren of overtreffen, maar dat de inductieve biases van de architectuur — de 1×21 \times 2 filters, het inception-parallellisme, het recurrente geheugen — echte domeinkennis coderen over hoe orderboeken werken. Dit is geen generiek diep netwerk dat op financiële data wordt losgelaten; het is een architectuur ontworpen rond de specifieke geometrie van het limit order book. En het meest overtuigende bewijs ervan is niet het FI-2010-klassement dat de auteurs zelf wantrouwen, maar het transfer-learning-resultaat op een jaar LSE-data.

Voor praktijkmensen is DeepLOB een sterke baseline en een nuttige bouwsteen. Voor onderzoekers toont het aan dat doordacht architectuurontwerp — niet alleen modelschaal — de prestaties op gestructureerde financiële data drijft. Vergeet niet de echte cijfers aan te halen, het labelschema in de gaten te houden, en classificatienauwkeurigheid te behandelen als iets dat nog een heel eind verwijderd is van handelswinst.


Referenties

  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

Disclaimer: De informatie in dit artikel is uitsluitend bedoeld voor educatieve en informatieve doeleinden en vormt geen financieel, beleggings- of handelsadvies. Het handelen in cryptovaluta brengt een aanzienlijk risico op verlies met zich mee.

Auteurs

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

Blijf de markt voor

Abonneer je op onze nieuwsbrief voor exclusieve AI-handelsinzichten, marktanalyses en platformupdates.

We respecteren je privacy. Je kunt je op elk moment afmelden.