Trading Quantitativo Multi-Strategia per gli investitori

In questo articolo introduciamo DataInvestor, un progetto open-source di TradingQuant.it per il backtesting di strategie quantitative e sistematiche per investimenti e la gestione attiva di portafogli. Se cerchi un framework potente per la simulazione di portafogli quantitativi, DataInvestor è la soluzione ideale. Supporta il trading quantitativo multi-strategia in Python, permettendo l’analisi di strategie dinamiche su scala multi-asset, con un controllo totale sulla logica di portafoglio e sull’allocazione. DataInvestor è un software sviluppato in Python disponibile su github.

Un sistema di trading quantitativo multi-strategia

Abbiamo sviluppato il precedente framework DataTrader come un sistema modulare di backtesting basato su eventi, pensato principalmente per strategie sul mercato azionario. Tuttavia, ci siamo presto resi conto che i trader retail e i fondi istituzionali adottano un approccio diverso rispetto a quello previsto inizialmente in DataTrader.

DataTrader consente una gestione di base del portafoglio, ma non raggiunge la complessità di un sistema di trading quantitativo multi-strategia e multi-account, di cui molti trader hanno bisogno. Per questo motivo abbiamo deciso di riprogettare da zero un sistema più avanzato. Il nostro obiettivo è creare uno strumento in grado di simulare grandi allocazioni di capitale, partendo dalla fase di backtesting e ricerca, passando per la simulazione forward (ad esempio con modelli matematici per l’allocazione degli asset), fino al paper trading e infine al trading live.

Per riuscirci, vogliamo trasformare DataTrader da semplice backtester azionario a un vero e proprio motore di trading in tempo reale, con funzionalità di reporting delle performance su più classi di asset, valute e strumenti, attraverso un framework per costruire portafogli in stile istituzionale. Nei prossimi paragrafi descriviamo in dettaglio alcune delle principali componenti che abbiamo progettato.

Brokerage

Attualmente DataTrader non include un vero concetto di “broker” o “fornitore di liquidità” che il sistema di trading può usare per ottenere dati di mercato in tempo reale o monitorare il portafoglio. In DataInvestor abbiamo introdotto una gerarchia di classi Broker, che gestisce il tracciamento delle posizioni. 

Abbiamo previsto un account “master” che gestisce più account secondari, ciascuno con un proprio PnL. Ogni conto può usare una valuta diversa, permettendo portafogli multiregione. Aggregando i PnL dei sottoconti otteniamo il PnL complessivo, che convertiamo in mark-to-market nelle diverse valute usando dati F/X point-in-time.

Facciamo in modo che la strategia si interfacci al broker solo per depositi, prelievi, gestione dei sottoconti, accesso ai dati di mercato ed esecuzione degli ordini. In questo modo centralizziamo tutta la logica di gestione del portafoglio e delle posizioni nella classe broker. Riteniamo che questo modello sia abbastanza flessibile da supportare sia trader retail che piccoli fondi o family office con requisiti meno stringenti.

In futuro prevediamo di introdurre una gerarchia di classi LiquidityProvider, di cui Broker sarà una sottoclasse, per gestire fornitori di accesso ai mercati come prime brokerage o ECN.

Margine

In DataInvestor abbiamo implementato metodi realistici per calcolare il margine. Questi calcoli si basano inizialmente sui requisiti di margine di Interactive Brokers. IB offre due tipi di conto a margine: uno standard Reg T e uno più sofisticato chiamato “portafoglio commodities”, che considera le coperture per ridurre il rischio. Entrambi richiedono calcoli complessi per margini e scenari di liquidazione.

Il prestito su margine comporta il pagamento di interessi calcolati in modo complesso su più valute e in base a tassi point-in-time (come il LIBOR). Per vedere come IB gestisce gli interessi, puoi consultare la loro pagina sul programma interessi. DataInvestor gestisce il margine e gli interessi in questo modo, permettendo posizioni con leva su futures e f/x.

Commissioni

In DataInvestor calcoliamo le commissioni con estrema precisione. I broker offrono strutture a più livelli, variabili in base al patrimonio totale e alla frequenza delle operazioni, con tariffe introduttive nella fase iniziale. Consideriamo anche le tasse sulle transazioni, che si applicano alla maggior parte degli asset, salvo eccezioni.

Poiché le commissioni possono incidere significativamente, per un backtest accurato calcoliamo questi costi in modo dettagliato. Usiamo funzioni di base modellate su Interactive Brokers.

Questo è solo un esempio. Prevediamo di integrare più broker per rendere DataInvestor un backtester “internazionale”, non limitato alle regole di IB.

Flussi di cassa

Con DataInvestor gestiamo i flussi di cassa in modo flessibile. Possiamo aggiungere o rimuovere contanti dal capitale iniziale durante il periodo di simulazione della strategia.

Questo approccio riflette la realtà: i trader retail di livello elevato spesso prelevano dividendi o profitti come reddito oppure versano denaro regolarmente per acquistare nuovi asset. Per questo motivo, depositi e prelievi sono eventi frequenti.

Abbiamo integrato funzionalità per trasferire fondi in entrata e in uscita dal broker, e per allocarli su diversi sottoconti in varie valute. Questa funzione è fondamentale anche per la gestione dei dividendi, come vedremo nel prossimo paragrafo.

Dividendi

In DataInvestor trattiamo i dividendi in contanti come iniezioni dirette in un sottoconto. Per farlo, gestiamo le date di stacco dividendo e verifichiamo che la posizione fosse già aperta prima dello stacco.

In questo modo possiamo gestire anche operazioni societarie più complesse, come i frazionamenti stock-for-stock. Inserendo i dividendi nel sottoconto, permettiamo all’algoritmo di reinvestirli liberamente, senza costringere la strategia a usare una serie di rendimento totale.

Exchange e Asset

In DataTrader usiamo un’entità PriceHandler per generare oggetti BarEvent o TickEvent dai dati di mercato.

In DataInvestor abbiamo ricostruito questa logica. Abbiamo separato i dati OHLCV dagli eventi di trading. Un “timer di simulazione” genera eventi con timestamp a una frequenza specifica (giornaliera o intraday), consultando un oggetto Exchange per conoscere gli orari di apertura e costruire una sequenza di eventi su cui il backtest può operare.

Invece di lavorare direttamente sulle “barre”, la strategia richiama il metodo get_latest_price_volume(asset), che restituisce il prezzo di mercato più recente come interpretato dal broker. Internamente, interroghiamo il DataFrame dei dati OHLCV per ottenere il valore corretto al timestamp desiderato.

In questo modo separiamo la frequenza dei dati dalla frequenza dei segnali. Ad esempio, possiamo ribilanciare il portafoglio ogni ora anche con dati a un minuto. Questo rende il passaggio dal backtest al trading reale molto più semplice.

Inoltre, l’oggetto Exchange conosce gli orari di chiusura e le festività per ciascun mercato, sia passati che futuri, riducendo il divario tra simulazione e operatività reale.

Modelli Alpha/Previsioni

In tutti i nostri precedenti framework di backtesting basati sugli eventi, abbiamo utilizzato un modello Strategy in cui, a ogni iterazione, acquisivamo i dati di mercato e generavamo oggetti Order. Questo approccio è certamente sufficiente per un semplice sistema basato su eventi con ciclo while. Tuttavia, non risponde alle esigenze di ricerca di un moderno hedge fund. Dobbiamo adottare un approccio più sofisticato alla previsione.

Negli ambienti quant professionali, lavoriamo spesso allo sviluppo degli “alpha”, ovvero previsioni su uno specifico asset. Combiniamo poi questi alpha in “alpha pesati”, che inseriamo all’interno di un modello di costruzione del portafoglio, integrandoli con logiche per la gestione del rischio e stime dei costi di transazione per i ribilanciamenti.

In DataInvestor abbiamo ampliato il concetto di Signal, trasformandolo in una sottoclasse di oggetti Forecast, che generiamo tramite un’entità AlphaModel. Questo approccio ci consente di implementare un sistema avanzato multi-strategia, in cui combiniamo più AlphaModel per produrre diverse combinazioni di Forecast. Va sottolineato che l’AlphaModel non genera direttamente un’entità Order. Demandiamo questo compito all’entità PortfolioConstructionModel, descritta nel paragrafo successivo.

Questo framework si rivela estremamente flessibile, poiché associamo ogni entità Forecast a un asset, a un valore numerico in virgola mobile (che può rappresentare praticamente qualsiasi tipo di “segnale”) e a una data di validità della previsione. All’interno del codice, abbiamo incluso alcuni esempi che illustrano come questo approccio consenta di implementare tipiche strategie quant come il momentum delle serie temporali, l’arbitraggio statistico, la costruzione di fattori e persino strategie basate su dati alternativi.

Costruzione del portafoglio

Consideriamo la costruzione del portafoglio come il passaggio più importante nella creazione di un sistema di trading quantitativo multi-strategia e di un modello quantitativo sofisticato in contesti istituzionali. In DataInvestor abbiamo introdotto una nuova entità, PortfolioConstructionModel (PCM), che sostituisce gli oggetti PositionSizer e RiskManager di DataTrader, i quali operavano su un insieme di oggetti Order generati da un Strategy.

Con il PCM di DataInvestor gestiamo un RiskModel e un TransactionCostModel, che forniscono “opinioni” sull’opportunità di modificare, annullare o aggiungere un insieme di istanze Order. Ad esempio, il modello di rischio può suggerirci di introdurre una copertura o ridurre l’esposizione a un settore specifico. Il modello dei costi di transazione può stimare che il costo di ribilanciamento sia eccessivo rispetto al rendimento atteso, e quindi decidiamo di annullare l’ordine.

Il compito del PCM consiste nel valutare le “opinioni” provenienti dall’alpha model, dal modello di rischio e da quello dei costi di transazione, per costruire il portafoglio desiderato. Confrontiamo questo portafoglio con quello attuale presso il broker e generiamo una serie di ordini “delta” per allineare il portafoglio del broker a quello desiderato.

Possiamo costruire il portafoglio in diversi modi. In DataInvestor abbiamo incluso alcuni metodi di esempio da modificare o utilizzare come base, a seconda delle esigenze. Tra questi troviamo strutture semplici come i “pesi uguali”, i “pesi con proporzione fissa” e i “pesi con volume inverso” (la cosiddetta “risk parity”). Prevediamo inoltre di implementare metodi più avanzati come l’ottimizzazione della varianza media (basata sulla Modern Portfolio Theory), l’approccio Black-Litterman e tecniche più recenti come la Parità del rischio gerarchico di Marcos Lopez de Prado.

Aggiungendo e testando questi esempi, vogliamo permettere agli altri utenti di modificarli in base alle proprie esigenze e ridurre al minimo il “time to market” per lo sviluppo della propria strategia.

Gestione del rischio e dei costi di transazione

In DataTrader, le classi RiskManager e PositionSizer possono generare confusione. Abbiamo rivisto l’approccio, adottando uno stile più istituzionale nella costruzione del portafoglio e un controllo del rischio più accurato. In questo modo possiamo considerare DataInvestor come un sistema di trading quantitativo multi-strategia

Abbiamo strutturato la gerarchia delle classi RiskModel per permettere il calcolo di varie metriche di rischio, in base alla strategia di trading adottata. Ad esempio, possiamo monitorare la volatilità degli asset tramite deviazioni standard storiche o modelli di volatilità stocastica. Possiamo anche monitorare il rischio settoriale e generare avvisi se rileviamo esposizioni eccessive in uno specifico settore.

Nelle grandi società di asset allocation, effettuiamo spesso ribilanciamenti settimanali o mensili per ridurre al minimo il tracking error. Dobbiamo trovare un equilibrio tra la riduzione del tracking error e la minimizzazione dei costi derivanti dai ribilanciamenti frequenti. Per questo, abbiamo introdotto la gerarchia delle classi TransactionCostModel, che ci aiuta a bilanciare i costi, suggerendo di ribilanciare solo quando l’impatto sulla performance del portafoglio è accettabile.

Entrambi questi modelli forniscono indicazioni al PCM, aiutandoci nella costruzione del portafoglio ideale. Ovviamente alcuni modelli si sovrappongono. Ad esempio, in un ottimizzatore della varianza media, dobbiamo decidere se sia compito del RiskManager o del PCM calcolare la matrice di covarianza tra gli asset. Il framework DataInvestor ci offre abbastanza flessibilità per lasciare questa scelta al portfolio manager o al trader retail.

Ciclo di eventi di backtest

Nel simulare un backtest con DataTrader, utilizziamo all’interno della classe TradingSession un semplice ciclo while come event handler, che invia eventi ai vari componenti del sistema. Per avere un sistema di trading quantitativo multi-strategia abbiamo separato gli eventi di mercato dai dati a barre OHLCV, così possiamo gestire eventi “pre-mercato” e “post-mercato” per ogni giorno di trading. 

Questi eventi ci consentono di calcolare split azionari, dividendi in contanti, flussi di cassa degli investitori, calcoli mark-to-market e altri vincoli specifici dei broker o exchange, al di fuori della sessione principale di negoziazione. Per ogni entità simulata che deve tenere traccia del tempo, utilizziamo un metodo update, che assicura l’assenza di propagazione delle informazioni dal futuro al passato, evitando il cosiddetto lookahead bias. Questo metodo mantiene anche la sincronizzazione tra tutti i componenti del sistema.

Cosa abbiamo implementato finora

Ad oggi abbiamo già completato la maggior parte della gerarchia Broker, ad eccezione della gestione dei margini, dei future e del f/x. In particolare, abbiamo incluso i metodi per gestire i flussi di cassa, i piani di commissioni per alcuni broker, la gestione base dei dividendi in contanti e il monitoraggio generale delle posizioni.

Abbiamo anche sviluppato una semplice gerarchia Exchange, anche se non abbiamo ancora definito le specifiche entità di calendario per le principali borse come il NYSE e il LSE. Inoltre, gestiamo l’acquisizione e la distribuzione dei dati delle barre OHLCV.

Abbiamo parzialmente sviluppato la gerarchia PortfolioConstructionModel, includendo i metodi EqualWeightPCM e FixedWeightPCM. Fondamentalmente, tutto il codice attualmente presente nei rami di refactoring ha ora una copertura del 100%.

Come procederà lo sviluppo

Abbiamo rivisto radicalmente il processo di sviluppo di DataInvestor. Abbiamo adottato il più avanzato Gitflow Workflow per il controllo delle versioni e l’integrazione continua. Questo significa che svilupperemo tutte le funzionalità in rami indipendenti, separati dal ramo principale develop. Periodicamente, uniremo queste funzionalità in develop e creeremo un ramo release-*.*.* con uno specifico numero di versione. Questo ramo di rilascio sarà “fissato nel tempo”, completo di test e documentazione, e non consentirà l’aggiunta di nuove funzionalità per quella versione.

Quando una versione sarà pronta per il rilascio, la uniremo al ramo master, insieme al ramo develop. Contrassegneremo poi questa versione con un numero specifico per garantire un corretto controllo. In questa fase, inizieremo ad aggiungere nuove funzionalità a develop e il ciclo continuerà.

Abbiamo introdotto una delle modifiche più significative al flusso di lavoro: garantiremo il supporto a tutte le versioni di Python pari o superiori alla 3.7. In futuro, potremmo abbandonare il supporto alla versione 3.7, se necessario per introdurre nuove funzionalità. Oggi, questo non rappresenta più un problema grazie alla diffusione di Anaconda di Continuum Analytics, che rende semplice installare uno stack scientifico Python 3.7 su Windows, Mac e Linux.

Abbiamo anche modificato la gestione dei test, che ora richiede una copertura del codice del 100% per unire un ramo di rilascio a master. Anche se può sembrare un obiettivo impegnativo, crediamo che la “criticità della missione” del sistema lo renda essenziale. Ogni riga di codice dovrà essere coperta da almeno un test unitario. Abbiamo lavorato con impegno per raggiungere questo traguardo nella nuova versione e continueremo su questa linea.

Accogliamo con piacere le pull request della community, ma chiediamo di confrontarle sempre con il ramo develop e non con master, così da mantenere il flusso di lavoro Gitflow. Inoltre, elencheremo una serie di funzionalità desiderate e forniremo linee guida per sviluppatori, in modo da facilitare i contributi futuri.

Conclusioni

Al momento, abbiamo aggiunto la maggior parte del lavoro nel nostro repository privato interno, separato dal repository pubblico disponibile qui. Nelle prossime settimane renderemo disponibile il codice sui rami di sviluppo, così che chi è interessato possa partecipare all’alpha testing iniziale.

Il nostro obiettivo è avere un sistema ben testato pronto per il beta testing entro la fine dell’anno. Forniremo anche aggiornamenti più regolari sull’avanzamento dello sviluppo man mano che aggiungiamo nuove funzionalità. DataInvestor è un sistema di trading quantitativo multi-strategia open source, disponibile gratuitamente e guidato dalla community.

Il codice completo presentato in questo articolo, basato sul framework di trading quantitativo event-driven DataInvestor, è disponibile nel seguente repository GitHub: https://github.com/tradingquant-it/DataInvestor.

Benvenuto su TradingQuant!

Sono Gianluca, ingegnere software e data scientist. Sono appassionato di coding, finanza e trading. Leggi la mia storia.

Ho creato TradingQuant per aiutare le altre persone ad utilizzare nuovi approcci e nuovi strumenti, ed applicarli correttamente al mondo del trading.

TradingQuant vuole essere un punto di ritrovo per scambiare esperienze, opinioni ed idee.

SCRIVIMI SU TELEGRAM

Per informazioni, suggerimenti, collaborazioni...

Torna in alto