“Qual è il miglior linguaggio di programmazione per il trading algoritmico?” è una delle domande più frequenti. In breve la risposta è che non esiste un linguaggio “migliore”. Bisogna considerare molti aspetti come i parametri strategici, le prestazioni, la modularità, lo sviluppo, la resilienza e i costi. In questo articolo si descrive le componenti fondamentali dell’architettura del sistema di trading algoritmico, e come le decisioni relative all’implementazione influenzano la scelta del linguaggio.

In primo luogo, saranno presi in considerazione i componenti principali di un sistema di trading algoritmico, come gli strumenti di ricerca, l’ottimizzatore del portafoglio, la gestione del rischio e il motore di esecuzione. Successivamente, saranno esaminate come le diverse strategie di trading influenzino il design del sistema. In particolare, saranno discussi sia la frequenza degli scambi che il probabile volume di scambi.

Una volta selezionata la strategia di trading, è necessario progettare l’intero sistema. Ciò include la scelta dell’hardware, dei sistemi operativi e della resilienza del sistema contro eventi rari e potenzialmente catastrofici. Mentre l’architettura viene presa in considerazione, è necessario prestare attenzione alle prestazioni, sia per gli strumenti di backtesting, sia per l’ambiente di esecuzione live.

Cosa fa un Sistema di Trading Algoritmico?

Prima di decidere il linguaggio “migliore” con cui implementare un sistema di trading automatico è necessario definirne i requisiti. Il sistema sarà basato esclusivamente sull’esecuzione? Il sistema richiederà un modulo di gestione del rischio o di ottimizzazione del portafoglio? Il sistema richiederà un backtester ad alte prestazioni? Per la maggior parte delle strategie il sistema di trading può essere suddiviso in due categorie: Ricerca e Generazione di segnali.

La ricerca riguarda la valutazione di una prestazione strategica rispetto ai dati storici. Il processo di valutazione di una strategia di trading su dati di mercato precedenti è noto come backtesting. La dimensione dei dati e la complessità algoritmica avranno un grande impatto sull’intensità computazionale del backtester. La velocità della CPU e la concorrenza sono spesso i fattori limitanti nell’ottimizzazione della velocità di esecuzione della ricerca.

La generazione del segnale riguarda la generazione di un insieme di segnali di trading da un algoritmo e l’invio di tali ordini al mercato, solitamente tramite un broker. Per alcune strategie è richiesto un alto livello di prestazioni. Problemi di I / O come la larghezza di banda e la latenza della rete sono spesso il fattore limitante nell’ottimizzazione dei sistemi di esecuzione. Quindi la scelta dei linguaggi per ciascun componente dell’intero sistema potrebbe essere molto diversa.

Tipologia, Frequenza e Volume di una Strategia

Il tipo di strategia algoritmica utilizzata avrà un impatto sostanziale sul design del sistema. Sarà necessario considerare i mercati scambiati, i fornitori di dati esterni, la frequenza e il volume della strategia, il trade-off tra facilità di sviluppo e ottimizzazione delle prestazioni, nonché qualsiasi hardware personalizzato.

Le scelte tecnologiche per una strategia sul mercato azionario statunitense a bassa frequenza sono molto diverse da quelle di una strategia di arbitraggio statistico ad alta frequenza che opera sul mercato dei futures. Prima della scelta del linguaggio, devono essere valutati i vari fornitori di dati pertinenti alla strategia in questione.

Sarà necessario considerare la connettività verso il fornitore, la struttura delle API, la tempestività dei dati, i requisiti di archiviazione e la resilienza di fronte alla possibilità che un fornitore interrompa il flusso dati. È anche saggio avere un accesso rapido a più fornitori! I vari strumenti hanno tutti i propri formati e standard di archiviazione. Tutto questo deve essere preso in considerazione durante la progettazione della piattaforma.

La frequenza della strategia è probabilmente uno dei maggiori fattori che influenzano la scelta dello stack tecnologico. Le strategie che impiegano i dati su timeframe ridotti (come i minuti, o addirittura i secondi) richiedono un’analisi approfondita in termini di prestazioni.

Una strategia che opera sul secondo (cioè sui tick) comporta una progettazione focalizzata sulle prestazioni, come requisito principale. Per le strategie ad alta frequenza sarà necessario memorizzare e valutare una notevole quantità di dati di mercato. Software come HDF5 o kdb+ sono comunemente usati per questi ruoli.

Per elaborare gli ampi volumi di dati necessari per le applicazioni HFT, è necessario utilizzare un backtester e un sistema di esecuzione ampiamente ottimizzati. C / C ++ (probabilmente con un pò di assembler) è probabilmente il linguaggio ideale. Strategie ad altissima frequenza quasi certamente richiedono hardware personalizzato come FPGA, ed ottimizzazione del kernel e dell’interfaccia di rete.

Sistemi di Ricerca

I sistemi di ricerca consistono generalmente in una combinazione tra sviluppo interattivo e scripting automatizzato. Il primo si svolge all’interno di un IDE come Visual Studio, MatLab o R Studio. Quest’ultimo permette estesi calcoli numerici su moltissimi parametri e dati. Ciò porta alla scelta di un linguiggio che fornisca un ambiente semplice per testare il codice, ma fornisca anche prestazioni sufficienti per valutare le strategie multiparametriche.

I migliori IDE per questi scopi includono;

  • Microsoft Visual C++ / C#, che contiene estese utilità di debug, capacità di completamento del codice (tramite “Intellisense”) e panoramiche chiare dell’intero stack del progetto (tramite il database ORM, LINQ);
  • MatLab, che è progettato per l’algebra lineare numerica estesa e le operazioni vettorializzate, ma solamente in modalità da console interattiva;
  • R Studio, che avvolge la console del linguaggio statistico R in un IDE completo;
  • Eclipse IDE per Linux Java e C ++;
  • Python IDE semi-proprietari come Enthought Canopy, che includono librerie di analisi dei dati come NumPy, SciPy, scikit-learn e panda in un unico ambiente interattivo (console).

Per il backtesting numerico, sono datti tutti i suddetti linguaggi, anche se non è necessario utilizzare una GUI / IDE poiché il codice verrà eseguito “in background”. La prima considerazione in questa fase è quella della velocità di esecuzione. Un linguaggio compilato (come C ++) è spesso utile se le dimensioni dei parametri di backtesting sono grandi. 

Linguaggi interpretati come Python fanno spesso fanno uso di librerie ad alte prestazioni, come NumPy / Panda, per la fase di backtesting, al fine di mantenere un ragionevole grado di competitività con gli equivalenti codici compilati. In definitiva, il linguaggio scelto per il backtesting sarà determinato da specifiche esigenze algoritmiche e dalla gamma di librerie disponibili per quel linguaggio (maggiori dettagli in seguito). Tuttavia, il linguaggio utilizzato per i backtesting e gli ambienti di ricerca possono essere completamente indipendente da quelli utilizzati nell’ottimizzazione del portfolio, nella gestione del rischio e nelle componenti di esecuzione.

Ottimizzazione del Portafoglio e Gestione del Rischio

L’ottimizzazione del portafoglio e la gestione del rischio sono componenti spesso trascurate dai tradere algoritmici retail. Questo è quasi sempre un errore. Questi strumenti forniscono l’unico meccanismo con il quale preservare il capitale, che deve essere uno dei principali obiettivi di ogni trader. Non solo tentano di diminuire il numero di scommesse “rischiose”, ma minimizzano anche il tasso di insuccesso delle operazioni stesse, riducendo i costi di transazione.

Versioni sofisticate di questi componenti possono avere un effetto significativo sulla qualità e sulla costanza della redditività. Una strategia può essere resa stabile grazie a semplice modifiche sul meccanismo di ottimizzazione del portafoglio e di gestione del rischio, in modo da poter gestire più sistemi. Quindi devono essere considerati componenti essenziali all’inizio del progetto di un sistema di trading algoritmico.

Il lavoro del modulo di gestione del portafoglio consiste nel prendere in input un set di trade desiderati e produrre un sottoinsieme di trade effettivi che riducono al minimo il tasso di perdita, monitorando le esposizioni a vari fattori (come settori, classi di attività, volatilità ecc.) ed ottimizzare l’allocazione del capitale per le varie strategie.

La costruzione del portfolio spesso si riduce a un problema di algebra lineare (come la fattorizzazione della matrice) e quindi le prestazioni dipendono fortemente dalle performance dell’implementazione disponibile per gli algoritmi di algebra lineare numerica. Le librerie più usate sono  uBLAS, LAPACK e NAG per C ++. MatLab possiede anche funzioni native ampiamente ottimizzate che operano con le matrici. Python utilizza NumPy / SciPy per tali calcoli.

Un portafoglio costantemente riequilibrato richiederà una libreria nativa compilata (e ben ottimizzata!) al fine di eseguire queste operazioni matricali, se non si vuole creare un collo di bottiglia al sistema di trading.

La gestione del rischio è un altro modulo estremamente importante di un sistema di trading algoritmico. Il rischio può presentarsi in molte forme: aumento della volatilità (sebbene ciò possa essere visto come auspicabile per determinate strategie!), aumento delle correlazioni tra asset class, default del broker, malfunzionamenti del server, gli eventi “black swan” e bug non rilevati nel codice, per dirne alcuni.

Le componenti di questo modulo cercano e anticipano gli effetti di un’eccessiva volatilità e della correlazione tra asset e il loro possibile effetto sul capitale di trading. Spesso questo si riduce a un insieme di calcoli statistici come gli “stress test” di Montecarlo. Le performance computazionali di questi algoritmi sono vincolate alle prestazioni della CPU. Queste simulazioni sono altamente parallelizzabili (vedi di seguito) e, in una certa misura, è possibile affermare che “l’hardware non è un problema”.

Sistema di Esecuzione

Il compito del modulo di esecuzione consiste nel ricevere i segnali di trading, filtrati dai moduli di ottimizzazione del portafoglio e di gestione del rischio, ed inviarli a un broker, o altri punti di accesso al mercato. Per la maggior parte delle strategie di trading algoritmico retail si traduce in una connessione API o FIX ad una società di intermediazione come Interactive Brokers. Le considerazioni principali sulla scelta del linguaggio devono tenere conto anche della qualità dell’API, in quali linguaggi è disponibile l’interfaccia dell’API, la frequenza di esecuzione e lo slippage previsto.

La “qualità” dell’API si riferisce a quanto è ben documentato, quale tipo di prestazioni fornisce, se è necessario accedere tramite un software standalone o se è possibile stabilire un collegamento in modo headless (cioè senza GUI). Nel caso di Interactive Brokers, lo strumento Trader WorkStation deve essere eseguito in un ambiente GUI per poter accedere alle loro API. Una volta ho dovuto installare una versione desktop Ubuntu su un server cloud Amazon solamente per poter accedere a Interactive Brokers da remoto, solo per questo motivo!

La maggior parte delle API fornirà un’interfaccia C++ e/o Java. Di solito spetta alla comunità open-source sviluppare wrapper specifici per linguaggio per C#, Python, R, Excel e MatLab. Si noti che con ogni plug-in aggiuntivo utilizzato (in particolare i wrapper API) vi è spazio per l’insinuarsi di bug nel sistema. Verifica sempre i plug-in di questo tipo e assicurati che vengano attivamente mantenuti. Un indicatore utile consiste nel valutare quanti nuovi aggiornamenti ad un codebase sono stati fatti negli ultimi mesi.

La frequenza di esecuzione è di fondamentale importanza nell’algoritmo di esecuzione. Dato che centinaia di ordini possono essere inviati ogni minuto, le prestazioni diventano fondamentali. Lo slippage sarà amplificato da un sistema di esecuzione mal progettato e questo avrà un impatto drammatico sulla redditività.

I linguaggi a tipizzazione statica (vedi sotto) come C++ / Java sono generalmente ottimali per l’esecuzione ma c’è un compromesso in termini di tempo di sviluppo, test e facilità di manutenzione. I linguaggi tipizzati dinamicamente, come Python e Perl sono generalmente “abbastanza veloci”. Accertarsi sempre che i componenti siano progettati in modo modulare (vedere di seguito) in modo che possano essere “sostituiti” con la scalabilità del sistema.

Pianificazione dell'Architettura e Processo di Sviluppo

Finora abbiamo descritto le componenti di un sistema di trading e i requisiti di frequenza e volume sono stati discussi sopra, mentre dobbiamo ancora approfondire l’infrastruttura di sistema. Chi opera come trader retail o lavora in un piccolo fondo probabilmente “indossa molti cappelli”. Sarà necessario prevedere il modello alpha, la gestione del rischio, i parametri di esecuzione e anche l’implementazione finale del sistema. Prima di approfondire i specifici linguaggi, è bene introdurre alcuni approcci per progettare un’architettura  ottimale.

Separazione degli Interessi

Una delle decisioni più importanti che devono essere prese all’inizio è come “separare gli interessi” di un sistema di trading. Nello sviluppo del software, significa essenzialmente come suddividere i diversi aspetti del sistema in componenti modulari separati.

Esponendo le interfacce a ciascuno dei componenti è facile sostituire delle parti del sistema con altre versioni che migliorano prestazioni, affidabilità o manutenzione, senza modificare alcun codice esterno al modulo. Questo è il “miglior approccio” per tali sistemi nel caso di strategie a bassa frequenza. Per il trading HFT e UHFT questo approccio potrebbe essere ignorato al fine di focalizzare il sistema su prestazioni ancora maggiori. 

La creazione di una mappa delle componenti di un sistema di trading algoritmico è un tema vasto un solo articolo. Tuttavia, un approccio ottimale consiste nell’assicurarsi che vi siano componenti separati per gli input dei dati storici e i dati in tempo reale, l’archiviazione dei dati, l’API di accesso ai dati, il backtesting, i parametri della strategia, l’ottimizzazione del portafoglio, la gestione del rischio e i sistemi di esecuzione automatizzata.

Ad esempio, se l’archivio dati in uso è attualmente sottoperformante, anche a livelli significativi di ottimizzazione, può essere sostituito con riscritture minime nell’input dei dati o nell’API di accesso ai dati. Per quanto riguarda il backtester e le componenti successive, non c’è differenza.

Un altro vantaggio dei componenti separati è che consente di utilizzare diversi linguaggi di programmazione nel sistema globale. Non è necessario essere limitato a un solo linguaggiose il metodo di comunicazione dei componenti è indipendente dal linguaggio. Questo è il caso quando si prevede una comunicazione tra i moduli tramite TCP / IP, ZeroMQ o qualche altro protocollo indipendente.

Come esempio concreto, si consideri il caso in cui un sistema di backtesting viene scritto in C ++ per le prestazioni di “number crunching”, mentre il gestore del portafoglio e i sistemi di esecuzione sono scritti in Python usando SciPy e IBPy.

Considerazioni sulle Prestazioni

Le prestazioni sono fondamentali per la maggior parte delle strategie di trading. Per le strategie ad alta frequenza è il fattore più importante. Le “Prestazioni” coprono una vasta gamma di problemi, come velocità di esecuzione algoritmica, la latenza della rete, la larghezza di banda, I / O di dati, simultaneità / parallelismo e lo scaling. Ognuna di queste aree è coperta individualmente da voluminosi libri di testo, quindi questo articolo si introducono alcuni concetti di ogni argomento. La scelta dell’architettura e del linguaggio sono discussi in termini di effetti sulla performance.

La saggezza prevalente, come affermato da Donald Knuth, uno dei padri dell’informatica, è che “l’ottimizzazione prematura è la radice di tutti i mali”. Questo è quasi sempre valido – tranne quando si costruisce un algoritmo di trading ad alta frequenza! Per coloro che sono interessati alle strategie di bassa frequenza, un approccio comune è quello di costruire un sistema nel modo più semplice possibile e ottimizzare solo quando iniziano a comparire rallentamenti.

 

Gli strumenti di profilazione vengono utilizzati per determinare dove si verificano i colli di bottiglia. È possibile creare profili per tutti i fattori sopra elencati, in ambiente MS Windows o Linux. Ci sono molti strumenti disponibili per qualsiasi sistema operativo e linguaggio, così come software di terze parti.

C ++, Java, Python, R e MatLab hanno tutti a disposizione librerie ad alte prestazioni (nativamente o importandole esternamente) per la struttura del database e il lavoro computazionale. C ++ viene fornito con la libreria nativa, mentre Python utilizza le librerie NumPy / SciPy. I comuni algoritmi matematici si trovano in queste librerie, quindi raramente è necessario scrivere una nuova implementazione.

Un’eccezione è nel caso fosse richiesta un’architettura hardware altamente personalizzata ed un algoritmo deve far un ampio uso di estensioni proprietarie (come le cache personalizzate). Tuttavia, spesso “la reinvenzione della ruota” fa perdere tempo che potrebbe essere speso meglio nello sviluppo e nell’ottimizzazione di altre parti dell’infrastruttura di trading. Il tempo di sviluppo è estremamente prezioso, specialmente nel contesto dei soli sviluppatori.

 

La latenza è spesso un problema del sistema di esecuzione ma si verifica anche nei tool di ricerca nel si trovano solitamente sulla stessa macchina. Per il primo, la latenza può verificarsi in più punti il processo di esecuzione. I database devono essere consultati (latenza del disco / rete), i segnali devono essere generati (sistema operativo, latenza della messaggistica del kernel), i segnali di trading devono essere inviati (latenza NIC) e gli ordini devono essere elaborati (latenza interna dei sistemi di exchange).

Per le operazioni a frequenza più elevata è necessario acquisire familiarità con l’ottimizzazione del kernel e l’ottimizzazione della trasmissione di rete. Questa è un’area complessa ed è significativamente oltre lo scopo di questo articolo, ma se si desidera un algoritmo UHFT, è necessario avere un’approfondita della conoscenza del software e dell’hardware!

 

Il caching è uno strumento che deve essere presente nella “cassetta degli attrezzi” di uno sviluppatore di trading quantitativo. Il caching fa riferimento al concetto di archiviazione dei dati utilizzati frequentemente in un modo che consentire un accesso più performante, a scapito della potenziale inconsistenza dei dati. Un caso di uso comune si verifica nello sviluppo web quando si prendono i dati da un database relazionale presente su un supporto fisico (hard disk) e vengono caricati in memoria. Qualsiasi richiesta successiva per i dati non deve “accedere il database” e quindi si ottengono guadagni significativi in termini di prestazioni.

Per il trading il caching può essere estremamente utile. Ad esempio, lo stato corrente di un portafoglio di strategie può essere memorizzato in una cache fino a quando non viene ribilanciato, in modo tale che l’elenco non debba essere rigenerato su ciascun ciclo dell’algoritmo di trading. È probabile che tale rigenerazione sia un’operazione di CPU o di I / O molto lenta e dispendiosa.

Tuttavia, il caching non è privo di problemi. La rigenerazione dei dati della cache, a causa della natura volatile dello storage della cache, può comportare una notevole richiesta di lavoro. Un altro problema è la dog-piling, un tipo di errore a cascata che può verificarsi quando i sistemi di calcolo in parallelo con meccanismi di caching sono sottoposti a carichi molto elevati, come la generazione multipla di una nuova copia.

L’allocazione dinamica della memoria è un’operazione costosa in termini di esecuzione del software. Pertanto, è imperativo che le applicazioni di trading con prestazioni più elevate siano ben consapevoli di come la memoria viene allocata e rilasciata durante il flusso del programma. I nuovi standard linguistici come Java, C # e Python eseguono tutti una garbage collection automatica, che effettua una deallocazione dinamica della memoria allocata quando gli oggetti escono dal flusso del programma.

La garbage collection è estremamente utile durante lo sviluppo poiché riduce gli errori e facilita la leggibilità del codice. Tuttavia, è spesso non ottimale per determinate strategie di trading ad alta frequenza. In questi casi è richiesta una garbage collection personalizzata. In Java, ad esempio, attivando il garbage collector e la configurazione heap, è possibile ottenere prestazioni elevate per le strategie HFT.

C ++ non fornisce un garbage collector nativo e quindi è necessario gestire tutta l’allocazione / deallocazione di memoria come parte dell’implementazione di un oggetto. Anche se potenzialmente soggetti a errori (potenzialmente causati da puntatori non più validi) è estremamente utile avere un controllo preciso su come gli oggetti appaiono nell’heap di determinate applicazioni. Quando si sceglie un linguaggio è necessario verificare come funziona il garbage collector e se può essere modificato per ottimizzare per un particolare caso d’uso.

 

Molte operazioni nei sistemi di trading algoritmico sono ideali per la parallelizzazione. Questo si riferisce al concetto di eseguire più operazioni programmatiche allo stesso tempo, cioè in “parallelo”. I cosiddetti algoritmi “altamente paralleli” includono funzioni che possono essere eseguite completamente indipendentemente dalle altre. Alcune operazioni statistiche, come le simulazioni MonteCarlo, sono un buon esempio di algoritmi altamente paralleli, in quanto ogni estrazione casuale e la successiva processazione  possono essere calcolati senza dover aspettare il processo delle altre estrazioni..

Altri algoritmi sono solo parzialmente paralleli. Un esempio sono le simulazioni fluidodinamiche, dove il calcolo può essere suddiviso, ma alla fine questi calcoli devono comunicare tra loro e quindi le operazioni sono parzialmente sequenziali. Gli algoritmi in parallelo sono soggetti alla Legge di Amdahl, che fornisce un teorico limite all’aumento delle prestazioni di un algoritmo parallelizzato quando soggetto a N processi separati (ad esempio su un core o thread della CPU).

La parallelizzazione è diventata sempre più importante come mezzo di ottimizzazione poiché le velocità di clock dei processori hanno raggiunto un limite tecnico, per aumentare le prestazioni i moderni processori contengono molti core con cui eseguire calcoli paralleli. L’ascesa dell’hardware grafico consumer (prevalentemente per i videogiochi) ha portato allo sviluppo di Graphical Processing Units (GPU), che contengono centinaia di “core” per operazioni altamente concorrenti. Tali GPU sono ora molto convenienti. Framework di alto livello, come la CUDA di Nvidia, hanno portato ad una rapida diffusione nel mondo accademico e finanziario.

Tale hardware GPU è generalmente adatto solo per gli aspetti di ricerca della finanza quantitativa, mentre altri hardware più specializzati (inclusi i Field-Programmable Gate Arrays – FPGA) vengono utilizzati per (U) HFT. Oggigiorno, la maggior parte dei linguaggi moderni prevede un certo grado di concorrenza / multithreading. Pertanto è semplice ottimizzare un backtester, poiché tutti i calcoli sono generalmente indipendenti dagli altri.

 

Lo scaling del software si riferisce alla capacità del sistema di gestire carichi di lavoro sempre più consistenti sotto forma di maggiori richieste, maggiore utilizzo del processore e maggiore allocazione della memoria. Nel trading algoritmico una strategia è scalabile se può accettare maggiori quantità di capitale e produrre comunque rendimenti consistenti. Lo stack della tecnologia di trading viene scalato o ridimensianato se deve gestire maggiori volumi di scambio e una maggiore latenza, senza rallentamenti.

La difficoltà nella progettazione di sistemi scalabili consiste nel prevedere in anticipo dove si verificherà un collo di bottiglia. Il logging, il testing, la profilazione e il rigido monitoraggio contribuiranno notevolmente ad aumentare la scalabilità di un sistema. Gli stessi linguaggi sono spesso descritti come “non scalabili”. Questo è generalmente il risultato di disinformazione, piuttosto che fatti reali. Infatti è lo stack tecnologico, nella sua totalità, che dovrebbe essere scalabile, non certo il linguaggio. Ovviamente certi linguaggi hanno prestazioni migliori di altri in particolari casi d’uso, ma un linguaggio non è mai “migliore” di un’altro in tutti gli aspetti. Uno dei metodi per gestire la scalabilità è adottare un approccio di suddivisione in moduli, come affermato sopra. Al fine di introdurre ulteriormente la capacità di gestire i “picchi” nel sistema (cioè una volatilità improvvisa che innesca una serie di trade), è utile creare una “architettura di accodamento dei messaggi”, cioè prevedere un sistema di message queue tra i componenti in modo che gli ordini vengano “impilati” se un determinato componente non è in grado di elaborare molte richieste.

Invece di perdere queste richieste, sono semplicemente tenute in coda fino a quando il messaggio non viene gestito. Questo approccio è particolarmente utile per l’invio dei trade al motore di esecuzione. Se il motore sta subendo una forte latenza, eseguirà il backup delle transazioni. Una coda tra il generatore di segnali di trading e l’API di esecuzione allevierà questo problema a scapito di un potenziale slippage. RabbitMQ è uno dei migliori software open source per la gestione delle code di messaggi. 

Hardware e Sistema Operativo

L’hardware che esegue la strategia può avere un impatto significativo sulla redditività dell’algoritmo. Questo non è un problema limitato ai trader ad alta frequenza. Una scelta sbagliata nell’hardware e nel sistema operativo può causare un arresto anomalo della macchina o il riavvio nel momento più inopportuno. Quindi è necessario considerare dove risiederà l’applicazione. La scelta è generalmente tra un personal desktop, un server remoto, un provider di “cloud” o un server exchange condiviso.

Le macchine desktop sono semplici da installare e da amministrare, specialmente con i più recenti sistemi operativi di facile utilizzo come Windows 7/8, Mac OSX e Ubuntu. I sistemi desktop possiedono tuttavia alcuni svantaggi significativi. In primo piano, è probabile che le versioni dei sistemi operativi progettate per macchine desktop richiedano il riavvio e il patching del sistema (e spesso una perdita di tempo). Usano anche più risorse computazionali dato che richiedono un’interfaccia grafica (GUI).

L’utilizzo dell’hardware in un ambiente domestico (o locale) può portare a problemi di connettività Internet e di alimentazione elettrica. Il vantaggio principale di un sistema desktop è che è possibile acquistare una notevole potenza di calcolo per la frazione del costo di un server remoto dedicato (o un sistema basato su cloud) di velocità comparabile.

Un server dedicato o una macchina basata su cloud, anche se spesso più costosa dell’opzione desktop, consente una maggiore ridondanza dell’infrastruttura, come i backup automatici dei dati, la possibilità di assicurare in modo più diretto l’uptime e il monitoraggio remoto. Sono più difficili da amministrare poiché richiedono di utilizzare le funzionalità di accesso remoto del sistema operativo.

In Windows questo è generalmente svolto tramite l’interfaccia grafica del Remote Desktop Protocol (RDP). Nei sistemi basati su Unix si utilizza la command-line Secure SHell (SSH). L’infrastruttura server basata su Unix è quasi sempre basata su command-line che rende immediatamente inutilizzabili strumenti di programmazione basati su GUI (come MatLab o Excel).

Un server co-localizzato è semplicemente un server dedicato che risiede il più vicino possibile all’exchange in modo da ridurre la latenza dell’algoritmo di trading. Questo è assolutamente necessario per determinate strategie di trading ad alta frequenza, che si basano sulla bassa latenza per generare l’alpha.

L’aspetto finale sulla scelta dell’hardware consiste nell’indipendenza del linguaggio di programmazione  rispetto alla piattaforma. È necessario che il codice venga eseguito su più sistemi operativi diversi? Il codice è progettato per essere eseguito su una particolare tipo di architettura del processore, come Intel x86 / x64 o sarà possibile eseguire su processori RISC come quelli prodotti da ARM? Questi problemi dipenderanno in larga misura dalla frequenza e dal tipo di strategia implementata.

Resilienza e Testing

Uno dei modi migliori per perdere un sacco di soldi nel trading algoritmico è creare un sistema senza resilienza. Questo si riferisce all’affidabilità del sistema quando soggetto a eventi rari, come il fallimento del broker, un’improvvisa ed eccessiva volatilità, tempi di inattività per un fornitore di server cloud o la cancellazione accidentale di un intero database. Anni di profitti possono essere eliminati in pochi secondi con un’architettura mal progettata. È assolutamente essenziale considerare problemi come debuggng, testing, registrazione, backup, alta disponibilità e monitoraggio come componenti principali del sistema.

È probabile che in qualsiasi applicazione di trading quantitativa personalizzata ragionevolmente complicata, almeno il 50% del tempo di sviluppo sarà speso per il debugging, il test e la manutenzione.

Quasi tutti i linguaggi di programmazione sono forniti con un debugger associato o possiedono ottime alternative di terze parti. In sostanza, un debugger consente l’esecuzione di un programma con l’inserimento di punti di interruzione arbitrari nel percorso del codice, che interrompono temporaneamente l’esecuzione per indagare sullo stato del sistema. Il principale vantaggio del debugging consiste nella possibilità di esaminare il comportamento del codice prima di un noto punto di arresto.

Il debug è un componente essenziale nella casetta degli attrezzi per l’analisi degli errori di programmazione. Tuttavia, sono più ampiamente utilizzati in linguaggi compilati come C ++ o Java, poiché i linguaggi interpretati come Python il debug è molto più facilei da eseguire grazie al un minor numero di LOC e di istruzioni meno dettagliate. Nonostante questa tendenza, Python viene fornito con il pdb, un sofisticato strumento di debugging. L’IDE Microsoft Visual C++ è dotato di estese funzionalità di debug, mentre per il programmatore Linux C++ con command-line esiste il debugger gdb.

 

I testing nello sviluppo del software si riferisce al processo di applicazione di parametri e risultati noti a funzioni, metodi e oggetti specifici all’interno di una porzione di codice, al fine di simulare il comportamento e valutare le prestazioni, contribuendo a garantire che un sistema si comporti come dovrebbe. Un paradigma più recente è noto come Test Driven Development (TDD), in cui il codice di test è sviluppato a partire da una specifica interfaccia astratta. Prima del completamento della codebase effettiva, tutti i test falliranno. Poiché il codice è scritto per “riempire gli spazi vuoti”, i test finali dovrebbero essere tutti positivi quando termina lo sviluppo.

Scegliere il linguaggio

Dopo aver descritto i fattori che influenzano lo sviluppo di un sistema di trading algoritmico ad alte prestazioni. Il prossimo passo è capire come sono classificati i vari linguaggi di programmazione.

Tipologie di Sistemi

Quando si sceglie il linguaggio per l’infrastruttura di trading è necessario considerare il tipo di sistema. Per il trading si può utilizzare linguaggi a tipizzazione statica o dinamico. Un linguaggio tipizzato staticamente esegue verifiche dei tipi di dati(ad esempio interi, float, classi personalizzate ecc.) durante il processo di compilazione. Tali linguaggi includono C++ e Java. Un linguaggio tipizzato in modo dinamico esegue la maggior parte del controlli in fase di esecuzione. Tali linguaggi includono Python, Perl e JavaScript.

Per un sistema altamente numerico come un motore di trading algoritmico, il controllo del tipo al momento della compilazione può essere estremamente utile, in quanto può eliminare molti bug che altrimenti causerebbero errori numerici. Tuttavia, il controllo dei tipi non risolve tutti bug quindi è necessario implementare la gestione delle eccezioni in modo da controllare le operazioni impreviste. I linguaggi ‘dinamici’ (cioè quelli che sono tipizzati dinamicamente) possono spesso presentari errori di runtime che potrebbero essere catturati con un controllo del tipo dati in fase di compilazione. Per questo motivo è nato il concetto di TDD (vedi sopra) e gli unit test che, se eseguito correttamente, offre spesso più sicurezza rispetto al solo controllo in fase di compilazione.

Un altro vantaggio dei linguaggi tipizzati staticamente è l’ottimizzazione effettuata dal compilatore che non sono disponibili per un linguaggio dinamico, infatti grazie alla definizione delle tipologie di dati i requisiti di utilizzo della memoria sono già noti in fase di compilazione. In effetti, parte dell’inefficienza di molti linguaggi tipizzati dinamicamente deriva dal fatto che la verifica sul controllo del tipo di alcuni oggetti in fase di esecuzione, con un notevole calo delle prestazioni. Le librerie per le linguaggi dinamici, come NumPy / SciPy, riescono a minimizzare queste criticità grazie all’introduzione di una tipologia all’interno degli array.

Open-Source o Proprietario?

Uno sviluppatore di trading algoritmico può scegliere se utilizzare tecnologie proprietarie (commerciali) o open source. Ci sono vantaggi e svantaggi per entrambi gli approcci. È necessario considerare molti fattori, quali l’attività della comunity circonda un linguaggio, la facilità di installazione e manutenzione, la qualità della documentazione e gli eventuali costi di licenza / manutenzione.

Lo stack Microsoft .NET (incluso Visual C ++, Visual C#) e MatLab di MathWorks sono due delle maggiori opzioni per le soluzioni proprietarie. Entrambi gli strumenti hanno avuto significativi sviluppi nello spazio finanziario, dove il primo costituisce lo stack predominante dell’infrastruttura di trading delle banche di investimento banking, mentre il secondo è ampiamente utilizzato per le ricerche quantitative all’interno dei fondi di investimento.

Sia Microsoft che MathWorks forniscono un documentazione di alta qualità per i loro prodotti. Inoltre, inoltre dispongono di comunity molto attive sul web. Il software .NET consente l’integrazione coesiva con più linguaggi come C++, C# e VB, oltre ad un facile collegamento agli altri prodotti Microsoft come il database SQL Server tramite LINQ. MatLab ha anche molti plugin/librerie (alcuni gratuiti) per quasi tutti gli ambiti di ricerca quantitativa.

Ci sono anche degli svantaggi. Per entrambi i software i costi per un trader solitario non sono da trascurare (sebbene Microsoft offra gratuitamente la versione entry-level di Visual Studio). Gli strumenti Microsoft funzionano ottimamente l’uno con l’altro, ma si integrano meno facilmente con un  codice esterno. Visual Studio deve anche essere eseguito su Microsoft Windows, che è probabilmente molto meno performante di un equivalente server Linux facilmente ottimizzato.

Inoltre MatLab non ha a disposizione alcuni plugin chiave come un buon wrapper  perl’API di Interactive Brokers, uno dei pochi broker adatti al trading algoritmico ad alte prestazioni. Il problema principale con i prodotti proprietari è la mancanza di disponibilità del codice sorgente. Ciò significa che se le prestazioni sono veramente fondamentali, entrambi questi strumenti non sono la scelta migliore.

La maggioranza degli strumenti open source si basano su ambienti Linux, come MySQL / PostgreSQL, Python, R, C ++ e Java sono utilizati per implementazioni ad alte prestazioni. Tuttavia, non sono utilizzati solo per questo scopo. Python e R, in particolare, contengono una vasta gamma di librerie numeriche per eseguire praticamente qualsiasi tipo immaginabile di analisi dei dati immaginabile, spesso con velocità di esecuzione paragonabili ai linguaggi compilati.

Il vantaggio principale nell’uso di linguaggi interpretati è la velocità dei tempi di sviluppo. Python e R richiedono molte meno righe di codice (LOC) per ottenere funzionalità simili, principalmente grazie all’ampia gamma di librerie disponibili. Inoltre, spesso consentono lo sviluppo basato su console interattiva, riducendo rapidamente il processo di sviluppo iterativo.

Dato che il tempo di uno sviluppatore è estremamente prezioso mentre spesso la velocità di esecuzione non è così importantante (a meno che non si stia implementando un sistema HFT), vale la pena considerare ampiamente uno stack tecnologico open source. Python e R sono circondati da una solida comunity di sviluppatori e sono estremamente ben supportate, grazie alla loro popolarità. La documentazione è eccellente e gli errori (almeno per le librerie di base) rimangono scarsi.

Gli strumenti open source soffrono però la mancanza di un contratto di supporto commerciale dedicato, e funzionano in modo ottimale solo su ambienti senza interfacce utente. Un tipico server Linux (come Ubuntu) sarà spesso completamente command-line. Inoltre, se Python e R si dimostrano lenti nell’esecuzione di determinate attività, esistono meccanismi per integrare il codice C++ al fine di migliorare la velocità di esecuzione, ma richiede una certa esperienza nella programmazione multilinguaggio.

Mentre il software proprietario non è immune da problemi di dipendenza / versioning, è molto meno comune dover gestire versioni di librerie errate in tali ambienti. I sistemi operativi open source come Linux possono essere più complicati da amministrare.

E’ mia personale opinione e preferenza costruire tutti i miei strumenti di trading con tecnologie open source. In particolare utilizzo: Ubuntu, MySQL, Python, C++ e R. La maturità, la dimensione della community, la capacità di “scavare a fondo” se si verificano problemi e ridurre la TCO sono di gran lunga superiori alla semplicità delle GUI proprietarie e alla facilità d’installazione. Detto questo, Microsoft Visual Studio (specialmente per C++) è un fantastico ambiente di sviluppo integrato (IDE) che raccomando vivamente.

Batterie incluse?

Il titolo di questa sezione fa riferimento alle funzionalità “out of the box” di un linguaggio – quali librerie contiene e quanto sono affidabili? È qui che i linguaggi maturi hanno un vantaggio rispetto alle nuove varianti. C++, Java e Python dispongono ora di ampie librerie per la programmazione di rete, HTTP, interazione con sistemi operativi, GUI, espressioni regolari (regex), iterazione e algoritmi di base.

Il C++ è famoso per la sua STL (Standard Template Library) che contiene un’ampia gamma “gratuita” di strutture dati e algoritmi ad alte prestazioni. Python è noto per essere in grado di comunicare con quasi ogni altro tipo di sistema / protocollo (in particolare il web), principalmente attraverso la propria libreria standard. R è ricco di strumenti statistici ed econometrici nativi, mentre MatLab è estremamente ottimizzato per qualsiasi codice algebrico lineare numerico (che può essere usato per l’ottimizzazione del portafoglio).

Al di fuori delle librerie standard, C ++ fa uso della libreria Boost, che riempie le “parti mancanti” della libreria standard. In effetti, molte parti di Boost sono entrate nello standard TR1 e successivamente disponibili nelle specifiche C++ 11, incluso il supporto nativo per espressioni lambda e simultaneità.

Python ha la combinazione di librerie ad altre prestazioni per l’analisi dei dati NumPy / SciPy / Pandas, che ha ottenuto un’ampia diffusione nei sistema di trading algoritmico. Inoltre, esistono plugin ad alte prestazioni per l’accesso ai principali database relazionali, come MySQL ++ (MySQL / C ++), JDBC (Java / MatLab), MySQLdb (MySQL / Python) e psychopg2 (PostgreSQL / Python). Python può persino comunicare con R tramite il plugin RPy!

Un aspetto spesso trascurato di un sistema di tradingdurante la fase iniziale di progettazione è la connettività all’API del broker. La maggior parte delle API supporta nativamente C++ e Java, ma alcune supportano direttamente anche C# e Python, o tramite wrapper fornito dalla comunity. In particolare, Interactive Brokers può essere collegato tramite il plugin IBPy. Se sono richieste prestazioni elevate, i broker supporteranno il protocollo FIX.

 

Conclusioni

Ora è evidente come la scelta dei linguaggi di programmazione per un sistema di trading algoritmico non è semplice e richiede una profonda riflessione. Le principali considerazioni sono: prestazioni, facilità di sviluppo, resilienza e testing, separazione degli interessi, manutenzione, disponibilità del codice sorgente, costi di licenza e maturità delle librerie.

Il vantaggio di un’architettura separata consiste nel poter “collegare” diversi linguaggi ai diversi aspetti di uno stack di trading, man mano che cambiano i requisiti. Un sistema di trading è uno strumento in evoluzione ed è probabile che qualsiasi scelta si evolverà insieme ad esso.

Recommended Posts