← Zurück zu den Artikeln
May 26, 2026
5 min read

DeepLOB: Deep Learning für Limit-Orderbücher

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

Das Limit-Orderbuch ist die zentrale Datenstruktur moderner elektronischer Märkte. Jedes Kaufgebot, jedes Verkaufsangebot, jede Stornierung — alles findet sich im LOB. Jahrzehntelang haben quantitative Forscher Merkmale aus diesen Daten von Hand konstruiert: Order-Imbalance-Verhältnisse, gewichtete Mid-Prices, Signale zur Warteschlangenposition. DeepLOB geht einen anderen Weg — es lernt diese Merkmale direkt aus rohen Orderbuch-Snapshots mithilfe einer hybriden CNN-LSTM-Architektur.

Veröffentlicht von Zhang, Zohren und Roberts am Oxford-Man Institute im Jahr 2019, zeigte DeepLOB, dass ein einziges, end-to-end auf rohen Buchdaten trainiertes tiefes Netz bei der Vorhersage von Mid-Price-Bewegungen mit starken klassischen Baselines konkurrieren kann oder sie übertrifft. Man sollte hier jedoch bei der Beweislage präzise bleiben. Der Benchmark-Datensatz FI-2010 ist derjenige, den die meisten Papers zitieren, aber die Autoren selbst bezeichnen ihn als unzureichend — "viel zu kurz, downgesampelt und aus einem weniger liquiden Markt entnommen" — und präsentieren als ihr stärkeres Ergebnis eine zweite, deutlich größere Auswertung anhand von Daten der London Stock Exchange (LSE). Diese LSE-Studie umfasst etwa ein Jahr mit fünf Trainingsaktien und fünf Testaktien, in der Größenordnung von über 100 Millionen Samples, und demonstriert Transfer Learning: Ein Modell, das auf einer Gruppe von Aktien trainiert wurde, sagt weiterhin für ungesehene Aktien korrekt voraus. Dieses Transfer-Ergebnis, nicht die FI-2010-Rangliste, ist die eigentliche Kernaussage des Papers. Dieser Beitrag seziert die Architektur, die Mathematik, die echten Zahlen und eine funktionierende PyTorch-Reimplementierung.

Das Limit-Orderbuch als Datenstruktur

Isometrische Ansicht eines Limit-Orderbuchs: gestapelte Bid- und Ask-Volumen-Level, die einen Tiefen-Tensor um den Mid-Price bilden

Ein Limit-Orderbuch führt zwei sortierte Listen — Bids (Kaufaufträge) und Asks (Verkaufsaufträge) —, jeweils nach Preisniveau organisiert. Auf jedem Level verzeichnet das Buch den Preis und das aggregierte Volumen der wartenden Orders.

Für ein Orderbuch mit LL Levels ist ein einzelner Snapshot zum Zeitpunkt tt ein Vektor von 4L4L Werten:

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

wobei piap_i^{a} und viav_i^{a} Ask-Preis und -Volumen auf Level ii sind, und pibp_i^{b}, vibv_i^{b} die entsprechenden Bid-seitigen Werte. Level 1 enthält das beste Bid und das beste Ask (das BBO); ihre Differenz ist der Bid-Ask-Spread.

Im FI-2010-Benchmark-Datensatz gilt L=10L = 10, was 40 Merkmale pro Snapshot ergibt. FI-2010 basiert auf rund 4 Millionen rohen Limit-Order-Nachrichten von fünf finnischen Aktien an der NASDAQ Nordic (Kesko, Outokumpu, Sampo, Rautaruukki, Wartsila) über zehn aufeinanderfolgende Handelstage, 1.-14. Juni 2010. Entscheidend ist: DeepLOB trainiert nicht auf 4 Millionen Snapshots — der Benchmark downsampelt diese Nachrichten (jedes 10. Ereignis) zu einer normalisierten Darstellung von 394.337 Samples. Diese downgesampelte, normalisierte Form ist das, was das Modell tatsächlich sieht — einer der Gründe, warum die Autoren davor warnen, FI-2010 als das letzte Wort zu betrachten.

Mid-Price und seine Bewegungen

Der Mid-Price zum Zeitpunkt tt ist der Durchschnitt aus bestem Bid und bestem Ask:

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

DeepLOB sagt die Richtung der Mid-Price-Bewegung über einen zukünftigen Horizont von kk Ereignissen voraus. Das Label ist eine geglättete Rendite, aber Vorsicht: In der Literatur gibt es zwei Glättungskonventionen, die nicht austauschbar sind:

  • Zukunftsmittel vs. aktueller Preis (Tsantekidis et al.): vergleicht den durchschnittlichen Mid-Price über die nächsten kk Ereignisse mit dem rohen aktuellen Mid-Price.
  • Zukunftsmittel vs. vorheriges Mittel (Ntakaris et al., das kanonische FI-2010-Label, auch das von DeepLOB für seine LSE-Daten verwendete): sowohl Referenz als auch Ziel werden geglättet.
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}

Das Glätten der Referenz (nicht nur der Zukunft) reduziert Label-Rauschen durch einzelne Tick-Schwankungen erheblich, weshalb die Vorher-/Nachher-Mittel-Form bevorzugt wird. Unabhängig davon, welche Variante man verwendet, wird die kontinuierliche Rendite mittels fester Schwellenwerte ±α\pm\alpha in drei Klassen diskretisiert — auf, ab, stationär:

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

Der FI-2010-Datensatz liefert Labels für fünf Vorhersagehorizonte: k{10,20,30,50,100}k \in \{10, 20, 30, 50, 100\} Ereignisse.

Traditionelle LOB-Merkmale

Vor dem Deep Learning verließen sich Forscher auf handgefertigte Merkmale. Diese zu verstehen ist wesentlich, denn DeepLOB ist bewusst so strukturiert, dass es vielen davon ähnelt — seine Architektur ist von diesen Größen motiviert, auch wenn sie nicht exakt reproduziert werden.

Order Book Imbalance (OBI)

Das am weitesten verbreitete Mikrostruktursignal. An der Spitze des Buchs:

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

Mehr Bid-Volumen relativ zum Ask-Volumen deutet auf Aufwärtsdruck hin. Multi-Level-Imbalance aggregiert über LL Levels:

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

Volumengewichteter Mid-Price

Der Standard-Mid-Price gewichtet beide Seiten gleich. Der volumengewichtete Mid-Price (auch gewichteter Mid oder VAMP genannt) neigt sich zur schwereren Seite des Buchs. Schreibt man die Top-of-Book-Imbalance als I=v1b/(v1b+v1a)I = v_1^{b}/(v_1^{b}+v_1^{a}), ist er der imbalance-gewichtete Durchschnitt der beiden Preise:

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

Man beachte die Kreuzgewichtung: Der Ask-Preis wird mit dem Bid-Volumen gewichtet und umgekehrt. Wenn das Bid-Volumen dominiert (I1I \to 1), verschiebt sich der gewichtete Mid in Richtung Ask — dies spiegelt die Erwartung wider, dass sich ein unausgeglichenes Buch auflöst, indem es sich zur schwereren Seite hin bewegt.

Ein Wort der Vorsicht zur Namensgebung. Dieser statische, volumengewichtete Mid ist nicht Stoikovs Microprice. Stoikovs Micro-Price (2018) ist ein martingal-korrigierter Schätzer Mt+g(I,S)M_t + g(I, S), der aus einer rekursiven Korrektur dafür gewonnen wird, wie sich Spread und Imbalance entwickeln — sein gesamter Punkt ist, dass der naive gewichtete Mid verzerrt ist und kein Martingal darstellt. Es handelt sich um zwei verschiedene Objekte; wir verwenden hier den gewichteten Mid wegen seiner Einfachheit, nicht als Ersatz für den Microprice.

Depth Imbalance

Misst die Form des Buchs jenseits des 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 und relativer 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}}

Diese Merkmale sind mächtig, aber begrenzt. Sie erfordern Fachwissen zur Konstruktion, erfassen keine komplexen nichtlinearen Interaktionen und ignorieren die zeitliche Dynamik über aufeinanderfolgende Snapshots hinweg. DeepLOB adressiert alle drei Einschränkungen.

DeepLOB-Architektur

DeepLOB-Architektur: ein roher Orderbuch-Tensor fließt durch Convolutional Layers, ein Inception-Modul und ein LSTM

Die Architektur besteht aus drei Blöcken, jeder mit einer eigenen Rolle:

  1. Convolutional-Block — extrahiert räumliche Merkmale aus dem Orderbuch-Snapshot
  2. Inception-Modul — erfasst zeitliche Muster über mehrere Skalen hinweg
  3. LSTM-Block — modelliert sequenzielle Abhängigkeiten über die Zeit

Der Input des Netzes ist ein Tensor der Form (T,L×4)(T, L \times 4), wobei TT das Lookback-Fenster ist (100 Zeitschritte im Paper) und L×4=40L \times 4 = 40 für 10 Levels von Preis und Volumen auf beiden Seiten.

Block 1: Convolutional-Merkmalsextraktion

Die ersten Convolutional-Schichten operieren entlang der Merkmalsdimension, um Interaktionen zwischen Preis und Volumen auf jedem Level zu lernen.

Layer 1: Eine Convolution mit Filtergröße 1×21 \times 2 und Stride (1,2)(1, 2), angewendet auf jedes Preis-Volumen-Paar. Dies fasst jedes (pi,vi)(p_i, v_i)-Paar zu einem einzigen Merkmal zusammen — analog zur Berechnung einer "Preis-Volumen-Interaktion" auf jedem Level. Die Breite schrumpft von 40 auf 20.

Layer 2: Eine 1×21 \times 2-Convolution mit Stride (1,2)(1, 2), die über zusammengehörige Bid-Ask-Paare auf demselben Level operiert. Dies erfasst Spread- und Imbalance-Informationen pro Level. Die Breite schrumpft von 20 auf 10.

Layer 3: Eine 1×101 \times 10-Convolution, die über alle 10 Levels integriert und die Tiefendimension auf Breite 1 reduziert. Dies erzeugt ein einziges aggregiertes Merkmal pro Zeitschritt, das das gesamte Tiefenprofil kodiert.

Auf jeden 1×21 \times 2-/1×101 \times 10-Schritt folgen zwei (4,1)(4, 1)-Convolutions entlang der Zeitachse, jeweils mit LeakyReLU-Aktivierung und Batch-Normalisierung. Zu beachten ist, dass diese (4,1)(4, 1)-Convolutions mit padding=(1, 0) die Zeitdimension nicht erhalten: Jede verkürzt sie um einen Schritt (Tout=Tin1T_{\text{out}} = T_{\text{in}} - 1). Über die sechs solchen Schichten in den Blöcken 1-3 schrumpft die Zeitachse von 100 auf 94, bevor sie das Inception-Modul erreicht — etwas, das die Shape-Kommentare im Code unten explizit machen.

Das Design vermeidet bewusst große räumliche Filter. Die 1×21 \times 2-Filter respektieren die natürliche Paarungsstruktur von LOB-Daten — Preis mit Volumen, Bid mit Ask. Dieser induktive Bias ist entscheidend: Ein 3×33 \times 3-Filter würde benachbarte Levels und Seiten auf eine Weise vermischen, die finanziell keinen Sinn ergibt.

Block 2: Inception-Modul

Nachdem der Convolutional-Block die 40 Merkmale auf eine kompakte räumliche Repräsentation (Breite 1) reduziert hat, operiert das Inception-Modul entlang der Zeitdimension, um Muster auf mehreren zeitlichen Skalen gleichzeitig zu erfassen.

Das DeepLOB-Inception-Modul hat vier parallele Pfade, von denen jeder 32 Kanäle erzeugt, die alle konkateniert werden:

  • 1×11 \times 1-Bottleneck \to 3×13 \times 1-zeitliche Convolution: kurzfristige zeitliche Muster (3 Zeitschritte)
  • 1×11 \times 1-Bottleneck \to 5×15 \times 1-zeitliche Convolution: mittelfristige zeitliche Muster (5 Zeitschritte)
  • 3×13 \times 1-Max-Pool \to 1×11 \times 1-Convolution: ein Pooling-Pfad, der eine geglättete, downgesampelte Sicht beibehält
  • (die 1×11 \times 1-Reduktionen fungieren als punktuelle Bottlenecks vor den zeitlichen Convolutions)

Jeder zeitliche Pfad verwendet symmetrisches Padding, sodass die Zeitdimension innerhalb des Inception-Blocks erhalten bleibt (anders als in den Conv-Blöcken oben). Die Outputs werden entlang der Kanaldimension konkateniert, was dem Netz Zugriff auf mehrskalige zeitliche Merkmale gleichzeitig gibt.

Dies ist von der GoogLeNet-Inception-Architektur inspiriert, jedoch für 1D-zeitliche Daten statt 2D-Bilder adaptiert. Die zentrale Einsicht ist, dass LOB-Dynamik auf mehreren Zeitskalen operiert: Tick-für-Tick-Rauschen, kurzfristiges Momentum und längerfristige Mean-Reversion existieren alle gleichzeitig.

Die untenstehende PyTorch-Reimplementierung präsentiert eine vereinfachte Drei-Pfad-Variante (die beiden zeitlichen Convolutions plus ein 1×11\times1-Pfad), damit der Code lesbar bleibt; sie lässt den Max-Pool-Pfad weg. Wir weisen explizit darauf hin, weil es sich um eine Abweichung vom Paper handelt, nicht um das ursprüngliche Modul.

Block 3: LSTM-Sequenzmodellierung

Der Output des Inception-Moduls wird in eine LSTM-Schicht eingespeist, die die gesamte Sequenz der Länge TT verarbeitet. Das LSTM erfasst langfristige zeitliche Abhängigkeiten, die reine Convolutions nicht erfassen würden — Regimewechsel, Tageszeiteffekte und sich entwickelnde Marktbedingungen.

Der finale versteckte Zustand des LSTM wird durch eine vollständig verbundene Schicht mit Softmax-Aktivierung geführt, um eine Wahrscheinlichkeitsverteilung über die drei Klassen (auf, ab, stationär) zu erzeugen.

Verlustfunktion

Das Modell wird mit kategorischer Kreuzentropie trainiert:

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

wobei yi,cy_{i,c} das One-Hot-kodierte wahre Label und y^i,c\hat{y}_{i,c} die vorhergesagte Wahrscheinlichkeit für Klasse cc ist.

PyTorch-Reimplementierung

Das originale DeepLOB wurde in Keras auf dem TensorFlow-Backend gebaut; das Repository der Autoren (zcakhaa) fügte später einen PyTorch-Port hinzu. Der untenstehende Code ist unsere eigene illustrative PyTorch-Reimplementierung, nicht die offizielle. Sie weicht vom Paper in zwei Punkten ab, die wir inline kennzeichnen: Sie lässt den Inception-Max-Pool-Pfad weg (drei Pfade statt vier) und skaliert die Kanalzahlen hoch. Das Paper verwendet durchgehend 16 Filter in den Conv-Blöcken und 32 Filter pro Inception-Pfad (LSTM-Input 3×32=963 \times 32 = 96), was ein kompaktes Modell mit rund 60k Parametern ergibt. Der untenstehende Ausschnitt verwendet 32 Conv-/64 Inception-Filter (LSTM-Input 192) — intern konsistent und lauffähig, aber schwerer als das Original. Um das Paper nachzubilden, setze die Conv-Kanäle auf 16, die Inception-Kanäle auf 32 und 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

Datenvorverarbeitung

Der FI-2010-Datensatz wird z-score-normalisiert ausgeliefert. Für Live-Orderbuchdaten musst du ähnlich normalisieren:

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
        )

Trainingsschleife

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

Wichtige Trainings-Hyperparameter aus dem Originalpaper:

  • Optimizer: Adam mit ϵ=1\epsilon = 1 (ungewöhnlich groß — hilft, Updates bei verrauschten Finanzdaten zu stabilisieren)
  • Lernrate: 0,01
  • Batch-Größe: 32
  • Lookback-Fenster: T=100T = 100 Zeitschritte

Wir verfolgen den gewichteten F1-Score statt der Accuracy bei der Validierung, weil die drei Klassen unausgeglichen sind — und, wie der nächste Abschnitt zeigt, ist F1 genau aus diesem Grund die Metrik, die das Originalpaper betont.

Wofür DeepLOB gebaut ist

Mikrostruktursignale, die DeepLOB lernt: Order-Flow-Imbalance, Warteschlangendynamik und volumengewichteter Mid-Price-Druck

Ein reizvoller Aspekt von DeepLOB ist, wie eng seine Blöcke durch die oben genannten handgefertigten Merkmale motiviert sind. Hier sollten wir vorsichtig sein: Das Paper präsentiert dies als Design-Intuitionen, nicht als bewiesene Eigenschaften des trainierten Netzes. Es veröffentlicht keine Filter-Visualisierungen, keine Korrelationen zwischen Aktivierung und Imbalance und keine Analyse dessen, worauf das LSTM achtet. Lies das Folgende also als architektonische Absicht, nicht als gemessene Befunde.

Erster Convolutional-Block. Die 1×21 \times 2-Filter paaren Preis mit Volumen auf jedem Level. Strukturell ist dies dieselbe Operation, die einen volumengewichteten Preis bildet — das Paper motiviert die erste Schicht damit, dass ihre Feature-Maps eine microprice-ähnliche Größe bilden. Das Netz ist dafür gebaut, etwas aus dieser Familie zu erlernen; wir behaupten nicht, dass es nachweislich irgendeinen spezifischen Schätzer rekonstruiert.

Zweiter Convolutional-Block. Indem sie über Bid-Ask-Paare operieren, sind diese Filter darauf ausgelegt, imbalance-ähnliche Merkmale auf mehreren Levels zu lernen — wiederum durch Konstruktion, nicht durch nachgewiesene Korrelation.

Inception-Modul. Die mehrskaligen zeitlichen Convolutions sollen Momentum bei unterschiedlichen Frequenzen erfassen: der 1×11\times1-Pfad reagiert auf punktuelle Struktur, der 3×13\times1-Pfad auf kurzfristige Trends, der 5×15\times1-Pfad auf etwas längerfristige, und (im vollständigen Modul) der Max-Pool-Pfad auf eine geglättete Sicht.

LSTM-Schicht. Die rekurrente Komponente fügt ein Gedächtnis für langfristige Abhängigkeiten hinzu, das die Convolutions allein nicht darstellen können — Regimewechsel und Tageszeiteffekte. Wir vermeiden stärkere Behauptungen (etwa dass das LSTM eine "adaptive Aufmerksamkeit" implementiert, die aktuelle Daten in volatilen Regimen umgewichtet); das Paper zeigt dies nicht.

Performance auf FI-2010

Das Originalpaper evaluiert zwei Protokolle. Setup 1 verwendet den früheren Normalisierungs-Split des Datensatzes; Setup 2 ist das Deep-Learning-Setup, mit dem die meisten nachfolgenden Arbeiten vergleichen. Da FI-2010 klassenunausgeglichen ist, betont das Paper F1 statt Accuracy und berichtet Precision-/Recall-basierte Zahlen für die nicht-neuronalen Baselines (SVM, MLP). Hier sind die echten Setup-2-Zahlen (F1, %):

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

Bei k=10k = 10 berichtet DeepLOB außerdem 84,47% Accuracy neben seinem F1 von 83,40. Setup 2 berichtet nur k{10,20,50}k \in \{10, 20, 50\} — es gibt hier keine k=100k = 100-Zeile. (Setup 1 berichtet k{10,50,100}k \in \{10, 50, 100\}, mit DeepLOB-F1-Werten von 77,66 / 74,96 / 76,58.) Die Bindestriche oben markieren Baseline-Zellen, die das Paper in diesem Protokoll bei diesen Horizonten nicht tabelliert.

Mehrere Beobachtungen fallen auf:

  1. DeepLOB schlägt die Baselines beim kurzen Horizont mit großem Abstand. Bei k=10k = 10 liegt sein F1 (83,40) deutlich über LSTM (66,33), CNN-I (55,21), MLP (48,27) und SVM (35,88). Die qualitative Reihenfolge — tiefe rekurrente Convolutional-Modelle vor reinen CNNs, MLPs und SVMs — ist die robuste Erkenntnis.
  2. Der Horizont-Trend ist nicht-monoton, nicht "länger ist einfacher". Über Setup 2 hinweg geht DeepLOBs F1 83,4072,8280,3583,40 \to 72,82 \to 80,35, während kk von 10205010 \to 20 \to 50 geht — ein U-förmiges Muster, bei dem k=20k=20 am schwierigsten ist, kein sauberer Aufwärtstrend. Ob ein Horizont "einfacher" ist, hängt stark vom Setup, dem Schwellenwert α\alpha und dem Label-Glättungsschema ab; wir sollten diesen Zahlen kein universelles Gesetz entnehmen.
  3. DeepLOB dominiert nicht jede klassische Methode in jeder Zelle. Gegen starke Baselines wie C(TABL) verengen sich die Margen bei manchen Horizonten, sodass "übertrifft alle klassischen Ansätze überall" die Sache übertreibt. Die vertretbare Behauptung ist, dass es konkurrenzfähig bis best ist, und beim kurzen Horizont klar am besten.

Und nochmals: Die eigene Sicht des Papers ist, dass FI-2010 zu klein und zu downgesampelt ist, um die Frage zu klären. Der stärkere Beweis ist die LSE-Studie, bei der DeepLOB auf einer Gruppe von Aktien trainiert und weiterhin auf zurückgehaltenen Aktien vorhersagt — ein Transfer-Learning-Ergebnis, das FI-2010 nicht zeigen kann.

LOBFrame und die Replikationskrise

Auseinanderlaufende In-Sample- und Out-of-Sample-Kurven, die die Replikationskrise im Deep Learning für Orderbücher illustrieren

Das LOBFrame-Benchmark-Framework (Briola, Bartolucci, Aste, 2024) liefert eine ernüchternde Ergänzung zu diesen Ergebnissen. Während DeepLOBs Architektur solide ist, hebt das Framework mehrere wichtige Vorbehalte hervor:

Mikrostrukturabhängigkeit. Die Modellperformance variiert erheblich über Aktien mit unterschiedlichen mikrostrukturellen Merkmalen. Liquide Aktien mit großen Tick-Größen sind leichter vorherzusagen als illiquide Instrumente mit kleinen Tick-Größen. Ein Modell, das bei einer Aktie stark aussieht, kann bei einer anderen deutlich abfallen.

Vorhersage vs. Profit. Hohe Klassifikationsgenauigkeit übersetzt sich nicht automatisch in Handelsprofit. LOBFrame betont Metriken, die daran gebunden sind, ob eine vorhergesagte Bewegung groß genug ist, um den Bid-Ask-Spread zu überwinden. Ein Modell muss oft genug recht haben, und bei ausreichend großen Bewegungen, um den Spread zu überwinden — und für Aktien mit engem Spread liegt diese Hürde hoch.

Label-Sensitivität. Die Wahl des Schwellenwerts α\alpha und des Horizonts kk beeinflusst sowohl die berichtete Genauigkeit als auch den praktischen Wert der Vorhersagen dramatisch. Zu enge Labels erzeugen verrauschte Ziele; zu weite Labels erzeugen trivial "genaue", aber nutzlose Vorhersagen.

Jenseits von DeepLOB: Die aktuelle Landschaft

Konstellation moderner Orderbuch-Vorhersageansätze, die das ursprüngliche DeepLOB-Modell umkreisen

Seit der Veröffentlichung von DeepLOB sind mehrere Erweiterungen erschienen:

Transformer für Limit-Orderbücher (Wallbridge, 2020). In der späteren Literatur oft locker als "TransLOB" bezeichnet, ist das Modell selbst ein kausaler/dilatierter Convolutional-Feature-Extraktor gefolgt von maskierter Multi-Head-Self-Attention — nicht einfach DeepLOB mit dem gegen einen Transformer getauschten LSTM; es verwendet nicht den Inception+Conv-Stack wieder. Es berichtete einen für seine Zeit neuen FI-2010-State-of-the-Art.

Crypto-Domain-Varianten. Mehrere Gruppen wenden DeepLOB-artige Architekturen auf Kryptowährungsmärkte an, wo sich die Orderbuch-Dynamik erheblich von Aktien unterscheidet — breitere Spreads, 24/7-Handel und eine andere Teilnehmermischung, die alle ein Neutraining erfordern.

Attention-erweiterte Varianten. Eine Reihe von Arbeiten fügt Multi-Head-Attention zwischen den Inception- und LSTM-Blöcken ein, sodass das Netz sich auf die Levels oder Zeitschritte konzentrieren kann, die für die aktuelle Vorhersage am informativsten sind.

LOB-Bench (Nagy, Frey, Li, Sarkar, Vyetrenko, Zohren, Calinescu, Foerster, 2025). Dieser Benchmark lenkt die Aufmerksamkeit von der Vorhersage auf die Generierung und bewertet, wie realistisch generative Modelle von Orderbuchdaten sind — relevant für Backtesting und Agenten-Training statt für gerichtete Vorhersagen.

Praktische Überlegungen für den Produktionseinsatz

Wenn du erwägst, ein DeepLOB-artiges Modell in einem Live-Handelssystem einzusetzen, ergeben sich mehrere technische Fragestellungen:

Latenz

Das Modell muss Vorhersagen innerhalb des Latenzbudgets deines Handelssystems liefern. Für HFT-Systeme, die mit Sub-Millisekunden-Latenzen arbeiten, kann selbst ein optimierter PyTorch-Inferenzlauf zu langsam sein. Optionen umfassen:

  • ONNX-Export mit TensorRT-Optimierung
  • Quantisierung zu INT8
  • FPGA-Deployment für die latenzkritischsten Anwendungen

Datenpipeline

Das Modell erwartet normalisierte, ausgerichtete Orderbuch-Snapshots mit fester Frequenz. In der Praxis treffen LOB-Updates asynchron ein. Du benötigst:

  • Eine Snapshot-Rekonstruktions-Engine, die den aktuellen Buchzustand pflegt
  • Einen Sampler mit fester Frequenz, der den (T,40)(T, 40)-Input-Tensor erzeugt
  • Online-Normalisierung mit rollierenden Statistiken

Merkmalsstabilität

Auf Trainingsdaten berechnete Z-Score-Normalisierungsparameter driften mit der Zeit. Preise ändern sich, Volatilitätsregime verschieben sich, die Marktstruktur entwickelt sich weiter. Ein Produktionssystem benötigt:

  • Rollierende Normalisierungsfenster (z.B. tägliche Neuberechnung der Statistiken)
  • Regime-Erkennung, um Modell-Retraining auszulösen
  • Überwachung auf Verschiebungen in der Input-Verteilung

Überanpassung an Mikrostruktur

LOB-Muster sind venue- und instrumentenspezifisch. Ein auf NASDAQ-Aktien trainiertes Modell wird ohne Neutraining nicht auf Binance BTC/USDT funktionieren. Selbst innerhalb derselben Venue können Modelle überanpassen an:

  • Tick-Size-Regime (Aktien mit großer vs. kleiner Tick-Größe)
  • Tageszeit-Muster (Eröffnungsauktion, Mittagsflaute, Schlussauktion)
  • Market-Maker-Verhalten, das sich über die Zeit ändert

Fazit

DeepLOB stellt eine saubere, gut motivierte Anwendung von Deep Learning auf Marktmikrostruktur dar. Seine Drei-Block-Architektur — CNN für räumliche Merkmale, Inception für mehrskalige zeitliche Muster, LSTM für sequenzielle Abhängigkeiten — bildet sich natürlich auf die Struktur von Limit-Orderbuchdaten ab.

Die zentrale Einsicht ist nicht nur, dass ein tiefes Modell handgefertigte Merkmale erreichen oder übertreffen kann, sondern dass die induktiven Biases der Architektur — die 1×21 \times 2-Filter, der Inception-Parallelismus, das rekurrente Gedächtnis — echtes Domänenwissen darüber kodieren, wie Orderbücher funktionieren. Dies ist kein generisches tiefes Netz, das auf Finanzdaten geworfen wurde; es ist eine Architektur, die um die spezifische Geometrie des Limit-Orderbuchs herum entworfen wurde. Und der überzeugendste Beleg dafür ist nicht die FI-2010-Rangliste, der die Autoren selbst misstrauen, sondern das Transfer-Learning-Ergebnis auf einem Jahr von LSE-Daten.

Für Praktiker ist DeepLOB eine starke Baseline und ein nützlicher Baustein. Für Forscher zeigt es, dass durchdachtes Architekturdesign — nicht nur Modellgröße — die Performance bei strukturierten Finanzdaten antreibt. Denk nur daran, die echten Zahlen zu zitieren, auf das Label-Schema zu achten und Klassifikationsgenauigkeit als weit entfernt von Handelsprofit zu behandeln.


Referenzen

  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

blog.disclaimer

Authors

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

Dem Markt einen Schritt voraus

Abonniere unseren Newsletter für exklusive KI-Trading-Einblicke, Marktanalysen und Plattform-Updates.

Wir respektieren deine Privatsphäre. Jederzeit abbestellbar.