Unregelmäßige Zeit in Tick-Modellen: zeitkontinuierliche Codierungen vs. einfache Positionseinbettungen
Zeitbalken sind eine willkürliche Komprimierung, die das Timing von Ereignissen zerstört, und stattdessen sollte die Aktivität Balkengrenzen definieren – dieses Argument wird vollständig, mit 17 Balkentypen und funktionierenden Generatoren, in Balkentypen und Aggregationsmethoden für den algorithmischen Handel vorgebracht. Dieser Artikel beginnt einen Schritt später mit dem Teil, den niemand gelöst hat: Selbst wenn Sie auf Tick-, Lautstärke- oder Ungleichgewichtsbalken umgestellt haben, nimmt das Modell, dem Sie sie zuführen, immer noch regelmäßige Abstände an. Ein nn.TransformerEncoder Bei einer erlernten Positionseinbettung wird Position 7 als „der siebte Slot“ behandelt, unabhängig davon, ob dieser Tick 200 Mikrosekunden oder 40 Sekunden nach Position 6 angekommen ist. Die Zwischenankunftszeit – die Sache, zu deren Beibehaltung Aktivitätsbalken erstellt wurden – wird auf der Eingabeebene wieder verworfen.
Die Frage, um die es in diesem Artikel geht, ist also eng gefasst und überprüfbar: Wie soll einem Sequenzmodell mitgeteilt werden, wann tatsächlich ein Tick stattgefunden hat, und ist die anspruchsvolle Antwort besser als die triviale?
Angenommener Hintergrund

Im Folgenden sind die Voraussetzungen aufgeführt, die alle bereits in diesem Blog behandelt wurden, und keine davon wird hier erneut abgeleitet:
- Mikrostrukturrauschen und Bid-Ask-Bounce. Das implizite Spread-Modell von Roll leitet die negative serielle Kovarianz in Tick-Renditen aus ersten Prinzipien in Preiseinheiten ab, wobei der häufige Implementierungsfehler aufgerufen wird: Bid-Ask-Spread-Modellierung mit ML.
- Funktionen des Orderbuchs. Buchungleichgewicht, gewichteter Mittelpreis, Tiefenverhältnis über die Ebenen 1–5, Buchdruck und Spread/Tick-Verhältnis werden im selben Artikel aufgeführt; Die Ableitungen des OBI und des volumengewichteten Mittelwerts – einschließlich des Vorbehalts, dass der gewichtete Mittelwert nicht Stoikovs Mikropreis ist – sind in DeepLOB: Deep Learning on Limit Order Books enthalten.
- Auftragsfluss und Handelsintensität. Handelsungleichgewicht, VPIN und Kyles Lambda sind im Artikel zur Spread-Modellierung enthalten; Inter-Order-Intervall und Hawkes-Selbsterregungsintensität als Timing-Merkmale finden Sie in Digitaler Fingerabdruck: Händleridentifikation. Das Rohe
1/dtIntensität Dieser ursprünglich vorgeschlagene Entwurf ist eine schwächere Version der dortigen Hawkes-Formulierung. - Intraday-Nichtstationarität. Das U-förmige Volumenmuster, die Ausweitung des Spreads in ruhigen Perioden und die tageszeitliche Sin/Cos-Kodierung werden in der Spread-Modellierung als Marktregimemerkmale berücksichtigt.
- Beschriftungen. Beide Glättungskonventionen (zukünftiger Mittelwert vs. aktueller Preis, zukünftiger Mittelwert vs. vorheriger Mittelwert), die Drei-Klassen-Diskretisierung mit Schwellenwert und LOBFrames Warnung bezüglich der Etikettenempfindlichkeit gegenüber Und finden Sie im DeepLOB-Artikel.
- Klassenungleichgewicht. Bei einer Totzone dominiert die FLAT-Klasse, weshalb F1 und nicht Genauigkeit die meldepflichtige Metrik ist – gleicher Artikel.
- Balkenfülldauer. Die Zeit, die ein Lautstärkebalken benötigt, um sich zu füllen, ist eine nutzbare Funktion für ein Tick-Modell. Die Generatoren, die es erzeugen, finden Sie in Balkentypen und Aggregationsmethoden.
Eines ist es wert, ausdrücklich erwähnt zu werden, da die ursprüngliche Version dieses Entwurfs einen Fehler gemacht hat: Ein Sprung von 50,0 % auf 50,5 % Richtungsgenauigkeit ist offensichtlich nicht profitabel. Der zentrale Punkt von LOBFrame, der im DeepLOB-Artikel diskutiert wird, ist, dass eine vorhergesagte Bewegung groß genug sein muss, um die Spanne zu beseitigen, bevor Genauigkeit etwas bedeutet, und das ehrliche negative Ergebnis in diesem Blog ist das, was passiert, wenn man etwas anderes annimmt.
Grundlinie

Gehen Sie von einem CNN-Inception-LSTM im DeepLOB-Stil als Basis-Encoder aus. die Architektur, die verifizierten FI-2010 Setup 2 Zahlen (F1 83,40 / Genauigkeit 84,47 bei ), die LOBFrame-Vorbehalte und eine funktionierende PyTorch-Neuimplementierung finden Sie alle in DeepLOB: Deep Learning on Limit Order Books. Darunter ändert sich nichts an diesem Encoder – die ganze Frage ist, was zu seiner Eingabedarstellung hinzugefügt wird.
Drei Möglichkeiten, unregelmäßige Zeit zu kodieren

Option A: delta_t als einfaches Feature
Die triviale Antwort. Anhängen (logarithmisch transformiert, z-bewertet) als eine weitere Spalte im Merkmalsvektor neben OFI und Buchungleichgewicht und behalten Sie die standardmäßig erlernte Positionseinbettung bei. Kostet nichts, fügt eine Eingabedimension hinzu und ist das, was die meisten Produktions-Tick-Modelle tatsächlich tun.
Dies ist der Arm, den jeder ausgefallenere Vorschlag überwinden muss, und es ist der Arm, der in Papieren, die ausgefallenere Alternativen vorschlagen, weggelassen wird.
Option B: zeitkontinuierliche Positionskodierung
Ersetzen Sie die diskrete Positionseinbettung durch eine Sinusfunktion der verstrichenen Zeit über lernbare Zeitskalen:
Wo sind lernbare Zeitskalenparameter, die logarithmisch über Mikrosekunden bis Sekunden initialisiert werden. Der beabsichtigte Effekt besteht darin, dass Aufmerksamkeit Ereignisse nach zeitlicher Relevanz und nicht nach Slot-Index gewichten kann – ein großer Trade vor 200 Mikrosekunden sollte anders erreichbar sein als ein kleiner Trade vor 50 Millisekunden.
Der lernbare Teil ist der interessante Teil. Wenn Sie Zeitskalen mit logarithmischen Abständen von 1 Mikrosekunde bis 10 Sekunden initialisieren und trainieren, ist wo die Zeitskalen enden, selbst ein Maß: Wenn sie zum Millisekundenende hin zusammenbrechen, sagt Ihnen das Modell, dass alles über ein paar Millisekunden hinaus austauschbar ist, und das ist eine Erkenntnis über den Markt, nicht über die Architektur.
import numpy as np
import torch
import torch.nn as nn
class ContinuousTimeEncoding(nn.Module):
"""Sinusoidal encoding for irregular inter-arrival times."""
def __init__(self, d_model: int, num_timescales: int = 64):
super().__init__()
log_timescales = torch.linspace(
np.log(1e-6), np.log(10.0), num_timescales
)
self.log_timescales = nn.Parameter(log_timescales)
self.proj = nn.Linear(num_timescales * 2, d_model)
def forward(self, delta_t: torch.Tensor) -> torch.Tensor:
"""
Args:
delta_t: (batch, seq_len) inter-arrival times in seconds
Returns:
(batch, seq_len, d_model) time encoding
"""
timescales = torch.exp(self.log_timescales) # (num_timescales,)
scaled = delta_t.unsqueeze(-1) / timescales.unsqueeze(0).unsqueeze(0)
encoding = torch.cat([torch.sin(scaled), torch.cos(scaled)], dim=-1)
return self.proj(encoding)
In einen Standard-Encoder eingefügt, ersetzt es genau eine Zeile – die Positionseinbettung fügt Folgendes hinzu:
class TickTransformer(nn.Module):
def __init__(self, input_dim: int, d_model: int = 128,
nhead: int = 8, num_layers: int = 4, num_classes: int = 3):
super().__init__()
self.feature_proj = nn.Linear(input_dim, d_model)
self.time_encoding = ContinuousTimeEncoding(d_model)
encoder_layer = nn.TransformerEncoderLayer(
d_model=d_model, nhead=nhead,
dim_feedforward=d_model * 4,
dropout=0.1, batch_first=True
)
self.encoder = nn.TransformerEncoder(
encoder_layer, num_layers=num_layers
)
self.head = nn.Linear(d_model, num_classes)
def forward(self, features: torch.Tensor,
delta_t: torch.Tensor) -> torch.Tensor:
h = self.feature_proj(features) + self.time_encoding(delta_t)
h = self.encoder(h)
return self.head(h[:, -1, :])
Das Encoder-Boilerplate selbst ist nichts Neues – das Gleiche nn.TransformerEncoderLayer scaffolding ist bereits als Architecture 2 in Spread Modeling veröffentlicht. Nur ContinuousTimeEncoding und das Argument der erlernbaren Zeitskala sind.
Option C: kontinuierlicher latenter Zustand zwischen Ticks (ODE-RNN)
Die prinzipiellste Option behandelt den latenten Zustand als einen zeitkontinuierlichen Prozess, der durch eine Neuronale ODE modelliert wird (Chen et al., 2018). Zwischen den Ticks entwickelt sich der verborgene Zustand gemäß einer erlernten Differentialgleichung:
Wenn ein Häkchen eintrifft, wird der Status mit der Beobachtung aktualisiert:
Dies ist das ODE-RNN-Framework. Es behandelt unregelmäßige Abstände strukturell und nicht als Eingabemerkmal: Der Löser integriert for genau zwischen Ereignissen, ohne Auffüllung, ohne Interpolation und ohne Resampling-Schritt, bei dem Informationen verloren gehen.
Die Kosten sind rechnerisch. ODE-Löser sind sequentiell und schwer zu parallelisieren, weshalb es am unwahrscheinlichsten ist, dass dieser Arm ein Latenzbudget übersteht – siehe die in The IPC Tax und the Backtest Engine Speed Ladder festgelegte Latenzdisziplin, um zu erfahren, wie solche Behauptungen gemessen werden sollten, bevor sie aufgestellt werden. Es wird hier als Obergrenze dafür aufgeführt, was durch explizite Zeitmodellierung eingekauft werden kann, nicht als Bereitstellungskandidat.
Ereignisgewichtete Aufmerksamkeit

Eine zweite, orthogonale Idee: Nicht alle Ticks sind gleich informativ. Ein 100-Aktien-Handel zum Gebot ist Routine. Ein Handel, der drei Preisniveaus umfasst, ist ein Regimewechsel. Wir können das Prior direkt in die Aufmerksamkeit einbringen. Definieren Sie einen Ereigniswichtigkeitswert:
Wo ist die Handelsgröße, die Preisänderung, jüngste Volatilität und kennzeichnet einen mehrstufigen Sweep. Fügen Sie es den Aufmerksamkeitsprotokollen vor Softmax hinzu:
mit . Das Modell behält die Fähigkeit, willkürliche Aufmerksamkeitsmuster zu erlernen, ist jedoch zunächst auf Trades ausgerichtet, die den Markt bewegt haben.
Dies ist eine erfundene Formel. Die funktionale Form, die Wahl der drei Begriffe und das Softplus sind allesamt Vermutungen, und nichts hier zeigt, dass die Voreingenommenheit eher hilft als nur die Kapazität zu verbrennen.
Multi-Horizont-Köpfe

Unterschiedliche Horizonte beeinflussen unterschiedliche Entscheidungen, ein gemeinsamer Encoder mit mehreren Horizontausgängen schlägt separate Modelle pro Horizont und der gemeinsame Verlust reguliert sich – dieses Argument sowie Quantilausgänge und Interpretierbarkeit werden in Temporal Fusion Transformer for Trading angeführt. Der einzige zeckenspezifische Teil ist der Horizontsatz: 1, 10, 50 und 100 Ereignisse statt Tage, was bedeutet, dass sich die Horizonte in der Tageszeit bei Ausbrüchen stark und in ruhigen Zeiten kaum überlappen – eine Komplikation, mit der sich die Literatur zum Tageshorizont nie auseinandersetzen muss.
Säuberung muss in Ereignissen gemessen werden

Die bereinigte Walk-Forward-Validierung wird durchgängig in Walk-Forward-Optimierung behandelt (verankerter, rollierender, kombinatorischer bereinigter CV, WFER, Degradationsrate) und eine funktionierende purged_walk_forward() mit einer expliziten Säuberungs- und Embargolücke in Spread Modeling. Die Look-Ahead-Bias-Taxonomie quantifiziert, was Leaks dieser Art mit dem gemeldeten Sharpe bewirken.
Das Einzige, was für Tick-Daten spezifisch ist: Benachbarte Ticks, die Millisekunden voneinander entfernt sind, sind nahezu Duplikate, sodass eine Löschlücke in Tagen bedeutungslos ist, wenn ein Burst 500 nahezu identische Samples innerhalb einer Sekunde platziert. Die Lücke muss in Ereignissen bemessen werden, und die richtige Größe ist eine empirische Frage zur Burst-Struktur Ihres Instruments und keine Konstante, die man aus einem Papier kopieren kann.
Diese Behauptung ist kostengünstig zu testen – fegen Sie die Säuberungslücke in den Ereignissen und zeichnen Sie die Validierung F1 dagegen auf. Wenn die Validierung F1 sinkt, wenn sich die Lücke vergrößert und dann abflacht, ist der Abflachungspunkt Ihre Lücke. Wenn es nie herunterfällt, ist das Austreten benachbarter Zecken nicht das Problem, das Sie vermutet haben.
Das Experiment, das darüber entscheidet

Alles darüber ist Architektur. Nichts davon ist ein Beweis. Der Artikel löscht die Leiste dieses Blogs erst, wenn Folgendes ausgeführt wird:
Einrichtung. Korrigieren Sie einen Datensatz (BTC/USDT-Trades ist der Hausdatensatz), einen Encoder, eine Label-Definition und eine bereinigte Aufteilung. Variieren Sie genau eine Sache.
Drei Arme.
| Arm | Zeitinformationen | Positionskodierung |
|---|---|---|
| A | als Roheingabe-Feature | einfach erlernte Positionseinbettung |
| B | keine, jenseits der Kodierung | ContinuousTimeEncoding (lernbare Zeitskalen) |
| C | als Feature und zeitkontinuierliche Kodierung | ContinuousTimeEncoding |
Was zu melden ist.
- F1 pro Klasse, nicht Genauigkeit – unter der Bezeichnung „Tote Zone“ dominiert die FLAT-Klasse, und die Genauigkeit ist aus genau dem Grund, den der DeepLOB-Artikel angibt, nicht aussagekräftig.
- Ein Konfidenzintervall aus wiederholten Durchläufen mit unterschiedlichen Seeds. Ein F1-Abstand von 0,4 Punkten zwischen den Armen bedeutet nichts ohne einen.
- Die angepassten Zeitskalen. Drucken
torch.exp(model.time_encoding.log_timescales)nach dem Training. Wo sie sich niederlassen, ist die interessanteste Zahl im Experiment, deren Ermittlung eine Zeile kostet. - Hardware- und Wanduhrkosten pro Arm, im Stil von The IPC Tax – gemessen, auf offengelegter Hardware, Median über N. Wenn Arm B für einen Bruchteil eines F1-Punkts das Dreifache der Inferenzzeit von Arm A kostet, ist das die Antwort.
Melden Sie das negative Ergebnis, wenn es eines gibt. „Kontinuierliche Codierung bringt nichts über die Fütterung hinaus.“ „as a feature, on 30 days of BTC ticks“ ist ein nützlicherer Beitrag als eine Umfrage unter drei Architekturen, die niemand gemessen hat. Es würde auch mit der allgemeinen Feststellung in diesem Blog übereinstimmen, dass sorgfältig konstruierte Kanten bei ehrlicher Validierung tendenziell verschwinden.