📝

Draft article

This draft is visible to admins and superusers only. Sign in with an authorized account.

← Мақалаларға оралу
August 22, 2026
5 мин оқу

Irregular Time in Tick Models: Continuous-Time Encodings vs. Plain Positional Embeddings

Irregular Time in Tick Models: Continuous-Time Encodings vs. Plain Positional Embeddings
#HFT
#tick-data
#deep-learning
#high-frequency
#prediction

Уақыттық барлар — оқиғалардың уақытын жоятын ерікті сығымдау тәсілі, сондықтан бар шекараларын белсенділік анықтауы керек. Бұл уәж 17 бар түрімен және жұмыс істейтін генераторлармен бірге Алгоритмдік саудаға арналған бар түрлері мен агрегация әдістері мақаласында толық берілген. Бұл мақала сол жерден бір қадам кейін басталады: тик, көлем немесе дисбаланс барларына ауысқаннан кейін де, оларды қабылдайтын модель оқиғалар арасы тұрақты деп есептейді. Үйретілетін позициялық эмбеддингі бар nn.TransformerEncoder 7-позицияны "жетінші ұяшық" деп қабылдайды: бұл тик 6-позициядан кейін 200 микросекундтан соң келсе де, 40 секундтан соң келсе де бірдей. Белсенділік барлары сақтауы тиіс оқиғалар аралығы кіріс қабатында қайтадан жоғалады.

Сондықтан бұл мақаланың сұрағы тар әрі тексерілетін: тізбектік модельге тиктің нақты қашан болғанын қалай көрсету керек және күрделі шешім қарапайым шешімнен оза ма?

Қажетті бастапқы білім

Біркелкі емес оқиғалардың импульстары

Төмендегілер — осы блогта бұрын қамтылған алғышарттар; мұнда олардың ешқайсысы қайта шығарылмайды:

  • Микроқұрылым шуы және bid-ask серпілісі. Roll-дың жасырын спред моделі тик кірістері арасындағы теріс сериялық ковариацияны баға бірліктерінде бірінші қағидалардан шығарады; жиі кездесетін іске асыру қатесі де көрсетілген: ML көмегімен bid-ask спредін модельдеу.
  • Ордер кітабының белгілері. Кітап дисбалансы, салмақталған орта баға, 1-5 деңгейлердегі тереңдік қатынасы, кітап қысымы және спред/тик қатынасы сол мақалада кестеленген; OBI және көлеммен салмақталған орта бағаның шығарылымдары — салмақталған орта баға Stoikov-тың микробағасы емес деген ескертумен бірге — DeepLOB: лимиттік ордер кітаптарын терең оқыту мақаласында берілген.
  • Ордер ағыны және сауда қарқындылығы. Сауда дисбалансы, VPIN және Kyle лямбдасы спредті модельдеу мақаласында; ордерлер аралығы мен Hawkes-тың өзін-өзі қоздыратын қарқындылығы уақыт белгілері ретінде Цифрлық із: трейдерді идентификациялау мақаласында бар. Осы нобайда бастапқыда ұсынылған қарапайым 1/dt қарқындылығы ондағы Hawkes тұжырымының әлсіз нұсқасы.
  • Күн ішіндегі стационарлық емес сипат. Көлемнің U-тәрізді үлгісі, тыныш кезеңдердегі кеңейетін спред және тәулік уақытының sin/cos кодтауы спредті модельдеуде нарық режимінің белгілері ретінде қарастырылған.
  • Таңбалар. Екі тегістеу келісімі (болашақ орташа мен ағымдағы баға, болашақ орташа мен алдыңғы орташа), α\alpha шегі бар үш сыныпты дискреттеу және LOBFrame-ның таңба сезімталдығы туралы ескертуі DeepLOB мақаласында берілген.
  • Сыныптар теңгерімсіздігі. Өлі аймақ болғанда FLAT сыныбы басым болады, сондықтан есеп берілетін метрика дәлдік емес, F1 — сол мақаладағыдай.
  • Бардың толу ұзақтығы. Көлем барының толуына кететін қабырға сағаты уақыты тик моделі үшін қолдануға болатын белгі; оны жасайтын генераторлар Бар түрлері мен агрегация әдістері мақаласында бар.

Бастапқы нұсқадағы қателікті ескере отырып, бір нәрсені ашық айту керек: бағытты дәлдіктің 50,0%-дан 50,5%-ға өсуі өздігінен пайдалы дегенді білдірмейді. DeepLOB мақаласында талқыланған LOBFrame-ның негізгі ойы: болжанған қозғалыс спредтен өтуге жеткілікті үлкен болуы керек, сонда ғана дәлдік мәнге ие; бұл блогтағы адал теріс нәтиже керісінше жорамалдағанда не болатынын көрсетеді.

Базалық модель

Базалық тізбектік оқиға кодтауы

Базалық кодтаушы ретінде DeepLOB стиліндегі CNN-Inception-LSTM алайық; архитектура, тексерілген FI-2010 Setup 2 сандары (k=10k=10 кезінде F1 83,40 / дәлдік 84,47), LOBFrame ескертулері және жұмыс істейтін PyTorch қайта іске асыруы DeepLOB: лимиттік ордер кітаптарын терең оқыту мақаласында берілген. Төмендегі ештеңе бұл кодтаушыны өзгертпейді — мәселе оның кіріс көрінісіне не қосылатынында.

Біркелкі емес уақытты кодтаудың үш тәсілі

Біркелкі емес уақыттың балама кодтаулары

A нұсқасы: delta_t — қарапайым белгі ретінде

Ең қарапайым жауап. OFI және кітап дисбалансымен қатар feature векторына Δti=titi1\Delta t_i = t_i - t_{i-1} мәнін (логарифмделген, z-бағаланған) тағы бір баған ретінде қосып, стандартты үйретілетін позициялық эмбеддингті қалдырыңыз. Еш шығынсыз бір кіріс өлшемін қосады және өндірістегі тик модельдерінің көбі іс жүзінде осылай жұмыс істейді.

Бұл — кез келген күрделірек ұсыныс басып озуы тиіс нұсқа және күрделірек баламалар ұсынатын мақалаларда жиі алынып тасталатын нұсқа.

B нұсқасы: үздіксіз уақыттың позициялық кодтауы

Дискретті позициялық эмбеддингті үйретілетін уақыт масштабтарындағы өткен уақыттың синусоидалық функциясымен алмастырыңыз:

TE(Δt)=[sin(Δtτ1),cos(Δtτ1),,sin(Δtτd),cos(Δtτd)]\text{TE}(\Delta t) = \left[\sin\left(\frac{\Delta t}{\tau_1}\right), \cos\left(\frac{\Delta t}{\tau_1}\right), \ldots, \sin\left(\frac{\Delta t}{\tau_d}\right), \cos\left(\frac{\Delta t}{\tau_d}\right)\right]

мұнда τ1,,τd\tau_1, \ldots, \tau_d — микросекундтардан секундтарға дейін логарифмдік аралықпен бастапқы мән берілетін үйретілетін уақыт масштабы параметрлері. Мақсат — attention-ге оқиғаларды ұяшық индексімен емес, уақыттық өзектілігімен салмақтауға мүмкіндік беру: 200 микросекунд бұрын болған ірі саудаға 50 миллисекунд бұрын болған шағын саудадан өзгеше қол жеткізілуі тиіс.

Үйретілетін бөлік — ең қызығы. Егер уақыт масштабтарын 1 микросекундтан 10 секундқа дейін логарифмдік аралықпен бастапқы мәндеп, модельді үйретсеңіз, уақыт масштабтарының қайда орныққанының өзі өлшем: егер олар миллисекундтар жағына қысылса, модель бірнеше миллисекундтан кейінгі нәрсенің бәрі өзара алмастырмалы екенін айтып тұр, бұл архитектура туралы емес, нарық туралы нәтиже.

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)

Стандартты кодтаушыға енгізгенде ол дәл бір жолды — позициялық эмбеддингті қосуды — алмастырады:

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, :])

Кодтаушының негізгі қаңқасы жаңа нәрсе емес — дәл сол nn.TransformerEncoderLayer қаңқасы спредті модельдеу мақаласында 2-архитектура ретінде жарияланған. Мұнда жаңасы — тек ContinuousTimeEncoding және үйретілетін уақыт масштабы туралы уәж.

C нұсқасы: тиктер арасындағы үздіксіз жасырын күй (ODE-RNN)

Ең принципті тәсіл жасырын күйді Neural ODE арқылы модельденетін үздіксіз уақыттық процесс ретінде қарастырады (Chen және басқалар, 2018). Тиктер арасында жасырын күй үйретілетін дифференциалдық теңдеу бойынша өзгереді:

dhdt=fθ(h(t),t)\frac{d\mathbf{h}}{dt} = f_\theta(\mathbf{h}(t), t)

Тик келгенде күй бақылаумен жаңартылады:

h(ti+)=Update(h(ti),xi)\mathbf{h}(t_i^+) = \text{Update}(\mathbf{h}(t_i^-), \mathbf{x}_i)

Бұл — ODE-RNN қаңқасы. Ол біркелкі емес аралықты кіріс белгісі ретінде емес, құрылымдық түрде өңдейді: шешуші оқиғалар арасындағы дәл Δti\Delta t_i уақыт бойы интегралдайды, толтырусыз, интерполяциясыз және ақпарат жоғалатын қайта іріктеу қадамынсыз.

Бағасы — есептеу күрделілігі. ODE шешушілері бірізді және параллельдеуі қиын, сондықтан бұл latency бюджетінде сақталуы ең ықтимал емес нұсқа — мұндай мәлімдемелер жасамас бұрын қалай өлшеу керегін IPC салығы және бэктест қозғалтқышының жылдамдық баспалдағы мақалаларында бекітілген latency тәртібінен қараңыз. Бұл нұсқа іске енгізу кандидаты ретінде емес, уақытты ашық модельдеу не бере алатынының жоғарғы шегі ретінде қосылды.

Оқиға салмағы бар attention

Оқиға салмағы бар attention өрісі

Екінші, ортогоналды идея: барлық тик бірдей ақпаратты емес. Bid бағасындағы 100 акциялы сауда — қалыпты жағдай. Үш баға деңгейін сыпырып өтетін сауда — режимнің өзгеруі. Бұл априорлық білімді attention-ге тікелей енгізуге болады. Оқиғаның маңыздылық ұпайын анықтайық:

wi=softplus(α1log(qi)+α2Δpi/σ+α31[sweepi])w_i = \text{softplus}\left(\alpha_1 \cdot \log(q_i) + \alpha_2 \cdot |\Delta p_i| / \sigma + \alpha_3 \cdot \mathbb{1}[\text{sweep}_i]\right)

мұнда qiq_i — сауда көлемі, Δpi\Delta p_i — баға өзгерісі, σ\sigma — соңғы волатильдік, ал sweepi\text{sweep}_i бірнеше деңгейлі сыпыруды белгілейді. Оны softmax алдында attention логиттеріне қосыңыз:

Attention(Q,K,V)=softmax(QKTdk+W)V\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}} + \mathbf{W}\right) V

мұнда Wij=wj\mathbf{W}_{ij} = w_j. Модель еркін attention үлгілерін үйрену қабілетін сақтайды, бірақ нарықты қозғаған саудаларға бастапқыда көбірек салмақ береді.

Бұл ойдан шығарылған формула. Функционалдық түрі де, үш мүшені таңдау да, softplus та — болжамдар; мұндағы ештеңе бұл ығыстырудың сыйымдылықты босқа жұмсамай, шынымен көмектесетінін көрсетпейді.

Көп горизонтты бастар

Көп горизонтты болжам таспалары

Әртүрлі горизонттар әртүрлі шешімдерге ақпарат береді; бірнеше горизонт шығысы бар ортақ кодтаушы әр горизонтқа бөлек модельдерден жақсы, ал бірлескен loss регуляризация жасайды — бұл уәж, сондай-ақ квантильдік шығыстар мен интерпретациялылық, Саудаға арналған Temporal Fusion Transformer мақаласында берілген. Тикке тән жалғыз бөлік — горизонттар жиыны: күндер емес, 1, 10, 50 және 100 оқиға. Сондықтан қарбалас кезінде горизонттар қабырға уақыты бойынша қатты қабаттасады, ал тыныш кезеңде дерлік қабаттаспайды — күндік горизонт әдебиетінде кездеспейтін қиындық.

Purging оқиғалармен өлшенуі керек

Оқиға кеңістігіндегі алып тастау аралығы

Purged walk-forward валидациясы Walk-Forward Optimization мақаласында басынан аяғына дейін қамтылған (anchored, rolling, combinatorial purged CV, WFER, degradation rate), ал нақты purge және embargo аралығы бар жұмыс істейтін purged_walk_forward() спредті модельдеу мақаласында берілген. Look-ahead bias таксономиясы мұндай ағып кетулердің есептелген Sharpe-ге әсерін сандық бағалайды.

Тик деректеріне ғана тән нәрсе: миллисекундтармен бөлінген көршілес тиктер бір-біріне өте ұқсас, сондықтан күндермен өлшенген purge аралығы бір секунд ішіне 500 дерлік бірдей үлгі сыйған қарбаласта мағынасыз. Аралық оқиғалармен өлшенуі керек, ал дұрыс өлшем — қағаздан көшірілетін тұрақты шама емес, инструментіңіздің қарбалас құрылымы туралы эмпирикалық сұрақ.

Бұл тұжырымды тексеру арзан: оқиғалардағы purge аралығын өзгертіп, валидациялық F1-ді соған қарсы салыңыз. Егер аралық кеңейгенде валидациялық F1 төмендеп, кейін тегістелсе, тегістелу нүктесі — сіздің аралығыңыз; егер ол ешқашан төмендемесе, көршілес тиктердің ағып кетуі мәселе деп қате ойлағансыз.

Мұны шешетін эксперимент

Уақыттық модельді бақыланатын бағалау

Жоғарыдағылардың бәрі — архитектура. Олардың ешқайсысы дәлел емес. Мақала төмендегі іске қосулар жасалмайынша осы блогтың талабынан өтпейді:

Баптау. Бір деректер жиынын (BTC/USDT саудасы — негізгі деректер жиыны), бір кодтаушыны, бір таңба анықтамасын және бір purged split-ті бекітіңіз. Дәл бір ғана нәрсені өзгертіңіз.

Үш нұсқа.

Нұсқа Уақыт ақпараты Позициялық кодтау
A Δt\Delta t шикі кіріс белгісі ретінде қарапайым үйретілетін позициялық эмбеддинг
B кодтаудан тыс уақыт ақпараты жоқ ContinuousTimeEncoding (үйретілетін уақыт масштабтары)
C Δt\Delta t белгі ретінде және үздіксіз уақыт кодтауы ContinuousTimeEncoding

Не туралы есеп беру керек.

  1. Әр сыныпқа F1, дәлдік емес — өлі аймақ таңбасында FLAT сыныбы басым, сондықтан дәлдік DeepLOB мақаласында түсіндірілген себеппен ақпаратсыз.
  2. Сенімділік интервалы, әртүрлі seed-пен қайталанған іске қосулардан есептелген. Нұсқалар арасындағы 0,4 пункттік F1 айырмасы онсыз ештеңені білдірмейді.
  3. Үйретілген уақыт масштабтары. Үйретуден кейін torch.exp(model.time_encoding.log_timescales) мәнін басып шығарыңыз. Олардың қайда орныққаны — эксперименттегі ең қызықты сан, оны алу бір жол ғана.
  4. Әр нұсқаның аппараттық құралдары және қабырға уақыты бойынша құны, IPC салығы стилінде — өлшенген, жарияланған аппаратта, N бойынша медиана. Егер B нұсқасының inference уақыты A-дан 3 есе көп болып, F1-дің бір бөлігін ғана берсе, жауап осы.

Теріс нәтиже болса, оны есеп беріңіз. "30 күндік BTC тиктерінде үздіксіз уақыт кодтауы Δt\Delta t мәнін белгі ретінде беруден артық ештеңе бермейді" — ешкім өлшемеген үш архитектураға шолу жасағаннан пайдалырақ мақала. Бұл блогтағы мұқият құрылған edge-тер адал валидация кезінде жойылуға бейім деген жалпы қорытындыға да сәйкес келеді.

Қазіргі жағдай

Үздіксіз уақыт зерттеу жолы

Ашық қалған сұрақ нақты: біркелкі емес аралықтағы оқиғаларды қабылдайтын тізбектік модельдер уақыттың орнына позицияны кодтайды, ал блогтың қолданыстағы материалдары — спредті модельдеудегі Transformer кодтаушысын қоса — қарапайым үйретілетін позициялық эмбеддингті пайдаланады. Үздіксіз уақыт кодтауы, ODE-RNN жасырын күйі және оқиға салмағы бар attention — мұны түзетудің үш тәсілі.

Жетіспейтіні — бұл түзетудің маңызды екеніне дәлел. Үшеуі де әзірше нәтиже емес, тақырып. Шешуші өлшем — бекітілген кодтаушы мен бекітілген purged split бойынша үш нұсқалы ablation; онда әр сыныпқа F1, сенімділік интервалы және үйретілген уақыт масштабтары есеп берілуі керек. Бұл эксперимент әлі жүргізілген жоқ.

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

Нарықтан бір қадам алда болыңыз

AI сауда талдаулары, нарық аналитикасы және платформа жаңалықтары үшін біздің ақпараттық бюллетеньге жазылыңыз.

Біз сіздің жекелігіңізді құрметтейміз. Кез келген уақытта жазылымнан шығуға болады.