Come Elaborare i Dati Finanziari

In un precedente precedente articolo abbiamo descritto come come creare un Securities Master Database.
Questo articolo introduce un argomento che spesso non è molto considerato dalla maggior parte dei libri sul trading, cioè quello di elaborare i dati del mercato finanziario prima del loro utilizzo nel backtesting di strategia.

Si introducono inizialmente i diversi tipi di dati a cui i trader algoritmici sono interessati. Si prenderà quindi in considerazione la frequenza dei dati, dai dati trimestrali (come i report SEC) fino ai tick e l’order-book sulla scala del millisecondo. Le fonti di tali dati (sia gratuiti che commerciali) saranno quindi delineati insieme al codice per ottenere i dati. Infine, si descrive la pulizia e la preparazione dei dati per l’utilizzo nelle strategie.

Classificazione dei Mercati e degli Strumenti

Come trader algoritmici siamo spesso interessati ai dati su una vasta gamma di mercati finanziari. I dati possono spaziare dalle serie temporali dei prezzi di uno strumento, a dati testuali non strutturati (come feed di notizie) fino alle informazioni sugli utili societari. In questo articolo ci concentriamo prevalentemente sui dati delle serie temporali finanziarie.

Mercati

Le azioni statunitensi e internazionali, le valute estere, le materie prime e il reddito passivi sono le principali fonti di dati di mercato che sono di interesse per un trader algoritmico. Nel mercato azionario è ancora estremamente comune acquistare direttamente l’attività sottostante, mentre negli ultimi tre mercati gli strumenti derivati ​​altamente liquidi (come futures, opzioni o strumenti più esotici) sono più comunemente utilizzati per il trading.
Questa ampia categorizzazione rende essenzialmente relativamente semplice trattare i mercati azionari, sebbene con problemi relativi alla gestione dei dati delle azioni societarie (vedi sotto). Quindi gran parte del panorama di trading algoritmico retail è basato sulle azioni, come le azioni societarie dirette o gli Exchange Traded Funds (ETF). Anche i mercati delle valute estere (“forex”) sono molto apprezzati dal momento che i broker permetteranno il margin trading su movimenti percentuali in punti (PIP).
Un pip è un’unità del quarto decimale in un tasso di cambio. Per le valute denominate in dollari statunitensi questo è equivalente a 1/100 di centesimo.
Le materie prime e i mercati dei titoli a reddito fisso sono più difficili da scambiare direttamente con il sottostante. Un trader algoritmico retail non è spesso interessato a consegnare barili di petrolio a un deposito di petrolio! Invece, i contratti futures sull’attività sottostante sono utilizzati a fini speculativi. Ancora una volta, è permetto il margin trading consentendo ampia leva su tali contratti.

Strumenti

Per il trader algoritmico è disponibile una vasta gamma di sottostanti e di derivati.
Mercato Strumenti
Azioni / Indici Stock, ETFs, Futures, Options
Foreign Exchange (FOREX) Margin/Spot, ETFs, Futures, Options
Commodities Futures, Options
Obbligazioni Futures, Options

Al fine di semplificare l’implementazioni, in questo articolo ci concetriamo sul mercato azionario e ETF

Dati Fondamentali

Sebbene i trader algoritmici eseguano principalmente analisi sulle serie temporali dei prezzi finanziari, anche i dati fondamentali (con diverse frequenze) sono spesso aggiunti all’analisi. Le cosiddette strategie di Quantitative Value (QV) fanno molto affidamento sull’acquisizione e sull’analisi dei dati fondamentali, come ad esempio le informazioni macroeconomiche, i dati storici degli utili societari, gli indici di inflazione, i rapporti sui salari, i tassi di interesse e depositi SEC. Tali dati sono spesso disponibili anche in formato temporale, sebbene su frequenze molto grandi, come mesi, trimestri o anni.

Questo articolo non approfondisce l’elaborazione dei dati fondamentali per le strategie QV o strategie su timeframe molto elevati, mentre mi voglio concentrare su strategie daily o più frequenti (intraday) derivate principalmente dall’azione dei prezzi (price action).

Dati non Strutturati

I dati non strutturati sono costituiti da documenti come news, post di blog, articoli o relazioni. L’analisi di tali dati può essere complicata poiché si basa sulle tecniche di Natural Language Processing (NLP). Uno di questi è l’analisi dei dati non strutturati nel tentativo di determinare il livello di sentiment. Questo può essere utile per guidare una strategia di trading. Ad esempio, classificando i testi come “bullish”, “bearish” o “neutral”, potrebbe essere generato un insieme di segnali di trading. Il termine per questo processo è la Sentiment Analysis.

Python fornisce una libreria estremamente completa per l’analisi di dati di testo, conosciuta come Natural Language Toolkit (NLTK).

Dati Testuali

Esistono numerose fonti di dati testuali che possono essere utili per generare una strategia di trading. Le popolari fonti di news finanziarie come Bloomberg e il Financial Times, così come blog di commenti finanziari come Seeking Alpha e ZeroHedge, forniscono importanti fonti di testo da analizzare. Inoltre, i feed di notizie proprietari forniti dai fornitori di dati sono anch’esse buone fonti di dati.

Per ottenere dati su una scala più ampia, è necessario utilizzare strumenti di “web scraping”, progettati per automatizzare il download di massa di siti Web. In questo caso è necessario prestare la massima attenzione perché gli strumenti automatici di web scraping possono violano i Termini di servizio di questi siti. Assicurati di controllare prima di iniziare a scaricare questo tipo di dati. Uno strumento particolarmente utile per il web scraping, che rende il processo efficiente e strutturato, è la libreria ScraPy di Python.

Dati dai Social Media

Negli ultimi anni c’è stato un significativo interesse per ottenere le informazioni sul sentiment dai dati dei social media, in particolare tramite il servizio di microblogging di Twitter. Nel 2011, un hedge fund è stato creato intorno al sentiment di Twitter, noto come Derwent Capital. In effetti, studi accademici hanno dimostrato che è possibile generare un buion livello di capacità predittiva basato su tale sull’analisi del sentimento. Nel caso si desideri effettuare ricerche approfondite sul sentiment, allora ci sono due libri di Matt Russell relativi all’acquisizione dei dati sui social media tramite le API pubbliche fornite da questi servizi web.

Frequenza dei Dati

La frequenza dei dati è un fattore fondamentale quando si progetta un sistema di trading algoritmico. Influisce su ogni decisione progettuale riguardante l’archiviazione dei dati, il backtesting di una strategia e l’esecuzione di un algoritmo. È probabile che le strategie ad alta frequenza portino a un’analisi statisticamente più solida, semplicemente a causa del maggior numero di punti di dati (e quindi di trade) che verranno utilizzati. Le strategie HFT richiedono spesso un investimento significativo in termini di tempo e capitale per lo sviluppo del software necessario per eseguirle. Le strategie a bassa frequenza sono più facili da sviluppare e implementare, poiché richiedono meno automazione. Tuttavia, spesso generano molti meno trade rispetto ad una strategia ad alta frequenza che porta a un’analisi  statisticamente meno solida.

Dati Mensili e Settimanali

I dati fondamentali sono spesso riportati su base settimanale, mensile, trimestrale o anche annuale. Tali dati includono dati sui salari, report sulle prestazioni dei fondi hedge, depositi SEC, indici basati sull’inflazione (come l’indice dei prezzi al consumo), la crescita economica e i conti aziendali. Per l’archiviazione di tali dati è più idoneo utilizzare database non strutturati, come MongoDB, che può gestire dati nidificati gerarchicamente e quindi consentire un ragionevole grado di capacità di interrogazione. L’alternativa è archiviare il testo di file flat in un RDBMS, che è meno appropriato, dal momento che le interrogazioni full-text sono più complicate.

Dati Giornalieri

La maggior parte dei trader algoritmici retail fa uso di serie giornaliere di dati finanziari (“end of day” / EOD), in particolare per le azioni e il forex. Tali dati sono liberamente disponibili (vedi sotto), ma spesso sono di qualità discutibile e soggetti a determinati bias. I dati di fine giornata sono spesso archiviati in RDBMS, poiché la natura della mappatura ticker / simboli si applica perfettamente al modello relazionale.

I dati EOD non comportano requisiti di archiviazione particolarmente ampi. Ci sono 252 giorni di trading in un anno per il mercato degli Stati Uniti e quindi in un decennio ci saranno 2.520 record per ogni titolo azionario. Anche con un mercato di 10.000 simboli si avrà 25.200.000 record, che possono essere facilmente gestiti all’interno di un database relazionale.

Dati Intraday

Le strategie infragiornaliere utilizzano spesso le barre OHLCV di ogni ora, quindici, cinque, un minuto o addirittura secondi. I provider di feed intraday come QuantQuote e DTN IQFeed forniscono spesso dati al minuto o al secondo in base ai loro dati di tick. I dati a tali frequenze possono presentare molte barre “mancanti” semplicemente perché non sono state effettuate transazioni in quel periodo di tempo. La libreria Pandas può essere utilizzata per ottimizzare questi dati, a discapito della precisione dei dati. Inoltre, Pandas può essere utilizzati per creare dati su scale temporali meno granulari, quando necessario.

Per un periodo di dieci anni, i dati al minuto genereranno quasi un milione di barre per ogni titolo. Analogamente per i dati al secondo il numero di punti dati nello stesso periodo ammonterà a quasi sessanta milioni per ogni titolo. Pertanto, per immagazzinare un migliaio di tali titoli si otterranno 60 miliardi di barre di dati. Si tratta di una grande quantità di dati da conservare in un RDBMS e di conseguenza sono necessari approcci più sofisticati.

Dati di Tick e dell'Order-Book

Quando un trade viene eseguito all’interno di un exchange, o in un’altra sede, viene generato un tick. I feed di tick sono costituiti da tutte le transazioni di un exchange. Ogni tick retail è memorizzato all’interno del feed con un timestamp accurato al millisecondo. I dati di tick spesso includono anche il miglior prezzo di domanda/offerta (bid/ask). L’archiviazione dei dati tick è ben oltre lo scopo di un trader retail, ma inutile dire che i volumi di tali dati sono notevoli. I comuni meccanismi di archiviazione comprendono HDF5, kdb e semplicemente flat-file / CSV.

Più ordini limite in un exchange portano al concetto dell’order book. Questo è essenzialmente l’elenco di tutti gli ordini limite di acquisto e di vendita a determinati volumi per ogni partecipante al mercato. Porta alla definizione dello spread bid-ask (o semplicemente dello “spread”), che è la più piccola differenza tra i prezzi bid e ask per gli ordini “top of book”. La creazione di una rappresentazione storica, o un simulatore di mercato, di un order book è solitamente necessario per realizzare strategie di trading a frequenze ultra alte (UHFT).

Fonti di Dati

Esistono numerose fonti e venditori di dati finanziari. Variano sostanzialmente in ampiezza, tempestività, qualità e prezzo.
In generale, i dati sui mercati finanziari forniti su una frequenza giornaliera (con un leggero ritardo), o più ampie sono disponibili gratuitamente, anche se non sono di buona qualità e presentano bias di sopravvivenza. Per ottenere i dati intraday è di solito necessario acquistare un feed di dati commerciale. I fornitori di tali feed variano enormemente nella loro capacità di servizio al cliente, nella qualità complessiva dei feed e nell’ampiezza degli strumenti.

Fonti gratuite

I dati gratuiti di fine giornata, che consistono in prezzi Open-High-Low-Close-Volume (OHLCV) degli  strumenti, sono disponibili per un’ampia gamma di titoli azionari e futures statunitensi e internazionali tramite i servizi come Yahoo Finance, Google Finance e Quandl.

Yahoo Finance

Yahoo Finance è la risorsa “base” quando si costituisce un database dei dati EOD dei titoli azionari statunitensi. L’ampiezza dei dati è estremamente completa, e contiene migliaia di azioni scambiate. Inoltre, le scissioni di azioni e i dividendi vengono gestiti utilizzando il metodo di back-adjustment, il cui risultato è contenuto nella colonna “Adj Close” nell’output CSV dell’API (che discuteremo di seguito). Pertanto, questi dati permettono ai trader algoritmici di iniziare rapidamente e a costo zero. Personalmente ho avuto molta esperienza nella pulizia dei dati di Yahoo. Devo dire che i dati possono essere abbastanza errati. Innanzitutto, è soggetto a un problema noto come backfilling. Questo problema si verifica quando i dati storici passati vengono corretti in una data futura, causando dei backtesting di scarsa qualità che cambiano man mano che il proprio database viene aggiornato. Per gestire questo problema, un record di registrazione viene generalmente aggiunto al securities master (in una apposita tabella) ogni volta che viene modificato un dato storico. In secondo luogo, il feed di Yahoo aggrega solo i prezzi di poche fonti per formare i punti OHLCV. Ciò significa che i valori possono essere ingannevoli, poiché altre fonti potrebbero aver eseguito prezzi diversi rispetto ai valori. In terzo luogo, ho notato che quando si ottengono i dati finanziari in massa, gli errori si insinuano nell’API. Ad esempio, più chiamate all’API con identici parametri data / ticker occasionalmente portano a set di risultati diversi. Questo è chiaramente un problema sostanziale e deve essere attentamente controllato. In sintesi, preparatevi a eseguire una vasta pulizia dei dati sui dati di Yahoo Finance, se scegliete di utilizzarlo per popolare un grande master di titoli e avete bisogno di dati altamente accurati.

Quandl

Quandl è un servizio che si propone come “il modo più semplice per trovare e utilizzare i dati finanziari”. Credo che siano sulla buona strada per raggiungere questo obiettivo! Il servizio fornisce una serie di dati giornalieri per i titoli azionari statunitensi e internazionali, i tassi di interesse, le materie prime / futures, le valute estere e altri dati economici. Inoltre, il database viene continuamente ampliato e il progetto viene attivamente mantenuto.

Tutti i dati sono accessibili tramite una moderna API HTTP (CSV, JSON, XML o HTML), con plugin per una vasta gamma di linguaggi di programmazione tra cui R, Python, Matlab, Excel, Stata, Maple, C#, EViews, Java , C/C++, .NET, Clojure e Julia. Senza un account sono consentite 50 chiamate all’API al giorno, ma questo può essere aumentato a 500 se si registra un account. In effetti, le chiamate possono essere aggiornate a 5.000 all’ora, se lo si desidera, contattando il team.
Non ho avuto molta esperienza con Quandl “su larga scala” e quindi non posso commentare il livello di errori presenti all’interno del set di dati, ma la mia sensazione è che è probabile che tutti gli errori vengano costantemente segnalati e corretti. Pertanto, vale la pena considerarlo come fonte di dati primaria per un master di titoli di fine giornata.

 

Fonti a pagamento

Per effettuare trading algoritmico intraday è solitamente necessario acquistare un feed commerciale. I prezzi possono variare dai $30 al mese fino a circa $500 al mese per il “livello retail”. I feed di qualità istituzionali saranno spesso nel range di quattro zeri al mese e come tali non sono considerati in questo articolo.

EODData.

Ho utilizzato EODData solo con dati giornalieri e prevalentemente per il mercato forex. Nonostante il loro nome, forniscono un certo grado di dati intraday. Il costo è $25 al mese per il loro pacchetto “platino”. La risorsa è molto utile per trovare un elenco completo dei simboli scambiati sulle borse globali, ma ricorda che questo è soggetto al bias di sopravvivenza poiché ritengo che la lista rappresenti le attuali entità quotate. Sfortunatamente ho scoperto che il feed di divisione azionaria era in qualche modo inaccurato (almeno se confrontato con le informazioni di Morningstar). Ciò ha portato a notevoli problemi di picco (vedi sotto) nei dati, che hanno aumentato l’attrito nel processo di pulizia dei dati.

DTN IQFeed

DTN IQFeed è uno dei feed di dati più popolari per i trader algoritmici retail di fascia alta. Dicono di avere oltre 80.000 clienti. Forniscono dati tick-by-tick in tempo reale non filtrati dall’exchange e una grande quantità di dati storici. Il prezzo inizia a $50 al mese, ma in realtà sarà compreso tra $150 e $200 al mese una volta selezionati determinati servizi. Io uso DTN IQFeed per tutte le mie strategie intraday sul mercato azionario e dei futures.

In termini di dati storici, IQFeed fornisce i dati per le azioni, futures e opzioni:

  • 180 giorni di calendario di tick (ogni trade)
  • 7+ anni di dati storici da 1 minuto
  • Oltre 15 anni di dati storici giornalieri

Il principale svantaggio è che il software DTN IQFeed (il mini-server, non gli strumenti per la creazione di grafici) funzionerà solo su Windows. Questo potrebbe non essere un problema se tutto il tuo trading algoritmico viene eseguito in questo sistema operativo, ma personalmente sviluppo tutte le mie strategie in Ubuntu Linux. Tuttavia, anche se non l’ho testato attivamente, ho sentito che è possibile eseguire DTN IQFeed con l’emulatore WINE.

QuantQuote

QuantQuote fornisce a prezzi ragionevoli dati storici al minuto, al secondo e a livello di tick per il mercato azionario statunitense, a partire dal 1998. Inoltre, forniscono a livello istituzionale il feed dei tick in tempo reale, sebbene questo sia di minore interesse per i trader algoritmici retail. Uno dei principali vantaggi di QuantQuote è che i loro dati sono forniti senza bias di sopravvivenza, grazie al loro software di abbinamento simbolico TickMap e all’inclusione di tutti i titoli entro un determinato indice nel tempo. Ad esempio, per acquistare l’intera cronologia dello S&P500 risalente al 1998 in barre al minuto, comprensivo dei simboli delistati, il costo al momento della scrittura di questo articolo era di $895.

QuantQuote è attualmente il principale fornitore di dati per il servizio di backtesting basato sul web QuantConnect. QuantQuote fa di tutto per garantire la minimizzazione degli errori, quindi se stai cercando titoli azionari statunitensi ad alta risoluzione, dovresti considerare di usare il loro servizio.

Storico Intraday per l’Azionario Statunitenze da DTN IQFeed con Python

In questo articolo voglio presentare un approccio per scaricare i dati storici intraday delle azioni statunitensi  dal fornitore di dati DTN IQFeed. È possibile ottenere i dati tramite una connessione socket al server locale IQLink che viene fornito quando si crea un account. In questo articolo utilizzeremo una connessione di streaming socket con Python per bufferizzare questi dati e creare file CSV di dati infragiornalieri il mercato  delle azioni statunitensi.

Nota: non ho alcuna affiliazione con DTN IQFeed oltre ad essere un loro cliente. Sto semplicemente scrivendo questo articolo per aiutare coloro che hanno un account IQFeed (o stanno prendendo in considerazione l’idea di acquistarne uno) per scaricare i dati senza la necessità di un software GUI.

Connessioni Socket in Python per IQFeed

Ipotizziamo che tu abbia già un account con IQFeed. In caso contrario, è possibile ottenere una prova gratuita di due settimane al momento dell’iscrizione.

Dopo esserti iscritto agli  exchange e al livello di granularità dei dati che si desidera, ti verrà chiesto di scaricare il launcher IQLink. Questo strumento funziona solo in modo nativo su Windows ma può essere eseguito anche su un Mac o una macchina Linux con WINE e un po ‘di lavoro!

L’avvio di IQLink avvia la finestra di dialogo di connessione:

Facendo clic su “Avvia IQLink” verrà avviato il server. Ti verrà richiesto il nome utente e la password. Una volta che il server è in esecuzione, è necessario creare una connessione Stream Socket  a una porta locale (9100 è l’impostazione predefinita). È quindi possibile inviare messaggi attraverso questo socket e ricevere i dati. Il primo compito è creare il file iqfeed.py e importare sia il sistema che le librerie di socket:
                    
# iqfeed.py

import sys
import socket                    
                
Il buffering dei dati viene gestito dalla funzione read_historical_data_socket, che richiede in input un oggetto socket e il numero di byte da prelevare per ogni. La funzione aggiunge semplicemente l’ultima serie di dati prelevati ad una stringa e la restituisce una volta che trova la stringa “! ENDMSG!” all’interno dei dati (cioè il buffer ha raggiunto la fine!):
                    
# iqfeed.py

def read_historical_data_socket(sock, recv_buffer=4096):
    """
    Read the information from the socket, in a buffered
    fashion, receiving only 4096 bytes at a time.

    Parameters:
    sock - The socket object
    recv_buffer - Amount in bytes to receive per read
    """
    buffer = ""
    data = ""
    while True:
        data = sock.recv(recv_buffer)
        buffer += data

        # Check if the end message string arrives
        if "!ENDMSG!" in buffer:
            break
   
    # Remove the end message string
    buffer = buffer[:-12]
    return buffer                    
                

Il socket deve connettersi al computer locale sulla porta 9100. In questo esempio, scarichiamo quattro simboli di azioni: SPY, AAPL, GOOG e AMZN dall’inizio del 2014 ad oggi.

IQFeed accetta messaggi nel seguente formato: CMD,SYM,[options]\n. Si noti il carattere “newline”, che deve aggiunta altrimenti il messaggio non funzionerà. Le opzioni fornite sono [bars in seconds], [beginning date: CCYYMMDD HHmmSS], [ending date: CCYYMMDD HHmmSS], [empty],[beginning time filter: HHmmSS], [ending time filter: HHmmSS], [old or new: 0 or 1], [empty] ,[queue data points per second].

Ad esempio il messaggio avrà un formato simile al seguente: "HIT,GOOG,60,20140101 075000,,,093000,160000,1\n". Questo messaggio ci restituisce i dati storici (HIT) per il simbolo del ticker GOOG, ad frequenza pari a una volta ogni 60 secondi (ad es. Timeframe ad un minuto), dalle 07:50:00 del 1 ° gennaio 2014 fino ad oggi. I dati vengono filtrati per essere presenti solo dalle 09:30:00 alle 16:00:00, che è l’orario di apertura del mercato NYSE.

Il primo compito è definire l’host, la porta e i simboli per il download. Ciascuno dei quattro simboli viene ripetuto e viene creato il messaggio per i dati storici. Quindi viene aperto un socket. AF_INET specifica che una tupla (host, port) deve essere prevista durante la connessione. SOCK_STREAM afferma che il socket deve essere un socket di streaming.

Una volta aperto il socket, il messaggio viene inviato e i dati cronologici vengono memorizzati nel buffer, dopodiché il socket viene chiuso. Tutte le terminazioni di riga vengono rimosse e i dati vengono scritti in un file chiamato “sym.csv” archiviato nella stessa directory del codice Python, dove “sym” è il simbolo ticker:

                    
# iqfeed.py

if __name__ == "__main__":
    # Define server host, port and symbols to download
    host = "127.0.0.1"  # Localhost
    port = 9100  # Historical data socket port
    syms = ["SPY", "AAPL", "GOOG", "AMZN"]

    # Download each symbol to disk
    for sym in syms:
        print "Downloading symbol: %s..." % sym

        # Construct the message needed by IQFeed to retrieve data
        message = "HIT,%s,60,20140101 075000,,,093000,160000,1\n" % sym

        # Open a streaming socket to the IQFeed server locally
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((host, port))

        # Send the historical data request
        # message and buffer the data
        sock.sendall(message)
        data = read_historical_data_socket(sock)
        sock.close

        # Remove all the endlines and line-ending
        # comma delimiter from each record
        data = "".join(data.split("\r"))
        data = data.replace(",\n","\n")[:-1]

        # Write the data stream to disk
        f = open("%s.csv" % sym, "w")
        f.write(data)
        f.close()                    
                
I dati restituite hanno il seguente formato: [YYYY-MM-DD HH:mm:SS],[OPEN],[LOW],[HIGH],[CLOSE],[VOLUME],[OPEN INTEREST] Quindi le righe all’interno del file dovrebbero essere come segue:
2012-01-03 09:31:00,30.6400,30.5000,30.6400,30.5100,6128,6128
2012-01-03 09:32:00,30.5600,30.4900,30.4900,30.5600,6528,400
2012-01-03 09:33:00,30.5000,30.5000,30.5000,30.5000,6672,144
2012-01-03 09:34:00,30.3800,30.3400,30.3400,30.3500,8423,1751
2012-01-03 09:35:00,30.5300,30.5300,30.5300,30.5300,8623,200
2012-01-03 09:36:00,30.6400,30.5500,30.5500,30.6400,9423,800
2012-01-03 09:37:00,30.6500,30.6500,30.6500,30.6500,10329,906
2012-01-03 09:38:00,30.6900,30.6600,30.6900,30.6600,12329,2000
2012-01-03 09:39:00,30.7200,30.6400,30.6500,30.7200,13729,1400
2012-01-03 09:40:00,30.7500,30.6900,30.7200,30.7500,17029,3300
I dati disponibili su IQFeed possono risalire anche a molti anni indietro. Tuttavia, può essere necessario del tempo per scaricarli una volta che si inizia a considerare i dati di molti simboli, risalenti a cinque o più anni di storico. Se desideri scaricare tipi di dati diversi dalle azioni, puoi consultare la guida di IQFeed. Ovviamente è necessario essere abbonati all’exchange appropriato affinché il download del simbolo funzioni. Tieni presente che i dati intraday forniti da IQFeed non sono “aggiustati”. Ciò significa che non tiene conto degli eventi societarie come i  dividendi o le scissioni di azioni. Dovrai personalmente seguire la correzione dei prezzi delle azioni.

Utilizzare i Futures Continuous per scopi di Backtesting

In questo articolo discuteremo le caratteristiche dei contratti future che rappresentano un’interessate sfida dal punto di vista di backtesting. In particolare, i concetti di “continuous contract” e”roll returns”. Descriveremo le principali difficoltà dei futures e forniremo un’implementazione in Python con pandas in grado di risolvere parzialmente questi problemi.

Breve panoramica sui Futures

I futures sono una forma di contratto stipulato tra due parti per l’acquisto o la vendita di una certa quantità di un sottostante in una specifica data specifica. Questa data è conosciuta come  scadenza. Quando questa data viene raggiunta, l’acquirente deve consegnare il sottostante fisico (o equivalente in contanti) al venditore per il prezzo concordato alla data di costituzione del contratto.

In pratica i futures sono negoziati in borsa (al contrario del trading Over The Counter – OTC) per quantità e qualità standardizzate del sottostante. I prezzi sono  marked to market ogni giorno. I futures sono incredibilmente liquidi e sono utilizzati pesantemente per scopi speculativi. In passato i futures venivano utilizzati principalmente per i prezzi dei prodotti agricoli o industriali, ora è possibile stipulare un contratto futures su qualsiasi sottostante tangibile o intangibile, come indici azionari, tassi di interesse dei tassi di cambio.

Un elenco dettagliato di tutti i codici e simboli utilizzati per i contratti futures in varie borse può essere consultato dal sul sito del CSI Data: Futures Factsheet.

La principale differenza tra un contratto futures e la partecipazione azionaria è il fatto che un contratto futures ha una finestra di disponibilità limitata in virtù della data di scadenza. In qualsiasi momento ci sarà una varietà di contratti futures sullo stesso sottostante, tutti con diverse date di scadenza. Il contratto con la data di scadenza più vicina è noto come near contract. Il problema che affrontiamo come trader quantitativi è la possibilità di scegliere in qualsiasi momento con molti contratti con cui fare trading. Quindi abbiamo a che fare con un insieme di serie temporali sovrapposte piuttosto che un flusso continuo come nel caso di azioni o valute.

L’obiettivo di questo articolo è di delineare vari approcci per costruire un flusso continuo di contratti a partire da questo insieme di serie multiple e per evidenziare i compromessi associati a ciascuna tecnica.

I contratti di Futures continui

La principale difficoltà nel cercare di generare un contratto continuo dai contratti sottostanti con differenti scadenze è causata dalla variazione dei prezzi con cui vengono scambiati. Quindi si verificano casi dove i prezzi non sono lineari tra un contratto a scadenza e il successivo. Ciò è dovuto agli effetti di Contango (i prezzi delle scadenze più lontane sono più alti di quelli delle scadenze più vicine) o in Backwardation (esattamente l’opposto).

Esistono diversi approcci per affrontare questo problema. Sfortunatamente non esiste un unico metodo “standard” per unire i contratti futures. In definitiva, il metodo scelto dipenderà in gran parte dalla strategia e dal metodo di esecuzione. Nonostante non esista un unico metodo, ci sono alcuni approcci comuni:

Aggiustamento Back/Forward ("Panama")

Questo metodo riduce il “divario” tra più contratti spostando ciascun contratto in modo tale che le singole scadenze si uniscano in modo agevole con contratti adiacenti. Pertanto, il prezzo di apertura / chiusura tra i contratti coincide.

Il problema chiave con il metodo Panama consiste nell’introduzione di un bias di trend, che causa una forte deriva dei prezzi, soprattutto con orizzonti temporali di lungo periodo (e anche la possibilità di prezzi negativi). Inoltre, vi è una perdita delle differenze di prezzo relative a causa di uno spostamento assoluto dei valori. Ciò significa che i rendimenti sono più complicati da calcolare (o semplicemente errati).

Aggiustamento Proporzionale

Questo approccio è simile al metodo del “handling stock splits” per le azioni (gestione dei frazionamenti azionari). In questo caso, il rapporto tra il vecchio prezzo di chiusura e il nuovo prezzo di apertura è utilizzato per adeguare proporzionalmente i prezzi dei contratti storici. Ciò consente un flusso continuo senza un’interruzione del calcolo dei ritorni percentuali. Il problema principale con l’adeguamento proporzionale è la necessità di applicare la stessa logica a qualsiasi trading basata su un livello di prezzo assoluto, al fine di eseguire il segnale corretto. Questo è un processo problematico e soggetto a errori. Quindi questo tipo di flusso continuo è spesso utile solo per una analisi statistica sommaria, al contrario della ricerca diretta di backtesting.

Rollover e serie Perpetual

La base di questo approccio consiste nel creare un contratto continuo come successione di contratti, considerano una percentuale ponderata linearmente di ciascun contratto su uno specifico numero di giorni, in modo da assicurare una transizione più fluida tra i contratti.

Per esempio, consideriamo 5 giorni di transizione. Il prezzo al giorno1, P1 è pari all’80% del prezzo del contratto più lontano (F1) e il 20% del prezzo del contratto più vicino (N1).

In modo analogo, al giorno 2 il prezzo è pari a: P2=0.6×F2+0.4×N2.

Al giorno 5 si ha: P5=0.0×F5+1.0×N5=N5

e quindi il contratto diventa continuo al prezzo più vicino. Dopo 5 giorni il contratto è transitato dal prezzo più lontano a quello più vicino.

Il problema con il metodo rollover consiste nel dovre effettuare operazioni tutti e 5 i giorni e quindi si ha un incremento dei costi di transazione days.

Implementazione del Roll-Return in Python e Pandas

Il resto dell’articolo si concentra sull’implementazione del metodo delle serie perpetue poiché è più appropriato per la fase di backtesting. È un modo utile per condurre ricerche sulle pipeline strategiche.

Vogliamo collegare  i contratti futures del WTI Crude Oil “near” e “lontano” (simbolo CL) per generare una serie di prezzi continui. Ad esempio possiamo considerare il contratto a breve termine è CLF2014 (gennaio) e il contratto lontano è CLG2014 (febbraio).

Per effettuare il download dei dati relativi ai Futures, ho utilizzato la lbreria Quandl. Assicurati di impostare il corretto ambiente virtuale di Python sul tuo sistema e installa il pacchetto Quandl digitando quanto segue nel terminale:

                    
pip install quandl                    
                

Ora che il pacchetto quandl è installato, dobbiamo usare NumPy e Pandas per eseguire la creazione dei roll-return. Se non hai installato NumPy o Pandas, ti consiglio di seguire il mio tutorial. Crea un nuovo file e inserisci le seguenti istruzioni:

                    
import datetime
import numpy as np
import panda as pd
import quandl                    
                
Il lavoro principale è svolto nella funzione futures_rollover_weights. Richiede una data di inizio (la prima data del contratto vicino), un dizionario delle date di regolamentazione del contratto (expiry_dates), i simboli dei contratti e il numero di giorni per il rinnovo del contratto (cinque, come default). Di seguito il codice di questa logica:
                    
def futures_rollover_weights(start_date, expiry_dates, contracts, rollover_days=5):
    """This constructs a pandas DataFrame that contains weights (between 0.0 and 1.0)
    of contract positions to hold in order to carry out a rollover of rollover_days
    prior to the expiration of the earliest contract. The matrix can then be
    'multiplied' with another DataFrame containing the settle prices of each
    contract in order to produce a continuous time series futures contract."""

    # Construct a sequence of dates beginning from the earliest contract start
    # date to the end date of the final contract
    dates = pd.date_range(start_date, expiry_dates[-1], freq='B')

    # Create the 'roll weights' DataFrame that will store the multipliers for
    # each contract (between 0.0 and 1.0)
    roll_weights = pd.DataFrame(np.zeros((len(dates), len(contracts))),
                                index=dates, columns=contracts)
    prev_date = roll_weights.index[0]

    # Loop through each contract and create the specific weightings for
    # each contract depending upon the settlement date and rollover_days
    for i, (item, ex_date) in enumerate(expiry_dates.iteritems()):
        if i < len(expiry_dates) - 1:
            roll_weights.ix[prev_date:ex_date - pd.offsets.BDay(), item] = 1
            roll_rng = pd.date_range(end=ex_date - pd.offsets.BDay(),
                                     periods=rollover_days + 1, freq='B')

            # Create a sequence of roll weights (i.e. [0.0,0.2,...,0.8,1.0]
            # and use these to adjust the weightings of each future
            decay_weights = np.linspace(0, 1, rollover_days + 1)
            roll_weights.ix[roll_rng, item] = 1 - decay_weights
            roll_weights.ix[roll_rng, expiry_dates.index[i+1]] = decay_weights
        else:
            roll_weights.ix[prev_date:, item] = 1
        prev_date = ex_date
    return roll_weights                    
                

Ora che la matrice pesata è è stata prodotta, è possibile applicarla alle singole serie temporali. La funzione principale scarica i contratti vicini e lontani, crea un singolo DataFrame per entrambi, costruisce la matrice del rollover ed infine produce una serie continua di entrambi i prezzi, opportunamente ponderata:

                    
if __name__ == "__main__":
    # Download the current Front and Back (near and far) futures contracts
    # for WTI Crude, traded on NYMEX, from Quandl.com. You will need to 
    # adjust the contracts to reflect your current near/far contracts 
    # depending upon the point at which you read this!
    wti_near = quandl.get("OFDP/FUTURE_CLF2014")
    wti_far = quandl.get("OFDP/FUTURE_CLG2014")
    wti = pd.DataFrame({'CLF2014': wti_near['Settle'],
                        'CLG2014': wti_far['Settle']}, index=wti_far.index)

    # Create the dictionary of expiry dates for each contract
    expiry_dates = pd.Series({'CLF2014': datetime.datetime(2013, 12, 19),
                              'CLG2014': datetime.datetime(2014, 2, 21)}).order()

    # Obtain the rollover weighting matrix/DataFrame
    weights = futures_rollover_weights(wti_near.index[0], expiry_dates, wti.columns)

    # Construct the continuous future of the WTI CL contracts
    wti_cts = (wti * weights).sum(1).dropna()

    # Output the merged series of contract settle prices
    wti_cts.tail(60)                    
                

Eseguendo questo script si ottiene il seguente output:

Da notare come ora la serie è continua tra i due contratti. Il prossimo passo è eseguire questo per scadenze multiple per un buon numero di anni, a seconda delle vostre esigenze di backtesting.

Sviluppo di Ambiente di BackTesting con Python e Pandas

Il backtesting è un  processo di ricerca che prevede di applicare l’idea di strategia di trading ai dati storici di uno o più sottostanti, al fine di verificare le performance della strategia nel  passato. In particolare, un backtest non fornisce alcuna garanzia circa le prestazioni future della strategia. Sono tuttavia una componente essenziale nel processo di ricerca della pipeline delle strategie, che consente di filtrare le strategie prima di essere inserite in produzione.

In questo articolo (e in quelli che seguono) voglio descrivere un semplice sistema di backtesting, orientato agli oggetti e scritto in Python. Questo sistema di base sarà principalmente un “sussidio didattico”, utilizzato per dimostrare i vari componenti di un sistema di backtesting. Man mano che procediamo attraverso gli articoli, verranno aggiunte funzionalità più sofisticate.

Panoramica di un Sistema di Backtesting

Il processo di progettazione di un robusto sistema di backtesting è estremamente difficile. Una efficace simulazione di tutte le componenti che influiscono sulle prestazioni di un sistema di trading algoritmico è molto impegnativa. La scarsa granularità dei dati, l’opacità del routing degli ordini all’interno di un broker, la latenza degli ordini e una miriade di altri fattori contribuiscono a modificare le prestazioni “reali” di una strategia rispetto alle prestazioni di backtesting.

Quando si implementa un sistema di backtesting si è tentati di voler costantemente “riscriverlo da zero” poiché sempre più fattori si rivelano cruciali nella valutazione delle prestazioni. Nessun sistema di backtesting si può dire completato, cioè è un sistema in continua evoluzione, e deve constantemente verificare che un numero sufficiente di fattori sia stato correttamente implementato nel sistema.

Tenendo presente questi vincoli, il sistema di backtesting descritto in questo articolo contiene delle semplificazioni. Durante l’esplorazione di aspetti avanzati (ottimizzazione del portafoglio, gestione del rischio, gestione dei costi di transazione), il sistema diventerà più solido.

Tipologie di Sistemi di Backtesting

Ci sono due principali tipologie di sistemi di backtest. Il primo è basato sulla ricerca, utilizzato principalmente nelle fasi iniziali, dove molte strategie saranno testate per selezionare quelle che meritano una valutazione più seria. Questi sistemi di backtesting di ricerca, chiamati  Explorers, sono spesso scritti in Python, R o MatLab poiché in questa fase la velocità e facilita di sviluppo del codice è più importante della velocità di esecuzione.

Il secondo tipo di sistema di backtest è basato sugli Event-based. Cioè, esegue il processo di backtest in un ciclo di esecuzione simile (se non identico) allo stesso sistema di esecuzione del trading. Modellerà realisticamente i dati di mercato e il processo di esecuzione degli ordini per fornire una valutazione più rigorosa di una strategia.

Questi ultimi sistemi sono spesso scritti in un linguaggio ad alte prestazioni come C ++ o Java, dove la velocità di esecuzione è essenziale. Per le strategie a bassa frequenza (anche se ancora intraday), Python è più che sufficiente per essere utilizzato anche in questo contesto.

Sistema di Backtesting Object-Oriented in Python

Vediamo ora la progettazione e l’implementazione di un ambiente di backtesting Explorer. Si vuole usare un paradigma di  programmazione orientata agli oggetti perchè permette di definire oggetti software in grado di interagire gli uni con gli altri attraverso lo scambio di messaggi.In particolare, questo approccio permette di:

  • specificare le interfacce di ciascun componente in anticipo, mentre le parti interne di ciascun componente possono essere modificate (o sostituite) durante l’evoluzione del progetto;
  • testare efficacemente come si comporta ciascun componente (tramite un unit test);
  • creare  nuovi componenti  sopra o in aggiunta agli altri, tramite l’ereditarietà e l’incaspulamento.

Il sistema è progettato per semplificare l’implementazione ed avere un ragionevole grado di flessibilità, a scapito della precisione. In particolare, questo backtester sarà in grado di gestire strategie che agiscono su un singolo strumento. Successivamente il backtester verrà modificato per gestire più strumenti.

Le componenti fondamentali di questo sistema sono le seguenti:

  • Strategia – è una classe che prevede in input un DataFrame pandas di barre, ovvero un elenco di dati Open-High-Low-Close-Volume (OHLCV) ad uno specifico timeframe. La strategia produrrà una lista di segnali, che consistono in un timestamp e un elemento dell’insieme {1,0, -1} che indica rispettivamente un segnale long, hold o short.
  • Portafoglio – La maggior parte del lavoro di backtesting avviene in questa classe. Riceve in input un insieme di segnali (come descritto sopra) e creerà una serie di posizioni, o ordini. Il compito delle Portfolio è di produrre una curva equity, incorporare i costi di transazione di base e tenere traccia delle operazioni.
    Prestazioni – prende un oggetto portfolio e produce una serie di statistiche sulle sue prestazioni. In particolare, produrrà caratteristiche di rischio / rendimento, metriche di trade / profit ed informazioni sui drawdown.

 

Come si può vedere, questo backtester non include alcun riferimento alla gestione del portafoglio o del  rischio, alla gestione dell’esecuzione (ad esempio non si gestisce i limit order ) né fornirà una sofisticata modellizzazione dei costi di transazione. Questo non è un grosso problema in questa fase. Ci consente di acquisire dimestichezza con il processo di creazione del di questo sistema e delle librerie Pandas / NumPy. Col tempo sarà poi ampliato con nuove componenti.

 

Implementazione

Vediamo ora l’implementazione di ciascuna delle componenti del sistema

Strategia

In questa fase, è necessario prevede un oggetto sufficientemente generico da poter gestire diverse tipologie di strategie, come mean-reversion, momentum e volatilità. Le strategie che vogliamo gestire devono essere basate sulle serie temporali, cioè “price driven“. Un requisito iniziale per questo tipo di  backtester è che le classi di strategie derivate dovranno accettere in input un elenco di DataFrame OHLCV, di tick (prezzi trade-by-trade) o i  dati degli order-book. Si prevede un limite inferiore di frenquenza dei trader ad 1 secondo.

Inoltre la classe Strategia  deve produrre avvisi sui segnali. Ciò significa che ‘avvisa’ un’istanza Portfolio della possibilità di andare long / short o mantenere una posizione. Questa flessibilità ci consentirà di creare più “explorers” di strategie che forniscono una serie di segnali, che una classe di Portfolio più avanzata può accettare per determinare quali ordini possono essere effettivamente immessi a mercato.

L’interfaccia delle classi è sviluppata utilizzando la metodologia abstract base class . Una classe base astratta è un oggetto che non può essere istanziato e quindi è possibile creare solo classi derivate. Il codice Python è riportato di seguito in un file chiamato backtest.py. La classe Strategy richiede che qualsiasi classe figlia implementi il metodo generate_signals.

Al fine di prevenire la possibilità che la classe Strategy sia inizializzata direttamente (dato che è astratta) è necessario usare gli oggetti ABCMeta e abstractmethod del modulo abc. In  particolare si introduce  una proprietà alla classe, chiamata __metaclass__ che corrisponde a ABCMeta e si applica il  decorate al metodo generate_signals con il decorator abstractmethod.

                    
# backtest.py

from abc import ABCMeta, abstractmethod

class Strategy(object):
    """Strategy is an abstract base class providing an interface for
    all subsequent (inherited) trading strategies.

    The goal of a (derived) Strategy object is to output a list of signals,
    which has the form of a time series indexed pandas DataFrame.

    In this instance only a single symbol/instrument is supported."""

    __metaclass__ = ABCMeta

    @abstractmethod
    def generate_signals(self):
        """An implementation is required to return the DataFrame of symbols
        containing the signals to go long, short or hold (1, -1 or 0)."""
        raise NotImplementedError("Should implement generate_signals()!")                    
                

Sebbene l’interfaccia di cui sopra sia molto semplice, diventerà più complicata quando questa classe verrà ereditata da ogni specifica tipologia di strategia. In definitiva, l’obiettivo della classe Strategy, in questa fase, è fornire un elenco di segnali long / short / hold per ogni strumento e poi inviare l’elenco da inviare a un portfolio.

Portafoglio

La classe Portfolio contiene la maggior parte della logica di trading. Per questo sistema di backtesting, il portafoglio è incaricato di determinare il dimensionamento delle posizioni, l’analisi dei rischi, la gestione dei costi di transazione e la gestione delle esecuzioni (vale a dire gli ordini market-on-open, market-on-close). In una fase successiva queste attività verranno suddivise in componenti separati. Per il momento, sono inserite all’interno di in una sola classe. Questa classe fa ampio uso dei funzionalità offerte dalla libreria Pandas e fornisce un ottimo esempio di come questa libreria permette di risparmiare una quantità enorme di tempo, in particolare per quanto riguarda la trasformazione dei dati storici in un formato “standard”. Per  principale vantaggio di Pandas e NumPy consiste di evitare di accedere a qualsiasi set di dati usando la sintassi for d in .... Questo perché NumPy (che è alla base di Pandas) ottimizza il loop tramite operazioni vettorializzate. L’obiettivo della classe Portfolio è di produrre una sequenza di ordini e una curva equity, che saranno analizzati dalla classe Performance. Per raggiungere questo obiettivo, è necessario fornire un elenco di ‘segnali’ da un oggetto Strategia. Più tardi, questo sarà un gruppo di oggetti strategici. La classe del Portfolio deve prevedere un logica per determinare quanto capitale possa essere utilizzato per uno specifico sottinsieme di segnali, prevedere la gestione dei costi di transazione e determinare quale tipo di ordine utilizzare. Devono essere previste logiche per utilizzare il set di dati forniti dalla Strategia (barre OHLCV) per determinare il prezzo di esecuzione di un ordine. Poiché i prezzi high/low di ogni barra sono sconosciuti a priori, è possibile utilizzare solo i prezzi di apertura e chiusura per effettuare il trade. In realtà è impossibile garantire che un ordine sarà eseguito esattamente ad uno specifico prezzo quando si utilizza un ordine market, quindi sarà sempre un’approssimazione della realtà. Oltre ai vincoli sull’esecuzione degli ordini, questo backtester ignorerà tutti i concetti di margin/brokerage e presumerà che sia possibile andare long o short con qualsiasi strumento, senza vincoli di liquidità. Questa è chiaramente un’ipotesi molto irrealistica, ma è una funzionalitò che può essere implementata in una seconda fase. Di seguito, il codice da aggiungere al nostro backtester.py
                    
# backtest.py

class Portfolio(object):
    """An abstract base class representing a portfolio of 
    positions (including both instruments and cash), determined
    on the basis of a set of signals provided by a Strategy."""

    __metaclass__ = ABCMeta

    @abstractmethod
    def generate_positions(self):
        """Provides the logic to determine how the portfolio 
        positions are allocated on the basis of forecasting
        signals and available cash."""
        raise NotImplementedError("Should implement generate_positions()!")

    @abstractmethod
    def backtest_portfolio(self):
        """Provides the logic to generate the trading orders
        and subsequent equity curve (i.e. growth of total equity),
        as a sum of holdings and cash, and the bar-period returns
        associated with this curve based on the 'positions' DataFrame.

        Produces a portfolio object that can be examined by 
        other classes/functions."""
        raise NotImplementedError("Should implement backtest_portfolio()!")                    
                
A questo punto, dopo aver introdotto le classi astratte Strategy e Portfolio abstract, possiamo ora creare alcuni concrete classi derivate da queste due, in modo da implementare una strategia funzionante. Iniziano con il definire una classe RandomForecastStrategy, derivata da Strategy, che prevedere di produrre segnali semplicemente scegliendo a caso (random) long o short! Chiaramente questa strategia non può funzionare nel mercato, ma è utile per scopi dimostrativi. Si crea quindi un nuovo file, chiamato random_forecast.py, con il codice per implementa la logica random della strategia:
                    
# random_forecast.py

import numpy as np
import pandas as pd
import quandl   # Necessary for obtaining financial data easily

from backtest import Strategy, Portfolio

class RandomForecastingStrategy(Strategy):
    """Derives from Strategy to produce a set of signals that
    are randomly generated long/shorts. Clearly a nonsensical
    strategy, but perfectly acceptable for demonstrating the
    backtesting infrastructure!"""    
    
    def __init__(self, symbol, bars):
    	"""Requires the symbol ticker and the pandas DataFrame of bars"""
        self.symbol = symbol
        self.bars = bars

    def generate_signals(self):
        """Creates a pandas DataFrame of random signals."""
        signals = pd.DataFrame(index=self.bars.index)
        signals['signal'] = np.sign(np.random.randn(len(signals)))

        # The first five elements are set to zero in order to minimise
        # upstream NaN errors in the forecaster.
        signals['signal'][0:5] = 0.0
        return signals                    
                
Ora che abbiamo una  strategia “concreta”, dobbiamo creare un’implementazione dell’oggetto Portfolio. Questo oggetto comprenderà la maggior parte del codice di backtesting. È progettato per creare due DataFram separati, il primo, chiamato positions, utilizzato per memorizzare la quantità detenuta di ogni strumento ad ogni specifica barra. Il secondo, portfolio, contiene in realtà il prezzo market di tutte le posizioni per ciascuna barra, nonché un conteggio del denaro contante, a partire da uno specifico capitale iniziale. Questo alla fine fornisce una curva equity su cui valutare la performance della strategia. L’oggetto Portfolio, sebbene estremamente flessibile nella sua interfaccia, richiede scelte specifiche su come gestire i costi di transazione, gli ordini a mercato, ecc. In questo esempio di base ho considerato la possibilità di andare facilmente long/short su uno strumento, senza restrizioni o margine, di acquistare o vendere direttamente al prezzo di apertura della barra, senza costi di transazione (compresi slippage e commisioni) e di specificare direttamente la quantità di azioni da acquistare ad ogni operazione. Di seguito il codice da aggiungere a random_forecast.py:
                    
# random_forecast.py

class MarketOnOpenPortfolio(Portfolio):
    """Inherits Portfolio to create a system that purchases 100 units of 
    a particular symbol upon a long/short signal, assuming the market 
    open price of a bar.

    In addition, there are zero transaction costs and cash can be immediately 
    borrowed for shorting (no margin posting or interest requirements). 

    Requires:
    symbol - A stock symbol which forms the basis of the portfolio.
    bars - A DataFrame of bars for a symbol set.
    signals - A pandas DataFrame of signals (1, 0, -1) for each symbol.
    initial_capital - The amount in cash at the start of the portfolio."""

    def __init__(self, symbol, bars, signals, initial_capital=100000.0):
        self.symbol = symbol        
        self.bars = bars
        self.signals = signals
        self.initial_capital = float(initial_capital)
        self.positions = self.generate_positions()
        
    def generate_positions(self):
    	"""Creates a 'positions' DataFrame that simply longs or shorts
    	100 of the particular symbol based on the forecast signals of
    	{1, 0, -1} from the signals DataFrame."""
        positions = pd.DataFrame(index=self.signals.index).fillna(0.0)
        positions[self.symbol] = 100*self.signals['signal']
        return positions
                    
    def backtest_portfolio(self):
    	"""Constructs a portfolio from the positions DataFrame by 
    	assuming the ability to trade at the precise market open price
    	of each bar (an unrealistic assumption!). 

    	Calculates the total of cash and the holdings (market price of
    	each position per bar), in order to generate an equity curve
    	('total') and a set of bar-based returns ('returns').

    	Returns the portfolio object to be used elsewhere."""

    	# Construct the portfolio DataFrame to use the same index
    	# as 'positions' and with a set of 'trading orders' in the
    	# 'pos_diff' object, assuming market open prices.
        portfolio = self.positions*self.bars['Open']
        pos_diff = self.positions.diff()

        # Create the 'holdings' and 'cash' series by running through
        # the trades and adding/subtracting the relevant quantity from
        # each column
        portfolio['holdings'] = (self.positions*self.bars['Open']).sum(axis=1)
        portfolio['cash'] = self.initial_capital - (pos_diff*self.bars['Open']).sum(axis=1).cumsum()

        # Finalise the total and bar-based returns based on the 'cash'
        # and 'holdings' figures for the portfolio
        portfolio['total'] = portfolio['cash'] + portfolio['holdings']
        portfolio['returns'] = portfolio['total'].pct_change()
        return portfolio                    
                

 

Questo di permette di generare una curva equity del sistema.

Lo step finale prevede di collegare tutto insieme con una funzione  __main__:

                    
if __name__ == "__main__":
    # Obtain daily bars of SPY (ETF that generally
    # follows the S&P500) from Quandl (requires 'pip install Quandl'
    # on the command line)
    symbol = 'SPY'
    bars = quandl.get("GOOG/NYSE_%s" % symbol, collapse="daily")

    # Create a set of random forecasting signals for SPY
    rfs = RandomForecastingStrategy(symbol, bars)
    signals = rfs.generate_signals()

    # Create a portfolio of SPY
    portfolio = MarketOnOpenPortfolio(symbol, bars, signals, initial_capital=100000.0)
    returns = portfolio.backtest_portfolio()

    print(returns.tail(10))                    
                

The output dello script è riportato di seguito. Questo output dipende ovviamente dal range di tempo considerato e il generatore random utilizzato:

In questo caso la strategia ha perso denaro, il che non sorprende vista la natura stocastica del generatore di segnali! Il  passo successivo consiste nel creare un oggetto Performance che accetta un’istanza Portfolio e fornisce un report delle metriche sul rendimento su cui basare le valutazioni su come filtrare la strategia. Possiamo anche migliorare l’oggetto portfolio per avere una gestione più realistica dei costi di transazione (come le commissioni di Interactive Brokers e lo slippage). Possiamo anche includere direttamente una logica ‘più realistica’ in una strategia, che (si spera) produrrà risultati migliori. Nei prossimi articoli esploreremo questi concetti in modo più approfondito.

Implementazione di un Database di Securities Master con MySQL e Python

Ora aver introdotto l’idea alla base di un securities master database, vediamo è ora come implementarne uno. Per questo faremo uso di due tecnologie open source: il database MySQL e il linguaggio di programmazione Python. Alla fine di questo articolo avrete un vero e proprio database master di titoli azionari con cui condurre ulteriori analisi alla ricerca di una strategia di trading quantitativo.

I vantaggi di un Securities Master Database

Prima di iniziare, ricapitoliamo i vantaggi che si ottengo grazie all’introduzione di un securities master database nel nostro sistema di trading quantitativo:

  • Velocità – con i dati azionari memorazzati su un rigido locale, qualsiasi applicazione di analisi dati (come pandas) può accedere rapidamente ai dati senza dover eseguire lente operazioni di input / output (I / O) attraverso un collegamento di rete latente.
  • Fonti Multiple: i securities master database consentono la memorizzazione diretta da più sorgenti dati per lo stesso ticker. Quindi possiamo aggiungere un codice personalizzato per la correzione degli errori e / o tracciare i dati che devono essere controllati manualmente.
  • Tempo di inattività – Se ci affidiamo a una connessione Internet per i nostri dati, nel caso il fornitore (per qualsiasi motivo) stia affrontando un  periodo di inattività, non sarà possibile effettuare ricerche. Un database locale, con un sistema di backup, è sempre disponibile.
  • Meta-dati: un securities master database permette di memorizzare metadati sulle informazioni del nostro ticker. Possiamo includere tabelle degli exchange, fornitori e simboli di corrispondenza, aiutandoci a minimizzare gli errori dalle sorgenti dati.
 Esistono molti altri motivi per archiviare i dati localmente (o almeno su un server remoto) anziché fare affidamento sulle connessioni API ad un fornitore di dati. Un securities master fornisce il modello su cui costruire l’intero archivio dati delle nostre applicazioni di trading algoritmico. Tuttavia, ai fini di questo articolo ci concentreremo sulla memorizzazione dei dati storici giornalieri.
 

MySQL per i Securities Master Database

Per costruire ed interagire con un securities master database si può utilizzare MySQL e Python/pandas. Non mi soffermerò sulle specifiche di installazione di ciascuno di questi strumenti, poiché la procedura di installazione è piuttosto specifica per ogni piattaforma. Tuttavia, ti indicherò alcune guide che ti saranno sicuramente di aiuto per installare il software.

Installare MySQL

Per installare MySQL, è necessario selezionare la piattaforma appropriata:

  • Windows – Per le informazioni sulla procedura di installazione  di MySQL su Microsoft Windows, si può consultare la documentazione di MySQL. Se vuoi scaricare i file binari disponibili per Windows, puoi consultare questa pagina.
  • Mac OSX: è possibile scaricare i file binari per Mac OSX nella pagina dei download di MySQL. In alternativa, puoi installare MySQL tramite homebrew.
  • Linux / UNIX: puoi scegliere se scaricare un file binario dalla tua distribuzione o compilare dai codici sorgente. Su un sistema Debian / Ubuntu puoi digitare sudo apt-get install mysql-server. Se si utilizza una distribuzione basata su RPM come Fedora o Cent OS, è possibile digitare yum install mysql-server.

Creare un nuovo database e utente

Ora che MySQL è installato sul tuo sistema, possiamo creare un nuovo database e un utente per interagire con esso. All’installazione ti verrà richiesta una password di root. Per accedere a MySQL dalla riga di comando, si può utilizzare il seguente comando e inserire la password:

                    
$ mysql -u root -p                    
                

Una volta effettuato l’accesso a MySQL, puoi creare un nuovo database chiamato securities_master e selezionarlo:

                    
mysql> CREATE DATABASE securities_master; 
mysql> USE securities_master;                    
                
I database documentali/NoSQL, sebbene non siano un nuovo concetto, negli ultimi anni hanno acquisito una notevole importanza grazie a loro utilizzo da parte dei giganti del web come Google, Facebook e Twitter. Differiscono sostanzialmente dai sistemi RDBMS in quanto non esiste alcun concetto di schemi di tabelle. Invece, ci sono collezioni e documenti, che sono le analogie più vicine, rispettivamente, alle tabelle e ai record. Esiste un’ampia tassonomia di archivi documentali, la cui discussione è ben al di fuori di questo articolo! Tuttavia, alcuni delle soluzioni più popolari sono MongoDBCassandra e CouchDB. I database documentali, nelle applicazioni finanziarie, sono adatti principalmente ai dati fondamentali o ai metadati. I dati fondamentali per le attività finanziarie sono disponibili in molte forme, come azioni aziendali, dichiarazioni di guadagni, archivi SEC ecc. Pertanto, la natura senza schema dei DB NoSQL è particolarmente adatta. Tuttavia, i DB NoSQL non sono ben progettati per le serie temporali come i dati sui prezzi ad alta risoluzione e quindi non li prenderemo in considerazione per tale scopo.

Una volta creato un database è necessario aggiungere un nuovo utente per interagire con il database. Anche se è possibile utilizzare l’utente root, è considerato una cattiva pratica dal punto di vista della sicurezza, in quanto garantisce troppe autorizzazioni e può portare a un sistema compromesso. Su una macchina locale questo è per lo più irrilevante, ma in un ambiente di produzione remoto sarà certamente necessario creare un utente con autorizzazioni ridotte. In questo caso il nostro utente verrà chiamato sec_user. Ricordarsi di sostituire ‘password’ con una password sicura:

                    
mysql> CREATE USER 'sec_user'@'localhost' IDENTIFIED BY 'password'; 
mysql> GRANT ALL PRIVILEGES ON securities_master.* TO 'sec_user'@'localhost'; 
mysql> FLUSH PRIVILEGES;                    
                

Con le tre righe precedenti si crea e si autorizza l’utente ad usare il db ‘securities_master’ e si applica tali privilegi. D’ora in poi qualsiasi interazione che si verifica con il database si dovrà far uso dell’utente ‘sec_user’.

Progettare lo schema per i Securities Master Azionari

Dopo aver installato MySQL e configurato un utente con cui interagire con il nostro database. In questa fase siamo pronti a costruire le tabelle necessarie per memorizzare i nostri dati finanziari. Per un semplice e diretto securities master azionario sono sufficienti quattro tabelle:
  • Exchange – La tabella elenca gli exchange da cui vogliamo ottenere le informazioni sui prezzi delle azioni. In questo caso sarà quasi esclusivamente la Borsa di New York (NYSE) e la National Association of Securities Dealers Automated Quotations (NASDAQ).
  • DataVendor – questa tabella elenca le informazioni sui fornitori di dati dei prezzi storici. Useremo Yahoo Finance per trovare i nostri dati di fine giornata (EOD). Introducendo questa tabella, rendiamo semplice aggiungere altri fornitori, se necessario, come ad esempio Google Finance.
  • Symbol: la tabella dei simboli memorizza l’elenco dei simboli dei ticker e delle informazioni aziendali. In questo momento eviteremo problemi come classi di azioni differenti e nomi di simboli multipli. Tratteremo tali questioni in articoli successivi!
  • DailyPrice – Questa tabella memorizza le informazioni dei prezzo giornaliero per ogni security. Può diventare molto grande se vengono aggiunti molti titoli. Quindi è necessario ottimizzare le prestazioni.
MySQL è un database estremamente flessibile in quanto consente di personalizzare la modalità di archiviazione dei dati tramite vari tipi di tabelle.  Le più usate sono senza dubbio le MyISAM e InnoDB. Anche se non entrerò nei dettagli dei vari tipi di tabelle gestite da MySQL (ce ne sono molte!), si può dire che MyISAM è più utile per una rapida lettura (come l’interrogazione sulle grandi quantità di informazioni relative ai prezzi), ma non supporta le transazioni (necessarie per il rollback completo di un’operazione a più passaggi che può fallire durante l’elaborazione). InnoDB permette la gestione delle foreign key e della transazionalità, con le quali è possibile creare una base di dati relazionale e transazionale, ma sono più lente a causa delle funzionalità aggiuntive di cui dispongono.. InnoDB consente inoltre il blocco a livello di riga durante le operazioni di scrittura, mentre MyISAM blocca l’intera tabella durante la scrittura. Questo può avere problemi di prestazioni quando si scrivono molte informazioni su punti arbitrari nella tabella (come con le istruzioni UPDATE). Questo è un argomento molto vasto, quindi rimando la discussione per un’altro articolo. Utilizzeremo InnoDB poiché è nativamente più sicuro per le transazioni e offre il blocco a livello di riga. Nel caso una tabella fosse lenta da leggere, possiamo in prima analisi creare degli indici e solo nel caso le prestazioni fossero ancora poco soddisfacenti si procederebbe alla modifica del tipo di tabella. Tutte le nostre tabelle utilizzeranno il set di caratteri UTF-8, poiché desideriamo supportare gli exchange internazionali. Puoi leggere ulteriori informazioni sulla codifica UTF-8 in questa pagina di Wikipedia. Iniziamo con schema e l’istruzione CREATE TABLE del linguaggio SQL per creazione della tabella exchange. Questa tabella memorizza la sigla e il nome dello exchange (ad esempio NYSE – New York Stock Exchange) così come la posizione geografica. Prevede anche una valuta e una differenza di fuso orario da UTC. Per scopi interni memorizziamo anche una data di creazione e dell’ultimo aggiornamento. Infine, impostiamo la chiave dell’indice primario come un numero intero a incremento automatico (che è sufficiente per gestire i record 232):
                    
CREATE TABLE `exchange` (
  `id` int NOT NULL AUTO_INCREMENT,
  `abbrev` varchar(32) NOT NULL,
  `name` varchar(255) NOT NULL,
  `city` varchar(255) NULL,
  `country` varchar(255) NULL,
  `currency` varchar(64) NULL,
  `timezone_offset` time NULL,
  `created_date` datetime NOT NULL,
  `last_updated_date` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;                    
                
Di seguito lo schema e l’istruzione CREATE TABLE di SQL per la tabelladata_vendor. Questa memorizza il nome, il sito web e l’email del fornitore dei dati. In caso di necessità potremmo aggiungere più informazioni, come l’API endpoint URL:
                    
CREATE TABLE `data_vendor` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(64) NOT NULL,
  `website_url` varchar(255) NULL,
  `support_email` varchar(255) NULL,
  `created_date` datetime NOT NULL,
  `last_updated_date` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;                    
                

Successivamente si passa allo schema e l’istruzione CREATE TABLE di SQL per la tabella symbol. Questa tabella contiene un link alla foreign key di un exchange (in questo articolo ci focalizziamo solamente sui strumenti tradati degli exchange), un simbolo di  ticker (ad esempio GOOG), il tipo di strumento (‘azione’ o ‘indice’), il nome del titolo azionario o dell’indici del mercato, il settore e la valuta di riferimento.

                    
CREATE TABLE `symbol` (
  `id` int NOT NULL AUTO_INCREMENT,
  `exchange_id` int NULL,
  `ticker` varchar(32) NOT NULL,
  `instrument` varchar(64) NOT NULL,
  `name` varchar(255) NULL,
  `sector` varchar(255) NULL,
  `currency` varchar(32) NULL,
  `created_date` datetime NOT NULL,
  `last_updated_date` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `index_exchange_id` (`exchange_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;                    
                
Infine si riporta lo schema e l’istruzione CREATE TABLE di SQL per la tabella daily_price. In questa tabella vengono memorizzati i dati storici dei prezzi. Il nome della tabella ha il prefisso daily_ poiché potremmo voler creare dati con diverse risoluzioni temporali (come minuti o secondi) e quindi gestirli in tabelle separate, in caso successivamente si vuole implementare strategie con frequenze più elevate. La tabella contiene due chiavi esterne: una verso il fornitore di dati e l’altra verso un simbolo. In questo modo si identifica in modo univoco il dato e ci consente di memorizzare nella stessa tabella gli stessi dati di prezzo per più fornitori. Per i nostri scopi memorizziamo anche la data del prezzo (vale a dire il periodo giornaliero su cui i dati OHLC sono validi) e le date di creazione e di ‘ultima aggiornamento. I campi rimanenti memorizzano i prezzi di apertura-massimo-minimo-chiusura ti-alti-bassi e il prezzo di chiusura aggiustato. Alcuni fornitori di dati calcola anche i dividendi e scissioni, e quindi memorizziamo il prezzo corretto nella colonna adj_close_price. Da notare che il tipo di dato corrisponde a decimal(19,4). Quando si tratta di dati finanziari è assolutamente necessario essere precisi. Se avessimo usato il tipo di dati float, avremmo riscontrato errori di arrotondamento dovuti alle modalità con le quali i dati float sono archiviati internamente. Il campo finale memorizza il volume di scambi giornalieri. Questo usa il tipo di dati bigint in modo da non troncare accidentalmente giorni di volumi estremamente elevati.
                    
CREATE TABLE `daily_price` (
  `id` int NOT NULL AUTO_INCREMENT,
  `data_vendor_id` int NOT NULL,
  `symbol_id` int NOT NULL,
  `price_date` datetime NOT NULL,
  `created_date` datetime NOT NULL,
  `last_updated_date` datetime NOT NULL,
  `open_price` decimal(19,4) NULL,
  `high_price` decimal(19,4) NULL,
  `low_price` decimal(19,4) NULL,
  `close_price` decimal(19,4) NULL,
  `adj_close_price` decimal(19,4) NULL,
  `volume` bigint NULL,
  PRIMARY KEY (`id`),
  KEY `index_data_vendor_id` (`data_vendor_id`),
  KEY `index_synbol_id` (`symbol_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;                    
                

Inserendo tutti i precedenti comandi SQL nella riga di comando MySQL verranno create le quattro tabelle previste.

Usare Python/Pandas per l'interazione con i Securities Master

Per iniziare a popolare il nostro database è necessario installare Python e Panda.

Installare Python/Pandas

Il modo migliore per installare Python consiste nell’utilizzare lo strumento dell’ambiente virtuale virtualenv e il gestore di pacchetti pip. Per installare Python in questo modo, è necessario attenersi alla seguente procedura:
  • Windows: visita la pagina Download Python per scaricare una versione di Python. Raccomando di usare l’ultima versione stabile disponibile di Python3. Una volta installato Python, è necessario scaricare setuptools. I passaggi finali consistono nell’eseguire easy_install pip e pip install virtualenv nella shell dei comandi.
  • Mac OSX – Il modo migliore per installare Python su Mac è usare homebrew. Quindi puoi installare Python tramite brew install python. Quindi è necessario eseguire pip install virtualenv per installare virtualenv.
  • Linux / UNIX – Per le distribuzioni di tipo Debian / Ubuntu sudo apt-get install python3-pip python3-dev per installare pip e le librerie di sviluppo Python. Quindi eseguire pip install virtualenv per installare virtualenv a livello globale.

Una volta installato virtualenv, è possibile creare un nuovo ambiente virtuale Python in una directory separata e quindi installare pandas (comandi per un ambiente UNIX):

                    
$ cd ~
$ mkdir -p python-apps/trading
$ cd python-apps/trading
$ virtualenv .
$ source bin/activate
$ pip install pandas                    
                
Il passaggio finale è installare la libreria Python-MySQL. Sulle macchine Debian/Ubuntu bisogna eseguire i seguenti comandi:
                    
sudo apt-get install default-libmysqlclient-dev
pip install mysqlclient                    
                
Ora siamo pronti per iniziare a interagire con il nostro database MySQL tramite Python e Pandas.

Usare un Object-Relational Mapper

Quelli di voi che hanno un background tecnico e sono familiari con lo sviluppo e la gestione di database, si potranno chiedere se sia più ragionevole utilizzare un Object-Relational Mapper (ORM). Un ORM consente agli oggetti all’interno di un linguaggio di programmazione di essere mappati direttamente nelle tabelle nei database in modo tale che il codice del programma sia completamente inconsapevole del motore di archiviazione sottostante. Tali software non sono esenti da errori, ma sono comunque in grado di far risparmiare molto tempo. Tuttavia, il risparmio di tempo viene generalmente a discapito delle prestazioni. Un ORM popolare per Python è SQLAlchemy. Permette di specificare lo schema del database all’interno di Python stesso e quindi genera automaticamente il codice CREATE TABLE. Poiché abbiamo scelto specificamente MySQL e sono interessato alle prestazioni, ho scelto di non utilizzare un ORM per questo articolo.

Ottenere i dati dei simboli listati

Iniziamo con il recuperare i simboli associati all’elenco di Standard & Poor’s dei 500 titoli a grande capitalizzazione, ad esempio S&P500. Naturalmente, questo è semplicemente un esempio. Se stai operando sul mercato italiano e desideri utilizzare gli indici domestici dell’Italia, puoi anche ottenere l’elenco delle società FTSE MIB quotate alla Borsa di Milano (LSE).

Inoltre Wikipedia elenca le componenti del S&P500. Analizzeremo questo sito web usando la libreria lxml di Python ed aggiungeremo direttamente il contenuto direttamente al database in MySQL. Innanzitutto assicurati che la libreria sia installata:

                    
pip install lxml                    
                

Il seguente codice utilizzerà la libreria lxml e aggiungerà i simboli direttamente al database MySQL creato in precedenza. Ricordarsi di sostituire “password” con la password scelta durante la creazione dell’utente del db:

                    
#!/usr/bin/python
# -*- coding: utf-8 -*-

import datetime
import lxml.html
import PyMySQL as mdb

from math import ceil


def obtain_parse_wiki_snp500():
  """Download and parse the Wikipedia list of S&P500 
  constituents using requests and libxml.

  Returns a list of tuples for to add to MySQL."""

  # Stores the current time, for the created_at record
  now = datetime.datetime.utcnow()

  # Use libxml to download the list of S&P500 companies and obtain the symbol table
  page = lxml.html.parse('http://en.wikipedia.org/wiki/List_of_S%26P_500_companies')
  symbolslist = page.xpath('//table[1]/tr')[1:]

  # Obtain the symbol information for each row in the S&P500 constituent table
  symbols = []
  for symbol in symbolslist:
    tds = symbol.getchildren()
    sd = {'ticker': tds[0].getchildren()[0].text,
        'name': tds[1].getchildren()[0].text,
        'sector': tds[3].text}
    # Create a tuple (for the DB format) and append to the grand list
    symbols.append( (sd['ticker'], 'stock', sd['name'], 
      sd['sector'], 'USD', now, now) )
  return symbols

def insert_snp500_symbols(symbols):
  """Insert the S&P500 symbols into the MySQL database."""

  # Connect to the MySQL instance
  db_host = 'localhost'
  db_user = 'sec_user'
  db_pass = 'password'
  db_name = 'securities_master'
  con = mdb.connect(host=db_host, user=db_user, passwd=db_pass, db=db_name)

  # Create the insert strings
  column_str = "ticker, instrument, name, sector, currency, created_date, last_updated_date"
  insert_str = ("%s, " * 7)[:-2]
  final_str = "INSERT INTO symbol (%s) VALUES (%s)" % (column_str, insert_str)
  print(final_str, len(symbols))

  # Using the MySQL connection, carry out an INSERT INTO for every symbol
  with con: 
    cur = con.cursor()
    # This line avoids the MySQL MAX_PACKET_SIZE
    # Although of course it could be set larger!
    for i in range(0, int(ceil(len(symbols) / 100.0))):
      cur.executemany(final_str, symbols[i*100:(i+1)*100-1])

if __name__ == "__main__":
  symbols = obtain_parse_wiki_snp500()
  insert_snp500_symbols(symbols)                    
                

A questo punto tutti gli attuali 500 simboli che compongono l’indice S&P500 sono inseriti nel database. Il nostro prossimo obiettivo sarà quello di ottenere lo storico dei prezzi da diverse fonti e collegarli ai simboli appena salvati.

Recuperare i dati storici

Per ottenere i dati storici degli attuali titoli che compongono l’S&P500, dobbiamo prima interrogare il database per farci restituire l’elenco di tutti i simboli. Una volta ottenuto l’elenco dei simboli (insieme agli ID dei simboli), è possibile richiamare l’API di Yahoo Finance e scaricare lo storico dei prezzi da ciascun simbolo. Quindi possiamo inserire i dati nel database per ogni simboli ottenuto. Ecco il codice Python che effettua queste operazioni:

                    
#!/usr/bin/python
# -*- coding: utf-8 -*-

import datetime
import PyMySQL as mdb
from urllib.request import urlopen

# Obtain a database connection to the MySQL instance
db_host = 'localhost'
db_user = 'sec_user'
db_pass = 'password'
db_name = 'securities_master'
con = mdb.connect(db_host, db_user, db_pass, db_name)


def obtain_list_of_db_tickers():
    """Obtains a list of the ticker symbols in the database."""
    with con:
        cur = con.cursor()
        cur.execute("SELECT id, ticker FROM symbol")
        data = cur.fetchall()
        return [(d[0], d[1]) for d in data]


def get_daily_historic_data_yahoo(ticker,
                                  start_date=(2000, 1, 1),
                                  end_date=datetime.date.today().timetuple()[0:3]):
    """Obtains data from Yahoo Finance returns and a list of tuples.

  ticker: Yahoo Finance ticker symbol, e.g. "GOOG" for Google, Inc.
  start_date: Start date in (YYYY, M, D) format
  end_date: End date in (YYYY, M, D) format"""

    # Construct the Yahoo URL with the correct integer query parameters
    # for start and end dates. Note that some parameters are zero-based!
    yahoo_url = "http://ichart.finance.yahoo.com/table.csv?s=%s&a=%s&b=%s&c=%s&d=%s&e=%s&f=%s" % \
                (ticker, start_date[1] - 1, start_date[2], start_date[0], end_date[1] - 1, end_date[2], end_date[0])

    # Try connecting to Yahoo Finance and obtaining the data
    # On failure, print an error message.
    try:
        yf_data = urlopen(yahoo_url).readlines()[1:]  # Ignore the header
        prices = []
        for y in yf_data:
            p = y.strip().split(',')
            prices.append((datetime.datetime.strptime(p[0], '%Y-%m-%d'),
                           p[1], p[2], p[3], p[4], p[5], p[6]))
    except Exception as e:
        print("Could not download Yahoo data: %s" % e)
    return prices


def insert_daily_data_into_db(data_vendor_id, symbol_id, daily_data):
    """Takes a list of tuples of daily data and adds it to the
    MySQL database. Appends the vendor ID and symbol ID to the data.

    daily_data: List of tuples of the OHLC data (with
    adj_close and volume)"""

    # Create the time now
    now = datetime.datetime.utcnow()

    # Amend the data to include the vendor ID and symbol ID
    daily_data = [(data_vendor_id, symbol_id, d[0], now, now,
                   d[1], d[2], d[3], d[4], d[5], d[6]) for d in daily_data]

    # Create the insert strings
    column_str = """data_vendor_id, symbol_id, price_date, created_date, 
          last_updated_date, open_price, high_price, low_price, 
          close_price, volume, adj_close_price"""
    insert_str = ("%s, " * 11)[:-2]
    final_str = "INSERT INTO daily_price (%s) VALUES (%s)" % (column_str, insert_str)

    # Using the MySQL connection, carry out an INSERT INTO for every symbol
    with con:
        cur = con.cursor()
        cur.executemany(final_str, daily_data)


if __name__ == "__main__":
    # Loop over the tickers and insert the daily historical
    # data into the database
    tickers = obtain_list_of_db_tickers()
    for t in tickers:
        print("Adding data for %s" % t[1])
        yf_data = get_daily_historic_data_yahoo(t[1])
        insert_daily_data_into_db('1', t[0], yf_data)                    
                

Da notare che ci sono sicuramente molti per ottimizzare questo codice. Ad esempio si può utilizzare la libreria Python ScraPy per ottenere dei download ad elevata concorrenza, dato che ScraPy è basato sul framework ad eventi chiamato Twisted

Nel nostro codice ogni download viene eseguito in sequenza.

Interfaccia Python/Pandas per i dati dei prezzi

Ora che abbiamo scaricato lo storico dei prezzi per tutti i titoli che compongono l’S&P500, vogliamo poter accedere a questi dati tramite strutture logiche implementante in Python. La libreria pandas rende passaggio questo estremamente semplice. Ecco uno script per ottiene i dati OHLC del titolo  Google in un determinato periodo di tempo dal nostro database master dei titoli e restituisce il tail del dataset:

                    
#!/usr/bin/python
# -*- coding: utf-8 -*-

import pandas as pd
import pandas.io.sql as psql
import PyMySQL as mdb


# Connect to the MySQL instance
db_host = 'localhost'
db_user = 'sec_user'
db_pass = 'password'
db_name = 'securities_master'
con = mdb.connect(db_host, db_user, db_pass, db_name)

# Select all of the historic Google adjusted close data
sql = """SELECT dp.price_date, dp.adj_close_price
         FROM symbol AS sym
         INNER JOIN daily_price AS dp
         ON dp.symbol_id = sym.id
         WHERE sym.ticker = 'GOOG'
         ORDER BY dp.price_date ASC;"""

# Create a pandas dataframe from the SQL query
goog = psql.frame_query(sql, con=con, index_col='price_date')    

# Output the dataframe tail
print(goog.tail())                    
                

L’output dello script è il seguente:

price_date              adj_close_price
2013-05-20            908.53
2013-05-21            906.97
2013-05-22            889.42
2013-05-23            882.79
2013-05-24            873.32

Ovviamente questo è solo un semplice script, ma mostra tutte le potenzialità di un securities master archiviato localmente. Con tale approccio è possibile testare in modo estremamente rapido alcune strategie, poiché l’I / O dal database sarà notevolmente più veloce rispetto a quello effettuato tramite una connessione Internet.

Il prossimo passo è quello di automatizzare la raccolta di dati in modo che ogni simbolo abbia i dati OHLC aggiornati dopo la chiusura di ogni giorno di negoziazione. Utilizzando un programma di pianificazione delle attività come Windows Task Scheduler o crontab, questo processo può essere programmato per essere eseguito in background. Ci porterà un passo avanti verso la creazione di un sistema di trading completamente automatizzato.

Base di Dati di Securities Master per il Trading Algoritmico

Solitamente, nel trading algoritmico i riflettori sono puntati sul componente del completo sistema di trading che implementa il modello alpha. Questa è la parte del sistema che genera i segnali di trading, prima della filtrazione tramite un sistema di gestione dei rischi o di costruzione del portafoglio. Di conseguenza, gli algotrader spendono una parte significativa del loro tempo alla ricerca di ottimizzazioni del modello alpha, in modo da generare un maggiore Shape Ratio durante la fase di backtesting prima di mettere in produzione il proprio sistema.

Tuttavia, un modello alpha è accurato solo quando viene alimentato da dati di ottima qualità. Per questo motivo è fondamentale utilizzare dati accurati e tempestivi come input per il modello alpha, altrimenti i risultati saranno scarsi nel migliore dei casi o addirittura completamente errati nel peggiore dei casi, con conseguenti ingenti perdite se il sistema verrà messo in produzione.

In questo articolo desidero discutere questioni relative all’acquisizione e alla fornitura di dati tempestivi e accurati per il backtesting di una strategia algoritmica e, in definitiva, per il motore di esecuzione di trading. In particolare voglio evidenziare come ottenere dati finanziari, come conservarli, come pulirli e come esportarli. Nel settore finanziario questo tipo di servizio dati è noto come securities master database.

Cos'è una Securities Master Database?

Un Securities Master è un database che memorizza i dati fondamentali, i prezzi e le transazioni per una varietà di strumenti finanziari in tutte le classi di attività. Fornisce l’accesso a queste informazioni in modo coerente per essere utilizzato da molti processi, come la gestione dei rischi, la compensazione / liquidazione e il trading proprietario.

Ecco alcuni degli strumenti che potrebbero comporre un securities master database:

  • Azioni
  • Opzioni azionarie
  • Indici
  • Foreign Exchange
  • Tassi di interesse
  • Futures
  • Commodities
  • Obbligazioni – Governo e società
  • Derivati – Caps, Floors, Swaps

 

La basi dati di Securities Master sono gestite da un team di sviluppatori e da specialisti di dati che garantiscono un elevato livello di disponibilità dei dati all’interno di un’azienda o istituzione.

Mentre questo è necessario nelle grandi aziende, a livello di un trader retail o in un piccolo fondo, un securities master può essere molto più semplice. Infatti, mentre i securities master dei grandi fondi fanno uso di costosi database aziendali e sistemi di analisi, a livello retail è possibile utilizzare software open source per fornire lo stesso livello di funzionalità, presupponendo che un sistema ben ottimizzato.

 

Quale database scegliere?

Per il trader algoritmico al dettaglio o il piccolo fondo quantitativo, i set di dati più comuni per azioni, indici, futures (principalmente materie prime o reddito fisso) e cambi (forex) sono i prezzi storici di fine giornata (EOD) e i dati  intraday . Al fine di semplificare questa discussione ci concentreremo esclusivamente sui dati di fine giornata (EOD) per titoli azionari, ETF e indici azionari. Gli articoli successivi discuteranno di aggiungere dati ad alta frequenza, classi di assets aggiuntivi e dati derivati, che hanno requisiti più avanzati.

I dati EOD per le azioni sono facili da ottenere. Esistono numerosi servizi che forniscono l’accesso gratuito tramite API disponibili sul Web:

È semplice scaricare manualmente i dati storici per singoli titoli, ma diventa molto dispendioso in termini di tempo se si devono scaricare giornalmente molti titoli. Pertanto un componente importante del nostro securities master si occuperà di aggiornare automaticamente il set di dati.

Un altro problema è il periodo di riferimento. Fino a che punto in passato abbiamo bisogno di andare con i nostri dati? Questo dipenderà dai requisiti della tua strategia di trading, ma ci sono alcuni problemi che riguardano tutte le strategie. Il più comune è il cambio di regime, che è spesso caratterizzato da un nuovo contesto normativo, periodi di volatilità superiore / inferiore o mercati con lunghe fasi di trend. Ad esempio, una strategia trend-following/momentum per movimenti corti nel lungo termine potrebbe performare molto bene tra il 2000-2003 e il 2007-2009. Tuttavia avrebbe avuto un periodo difficile dal 2003 al 2007 o dal 2009 ad oggi.

La mia regola generale consiste nell’ottenere quanti più dati possibili, specialmente per i dati EOD, che sono economici da archiviare (necessitano di poco spazio sul disco fisso). Solo perché i dati esistono nel tuo securities master, non significa che debbano essere utilizzati. Ci sono avvertenze circa le prestazioni, in quanto tabelle di database più grandi significano tempi di interrogazione più lunghi (vedi sotto), ma i benefici di avere più punti di campionamento generalmente superano qualsiasi problema di prestazione.

 

Come per tutti i dati finanziari, è imperativo essere consapevoli degli errori, come i prezzi massimi/minimi non corretti o il bias di sopravvivenza, di cui ho discusso a lungo in questo articolo (vedi qui).

 

Quali strumenti usare per memorizzare i dati?

Esistono tre principali modalità per archiviare i dati finanziari. Ognumo possiede diverse caratteristiche per l’accesso ai dati, le prestazioni e le capacità strutturali. Vediamo singolarmente.

Flat-File

Il modo più semplice per memorizzare i dati finanziari, che è anche il principale “formato” in cui si ricevono i dati da qualsiasi fornitore, è un file flat. I file flat utilizzano spesso il formato CSV (Comma-separated values), che memorizza una matrice bidimensionale di dati come una serie di righe, con i dati della colonna separati da un delimitatore (spesso una virgola, ma può essere anche uno spazio bianco, come ad esempio uno spazio o tab). Per i dati sui prezzi EOD, ogni riga rappresenta un giorno di negoziazione tramite il paradigma OHLC (vale a dire i prezzi di aperta , massimo, minimo, e di chiusura).

Il vantaggio dei file flat è la loro semplicità e la capacità di essere efficientemente  compressi per l’archiviazione o il download. Gli svantaggi principali risiedono nella mancanza di capacità di interrogazione e le scarse prestazioni per l’iterazione su set di dati di grandi dimensioni. SQLite ed Excel attenuano alcuni di questi problemi fornendo alcune funzionalità base di interrogazione.

 

Documentale / NoSQL

I database documentali/NoSQL, sebbene non siano un nuovo concetto, negli ultimi anni hanno acquisito una notevole importanza grazie a loro utilizzo da parte dei giganti del web come Google, Facebook e Twitter. Differiscono sostanzialmente dai sistemi RDBMS in quanto non esiste alcun concetto di schemi di tabelle. Invece, ci sono collezioni e documenti, che sono le analogie più vicine, rispettivamente, alle tabelle e ai record. Esiste un’ampia tassonomia di archivi documentali, la cui discussione è ben al di fuori di questo articolo! Tuttavia, alcuni delle soluzioni più popolari sono MongoDBCassandra e CouchDB.

I database documentali, nelle applicazioni finanziarie, sono adatti principalmente ai dati fondamentali o ai metadati. I dati fondamentali per le attività finanziarie sono disponibili in molte forme, come azioni aziendali, dichiarazioni di guadagni, archivi SEC ecc. Pertanto, la natura senza schema dei DB NoSQL è particolarmente adatta. Tuttavia, i DB NoSQL non sono ben progettati per le serie temporali come i dati sui prezzi ad alta risoluzione e quindi non li prenderemo in considerazione per tale scopo.

 

Relational Database Management Systems

Un Relational Database Management Systems utilizza il modello relazionale per archiviare i dati. Questi database sono particolarmente adatti ai dati finanziari perché diversi “oggetti” (come exchanges, sorgenti dati, prezzi) possono essere separati in tabelle con specifiche relazioni tra di loro.

Un RDBMS fa uso del Structured Query Language (SQL) per eseguire complesse query sui dati finanziari. Esempi di RDBMS includono Oracle, MySQL, SQLServer e PostgreSQL.

I principali vantaggi di unRDBMS sono la semplicità di installazione, l’indipendenza dalla piattaforma, la facilità di interrogazione, la semplicità di integrazione con i principali software di backtest e la capacità ad alte prestazioni su grandi set di dati. I loro svantaggi sono spesso dovuti alla complessità ddi effettuare personalizzazioni e alle difficoltà nel raggiungere tali prestazioni senza una conoscenza di base del modo in cui i dati RDBMS sono archiviati. Inoltre, possiedono schemi semirigidi e quindi i dati devono essere modificati per adattarsi a tali schemi. Questo è diverso dagli archivi dati NoSQL, dove non esiste uno schema.

Per tutti i futuri articoli di implementazione dei prezzi storici su DataTrading, utilizzeremo MySQL o PostgreSQL, che sono entrambi gratuiti, open source, multipiattaforma, estremamente robusti e le prestazioni sono ben documentate, che li rendono una scelta ottimale per il lavoro degli algotrader.

 

Come sono strutturati i dati storici?

Nel campo dell’informatica, esiste moltissimo lavoro teorico e di ricerca accademica relativo al design ottimale per gli archivi di dati. Tuttavia, non entreremo troppo nei dettagli in quanto è facile perdersi nei dettagli! In questo paragrafo voglio introdurre un modello comune per la costruzione di un securities master azionario, che è possibile modificare a seconda delle specifiche delle proprie applicazioni.

Il primo compito consiste nel definire le nostre entità, che corrispondono ad elementi dei dati finanziari mappati con le tabelle del database. Per una banca dati di titoli azionari si prevedono le seguenti entità:

  • Exchange – Qual è l’ultima fonte originale dei dati?
  • Fornitore: da dove viene ottenuto un particolare dataset?
  • Strumento / Ticker – Il ticker / simbolo per l’equity o l’indice, insieme alle informazioni aziendali, della società o del fondo sottostante.
  • Prezzo – il prezzo effettivo per un determinato titolo in un determinato giorno.
  • Azioni Aziendali: l’elenco di tutte le suddivisioni di azioni o gli aggiustamenti dei dividendi (ciò potrebbe portare a una o più tabelle), necessarie per adeguare i dati del prezzo.
  • Festività nazionali – Per evitare di classificare erroneamente le vacanze come errori per dati mancanti, può essere utile memorizzare festività nazionali e i riferimenti incrociati.

Ci sono problemi significativi riguardo alla memorizzazione dei ticker canonici. Posso affermare questo grazie ad esperienze dirette. Diversi fornitori utilizzano metodi diversi per risolvere i ticker e quindi è necessario combinare più fonti per avere una precisione dei dati soddisfaciante. Inoltre, le società falliscono, sono esposte all’attività di fusione e acquisizione (ad esempio acquisiscono e cambiano nomi / simboli) e possono avere più classi di azioni quotate in borsa. Molti di voi non dovranno preoccuparsi di ciò perché il vostro universo di ticker sarà limitato ai componenti dei grandi indici(come S&P500 o FTSE350).

Come si valuta l'accuratezza dei dati?

I dati storici dei forniti da società terze sono soggetti a molte forme di errore:

  • Azioni societarie – Errata gestione delle scissioni azionari e gli aggiustamenti dei dividendi. Bisogna essere assolutamente sicuri che le formule siano state implementate correttamente.
  • Spike: i picchi di prezzo che superano di gran lunga determinati livelli storici di volatilità. Bisogna stare molto attenti a questi picchi quando si verificano. I picchi possono anche essere causati dal non prendere in considerazione le divisioni azionarie, quando si verificano. Gli script di spike-filter vengono utilizzati per informare i trader di tali situazioni.
  • Aggregazione OHLC – I dati OHLC gratuiti, come quelli di Yahoo / Google, sono particolarmente soggetti a situazioni di “cattiva aggregazione di ticker” in cui le piccole borse trattano piccoli scambi ben al di sopra dei prezzi di scambio “principali” per un singolo giorno, portando così a massimi / minimi eccessivi una volta aggregati questi dati. Questo non è un vero e proprio ‘errore’ in quanto tale, ma al più  un problema di cui stare attenti.
  • Dati mancanti: i dati mancanti possono essere causati dalla mancanza di scambi in un particolare periodo di tempo (comune nei dati con timeframe al minuti o secondi in azioni small-cap ed illiquidi), dalle giornate di chiusura dei mercati o semplicemente da un errore nel sistema dell’exchange. I dati mancanti possono essere riempiti (cioè riempiti con il valore precedente), interpolati (linearmente o in altro modo) o ignorati, a seconda del sistema di trading.

Molti di questi errori si basano sul giudizio manuale per decidere come procedere. È possibile automatizzare la notifica di tali errori, ma è molto più difficile automatizzare la loro soluzione. Ad esempio, si deve scegliere la soglia per essere informati sui picchi: quante deviazioni standard usare e su quale periodo di ritorno? Un livello troppo alto di stdev salterà alcuni picchi, mentre un livello troppo basso produce molti falsi positivi (come molti annunci di notizie insolite). Tutti questi problemi richiedono un giudizio da parte dell’algotrader.

È anche necessario decidere come correggere gli errori. Gli errori devono essere corretti non appena sono individuati e, in caso affermativo, deve essere eseguita una procedura di controllo? Ciò richiederà una tabella aggiuntiva nel DB. Questo ci porta al tema del back-filling, che è un problema particolarmente insidioso per il backtesting. Riguarda la correzione automatica dei dati errati a monte. Ad esempio se il fornitore di dati inizia a corregge un errore storico, ma la nostra strategia di trading in produzione è stata ottimizzata (backtested)in base alla ricerca dei precedenti dati non validi, è necessario prendere decisioni in merito all’efficacia della strategia. Questo problema può essere un po’ attenuato grazie ad uno studio approfondito delle metriche sul rendimento della strategia (in particolare la variazione delle caratteristiche di vincita / perdita per ogni trade). Le strategie dovrebbero essere scelte o progettate in modo tale che un singolo dato non possa distorcere le prestazioni della strategia.

 

Come sono automatizzati questi processi?

Il vantaggio di scrivere il software per eseguire il download, l’archiviazione e la pulizia dei dati consiste che tali software possono essere automatizzati tramite gli strumenti forniti dal sistema operativo. Nei sistemi basati su UNIX (come Mac OSX o Linux), si può fare uso di crontab, che è un processo in esecuzione continua che consente di eseguire script specifici in periodi definiti dall’utente o periodi regolari. Esiste un processo equivalente su MS Windows noto come Utilità di pianificazione.

Un processo di produzione, ad esempio, potrebbe automatizzare il download di tutti i prezzi di fine giornata del S&P500 non appena vengono pubblicati tramite da fornitore di dati. Avvierà quindi automaticamente gli algoritmi per la verifica dei dati mancanti e gli script per il filtraggio degli spike, avvisando il trader via e-mail, SMS o qualche altra forma di notifica. A questo punto, qualsiasi strumento di backtesting avrà automaticamente accesso ai dati recenti, senza che il trader debba alzare un dito! A seconda che il tuo sistema di trading si trovi su un desktop o su un server remoto, puoi scegliere di avere un processo semi-automatico o completamente automatico per queste attività.

Come vengono forniti i dati al software di backtesting?

Una volta che i dati vengono aggiornati automaticamente e risiedono nell’RDBMS, è necessario acquisirlo ed includerlo  nel software di backtesting. Questo processo dipenderà in larga misura dal modo in cui il database è installato e dal fatto che il tuo sistema di trading sia locale (cioè su un computer desktop) o remoto (come con un server co-localizzato).

Una delle considerazioni più importanti è quella di ridurre al minimo l’input / output (I / O) eccessivo in quanto può essere estremamente costoso sia in termini di tempo che di denaro, presupponendo connessioni remote in cui la larghezza di banda è costosa. Il modo migliore per affrontare questo problema è trasferire i dati attraverso una connessione di rete solo quando ne hai bisogno (tramite query selettiva) o esportando e comprimendo i dati.

Molti RDBMS supportano la tecnologia di replicazione, che consente di clonare un database su un altro sistema remoto, solitamente con un certo grado di latenza. A seconda della configurazione e della quantità di dati, questo processo può impiegare alcuni secondi o minuti. Un semplice approccio consiste nel replicare un database remoto su un desktop locale. Tuttavia, tieni presente che si possono presentare problemi di sincronizzazione e richiedono molto tempo per essere risolti!

 

Di seguito descrivo alcuni casi di esempio, ma ci sono molti modi per affrontare questo problema e saranno altamente dipendenti dalla tua specifica configurazione:

MySQL
Se si utilizza MySQL, è possibile utilizzare un linguaggio di scripting open source come Python (tramite la libreria  MySQLdb o l’ORM di SQLAlchemy) per connettersi al database ed eseguire query su di esso.

Le più recenti librerie di analisi dei dati, come Pandas , consentono l’accesso diretto a MySQL (si consulti questo thread per un esempio).

È inoltre possibile utilizzare il linguaggio / ambiente preferiti (C ++, C #, Matlab) e un collegamento ODBC per connettersi ad un’istanza MySQL.

MS SQLServer
SQLServer è progettato per essere facilmente connesso ai linguaggi MS .NET, come C # e Visual Basic tramite l’ORM LINQ. È anche possibile connettersi a SQLServer con Python, tramite pyODBC.

Esistono chiaramente molte altre combinazioni di database e ambiente di backtesting. Tuttavia, tratterò la discussione di questi setup in successivi articoli!

 

Conclusioni

Nei prossimi articoli introdurrò i dettagli tecnici per l’implementazione di securities master.i. In particolare, installeremo MySQL, lo configureremo per i dati sui prezzi e otterremo i dati EOD da Yahoo / Google finance ed li esploreremo tramite la libreria di analisi dati pandas.

Ambiente di sviluppo per il Trading Algoritmico con Ubuntu/Linux e Python

In questo articolo voglio discutere sulle attività necessarie al fine di impostare un ambiente di sviluppo robusto, efficiente ed interattivo per il testing di strategie di trading algoritmico, utilizzando la distribuzione Linux molto conosciuta, come Ubuntu Desktop e il linguaggio di programmazione Python. Utilizzeremo questo ambiente per quasi tutti i successivi articoli sul trading algoritmico.

Per creare questo ambiente di sviluppo installeremo i seguenti strumenti software, tutti open-source e gratuiti da scaricare:

Oracle VirtualBox – Per la virtualizzazione del sistema operativo

Ubuntu Desktop Linux – Come nostro sistema operativo virtuale

Python – l’ambiente principale per la programmazione.

NumPy / SciPy – Per elaborare il calcolo di array / matrice in modo veloce ed efficiente

IPython – Per lo sviluppo interattivo visivo con Python

Matplotlib – Per la visualizzazione grafica dei dati

Pandas – Per il “wrangling” dei dati e analisi delle serie temporali

Scikit-learn – Per il machine learning e gli algoritmi di intelligenza artificiale

Questi strumenti (abbinati ad un adeguato database di Securities Master) ci permetteranno di creare un ambiente efficiente ed interattivo per la ricerca di nuove strategie. Pandas è progettato per la “manipolazione” dei dati, potendo importare e ripulire i dati delle serie temporali in modo molto efficiente. Utilizzando NumPy / SciPy si può mantiene il sistema ottimizzato e performante. IPython / matplotlib (e la qtconsole descritta di seguito) consentono la visualizzazione interattiva dei risultati e una rapida iterazione. Scikit-learn ci consente di applicare tecniche di machine learning alle nostre strategie per migliorare ulteriormente le prestazioni.

Inoltre ho scritto un tutorial in modo che gli utenti Windows o Mac OSX che non vogliono o non siano in grado di installare direttamente Ubuntu Linux possano comunque utilizzarlo tramite VirtualBox. VirtualBox ci consente di creare una “Macchina Virtuale” all’interno del sistema host in grado di emulare un sistema operativo guest senza influenzare l’host in alcun modo. Ciò consente la sperimentazione di Ubuntu e degli strumenti Python prima di eseguire l’installazione completa.

Per coloro che hanno già installato Ubuntu, possono direttamente iniziare dalla sezione “Installare i pacchetti di Python nel sistema Ubuntu“.

Installare VirtualBox e Ubuntu-Linux

Questa sezione del tutorial si concentra sull’installazione di VirtualBox ed è stata scritta per un sistema Mac OSX, ma è adatta anche per ambiente Windows. Una volta installato VirtualBox, la procedura sarà la stessa per qualsiasi sistema operativo host sottostante.

Prima di iniziare l’installazione del software, dobbiamo scaricare sia Ubuntu che VirtualBox.

 

Scaricare l'immagine ISO di Ubuntu-Linux

Apri il tuo browser web preferito e vai alla homepage di Ubuntu Desktop, e seleziona “Download Ubuntu”:

Scarica Ubuntu Desktop (32-bit o 64-bit)

Scarica l’ultima versione stabile di Ubuntu. Una volta raggiunta la pagina di download, assicurati di selezionarella versione LTS di Ubuntu. Dovrai scegliere se vuoi versione a 32 o 64 bit. È probabile che tu abbia un sistema a 64 bit, ma nel dubbio, scegli 32 bit. Su un sistema Mac OSX l’immagine del disco ISO di Ubuntu Desktop verrà memorizzata nella directory Download. Lo useremo successivamente, dopo aver installato la  VirtualBox.

Scaricare l'immagine ISO di Ubuntu-Linux

Ora che abbiamo scaricato Ubuntu, dobbiamo procurarci l’ultima versione del software VirtualBox di Oracle. Clicca qui per visitare il sito web e selezionare la versione relativa al tuo specifico sistema host (in questo tutorial abbiamo bisogno della versione per Mac OSX):

Pagina di download di Orcacle VirtualBox

Una volta scaricato il file, è necessario eseguirlo e fare clic sull’icona del pacchetto (questo varia leggermente in ambiente Windows ma è un processo simile):

Doppio click sull'icona "pacchetto" per installare Oracle VirtualBox

Dopo che il pacchetto è stato aperto, seguiamo le istruzioni di installazione, mantenendo le impostazioni predefinite (a meno che non sentiate la necessità di cambiarle!). Ora che VirtualBox è stato installato possiamo aprirlo dalla cartella Applicazioni (che può essere trovata con Finder). Questo inserisce VirtualBox sul dock delle icone durante l’esecuzione, quindi puoi fissarlo in questa posizione in modo permanente se vuoi esamire e provare Ubuntu Linux Live  prima di eseguire l’installazione completa:

VirtualBox senza l'immagine ISO di Ubuntu

A questo punto si procede a creare una nuova “scatola virtuale” (cioè un sistema operativo virtualizzato) facendo clic sull’icona Nuova, che sembra un ingranaggio. Ho chiamato la mia scatola virtuale come “Ubuntu Desktop Algorithmic Trading” (quindi potresti usare qualcosa di simile anche tu!):

Impostanzione di un nuovo ambiente virtuale

Scegli la quantità di RAM che desideri allocare al sistema virtuale. Personamente ho impostato 512 Mb poiché questo è solo un sistema di “test”. Per motivi di performance, un vero ambiente per il backtesting deve necessariamente prevedere un’installazione nativa (e quindi allocerà tutta la memoria disponibile per l’hardware utilizzato):

Scegliere la quantità di memoria da allocare per il sistema virtuale

Creare un disco rigido virtuale (si consiglia di utilizzare 8Gb) con una VirtualBox Image Box, dinamicamente assegnata, che abbia lo stesso nome dell’immagine virtuale impostata in precedenza:

Scegliere il tipo di disco rigido da utilizzare per l'immagine ISO

A questo punto si ottiene un sistema completo, con il seguente elenco delle caratteristiche hardware e software: 

Schermata riassuntiva dopo la creazione di un sistema virtuale

Ora dobbiamo dire a VirtualBox di includere un “CD drive” virtuale per la nuova immagine del disco, in modo da poter simulare l’avvio della nostra immagine del sistema Ubuntu da questa unità CD.

Vai alla sezione Impostazioni, fai clic sulla scheda “Archiviazione” e aggiungi un disco. A questo punto è necessario selezionare il file ISO dell’immagine del sistema Ubuntu, memorizzato direttamente nei tuoi download (o ovunque tu abbia scaricato Ubuntu). Selezionalo e quindi salva le impostazioni:

Selezionare l'ISO di Ubuntu Desktop per il primo avvio

 

A questo punto siamo pronti per avviare la nostra immagine di Ubuntu ed effettuare l’installazione del sistema operativo. E’ sufficiente fare clic su “Start” e quindi su “OK”.

Verrà quindi visualizzato la schermata di Ubuntu Desktop. Clicca su “Installa Ubuntu”:

Clicca su "Install Ubuntu" per avviare l'installazione

Assicurati di selezionare le opzioni corrette per installare i driver delle periferiche come la scheda grafica e il Wi-Fi:

Installa i specifici drivers per la scheda grafica e il Wi-Fi

Successivamente viene visualizzata una schermata in cui viene chiesto come si desidera memorizzare i dati creati per il sistema operativo. Non aver paura dell’opzione “Cancella disco e Installa Ubuntu”. Questo NON significa che cancellerà il tuo normale disco fisso!

In realtà si riferisce al disco virtuale che stai usando per avviare Ubuntu, che deve essere sicuramente cancellato e formattato (e comunque non ci sono dati al suo interno dato che lo abbiamo appena creato). Continua con l’installazione e ti verrà presentata una schermata che ti chiederà la tua posizione e, successivamente, il layout della tua tastiera:

Seleziona la tua zona geografica

Inserisci le tue credenziali ed assicurati di ricordare la password scelta perchè ne avrai bisogno ogni volta che accedi al sistema e per installare nuovi software e pacchetti:

Inserisci il tuo Username e Password (questa sarà la password utilizzata dall'amministratore)

Dopo aver inserito tutti i dati richiesti, Ubuntu installerà i tutti i file del sistema operativo. Dovrebbe essere un processo relativamente veloce dato che copia i dati dal disco fisso al disco fisso!

Quando questo processo sarà concluso, VirtualBox si riavvierà. Se non si riavvia da solo, puoi andare sul menu e forzare l’arresto. Al riavvio verrà visualizzata la schermata di login di Ubuntu:

Schermata di login di Ubuntu Desktop

Effettua il login con il tuo username e password (che hai inserito in precedenza) e entra nel tuo nuovo ambiente Ubuntu:

L'interfaccia Unity di Ubuntu Desktop dopo il login

Ora che abbiamo installato Ubuntu Desktop, possiamo iniziare ad installare i pacchetti per l’ambiente di ricerca e sviluppo del trading algoritmico.

Installare su i pacchetti Python per il Trading Algoritmo

Clicca sul bottone “Search” nell’angolo in alto a sinistra dello schermo e digita “Terminal” nella casella in modo da individuare l’interfaccia per la linea di comandi.

Doppio click sull’icona terminal per avviare il Terminal:

Il Terminal a linea di comando di Ubuntu Desktop

Tutti i comandi successivi dovranno essere digitati in questo terminale.

La prima cosa da fare su qualsiasi nuovo ambiente Ubuntu Linux è fare l’update e l’upgrade dei pacchetti. Il primo ci informa se ci sono aggiornamenti disponibili per Ubuntu, mentre il secondo esegue effettivamente il processo di aggiornamento dei vecchi pacchetti con le versioni più recenti.

Esegui i seguenti comandi (ti verrà richiesta la password):

                    
sudo apt -y update 
sudo apt -y upgrade                    
                

Da notare che il prefisso -y specifica ad Ubuntu che tu vuoi rispondere ‘yes’ a tutte le domande yes/no del sistema. “sudo” è un comando degli ambienti Ubuntu/Debian Linux che permette di eseguire altri comandi con i privilegi di amministatrore. Dato che staimo installando dei pacchetti per tutto il sistema, abbiamo bisogno dell’accesso ‘root’ alla macchina e per questo dobbiamo usare il comando ‘sudo’.

Dopo che entrambi i comandi sono stati eseguiti e il sistema è stato aggiornato, è necessario installare Python, NumPy / SciPy, matplotlib, panda, scikit-learn e IPython. Inizieremo installando i pacchetti di sviluppo Python e gli strumenti necessari per compilare tutto il software:

                    
sudo apt install python3-pip python-dev python3-dev build-essential liblapack-dev libblas-dev                    
                

Una volta installati i pacchetti necessari, possiamo procedere e installare NumPy tramite pip, il gestore di pacchetti Python.
Pip scaricherà un file zip del pacchetto e compilerà il codice sorgente per noi. Ricorda che ci vorrà un po ‘di tempo per compilare, probabilmente 10 minuti!

                    
sudo pip install numpy                    
                

Terminata l’installazione di  NumPy, è necessario verificare che tutto funzioni correttamente prima di procedere.

Se osservi il terminale vedrai il tuo nome utente seguito dal nome del tuo computer. Nel mio caso è “[email protected]”, seguito dal prompt. Nel prompt digitare python e quindi provare a importare NumPy. Verificheremo che funziona calcolando la media aritmetica di una lista di valori:

                    
[email protected]:~$ python 
Python 3.6.0 (default, Jan 12 2017, 03:20:26) 
[GCC 4.7.3] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 
>>> import numpy 
>>> from numpy import mean 
>>> mean([1,2,3]) 
2.0
>>> exit()                    
                

Ora che NumPy è stato installato con successo, vogliamo installare la libreria Python Scientific conosciuta come SciPy. 

Questo pacchetto ha alcune dipendenze tra cui la libreria ATLAS e il compilatore GNU Fortran:

                    
sudo apt install libatlas-base-dev gfortran                    
                

Ora siamo pronti per installare SciPy tramite il gestore. Ci vorrà molto tempo (circa 10 minuti, a seconda del computer), quindi potrebbe valere la pena andare a prendere un caffè:

                    
sudo pip install scipy                    
                

Perfetto! SciPy è stato installato.
Proviamolo calcolando la deviazione standard di un elenco di numeri interi:

                    
[email protected]:~$ python 
Python 3.6.0 (default, Jan 12 2017, 03:25:17) 
[GCC 4.7.3] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 
>>> import scipy
>>> from scipy import std
>>> std([1,2,3])
0.81649658092772603
>>> exit()                    
                

Successivamente dobbiamo installare i pacchetti necessari al corretto funzionamento di matplotlib, la libreria di grafi di Python. Dato che matplotlib è un pacchetto Python, non possiamo usare pip per installare le librerie sottostanti per lavorare con PNG, JPEG e font freetype, quindi abbiamo bisogno di Ubuntu per installarli per noi:

                    
sudo apt install libpng-dev libjpeg8-dev libfreetype6-dev                    
                

Ora possiamo installare matplotlib:

                    
sudo apt install matplotlib                    
                

Passiamo ora ad installare le librerie per l’analisi dei dati e per l’apprendimento automatico, Pandas e Scikit-learn. Non abbiamo bisogno di ulteriori dipendenze in questa fase in quanto sono coperti da NumPy e SciPy:

                    
sudo pip install -U scikit-learn 
sudo pip install pandas                    
                
Per effettuare il test di Scikit-learn si può procedere come segue:
                    
[email protected]:~$ python 
Python 3.6.0 (default, Jan 12 2017, 03:33:46) 
[GCC 4.7.3] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 
>>> from sklearn import datasets
>>> iris = datasets.load_iris()
>>> iris
..
..
'petal width (cm)']}
>>>                    
                

Inoltre possiamo testare il funzionamento di Pandas:

                    
>>> from pandas import DataFrame
>>> pd = DataFrame()
>>> pd
Empty DataFrame
Columns: []
Index: []
>>> exit()                    
                
Infine, vogliamo installare IPython. Si tratta di un interprete Python interattivo che offre un flusso di lavoro molto più snello, rispetto all’uso della console Python standard. Nei tutorial successivi illustrerò la piena utilità di Jupiter per lo sviluppo del trading algoritmico:
                    
sudo pip install ipython                    
                

Nonostante IPython è sufficientemente funzionale anche da solo, può essere reso ancora più potente aggiungendo la qtconsole, che fornisce la possibilità di visualizzare matplotlib in linea. Tuttavia, ci vuole un po ‘più di lavoro per farlo funzionare.

Per prima cosa, dobbiamo installare la libreria Qt. Per questo potrebbe essere necessario aggiornare nuovamente i pacchetti:

                    
sudo apt update                    
                

Adesso possiamo installare Qt:

                    
sudo apt-get install libqt4-core libqt4-gui libqt4-dev                    
                

La qtconsole ha alcuni pacchetti addizionali, nello specifico le librerie ZMQ e Pygments:

                    
sudo apt-get install libzmq-dev
sudo pip install pyzmq
sudo pip install pygments                    
                

A questo punto siamo pronti ad avviare IPython con la qtconsole:

                    
ipython qtconsole --pylab=inline                    
                

A questo punto siamo pronti ad avviare IPython con la qtconsole:

Quindi possiamo costruire un semplice grafico tramite i seguenti comandi (ho incluso anche i numeri input/output di IPython che non hai bisogno di digitare):

                    
In [1]: x=np.array([1,2,3])

In [2]: plot(x)
Out[2]: []                    
                
Che produce il seguente grafico:
un grafico prodotto con IPython e Qtconsole

Qui si conclude la procedura di installazione. Ora abbiamo a portata di mano di un ambiente di sviluppo per il trading algoritmico estremamente robusto, efficiente e interattivo.

Negli articoli successivi descriverò in dettaglio come IPython, matplotlib, panda e scikit-learn possano essere combinati per trovare ed effettuare il backtesting di nuove strategie di trading quantitative in modo diretto.