← Torna agli articoli
August 16, 2026
5 min di lettura

Neural ODEs: Does Continuous Time Beat a Delta-Time Feature?

Neural ODEs: Does Continuous Time Beat a Delta-Time Feature?
#deep-learning
#neural-ODE
#continuous-time
#SDE
#dynamics

Le equazioni differenziali ordinarie neurali (Chen et al., NeurIPS 2018) parametrizzano la derivata di uno stato nascosto con una rete neurale e consentono a un risolutore ODE di calcolare l'output. Il modello diventa a profondità continua e, più utilmente per i mercati, a tempo continuo: lo stato nascosto può essere valutato in qualsiasi momento tt, non solo nei punti della griglia su cui si trovano i tuoi dati.

Questa proprietà ha una tonalità ovvia per i dati finanziari, che arrivano a intervalli casuali. Ma la proposta nasconde un’affermazione molto più debole di quanto sembri fare. Un semplice GRU forniva una funzione di input extra: log(Δt)\log(\Delta t) dall'osservazione precedente — "sa" anche quanto tempo è passato. La vera domanda non è se i modelli a tempo continuo possano assorbire dati irregolari. Dipende se la dinamica continua appresa tra le osservazioni estrae qualcosa che una caratteristica delta-tempo non ti dà già, a un costo da dieci a cento volte superiore al costo di inferenza.

Questo articolo imposta l'esperimento, fornisce l'intera catena di strumenti per eseguirlo e riporta ciò che il confronto dovrebbe mostrare per giustificare il macchinario. L'aspetto dell'argomento relativo alla costruzione delle barre, cioè che il timing stesso del trading trasmette un segnale, è stabilito con prove misurate su 17 tipi di barre in beyond time bars; qui l'unica conseguenza che conta è quella di modellazione: il risolutore valuta lo stato nascosto come arbitrario tt, quindi non è necessaria alcuna interpolazione o riempimento.

L'affermazione in fase di test

Tre ipotesi nidificate per la modellazione del mercato a tempo continuo

Tre ipotesi annidate, ciascuna strettamente più forte della precedente:

  1. H1 — Un fit ODE-RNN su un flusso commerciale BTC irregolare e grezzo batte un fit GRU sullo stesso flusso senza alcuna informazione temporale. Questo è quasi certo e quasi inutile: l’ODE-RNN contiene semplicemente informazioni che mancano alla linea di base.
  2. H2 — L'ODE-RNN batte lo stesso GRU una volta assegnato il GRU log(Δt)\log(\Delta t) come funzionalità di input. Questo è il test onesto. È l’affermazione che viene solitamente interpretata nella letteratura sull’ODE neurale, ed è quella quasi mai dimostrata sui dati finanziari.
  3. H3 — Il vantaggio H2 sopravvive al budget di latenza, ovvero vale quando l'ODE-RNN viene distribuito con un risolutore a passaggio fisso sufficientemente veloce per l'inferenza live anziché adattiva dopri5.

Protocollo dell'esperimento

Stesso flusso di tick, stesso target, stesso budget di ottimizzazione per entrambe le leve. Nessun ricampionamento a 1 milione di barre: il ricampionamento distrugge l'esatta struttura sotto test.

Impostazione
Dati Stampe commerciali BTC, tempi di inter-arrivo variabili, non ricampionati
Armi ODE-RNN; GRU+ log(Δt)\log(\Delta t); GRU senza funzione temporale
Obiettivo Lo stesso per tutte le armi; specificato con l'adattamento
Metriche RMSE, probabilità di log mantenuta, orologio da parete per epoca, latenza di inferenza per passaggio
Spazzata del risolutore dopri5 (rtol 1e-5) vs Eulero a passo fisso, confrontato sulla precisione dell'allenamento
Diviso Walk forward, solo fuori campione
Braccio RMSE Log simile s/epoca Latenza/passo di inferenza
GRU (nessuna funzione temporale)
GRU + log(dt)
ODE-RNN (dopri5)
ODE-RNN (Eulero a passo fisso)

Un risultato negativo su H2 è completamente pubblicabile e probabilmente il risultato più prezioso: lo stesso standard applicato in il risultato negativo onesto e Sharpe sgonfio sotto test multipli.

Esperimento secondario: CNF vs Student-t

Se il confronto ODE-RNN è troppo costoso, l’ancora empirica più economica è quella distributiva: adattare un flusso di normalizzazione continuo sui rendimenti giornalieri reali di BTC e confrontare la sua densità adattata con un adattamento Student-t e con la coda empirica, riportando errori del quantile di coda ai livelli dell’1% e del 5%. Ciò si collega direttamente al lavoro distributivo già misurato in Previsione della volatilità di GARCH e GARCH asimmetrico.


Il resto di questo articolo costituisce lo sfondo e la toolchain necessaria per eseguire quanto sopra.

Background: dalle ResNet alle dinamiche continue

La rete residua diventa un campo dinamico continuo

Viene calcolata una rete residua ht+1=ht+f(ht,θt)h_{t+1} = h_t + f(h_t, \theta_t). Riduci la dimensione del passo e aumenta il numero di livelli e ti avvicini a un limite continuo:

dh(t)dt=f(h(t),t,θ)\frac{dh(t)}{dt} = f(h(t), t, \theta)

Invece di TT strati discreti con parametri separati, un'unica rete ff specifica il tasso di variazione istantaneo. L'output al momento TT risolve un problema sui valori iniziali:

h(T)=h(0)+0Tf(h(t),t,θ)dth(T) = h(0) + \int_0^T f(h(t), t, \theta) \, dt

Un solutore a scatola nera (Eulero, Runge-Kutta, Dormand-Prince) calcola l'integrale numericamente, scegliendo in modo adattivo le dimensioni del passo dalle stime dell'errore locale.

Il metodo aggiunto

La propagazione all'indietro attraverso ogni passaggio del risolutore memorizza tutti gli stati intermedi, con un consumo di memoria proporzionale al conteggio dei passaggi. Chen et al. risolvi invece un'ODE all'indietro con O(1)O(1) memoria. Definire lo stato aggiunto a(t)=L/h(t)a(t) = -\partial L / \partial h(t), che soddisfa

da(t)dt=a(t)Tf(h(t),t,θ)h\frac{da(t)}{dt} = -a(t)^T \frac{\partial f(h(t), t, \theta)}{\partial h}

e accumulare il gradiente del parametro lungo il passaggio all'indietro:

dLdθ=T0a(t)Tf(h(t),t,θ)θdt\frac{dL}{d\theta} = -\int_T^0 a(t)^T \frac{\partial f(h(t), t, \theta)}{\partial \theta} \, dt

Il passaggio all'indietro risolve un sistema informatico aumentato h(t)h(t), a(t)a(t) E dL/dθdL/d\theta contemporaneamente, eseguendo il risolutore al contrario da TT A 00.

Il compromesso: ricostruire h(t)h(t) all'indietro si accumula l'errore numerico, particolarmente grave nel caso di dinamiche rigide o caotiche. Il checkpoint è la via di mezzo: il negozio h(t)h(t) in alcuni momenti intermedi, ricalcolare tra di loro. I processi di prezzo sono semimartingale continui e moderatamente fluidi, quindi l'aggiunto generalmente regge; la rigidità attorno agli eventi della microstruttura può forzare risolutori adattivi o ibridi.

ODE-RNN: Stato nascosto continuo tra le osservazioni

Stato nascosto che evolve dolcemente tra osservazioni irregolari

L'ODE-RNN è l'architettura su cui si basa l'esperimento principale. Tra le osservazioni lo stato nascosto evolve sotto l'ODE:

h(t)=ODESolve(fθ,h(ti),ti,t)h(t) = \text{ODESolve}(f_\theta, h(t_i), t_i, t)

Quando l'osservazione xi+1x_{i+1} arriva a ti+1t_{i+1}, viene attivato un aggiornamento discreto:

h(ti+1+)=RNNCell(h(ti+1),xi+1)h(t_{i+1}^+) = \text{RNNCell}(h(t_{i+1}^-), x_{i+1})

Gli apici denotano lo stato appena prima e subito dopo l'osservazione. Tra le osservazioni, apprese dinamiche continue; alle osservazioni, nuove informazioni.

Il meccanismo analizzato dall’ablazione con H2 è questo: un lungo divario significa che lo stato nascosto si è evoluto molto tempo fa fθf_\theta – decadendo verso una linea di base, o divergente – e la forma di tale evoluzione viene appresa anziché fornita come scalare. Se tale espressività si ripaga sulla base dei dati commerciali reali è esattamente ciò che non viene misurato.

Adattamenti naturali oltre i tick: portafogli multi-asset in cui ogni asset ha il proprio programma di osservazione e lo stato latente degli asset correlati continua ad evolversi mentre ne viene osservato solo uno; e segnali guidati dagli eventi (notizie, utili, comunicati macro) che arrivano in orari irregolari.

SDE neurali: aggiunta di componenti stocastici

Dinamica continua con diffusione stocastica controllata

Gli ODE neurali sono deterministici e non possono rappresentare il rumore in un percorso di prezzo. Il trattamento classico (movimento browniano geometrico, deriva neutrale rispetto al rischio e modi in cui le ipotesi di volatilità costante falliscono contro il sorriso) è trattato in Prezzi delle opzioni di Black-Scholes. Gli SDE neurali sostituiscono la forma parametrica con un termine di diffusione appreso:

dh(t)=f(h(t),t,θ)dt+g(h(t),t,ϕ)dW(t)dh(t) = f(h(t), t, \theta) \, dt + g(h(t), t, \phi) \, dW(t)

ff è la deriva, gg la diffusione, W(t)W(t) un processo di Wiener, entrambi ff E gg reti neurali.

Architettura: rete derivante e rete di diffusione

  • Rete derivante fθf_\theta: traiettoria attesa – trend, inversione della media, momentum. Addestrato per ridurre al minimo l'errore di previsione.
  • Rete di diffusione gϕg_\phi: magnitudo del rumore, appresa in funzione dello stato. Alto nei regimi volatili, basso in quelli calmi.

La divisione rispecchia la finanza quantistica classica: la deriva è la dinamica neutrale al rischio (o misura P), la diffusione è la superficie della volatilità.

Addestramento degli SDE neurali

L'integrale stocastico gdW\int g\,dW non è classicamente differenziabile. Tre approcci:

  1. Gradienti percorso: trucco di riparametrizzazione: campiona percorsi browniani, differenziali tramite il risolutore trattando il rumore come input fisso.
  2. Corrispondenza del punteggio: stima hlogp(h)\nabla_h \log p(h) e addestrarsi tramite obiettivi di denoising: lo stesso meccanismo utilizzato come modello generativo autonomo in modelli di diffusione per la previsione delle criptovalute, qui retrocesso a opzione di formazione per l'SDE.
  3. Corrispondenza della distribuzione a dimensione finita: abbina i marginali ai tempi di osservazione piuttosto che alle misure del percorso completo.

Pathwise è l'impostazione predefinita pratica. torchsde implementa Eulero-Maruyama, Milstein e Runge-Kutta stocastico con supporto autodiff.

Imparare la superficie di volatilità

I modelli classici (Heston, SABR) impongono forme parametriche sul coefficiente di diffusione. Un SDE neurale apprende gϕ(S,t)g_\phi(S, t) come funzione arbitraria:

dS(t)=μθ(S(t),t)dt+σϕ(S(t),t)S(t)dW(t)dS(t) = \mu_\theta(S(t), t) \, dt + \sigma_\phi(S(t), t) \, S(t) \, dW(t)

Questo è un approssimatore universale per i processi di diffusione: qualsiasi processo Ito con precisione arbitraria data una capacità sufficiente.

ODE latenti per dati mancanti e asincroni

Stato continuo latente che ricostruisce i dati asincroni mancanti

L’ODE Latente (Rubanova, Chen, Duvenaud, 2019) accoppia un’ODE neurale con una VAE: le serie finanziarie sono osservazioni rumorose di un processo sottostante fluido, appreso in uno spazio latente a bassa dimensione.

  1. Rete di riconoscimento: un'ODE-RNN che corre a ritroso attraverso le osservazioni per produrre q(z0x1:N)q(z_0 | x_{1:N}).
  2. Dinamiche latenti: z(t)=z(t0)+t0tfθ(z(s),s)dsz(t) = z(t_0) + \int_{t_0}^{t} f_\theta(z(s), s)\, ds.
  3. Decodificatore: x^(t)=Decoder(z(t))\hat{x}(t) = \text{Decoder}(z(t)), valutabili in ogni momento richiesto.

La perdita è l'ELBO standard:

L=Eq(z0)[i=1Nlogp(xiz(ti))]KL(q(z0x1:N)p(z0))\mathcal{L} = \mathbb{E}_{q(z_0)} \left[ \sum_{i=1}^{N} \log p(x_i | z(t_i)) \right] - \text{KL}(q(z_0 | x_{1:N}) \| p(z_0))

Stima dello stato del portafoglio: da sparse osservazioni asincrone tra le risorse, dedurre uno stato latente continuo che cattura le dinamiche congiunte - essenzialmente una versione non lineare e appresa del filtro di Kalman.

Imputazione dei dati mancanti: le soste e gli intervalli del fine settimana ottengono una traiettoria latente dolcemente interpolata; il decodificatore produce percorsi plausibili attraverso lo spazio vuoto con incertezza dal VAE posteriore.

Fusione multifrequenza: chiusure giornaliere, VWAP intraday e dati tick in un unico modello senza griglia temporale condivisa.

Flussi normalizzanti continui per le distribuzioni dei rendimenti

Flusso continuo che modella una distribuzione dei rendimenti a coda pesante

I CNF utilizzano un ODE neurale per trasformare una semplice distribuzione di base in un bersaglio complesso. La trasformazione è dz(t)dt=f(z(t),t,θ)\frac{dz(t)}{dt} = f(z(t), t, \theta)e la densità logaritmica segue il cambiamento istantaneo delle variabili:

logp(z(t))t=tr(fz(t))\frac{\partial \log p(z(t))}{\partial t} = -\text{tr}\left(\frac{\partial f}{\partial z(t)}\right)

Integra lo stato e la densità di log in avanti da z(0)p0z(0) \sim p_0 ottenere z(T)z(T) E logp(z(T))\log p(z(T)). La traccia Jacobiana è stimata con lo stimatore stocastico di Hutchinson per la scalabilità.

I rendimenti delle criptovalute sono leptokurtici e distorti negativamente - misurati su dati reali in previsione della volatilità di GARCH e GARCH asimmetrico e effetto leva - e un CNF apprende quella forma invece di assumerla. Condizionare il flusso sulle variabili di stato fa sì che la forma cambi con il regime. Poiché la densità è esatta anziché approssimativa, può alimentare le metriche di coda calcolate in backtest Monte Carlo e bootstrap e la costruzione CVaR in la pipeline del portafoglio HRP/CVaR; la struttura della coda congiunta è gestita separatamente in modelli copula per rischio congiunto.

Stima condizionale della densità

L'inquadramento utile è un contrasto diretto tra tre approcci pubblicati allo stesso problema. TFT fornisce un insieme fisso di quantili da uno strato di output quantile. Previsione conforme ti offre intervalli calibrati con una garanzia di copertura. Un CNF condizionale fornisce una densità esatta e differenziabile:

dz(t)dt=f(z(t),t,featurest,θ)\frac{dz(t)}{dt} = f(z(t), t, \text{features}_t, \theta)

La differenziabilità è la proprietà distintiva: la densità può risiedere all'interno di un obiettivo a valle ed essere propagata all'indietro, cosa che né i quantili fissi né gli intervalli conformi supportano. Se la densità esatta valga il suo costo rispetto ai quantili TFT sullo stesso target non è stato testato qui.

Confronto con alternative discrete

Modelli discreti e continui a confronto su osservazioni condivise

Immobile LSTM/GRU Trasformatore ODE neurale SDE neurale
Gestione irregolare del tempo Scarso (necessita di imbottitura) Codifica posizionale Nativo Nativo
Memoria (formazione) O(T)O(T) O(T2)O(T^2) O(1)O(1) aggiunto O(1)O(1) aggiunto
Quantificazione dell'incertezza No (deterministico) No (deterministico) Via ensemble Nativo
Interpolazione tra osservazioni No No
Densità di tempo continuo No No Via CNF Tramite misura del percorso
Costo computazionale Basso Moderato Variabile (risolutore) Alto (solutore SDE)

La metà di questa tabella relativa al rapporto LSTM rispetto all'attenzione è discussa in dettaglio, con benchmark, nell'articolo TFT — vedere le sezioni "TFT vs LSTM vs Vanilla Transformer" e "Quando LSTM vince ancora". Le colonne nuove qui sono le due a destra, e le righe che decidono le domande H2/H3 sono le ultime due.

Implementazione Python con torchdiffeq

Risolutore di equazioni differenziali neurali come orbita computazionale precisa

torchdiffeq fornisce solutori ODE con propagazione all'indietro aggiunta.

ODE neurale di base per la dinamica dei prezzi

import torch
import torch.nn as nn
from torchdiffeq import odeint_adjoint as odeint

class PriceDynamics(nn.Module):
    """Neural network defining dh/dt = f(h, t)."""

    def __init__(self, hidden_dim: int = 64):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(hidden_dim, 128),
            nn.Tanh(),
            nn.Linear(128, 128),
            nn.Tanh(),
            nn.Linear(128, hidden_dim),
        )

    def forward(self, t, h):
        return self.net(h)


class NeuralODEPredictor(nn.Module):
    """
    Encode observed features -> latent state,
    evolve via Neural ODE,
    decode to price prediction.
    """

    def __init__(self, input_dim: int, hidden_dim: int = 64):
        super().__init__()
        self.encoder = nn.Linear(input_dim, hidden_dim)
        self.dynamics = PriceDynamics(hidden_dim)
        self.decoder = nn.Linear(hidden_dim, 1)

    def forward(self, x0, eval_times):
        """
        x0:         (batch, input_dim) features at t=0
        eval_times: (T,) times at which to evaluate the ODE
        Returns:    (T, batch, 1) predictions
        """
        h0 = self.encoder(x0)                          # (batch, hidden_dim)
        h_traj = odeint(self.dynamics, h0, eval_times,
                        method='dopri5', rtol=1e-5, atol=1e-7)
        return self.decoder(h_traj)

ODE-RNN per dati di tick irregolari

Questo è il braccio sperimentale. La linea di base che deve battere è nn.GRUCell sullo stesso flusso con log(t_next - t_prev) concatenato a x.

class ODERNNCell(nn.Module):
    """Single step: ODE-evolve, then RNN-update."""

    def __init__(self, input_dim: int, hidden_dim: int = 64):
        super().__init__()
        self.dynamics = PriceDynamics(hidden_dim)
        self.gru_cell = nn.GRUCell(input_dim, hidden_dim)

    def forward(self, h, x, t_prev, t_next):
        times = torch.tensor([t_prev, t_next], dtype=torch.float32)
        h_evolved = odeint(self.dynamics, h, times,
                           method='dopri5')[-1]  # state at t_next
        h_updated = self.gru_cell(x, h_evolved)
        return h_updated


class ODERNN(nn.Module):
    """Process irregularly-sampled sequence."""

    def __init__(self, input_dim: int, hidden_dim: int = 64):
        super().__init__()
        self.cell = ODERNNCell(input_dim, hidden_dim)
        self.decoder = nn.Linear(hidden_dim, 1)
        self.hidden_dim = hidden_dim

    def forward(self, observations, times):
        """
        observations: list of (batch, input_dim) tensors
        times:        list of floats, observation timestamps
        """
        batch_size = observations[0].shape[0]
        h = torch.zeros(batch_size, self.hidden_dim)

        outputs = []
        for i in range(len(observations)):
            t_prev = 0.0 if i == 0 else times[i - 1]
            h = self.cell(h, observations[i], t_prev, times[i])
            outputs.append(self.decoder(h))

        return torch.stack(outputs)  # (seq_len, batch, 1)

Ciclo di allenamento

def train_neural_ode(model, train_loader, epochs=100, lr=1e-3):
    optimizer = torch.optim.Adam(model.parameters(), lr=lr)
    scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(
        optimizer, T_max=epochs
    )

    for epoch in range(epochs):
        epoch_loss = 0.0
        for batch in train_loader:
            features, times, targets = batch
            optimizer.zero_grad()

            predictions = model(features, times)
            loss = torch.nn.functional.mse_loss(predictions, targets)

            loss.backward()
            torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
            optimizer.step()

            epoch_loss += loss.item()

        scheduler.step()

        if (epoch + 1) % 10 == 0:
            avg_loss = epoch_loss / len(train_loader)
            print(f"Epoch {epoch+1}/{epochs}, Loss: {avg_loss:.6f}")

Flusso di normalizzazione continuo per le distribuzioni dei rendimenti

from torchdiffeq import odeint

class CNFDynamics(nn.Module):
    """Dynamics for continuous normalizing flow."""

    def __init__(self, dim: int = 1, hidden_dim: int = 64):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(dim + 1, hidden_dim),  # +1 for time
            nn.Tanh(),
            nn.Linear(hidden_dim, hidden_dim),
            nn.Tanh(),
            nn.Linear(hidden_dim, dim),
        )
        self.dim = dim

    def forward(self, t, state):
        z = state[..., :self.dim]
        t_expand = t.expand(z.shape[0], 1)
        zt = torch.cat([z, t_expand], dim=-1)
        dz = self.net(zt)

        e = torch.randn_like(z)
        e_dz = torch.autograd.grad(
            dz, z, e, create_graph=True
        )[0]
        trace_jac = (e_dz * e).sum(dim=-1, keepdim=True)

        return torch.cat([dz, -trace_jac], dim=-1)


class ReturnDistributionCNF(nn.Module):
    """Model return distributions with continuous normalizing flows."""

    def __init__(self, dim: int = 1):
        super().__init__()
        self.dynamics = CNFDynamics(dim)
        self.dim = dim

    def log_prob(self, x):
        """Compute log probability of observed returns."""
        log_p0 = torch.zeros(x.shape[0], 1)
        state0 = torch.cat([x, log_p0], dim=-1)
        state0.requires_grad_(True)

        times = torch.tensor([1.0, 0.0])  # backward
        state_T = odeint(self.dynamics, state0, times,
                         method='dopri5')[-1]

        z_T = state_T[..., :self.dim]
        delta_log_p = state_T[..., self.dim:]

        log_p_base = -0.5 * (z_T ** 2 + torch.log(
            torch.tensor(2 * torch.pi)
        )).sum(dim=-1, keepdim=True)

        return log_p_base + delta_log_p

    def sample(self, n_samples: int):
        """Generate samples from learned distribution."""
        z0 = torch.randn(n_samples, self.dim)
        log_p0 = torch.zeros(n_samples, 1)
        state0 = torch.cat([z0, log_p0], dim=-1)

        times = torch.tensor([0.0, 1.0])  # forward
        state_T = odeint(self.dynamics, state0, times,
                         method='dopri5')[-1]

        return state_T[..., :self.dim]

Note pratiche

Compromesso equilibrato tra accuratezza numerica e velocità di inferenza

Queste sono le cose che ti morderanno durante l'esecuzione dell'esperimento sopra.

Scelta e velocità del risolutore

dopri5 fornisce garanzie di accuratezza ma costi di calcolo variabili, motivo per cui H3 è un'ipotesi separata da H2: un vantaggio che esiste solo con un risolutore adattivo potrebbe non sopravvivere a un budget di latenza attivo. I solutori a passo fisso (Eulero, RK4) forniscono una latenza prevedibile a scapito della precisione. Compromesso pratico: allenarsi con dopri5, eseguire l'implementazione con un solutore a passo fisso calibrato per corrispondere all'output del solutore adattivo su dati rappresentativi e misurare il divario anziché dare per scontato che sia piccolo.

odeint(f, h0, t, method='dopri5', rtol=1e-6, atol=1e-8)

odeint(f, h0, t, method='euler', options={'step_size': 0.1})

Per problemi rigidi in prossimità di salti discontinui, method='implicit_adams' O method='scipy_solver'.

Stabilità numerica

Se fθf_\theta emette valori elevati, lo stato diverge. Mitigazioni:

  • Normalizzazione spettrale su strati di fθf_\theta per controllare la costante di Lipschitz.
  • Ritaglio gradiente durante l'allenamento (mostrato nel ciclo di allenamento sopra).
  • Normalizzazione temporale: ridimensiona i timestamp su [0,1][0, 1].
  • Regolamentazione sulla norma dinamica: add λfθ(h,t)2\lambda \|f_\theta(h, t)\|^2 alla perdita.

Intervallo di integrazione

Per i dati che coprono mesi, non integrare da t=0t = 0 A t=10,000t = 10{,}000 minuti:

t_normalized = (timestamps - timestamps[0]) / (timestamps[-1] - timestamps[0])

Ciò mantiene il risolutore in un regime numericamente amichevole e rende le dinamiche apprese invarianti di scala. È importante anche per il confronto: un ODE-RNN non normalizzato può perdere rispetto alla linea di base del GRU per ragioni puramente numeriche, il che sarebbe un artefatto di misurazione piuttosto che un risultato.

Gestione di scale temporali multiple

I mercati hanno dinamiche su scala di microsecondi, secondi, minuti e giornalieri contemporaneamente, e un singolo ODE neurale potrebbe avere difficoltà a mantenerli tutti. Opzioni: impila più blocchi ODE su scale temporali diverse; espandere la dimensione nascosta sia per la capacità veloce che per quella lenta; oppure eseguire ODE neurali separati per banda di frequenza e uscite con fusibile. (Questa è una domanda sulla capacità del modello, distinta dalla domanda sulla fedeltà del backtest in approfondimento sulla risoluzione adattiva.)

Direzioni di ricerca aperte

Aprire traiettorie di ricerca in tempo continuo che si irradiano da un modello

SDE di salto neurale: aggiungi una componente di salto appreso per dislocazioni improvvise (arresti improvvisi, sorprese sugli utili):

dh=fdt+gdW+Rγ(h,z)N~(dt,dz)dh = f \, dt + g \, dW + \int_{\mathbb{R}} \gamma(h, z) \, \tilde{N}(dt, dz)

Dove N~\tilde{N} è una misura casuale di Poisson compensata e γ\gamma un kernel di salto appreso.

Equazioni differenziali controllate neuralmente (CDE neurali): sostituisci il processo di Wiener con un segnale di guida generale, lasciando che i flussi di dati osservati guidino la dinamica. Naturale per il flusso degli ordini, dove il flusso di scambi e quotazioni guida lo stato latente del mercato.

Simulazione di mercato differenziabile: utilizza un SDE neurale come modello generativo all'interno di un simulatore differenziabile e addestra strategie end-to-end propagandosi all'indietro attraverso di esso tramite l'aggiunto. Strategia e modello di mercato coevolvono.

ODE neurali basate sulla fisica: i PINN incorporano un residuo di equazione differenziale nella perdita: la tecnica stessa è introdotta nell'articolo di Navier-Stokes. L’applicazione finanziaria consiste nell’imporre condizioni di non arbitraggio, parità put-call e martingala come termini di penalità o vincoli rigidi sulle dinamiche apprese.

Conclusione

Dinamiche continue e fluide che si risolvono in una conclusione misurata

L’inquadramento temporale continuo è strutturalmente corretto: i mercati sono processi continui osservati in tempi discreti e irregolari, e i modelli dovrebbero rispettarlo. La toolchain è matura – torchdiffeq, torchsde, semplice PyTorch per il resto - e i costi noti sono la velocità del risolutore e la stabilità numerica su lunghi intervalli di integrazione.

Ciò che non è stabilito è la parte che decide se qualcosa di questo appartiene a uno stack di produzione. La correttezza strutturale non è prova di un vantaggio predittivo, e il vantaggio specifico rivendicato per la dinamica continua rispetto a a log(Δt)\log(\Delta t) questa caratteristica non è stata misurata qui sui dati commerciali reali. Fino a quando la tabella H2 sopra non viene compilata, tratta tutto ciò che segue come un'ipotesi ben specificata con un'implementazione funzionante allegata, non come un risultato.


Riferimenti

  • Chen, R.T.Q., Rubanova, Y., Bettencourt, J., Duvenaud, D. (2018). Equazioni differenziali ordinarie neurali. NeurIPS 2018. arXiv:1806.07366
  • Rubanova, Y., Chen, R.T.Q., Duvenaud, D. (2019). ODE latenti per serie temporali campionate in modo irregolare. NeurIPS 2019. arXiv:1907.03907
  • Jia, J., Benson, A.R. (2019). Equazioni differenziali stocastiche di salto neurale. NeurIPS 2019.
  • Kidger, P., Morrill, J., Foster, J., Lyons, T. (2020). Equazioni differenziali a controllo neurale per serie temporali irregolari. NeurIPS 2020.
  • Hasan, A., Pereira, J.M., Farsiu, S., Carin, L. (2021). Modelli di equazioni differenziali stocastiche della rete neurale con applicazioni alla previsione dei dati finanziari. arXiv:2111.13164
  • torchdiffeq: github.com/rtqichen/torchdiffeq
  • Tutorial di deep learning UvA - ODE neurali: uvadlc-notebooks.readthedocs.io
Disclaimer: le informazioni fornite in questo articolo hanno solo scopo didattico e informativo e non costituiscono consulenza finanziaria, di investimento o di trading. Il trading di criptovalute comporta un rischio significativo di perdita.

Autori

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

Resta un Passo Avanti al Mercato

Iscriviti alla nostra newsletter per approfondimenti esclusivi sul trading con IA, analisi di mercato e aggiornamenti sulla piattaforma.

Rispettiamo la tua privacy. Annulla l'iscrizione in qualsiasi momento.