Automatizzare i Trade con l’API di Interactive Brokers tramite Python e IBPy

Interactive Brokers è uno dei principali broker utilizzati dai trader algoritmici retail a causa dei suoi relativamente bassi requisiti minimi di capitale sul conto (10.000 USD) e la robusta API. In questo articolo faremo uso di un account demo per automatizzare gli ordini con l’API di Interactive Brokers, tramite Python e il package IBPy.

Nota: non ho alcuna affiliazione con Interactive Brokers. Ho avuto modo di usarlo in molti contesti e quindi ho familiarità con il loro software.

L' API di Interactive Brokers

Interactive Brokers è una grande azienda e in quanto tale si rivolge a una vasta gamma di operatori, che vanno dai trader retail discrezionali agli istituzionali automatizzati. Questo a fatto in modo che questo broker abbia sviluppato un’interfaccia GUI completa e robusta, la Trader Workstation (TWS).

Oltre a TWS c’è anche un semplice componente chiamato IB Gateway, che fornisce lo stesso accesso ai server di IB ma senza le funzionalità extra della GUI. Per i nostri scopi di trading automatico non avremo realmente bisogno della GUI TWS, ma penso che possa essere utile utilizzarla per questo tutorial dimostrativo.

L’architettura sottostante si basa su un modello client / server che offre sia feed di dati di esecuzione e di mercato (storici e in tempo reale) tramite un’API. Utilizzeremo questa API  per inviare ordini automatici, tramite IBPy.

IBPy è stato scritto per “avvolgere” l’API Java nativa e renderla semplice da essere chiamata tramite Python. Le due principali librerie a cui siamo interessati all’interno di IBPy sono ib.extib.opt. Quest’ultimo è di livello superiore e fa uso della funzionalità nel primo.

Nella seguente implementazione creeremo un esempio estremamente semplice, che invierà semplicemente un singolo ordine di mercato per acquistare 100 unità di azioni Google, utilizzando il routing intelligente degli ordini. Quest’ultimo è progettato per ottenere il miglior prezzo, anche se in determinate situazioni può non essere ottimale. Tuttavia, sarà sufficiente per gli scopi di questo tutorial.

Implementazione in Python

Prima di iniziare è necessario aver attivato un account su Interactive Brokers. Inoltre è necessario disporre di uno ambiente di lavoro in in modo da poter installare IBPy, che ti consentirà di legare insieme altri aspetti del tuo codice. Per creare un ambiente Python puoi leggere il mio precedente articolo su come installare di un ambiente di backtesting in Python.

Installare IBPy

IBPy è un wrapper Python sviluppato attorno all’API Interactive Brokers basata su Java. Rende un po ‘meno problematico lo sviluppo dei sistemi di trading algoritmico in Python. Sarà utilizzato come base per tutte le successive comunicazioni con Interactive Brokers finché non considereremo il protocollo FIX in un secondo momento. Poiché IBPy è gestito su GitHub come repository git, sarà necessario installare git. Su un sistema Ubuntu questo è effettuato con il comando:
sudo apt-get install git-core
Una volta installato git, è possibile creare una sottodirectory per memorizzare IBPy. Sul mio sistema l’ho semplicemente collocato sotto la mia home directory:
mkdir ~/ibapi
Lo step successivo è scaricare IBPy tramite git clone
Assicurarsi di entrare nella directory IbPy ed effettuare l’installazione con l’ambiente virtuale Python preferito:
cd ~/ibapi/IbPy
python setup.py.in install
Questo completa l’installazione di IBPy. Il prossimo passo è aprire TWS
Schermata del TWS Portfolio

Trading Automatico

Il seguente codice implementa una semplice gestione degli ordine basato sulle API. Il codice è lontano dall’essere usato in produzione, ma dimostra la funzionalità essenziale dell’API di Interactive Brokers e come utilizzarla per l’esecuzione degli ordini. Tutto il seguente codice è contenuto nel file ib_api_demo.py. Il primo passaggio consiste nell’importare gli oggetti Contract e Order dalla libreria ib.ext. Inoltre importiamo gli oggetti Connection e message dalla libreria ib.opt.
# ib_api_demo.py

from ib.ext.Contract import Contract
from ib.ext.Order import Order
from ib.opt import Connection, message
IB ci offre la possibilità di gestire gli errori e le risposte del server tramite un meccanismo di callback. Le seguenti due funzioni non fanno altro che stampare il contenuto dei messaggi restituiti dal server. Un sistema di produzione più sofisticato dovrebbe implementare la logica per assicurare il funzionamento continuo del sistema nel caso di generazione di eccezioni:
# ib_api_demo.py

def error_handler(msg):
    """Handles the capturing of error messages"""
    print "Server Error: %s" % msg

def reply_handler(msg):
    """Handles of server replies"""
    print "Server Response: %s, %s" % (msg.typeName, msg)
Le seguenti due funzioni completano la creazione degli oggetti ContractOrder, impostando i rispettivi parametri:
# ib_api_demo.py

def create_contract(symbol, sec_type, exch, prim_exch, curr):
    """Create a Contract object defining what will
    be purchased, at which exchange and in which currency.

    symbol - The ticker symbol for the contract
    sec_type - The security type for the contract ('STK' is 'stock')
    exch - The exchange to carry out the contract on
    prim_exch - The primary exchange to carry out the contract on
    curr - The currency in which to purchase the contract"""
    contract = Contract()
    contract.m_symbol = symbol
    contract.m_secType = sec_type
    contract.m_exchange = exch
    contract.m_primaryExch = prim_exch
    contract.m_currency = curr
    return contract

def create_order(order_type, quantity, action):
    """Create an Order object (Market/Limit) to go long/short.

    order_type - 'MKT', 'LMT' for Market or Limit orders
    quantity - Integral number of assets to order
    action - 'BUY' or 'SELL'"""
    order = Order()
    order.m_orderType = order_type
    order.m_totalQuantity = quantity
    order.m_action = action
    return order
La funzione __main__ inizialmente crea un oggetto Connection verso Trader Workstation, che deve essere in esecuzione affinché il codice funzioni. Le funzioni di gestione degli errori e di risposta vengono quindi registrate con l’oggetto di connessione. Successivamente viene definita una variabile order_id. In un sistema di produzione questo deve essere incrementato per ogni ordine. I passi successivi sono la creazione di un Contract e di un Order che rappresenta un market-order per acquistare 100 unità di azioni Google. L’ultimo compito è quello di piazzare quell’ordine tramite il metodo placeOrder dell’oggetto Connection. Quindi ci disconnettiamo da TWS:
# ib_api_demo.py

if __name__ == "__main__":
    # Connect to the Trader Workstation (TWS) running on the
    # usual port of 7496, with a clientId of 100
    # (The clientId is chosen by us and we will need 
    # separate IDs for both the execution connection and
    # market data connection)
    tws_conn = Connection.create(port=7496, clientId=100)
    tws_conn.connect()

    # Assign the error handling function defined above
    # to the TWS connection
    tws_conn.register(error_handler, 'Error')

    # Assign all of the server reply messages to the
    # reply_handler function defined above
    tws_conn.registerAll(reply_handler)

    # Create an order ID which is 'global' for this session. This
    # will need incrementing once new orders are submitted.
    order_id = 1

    # Create a contract in GOOG stock via SMART order routing
    goog_contract = create_contract('GOOG', 'STK', 'SMART', 'SMART', 'USD')

    # Go long 100 shares of Google
    goog_order = create_order('MKT', 100, 'BUY')

    # Use the connection to the send the order to IB
    tws_conn.placeOrder(order_id, goog_contract, goog_order)

    # Disconnect from TWS
    tws_conn.disconnect()
Infine è sufficiente lanciare lo script Python:
python ib_api_demo.py

Immediatamente si può vedere che si apre un tab “API” su Trader Workstation, mostrando il market order per andare long di 100 azioni su Google

Schermata del Tab API di TWS dopo l'ordine su Google

Strategia di Moving Average Crossover in Python con Pandas

Nel precedente articolo relativo allo Sviluppo di ambiente di Backtesting con Python e Pandas abbiamo creato un ambiente di backtesting orientato agli oggetti e testato su una strategia di previsione casuale. In questo articolo utilizzeremo gli strumenti che abbiamo introdotto per condurre ricerche su una strategia reale, ovvero il Moving Average Crossover su AAPL.

Strategia di Moving Average Crossover

La strategia di Moving Average Crossover (ovvero incrocio della media mobile) è una semplice strategia di momentum estremamente nota. È spesso considerata come l’esempio di “Hello World” per il trading quantitativo.

La strategia qui descritta è solo long. Si considerano due simple moving average separate, costruite su diversi periodi, di una particolare serie storica. I segnali per l’acquisto dell’asset si verificano quando la media mobile semplice più breve incrocia dal basso, cioè supera, la media mobile semplice più lunga. Se successivamente la media più lunga supera la media più breve, l’asset viene venduto. La strategia funziona bene quando una serie temporale entra in un periodo di forte trend e poi rallenta lentamente.

Per questo esempio, ho scelto Apple, Inc. (AAPL) come serie temporali, con una media mobile breve di 100 giorni e una media mobile lunga di 400 giorni. Questo è l’esempio presente nella libreria di trading algoritmico “zipline“. Quindi, se vogliamo implementare il nostro backtester, dobbiamo assicurarci che corrisponda ai risultati ottenuti da zipline, come metodo base per la convalidare il test.

Implementazione

Assicurati di aver letto il precedente tutorial, che descrive come viene costruita la gerarchia dell’oggetto iniziale del nostro backtester, altrimenti il codice sottostante non potrà funzionerà. Per questa particolare implementazione ho usato le seguenti librerie:
  • Python – 3.7
  • NumPy – 1.16.2
  • Panda – 0.24.2
  • Matplotlib – 3.0.3
L’implementazione di ma_cross.py richiede il file backtest.py del precedente tutorial. Il primo passaggio consiste nell’importare i moduli e gli oggetti necessari:
# ma_cross.py

import datetime
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

from pandas_datareader.data import DataReader
from backtest import Strategy, Portfolio
Come nel precedente tutorial, si eredità la classe astratta della strategia (Strategy) per produrre la classe MovingAverageCrossStrategy, che contiene tutti i dettagli per generare  segnali quando le medie mobili di AAPL si incrociano l’una sull’altra. L’oggetto richiede una short_window e una long_window su cui operare. I valori sono stati impostati com valori predefiniti, rispettivamente pari a 100 giorni e 400 giorni, che sono gli stessi parametri utilizzati nell’esempio di zipline. Le medie mobili vengono create tramite la funzione rolling_mean applicata a bars['Close'], cioè i prezzi di chiusura del titolo AAPL. Una volta che le singole medie mobili sono state calcolate, la serie di segnali viene generata impostando la colonna uguale a 1,0 quando la media mobile breve è maggiore della media mobile lunga o 0,0 in caso contrario. Da queste informazioni si può generare gli ordini positions per rappresentare i segnali di trading.
# ma_cross.py

class MovingAverageCrossStrategy(Strategy):
    """    
    Requires:
    symbol - A stock symbol on which to form a strategy on.
    bars - A DataFrame of bars for the above symbol.
    short_window - Lookback period for short moving average.
    long_window - Lookback period for long moving average."""

    def __init__(self, symbol, bars, short_window=100, long_window=400):
        self.symbol = symbol
        self.bars = bars

        self.short_window = short_window
        self.long_window = long_window

    def generate_signals(self):
        """Returns the DataFrame of symbols containing the signals
        to go long, short or hold (1, -1 or 0)."""
        signals = pd.DataFrame(index=self.bars.index)
        signals['signal'] = 0.0

        # Create the set of short and long simple moving averages over the 
        # respective periods
        signals['short_mavg'] = pd.rolling_mean(bars['Close'], self.short_window, min_periods=1)
        signals['long_mavg'] = pd.rolling_mean(bars['Close'], self.long_window, min_periods=1)

        # Create a 'signal' (invested or not invested) when the short moving average crosses the long
        # moving average, but only for the period greater than the shortest moving average window
        signals['signal'][self.short_window:] = np.where(signals['short_mavg'][self.short_window:] 
            > signals['long_mavg'][self.short_window:], 1.0, 0.0)   

        # Take the difference of the signals in order to generate actual trading orders
        signals['positions'] = signals['signal'].diff()   

        return signals
La classe MarketOnClosePortfolio è una classe derivata dalla classe astratta Portfolio, presente in backtest.py. È quasi identico all’implementazione descritta nel tutorial precedente, con l’eccezione che le operazioni vengono ora eseguite su base Close-to-Close, piuttosto che Open-to-Open. Ho trascritto tutto il codice completo per rendere autonomo questo tutorial:
# ma_cross.py

class MarketOnClosePortfolio(Portfolio):
    """Encapsulates the notion of a portfolio of positions based
    on a set of signals as provided by a Strategy.

    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):
        positions = pd.DataFrame(index=self.signals.index).fillna(0.0)
        positions[self.symbol] = 100*self.signals['signal']   # This strategy buys 100 shares
        return positions
                    
    def backtest_portfolio(self):
        portfolio = self.positions*self.bars['Close']
        pos_diff = self.positions.diff()

        portfolio['holdings'] = (self.positions*self.bars['Close']).sum(axis=1)
        portfolio['cash'] = self.initial_capital - (pos_diff*self.bars['Close']).sum(axis=1).cumsum()

        portfolio['total'] = portfolio['cash'] + portfolio['holdings']
        portfolio['returns'] = portfolio['total'].pct_change()
        return portfolio
Ora che sono state definite le classi MovingAverageCrossStrategy e MarketOnClosePortfolio, verrà chiamata una funzione __main__ per collegare insieme le funzionalità delle due classi. Inoltre, la performance della strategia sarà esaminato attraverso un grafico della curva equity. L’oggetto DataReader di pandas scarica i prezzi OHLCV del titolo AAPL per il periodo che va dal 1 gennaio 1990 al 1 gennaio 2002, in seguito di crea il DataFrame signals per generare i segnali long-only. Successivamente il portafoglio è generato con una base di capitale iniziale di 100.000 USD e i rendimenti sono calcolati sulla curva equity. Il passaggio finale consiste nell’utilizzare matplotlib per tracciare un grafico a due figure con i prezzi di AAPL, sovrapposti con le medie mobili e i segnali di acquisto / vendita, nonché la curva equity con gli stessi segnali di acquisto / vendita. Il codice di plotting è stato preso (e modificato) dall‘esempio di zipline.
# ma_cross.py

if __name__ == "__main__":
    # Obtain daily bars of AAPL from Yahoo Finance for the period
    # 1st Jan 1990 to 1st Jan 2002 - This is an example from ZipLine
    symbol = 'AAPL'
    bars = DataReader(symbol, "yahoo", datetime.datetime(1990,1,1), datetime.datetime(2002,1,1))

    # Create a Moving Average Cross Strategy instance with a short moving
    # average window of 100 days and a long window of 400 days
    mac = MovingAverageCrossStrategy(symbol, bars, short_window=100, long_window=400)
    signals = mac.generate_signals()

    # Create a portfolio of AAPL, with $100,000 initial capital
    portfolio = MarketOnClosePortfolio(symbol, bars, signals, initial_capital=100000.0)
    returns = portfolio.backtest_portfolio()

    # Plot two charts to assess trades and equity curve
    fig = plt.figure()
    fig.patch.set_facecolor('white')     # Set the outer colour to white
    ax1 = fig.add_subplot(211,  ylabel='Price in $')
    
    # Plot the AAPL closing price overlaid with the moving averages
    bars['Close'].plot(ax=ax1, color='r', lw=2.)
    signals[['short_mavg', 'long_mavg']].plot(ax=ax1, lw=2.)

    # Plot the "buy" trades against AAPL
    ax1.plot(signals.loc[signals.positions == 1.0].index, 
             signals.short_mavg[signals.positions == 1.0],
             '^', markersize=10, color='m')

    # Plot the "sell" trades against AAPL
    ax1.plot(signals.loc[signals.positions == -1.0].index, 
             signals.short_mavg[signals.positions == -1.0],
             'v', markersize=10, color='k')

    # Plot the equity curve in dollars
    ax2 = fig.add_subplot(212, ylabel='Portfolio value in $')
    returns['total'].plot(ax=ax2, lw=2.)

    # Plot the "buy" and "sell" trades against the equity curve
    ax2.plot(returns.loc[signals.positions == 1.0].index, 
             returns.total[signals.positions == 1.0],
             '^', markersize=10, color='m')
    ax2.plot(returns.loc[signals.positions == -1.0].index, 
             returns.total[signals.positions == -1.0],
             'v', markersize=10, color='k')

    # Plot the figure
    fig.show()
Il grafico output del codice è il seguente. Ho fatto uso del comando IPython %paste per inserirlo direttamente nella console IPython di Ubuntu, in modo che l’output grafico rimanesse visibile. Gli uptick rosa rappresentano l’acquisto del titolo, mentre i downtick neri rappresentano la vendita:
Performance di un Moving Average Crossover su AAPL tra il 01/01/1990 e il 01/01/2002
Come si può vedere la strategia perde denaro lungo questo periodo, con cinque trade di apertura e chiusra della posizione. Ciò non sorprende, tenuto conto del comportamento dell’AAPL nel periodo in esame, che era leggermente in calo, seguito da un significativo aumento a partire dal 1998. Il periodo di take profit dei segnali della media mobile è piuttosto ampio e ha influito sul profitto finale dell’operazione. , che altrimenti potrebbe l’avrebbe resa una strategia redditizia. Negli articoli successivi creeremo uno strumento più sofisticato per analizzare le prestazioni, oltre a descrivere come ottimizzare i take profit dei singoli segnali della media mobile.

I migliori linguaggi di programmazione per il trading algoritmico

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

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

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

Cosa fa un Sistema di Trading Algoritmico?

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

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

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

Tipologia, Frequenza e Volume di una Strategia

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

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

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

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

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

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

Sistemi di Ricerca

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

I migliori IDE per questi scopi includono;

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

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

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

Ottimizzazione del Portafoglio e Gestione del Rischio

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

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

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

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

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

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

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

Sistema di Esecuzione

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

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

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

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

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

Pianificazione dell'Architettura e Processo di Sviluppo

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

Separazione degli Interessi

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

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

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

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

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

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

Considerazioni sulle Prestazioni

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

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

 

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

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

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

 

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

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

 

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

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

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

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

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

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

 

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

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

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

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

 

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

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

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

Hardware e Sistema Operativo

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

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

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

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

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

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

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

Resilienza e Testing

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

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

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

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

 

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

Scegliere il linguaggio

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

Tipologie di Sistemi

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

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

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

Open-Source o Proprietario?

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

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

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

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

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

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

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

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

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

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

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

Batterie incluse?

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

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

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

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

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

 

Conclusioni

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

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

Il Backtesting di una Strategia di Trading Algoritmico – Parte II

Nel primo articolo sul backtesting di una strategia sono stati introdotti i bias statistici e comportamentali che influenzano le prestazioni del backtest. Inoltre sono stati descritti i principali linguaggi di programmazione usati per il backtesting, tra cui Excel, MATLAB, Python, R e C ++. In questo articolo si vuole descrivere come incorporare i costi di transazione ed alcune vincoli che devono essere introdotti quando si crea un motore di backtest, come i tipi di ordine e la frequenza dei dati.

Costi di Transazione

Uno degli errori più diffusi dei principianti quando si implementano modelli di trading è di trascurare (o grossolanamente sottostimare) gli effetti dei costi di transazione su una strategia. Inoltre spesso si presume che i costi di transazione comprendano solo le commissioni dei broker, esistono in realtà molti altri modi in cui i costi possono essere accumulati in un modello di trading. I tre principali tipi di costi che devono essere considerati includono:

Commissioni

La forma più diretta di costi di transazione causati dall’esecuzione di una strategia di trading algoritmico sono le commissioni. Tutte le strategie richiedono una qualche forma di accesso a un mercato, direttamente o attraverso un intermediario  (“il broker”). Questi servizi comportano un costo incrementale con ogni operazione, nota come commissione.

Generalmente i broker forniscono molti servizi, quindi le commissioni di intermediazione sono spesso piccole per un trade base. Inoltre i broker addebitano altri tipi di commissioni, che tengono conto dei costi sostenuti per gestire e liquidare le posizioni. Oltre a questo ci sono le imposte dei governi regionali o nazionali. Ad esempio, è prevista l’imposta di bollo per le transazioni in titoli azionari. Poiché le commissioni, le tasse e le imposte sono generalmente fisse, è relativamente semplice da implementare queste logiche in un motore di backtest (vedi il codice sotto).

Slippage/Latency

Lo slippage è la differenza di prezzo raggiunta tra il momento in cui un sistema di trading decide di effettuare una operazione e il momento in cui una operazione viene effettivamente eseguita nel mercato. Lo slippage è una componente considerevole dei costi di transazione e può fare la differenza tra una strategia molto redditizia e una che fa perdere denaro. Lo slippage è funzione della volatilità delle attività sottostanti, della latenza tra il sistema di trading e l’exchange e del tipo di strategia in corso.

Uno strumento con maggiore volatilità ha più probabilità che si verifichi dei movimenti  e quindi i prezzi tra il segnale ed l’esecuzione possono essere diversi. La latenza è definita come la differenza di tempo tra la generazione del segnale e il punto di esecuzione. Le strategie di frequenza più elevata sono più sensibili ai problemi di latenza e anche solo migliorare le performance di pochi millisecondi può fare la differenza in termini di redditività. Anche il tipo di strategia è importante. Le strategie di momentum sono più influenzate dallo slippage  perché stanno cercando di acquistare strumenti che si stanno già muovendo nella direzione prevista. Mentre per le strategie di mean-reverting risento poco dello slippage, poiché queste strategie stanno operando in direzione opposta al mercato.

Market Impact/Liquidity

L’impatto sul mercato è il costo sostenuto dai trader a causa delle dinamiche  domanda / offerta dell’exchange (e del strumento) con il quale si sta operando. Un ordine importante su un asset relativamente illiquido potrebbe sostanzialmente spostare il mercato poichè l’exchange avrà bisogno di accedere a una grande percentuale della quantità disponibile per quel specifico asset. Per contrastare questo, le grandi operazioni sono suddivise in “blocchi” più piccoli e vengono negoziate periodicamente, appena entra nuova liquidità nel mercato. Al contrario, per gli strumenti altamente liquidi come il contratto futures E-Mini sull’indice di S&P500, è improbabile che le operazioni a basso volume provochi l’esecuzione di un solo blocco di questi grandi ordini.

Inoltre le attività illiquide sono caratterizzate da uno spread più ampio, che è la differenza tra gli attuali prezzi bid e ask presenti nell’order-book. Questo spread è un costo di transazione aggiuntivo, presente per ogni trade. Lo spread è una componente molto importante del costo totale della transazione.

Modelli di Costi di Transazione

Al fine di modellare con successo i costi di cui sopra all’interno di un sistema di backtesting, sono stati introdotti modelli di transazioni con vari gradi di complessità. Si va dalla semplice modellazione flat a un’approssimazione quadratica non lineare. Qui illustreremo i vantaggi e gli svantaggi di ciascun modello:

Modelli Flat / a costo di transazione fisso

I costi di transazione flat sono la forma più semplice di modellazione dei costi di transazione. Si assume un costo fisso associato ad ogni operazione. Pertanto rappresentano al meglio il concetto di fees e commissioni di intermediazione. Non sono molto accurati per la modellazione di comportamenti più complessi come lo slippage o l’impatto sul mercato. In realtà, non considerano affatto la volatilità degli asset o la loro liquidità. Il loro principale vantaggio è che sono computazionalmente semplici da implementare. Tuttavia, è probabile che i costi di transazione siano significativamente inferiori o superiori a seconda della strategia utilizzata. Quindi nella pratica sono raramente usati.

Modelli di costo Lineare / Lineare a tratti / Quadratica

I modelli di costo di transazione più avanzati partono da modelli lineari, proseguono con modelli lineari a tratti e si concludono con modelli quadratici. Si trovano su uno spettro dal meno preciso al più accurato, anche con minimi sforzi di implementazione. Poiché lo slippage e l’impatto sul mercato sono fenomeni intrinsecamente non lineari, le funzioni quadratiche sono le più accurate nel modellare queste dinamiche. I modelli di costo delle transazioni quadratiche sono molto più difficili da implementare e possono richiedere molto più tempo per il calcolo rispetto ai modelli semplici o lineari, ma sono quelli maggiormente usati nella pratica.

I trader algoritmici tentano inoltre di utilizzare i costi effettivi delle operazioni storiche delle loro strategie in produzione come input per i loro attuali modelli di transazione in modo da renderli più accurati. Questo è un aspetto complesso e coinvolge approcci avanzati per la modellizzazione della volatilità, dello slippage e dell’impatto sul mercato. Tuttavia, se la strategia di trading sta muovendo grandi volumi in brevi periodi di tempo, allora stime accurate dei costi di transazione sostenuti possono avere un effetto significativo sulla bottom-line della strategia e quindi vale la pena investire nella ricerca di questi modelli.

Problemi nell'implementazione di una Strategia per il Backtesting

Nonostante i costi di transazione sono un aspetto molto importante per l’implementazione di un backtesting efficiente, ci sono molti altri problemi che possono influenzare le prestazioni della strategia.

Tipi di Ordini

Una scelta che un trader algoritmico deve assolutamnete fare consiste nel prevedere come e quando utilizzare i diversi tipi di ordini disponibili nell’exchange/broker. Questa scelta di solito rientra nell’ambito del sistema di esecuzione, ma possono influenzare notevolmente anche le prestazioni della strategia in backtesting. Esistono due tipi di ordini che possono essere eseguiti: ordini a mercato e ordini limite.

Un ordine a mercato esegue immediatamente una transazione, indipendentemente dal prezzo disponibile. Pertanto, le grandi operazioni con ordini a mercato sono spesso eseguite in un range ampio di di prezzi dato che viene associato una successione di piccoli ordini limite presenti nell’oder-book. Gli ordini di mercato sono considerati ordini aggressivi in ​​quanto sono quasi certamente liquidati, anche se con un costo potenzialmente sconosciuto.

Gli ordini limite forniscono alla strategia un metodo per determinare il prezzo peggiore al quale verrà eseguito un trade, con l’avvertimento che il trade potrebbe non essere eseguito parzialmente o completamente. Gli ordini limite sono considerati ordini passivi poiché spesso non sono disponibili, ma quando lo sono hanno un prezzo garantito. L’insieme degli ordini limite in un singolo exchange è chiamato limiti order-book, che consiste essenzialmente una coda di ordini di acquisto e vendita a determinati prezzi e dimensioni.

Quando si esegue il backtest, è essenziale modellare correttamente gli effetti dell’uso degli ordini a mercato o degli ordini limite. In particolare, per le strategie ad alta frequenza, i backtest possono significativamente sovraperformare il trading dal vivo se gli effetti dell’impatto sul mercato e il limit order-book non sono modellati accuratamente.

Peculiarità dei dati OHLC

Vi sono particolari problemi relativi alle strategie di backtesting quando si utilizzano dati giornalieri sotto forma di dati Open-High-Low-Close (OHLC), specialmente per le azioni. Si noti che questo è esattamente il formato dei dati che è usato dalla maggior parte dei trader algoritmici retail!

I set di dati economici o gratuiti, pur soffrendo del bias di sopravvivenza (che abbiamo discusso nella Parte I), sono spesso feed di prezzi ottenuti dalla composizione dei dati proveniente da più exchange. Ciò significa che i punti estremi (cioè open, close, high e low) dei dati sono molto sensibili ai valori “periferici” dovuti ai piccoli ordini effettuati negli exchange regionali. Inoltre, a volte questi dati hanno più probabilità di contentere gli  errori di tick, che devono ancora essere rimossi.

Ciò significa che se la tua strategia di trading fa ampio uso di uno qualsiasi dei punti OHLC, le prestazioni di backtest possono differire dalla performance dal vivo poiché gli ordini potrebbero essere indirizzati a diversi exchange, a seconda del tuo broker e dell’accesso alla  liquidità disponibile per l’asset scelto. L’unico modo per risolvere questi problemi consiste nell’utilizzare dati con frequenza più alta o ottenere direttamente i dati direttamente da un singolo exchange, piuttosto che un feed composito da più sorgenti.

 

Nei prossimi articoli vedremo come misurare le prestazioni del backtesting, nonché un esempio reale di un algoritmo di backtesting, con molti degli effetti sopra inclusi

Il Backtesting di una Strategia di Trading Algoritmico – Parte I

Questo articolo continua la serie sul trading quantitativo, che è iniziato con il tutorial introduttivo e l’articolo sull’identificazione di una strategia. Entrambi questi articoli sono molti lunghi e dettagliati, quindi continuerò su questo tema e voglio fornire ulteriori dettagli relativi al backtesting di una strategia.

Il backtesting algoritmico richiede competenze in molte aree, tra cui psicologia, la matematica, la statistica, lo sviluppo software e la conoscenza della microstruttura del mercato (o exchange). Non è possibile coprire tutti questi argomenti in un solo articolo, quindi  dividerò la trattazione in due o tre parti. Si inizia definendo cosa si intende per backtesting per poi descrivere le basi del suo funzionamento. Quindi si evidenziano i bias, già discussi nella Guida introduttiva al Trading Algoritmico. Successivamente si confronta i vari software di backtest disponibili.

Negli articoli successivi esamineremo i dettagli delle implementazioni della strategia che sono spesso menzionati a malapena o del tutto ignorati. Si descrive anche come rendere più realistico il processo di backtest includendo le imperfezioni di un trading exchange. Quindi discuteremo i costi di transazione e come modellarli correttamente nel settaggio di backtest. Concluderemo con una discussione sull’esecuzione del backtesting e forniremo infine un esempio di una semplice strategia quantitativa, nota come mean-reverting pairs trade.

Iniziamo discutendo di cosa sia il backtesting e perché dovremmo prevederlo nel trading algoritmico.

Che cosa è il backtesting?

Il trading algoritmico si distingue dagli altri tipi di investimento perché si può fornire in modo più affidabile le aspettative sulle prestazioni future dell’attività di trading, derivanti dalle performance passate, come conseguenza dell’abbondante disponibilità di dati. Il processo con cui viene effettuato questo è noto come backtesting.

In termini semplici, il backtesting viene eseguito esponendo lo specifico algoritmo di una strategia a un flusso di dati finanziari storici, che restiuisce una serie di segnali di trading. Ogni trade (che qui intendiamo come un “round-trip” di due segnali) è associato ad un utile o ad una perdita. L’accumulo di questo profitto / perdita per tutta la durata del backtest della strategia porterà ad un profitto e una perdita totale (noto anche come “P & L” o “PnL”). Questa è l’essenza dell’idea, anche se ovviamente il “diavolo è sempre nei dettagli”!

Quali sono le motivazioni principali per cui si effettua il backtest di una strategia algoritmica?

  • Filtraggio – Come introdotto nell’articolo sull’identificazione della strategia, l’obiettivo della fase iniziale della ricerca consiste nell’impostare una pipeline strategica e quindi filtrare qualsiasi strategia che non soddisfacesse determinati criteri. Il backtesting ci fornisce un altro meccanismo di filtraggio, in quanto possiamo eliminare le strategie che non soddisfano i criteri in termini di prestazioni.
  • Modellazione – Backtesting ci consente di testare (in tutta sicurezza) i nuovi modelli di determinati fenomeni di mercato, i costi di transazione, il routing degli ordini, la latenza, la liquidità o altre inefficienze del mercato.
  • Ottimizzazione – Sebbene l’ottimizzazione della strategia sia ricca di bias, il backtesting ci consente di aumentare le prestazioni di una strategia modificando la quantità o i valori dei parametri associati a tale strategia e ricalcolandone le prestazioni.
  • Verifica – Le strategie vengono spesso acquistate esternamente, tramite la nostra pipeline strategica. Il backtesting di una strategia garantisce che non sia stato implementata in modo errato. Anche se raramente avremo accesso ai segnali generati da strategie esterne, avremo spesso accesso alle metriche prestazionali come il SharpeRatio e il Drawdown. Quindi possiamo confrontarli con la nostra implementazione.

 

Il backtesting offre una serie di vantaggi per il trading algoritmico. Tuttavia, non è sempre possibile eseguire direttamente il backtest di una strategia. In generale, con l’aumentare della frequenza della strategia, diventa più difficile modellare correttamente gli effetti della microstruttura del mercato e degli exchange. Ciò porta a backtest meno affidabili e quindi a una valutazione più complessa della strategia scelta. Questo è un particolare problema dove il sistema di esecuzione è la chiave per le prestazioni della strategia, come con algoritmi a frequenza ultraelevata.

Sfortunatamente, i backtest sono pieni di bias di tutti i tipi. Abbiamo toccato alcuni di questi problemi negli articoli precedenti, ma ora li discuteremo in modo approfondito.

 

I Bias che influenzano il Backtesting di una Strategia

Esistono numerosi bias che possono influire sulle prestazioni del backtesting di una strategia. Sfortunatamente, questi bias tendono a gonfiare la performance piuttosto che a diminuirla. Pertanto, si dovrebbe sempre considerare un backtest come un ideale limite superiore delle performance effettive della strategia. È quasi impossibile eliminare i bias dal trading algoritmico, quindi è compito dell’algotrader cercare di minimizzarli il più possibile in modo da prendere le decisioni corrette.

Ci sono quattro principali tipoliga di bias a cui prestare estrema attenzione: Optimisation BiasLook-Ahead BiasSurvivorship Biasand Psychological Tolerance Bias.

Optimisation Bias

Questo è probabilmente il più insidioso di tutti i bias a cui il backtesting è soggetto. Si tratta di adeguare o introdurre parametri aggiuntivi fino a quando la performance della strategia sul set di dati storico diventano interessanti. Tuttavia, una volta live, le prestazioni della strategia possono essere notevolmente diverse. Un altro nome per questo bias è “curve fitting” o “data-snooping bias”.

Il bias di ottimizzazione è difficile da eliminare in quanto le strategie algoritmiche spesso implicano molti parametri. In questo caso, i “Parametri” possono essere i criteri di ingresso / uscita, i periodi di osservazione, i periodi di calcolo della media (ovvero il parametro di livellamento della media mobile) o la frequenza di misurazione della volatilità. Il bias di ottimizzazione può essere ridotto al minimo mantenendo il numero di parametri al minimo e aumentando la quantità di dati nel set di allenamento. In effetti, si deve anche fare attenzione a questi ultimi poiché i dati storici più vecchi possono essere soggetti a un regime precedente (come un obbligo normativo) e quindi potrebbero non essere rilevanti per l’attuale strategia.

Un metodo per attenuare questo bias consiste nell’eseguire una sensitivity analysis. Questa analisi consiste nel variare i parametri in modo incrementale e tracciare una “superficie” di prestazioni. In particolare, il ragionamento fondamentale per le scelte dei parametri dovrebbe, insieme a tutti gli altri fattori considerati, portare ad una superficie dei parametri più liscia. Se si dispone di una superficie di prestazione molto nervosa, spesso significa che un parametro non riflette un fenomeno ed è un artefatto dei dati del test. Esiste una vasta letteratura sugli algoritmi di ottimizzazione multidimensionale ed è un’area di ricerca molto attiva. Non mi soffermerò su di esso in questo articolo, ma tienilo a mente quando trovi una strategia con un fantastico backtest!

Look-Ahead Bias

Il bias di previsione viene introdotto in un sistema di backtesting quando i dati futuri vengono accidentalmente inclusi in un punto della simulazione in cui tali dati non sarebbero stati effettivamente disponibili. Se stiamo eseguendo il backtest in ordine cronologico e raggiungiamo il punto temporale N, il bias di look-ahead si verifica quando sono utilizzati i dati relativi a qualsiasi instante N + k, dove k > 0. Gli errori del bias di previsione possono essere incredibilmente insidiosi. Ecco tre esempi di come si può introdurre un bias di previsione:

  • Bug tecnici: gli array / i vettori nel codice hanno spesso iteratori o variabili dell’indice. Offset errati di questi indici possono portare a una distorsione look-ahead, incorporando dati a N + k per k>0.
  • Calcolo dei parametri – Un altro esempio comune di bias di previsione si verifica quando si calcolano i parametri ottimali di una strategia, ad esempio con le regressioni lineari tra due serie temporali. Se l’intera serie di dati (compresi i dati futuri) viene utilizzata per calcolare i coefficienti di regressione, e quindi applicata retroattivamente a una strategia di trading a fini di ottimizzazione, i dati futuri verranno incorporati e si verificherà un bias di previsione.
  • Maxima / Minima – Alcune strategie di trading fanno uso di valori estremi in un determinato periodo di tempo, come l’incorporazione dei prezzi massimi o minimi nei dati OHLC. Tuttavia, poiché questi valori massimi / minimi possono essere calcolati solo alla fine del periodo di tempo di riferimento, viene introdotto un bias di previsione se questi valori vengono utilizzati durante il periodo corrente. È sempre necessario ritardare i valori di almeno un punto per farne uso in qualsiasi strategia di trading.

 

Come nel caso dell’ottimizzazione, bisogna essere estremamente cauti per evitare la sua introduzione. Spesso è la ragione principale per cui le strategie di trading sottoperformano significativamente i loro backtests nel “trading dal vivo”.

Survivorship Bias

Il bias di sopravvivenza è un fenomeno particolarmente pericoloso e può portare a prestazioni notevolmente gonfiate per determinati tipi di strategie. Si verifica quando le strategie vengono testate su set di dati che non includono l’intero universo di asset precedenti che potrebbero essere stati scelti in un determinato momento, ma considerano solo quelli che sono “sopravvissuti” fino al tempo corrente.

Ad esempio, si consideri di testare una strategia su una selezione casuale di azioni prima e dopo il crollo del mercato del 2001. Alcuni titoli tecnologici sono andati in bancarotta, mentre altri sono riusciti a rimanere a galla e persino a prosperare. Se avessimo limitato questa strategia solo alle azioni che hanno superato il periodo di abbattimento del mercato, avremmo introdotto un bias di sopravvivenza perché si effettua il backtesting solamente per le azioni che hanno già dimostrato il loro successo. In realtà, questo è solo un altro caso specifico di distorsione previsionale, poiché le informazioni future vengono incorporate nell’analisi passata.

Esistono due modi principali per mitigare i bias di sopravvivenza nei backtests delle strategie:

  • Set di dati senza bias di sopravvivenza – Nel caso di dati azionari è possibile acquistare set di dati che includono entità rimosse, sebbene non siano a buon mercato e tendano ad essere utilizzate solo da aziende istituzionali. In particolare, i dati di Yahoo Finance NON sono privi di bias, ciò nonostante vengono comunemente usato da molti algotrader retail. Si può anche operare su strumenti che non sono inclini al biasdi sopravvivenza, come certe materie prime (e i loro futures derivati​).
  • Usare dati recenti – Nel caso di titoli azionari, l’utilizzo di un set di dati più recente riduce la possibilità che la selezione di azioni scelta sia ponderata ai soli “sopravvissuti”, semplicemente perché vi è meno probabilità di un delisting delle azioni in brevi periodi. Si può anche iniziare a costruire un personale set di dati libero da bias di sopravvivenza, raccogliendo dati dal tempo corrente in poi. Dopo 3-4 anni, avrai un solido set di dati senza bias con cui testare altre strategie.

 

Considereremo ora alcuni fenomeni psicologici che possono influenzare le prestazioni del trading.

Psychological Tolerance Bias.

Questo particolare fenomeno non è spesso discusso nel contesto del trading quantitativo. Tuttavia, è ampiamente discusso in merito a metodi di trading più discrezionali. Ha vari nomi, ma ho deciso di chiamarlo “bias di tolleranza psicologica” perché cattura l’essenza del problema. Quando si creano backtests su un periodo di 5 o più anni, è facile guardare una curva equity tendente al rialzo, calcolare il rendimento annuale composto, il SharpeRatio e persino le caratteristiche del drawdown e essere soddisfatti dei risultati. Ad esempio, la strategia potrebbe avere un drawdown relativo massimo del 25% e una durata massima di drawdown di 4 mesi. Questo non sarebbe atipico per una strategia di momentum. È facile convincersi che si possa tollerare tali periodi di perdite perché il quadro generale è roseo. Tuttavia, in pratica, è molto più difficile!

Se i prelievi storici del 25% o più si verificano nei backtests, allora con ogni probabilità vedrai periodi di drawdown simile nel trading dal vivo. Questi periodi di abbattimento sono psicologicamente difficili da sopportare. Ho osservato in prima persona che cosa può comportare un drawdown esteso, e non è piacevole – anche se i backtests suggeriscono che tali periodi si verificheranno. Il motivo per cui l’ho definito un “bias” è che spesso una strategia che altrimenti avrebbe successo viene fermata durante i periodi di drawdown esteso e quindi porterà ad una sottoperformance significativa rispetto al backtest. Pertanto, anche se la strategia è di natura algoritmica, i fattori psicologici possono ancora avere una forte influenza sulla redditività. L’obiettivo è quello di garantire che se si verificano drawdown di una certa percentuale e durata durante il backtesting, allora ci si dovrebbe aspettare che si verifichino anche nel live trading e sarà necessario perseverare per raggiungere nuovamente la redditività.

 

Pacchetti Software per il Backtesting

Lo spazio delle soluzioni software per il backtesting della strategia è molto vasto. Le soluzioni vanno da sofisticati software a livello istituzionale completamente integrati a linguaggi di programmazione come C ++, Python e R, dove quasi tutto deve essere scritto da zero (o con idonei “plug-in”). Come algotrader siamo interessati ad un equilibrio tra il “possedere” il nostro stack tecnologico di trading rispetto alla velocità e all’affidabilità della nostra metodologia di sviluppo. Ecco le considerazioni chiave per la scelta del software:

  • Abilità di programmazione – La scelta dell’ambiente dipenderà in gran parte dalla tua capacità di programmare il software. Direi che avere il controllo dello stack totale avrà un effetto maggiore sul P&L a lungo termine rispetto all’esternalizzazione del software tramite un fornitore. Ciò è dovuto al basso rischio di avere bug esterni o idiosincrasie che non è possibile risolvere nel software del fornitore, che altrimenti sarebbe facilmente risolvibile se si avesse più controllo sul proprio “stack tecnologico”. Desiderate anche un ambiente in grado di trovare il giusto equilibrio tra produttività, disponibilità delle librerie e velocità di esecuzione. Di seguito faccio la mia raccomandazione personale.
  • Funzionalità di esecuzione / Integrazione con il broker – Alcuni software di backtesting, come Tradestation, si collegano direttamente con una società di intermediazione. Non sono un fan di questo approccio poiché ridurre i costi di transazione è spesso un fattore fondamentale per ottenere un SharpeRatio più elevato. Se sei legato a un particolare broker (e Tradestation “obliga” a farlo), allora avrai più difficoltà a passare ad un nuovo software (o ad un nuovo broker) nel caso fosse necessario. Interactive Brokers fornisce una robusta API, sebbene con un’interfaccia leggermente ottusa.
  • Personalizzazione – Un ambiente come MATLAB o Python offre una grande flessibilità quando si creano strategie algoritmiche in quanto forniscono fantastiche librerie per quasi tutte le operazioni matematiche immaginabili, ma consentono anche un’ampia personalizzazione, ove necessario.
  • Complessità della strategia – Alcuni software non sono tagliati per il crunch numerico o per la complessità matematica. Excel è uno di questi software. Mentre è buono per le strategie più semplici, non può davvero far fronte a numerose risorse o algoritmi più complicati.
  • Minimizzazione del bias – un particolare software o set di dati si presta maggiormente ai bias di trading? Nel caso si desideri creare personalmente tutte le funzionalità, È necessario assicurarsi che non si introducano bug che possono causare bias.
  • Velocità di sviluppo – non è necessario passare mesi e mesi a implementare un motore di backtest. La prototipazione dovrebbe richiedere solo alcune settimane. Assicurati che il tuo software non stia ostacolando i tuoi progressi, solo per afferrare qualche punto percentuale extra nella velocità di esecuzione. C ++ è il must in questo ambito.
  • Velocità di esecuzione – Se la tua strategia dipende completamente dalla velocità di esecuzione (come nel  HFT / UHFT), sarà necessario un linguaggio come C o C++. Tuttavia, in questi ambiti siamo al limite dell’ottimizzazione del kernel Linux e dell’utilizzo FPGA, che è al di fuori dello scopo di questo articolo!
  • Costo – molti degli ambienti software con cui è possibile programmare strategie di trading algoritmico sono completamente gratuiti e open source. In effetti, molti hedge fund fanno uso di software open source per tutti i loro stack di trading algoritmico. Inoltre, Excel e MATLAB sono entrambi relativamente economici e ci sono anche alternative gratuite.

 

Ora che abbiamo elencato i criteri con i quali dobbiamo scegliere la nostra infrastruttura software, voglio esaminare alcuni dei pacchetti più popolari e confrontarli tra loro:

Nota: includo solamente i software disponibili per la maggior parte dei professionisti retail e degli sviluppatori di software. Sono disponibili altri software,  come gli strumenti usati a  livello più istituzionale, ma ritengo che questi siano troppo costosi per essere effettivamente utilizzati in un ambiente retail e non ho  esperienza diretta con questi software.

 
MS ExcelDescrizione: WYSIWYG (what-you-see-is-what-you-get) software di spreadsheet. Estremamente diffuso nel settore finanziario. Dati e algoritmo sono strettamente accoppiati.
Esecuzione: Sì, Excel può essere collegato alla maggior parte dei broker.
Personalizzazione: Le macro VBA permettono funzionalità più avanzate a scapito della complessità di implementazione.
Complessità della Strategia: Strumenti statistici avanzati sono più difficili da implementate, così come le strategie che operano su molti asset.
Minimizzazione dei Bias: Il bias Look-ahead è facile da rilevare tramite funzionalità di evidenziazione delle celle (presupponendo che non sia presente VBA).
Velocità di Sviluppo: Le strategie base sono implementate rapidamente.
Velocità di Esecuzione: Bassa velocità di esecuzione – adatto solo per strategie a bassa frequenza.
Costo: Economico o gratuito (a seconda della licenza).
Alternative: OpenOffice
 
MATLABDescrizione: Ambiente di programmazione originariamente progettato per matematica computazionale, fisica e ingegneria. Molto adatto per le operazioni vettorializzate e quelle che coinvolgono l’algebra lineare numerica. Fornisce una vasta gamma di plugin per il trading quantitativo. In uso in molti hedge fund quantitativi.
Esecuzione: Nessuna capacità di esecuzione nativa, MATLAB richiede un sistema di esecuzione separato.
Personalizzazione: Vasta gamma di plug-in per quasi tutte le aree della matematica computazionale.
Complessità della Strategia: Complessità strategica: molti metodi statistici avanzati già disponibili e ben testati.
Minimizzazione dei Bias: i bias di look-ahead sono più difficili da individuare e richiedeno test approfonditi.
Velocità di sviluppo: brevi script possono facilmente generare sofisticati backtest.

Velocità di esecuzione:
MATLAB è altamente ottimizzato per supponendo un algoritmi vettorizzati / parallelizzati. Scarse prestazioni nei cicli iterati tradizionali.
Costo: circa 1,000$ per una licenza.
Alternative: Octave, SciLab
 
PythonDescrizione:
linguaggio di alto livello progettato per avere una semplicità e rapidità di sviluppo. Ampia gamma di librerie per quasi tutte le attività di coding immaginabili. Sta ottenendo sempre maggiori apprezzamenti ed utilizzi da parte dei hedge fund e banche d’investmento. Non veloce come C/C++ relativamente alla velocità di esecuzione.
Esecuzione: esistono plugin Python per i principali broker, come Interactive Brokers. Quindi il backtest e il sistema di esecuzione possono essere parte dello stesso “stack tecnologico”.
Personalizzazione: Python ha una grandissima comunity di sviluppatori ed è un linguaggio maturo. Ciò garantisce un elevato numero di librerie plugin disponibili. NumPy / SciPy forniscono veloci strumenti di calcolo scientifico e di analisi statistica, essenzialiper il trading quantistico.
Complessità della Strategia: esistono molti plug-in per i principali algoritmi, ma non ha una comunità di algotrader abbastanza grande come quella presente per MATLAB.
Minimizzazione dei Bias: soffre degli stessi problemi di minimizzazione dei bias di qualsiasi altro linguaggio di alto livello. Devi essere estremamente attento ai test.
Velocità di sviluppo: il principale vantaggio di Pythons è la velocità di sviluppo, grazie a robuste funzionalità integrate per il testing.
Velocità di Esecuzione: Non è abbastanza veloce come il C++, ma i componenti per il calcolo scientifico sono ottimizzati e Python può utilizzare con il codice C nativo grazie a specifici plugin
Costo: Gratis/Open Source
Alternative: Ruby, Erlang, Haskell
 
RDescrizione: Ambiente progettato per i metodi statistici avanzati e l’analisi di serie temporali. Vasta gamma di strumenti statistici specifici, econometrici e nativi. Grande comunity di sviluppatori.
Esecuzione: R possiede plugin per alcuni broker, in particolare per Interactive Brokers. Quindi un sistema end-to-end può essere scritto interamente in R.
Personalizzazione: R può essere personalizzato con qualsiasi pacchetto, ma i suoi punti di forza sono in ambito statistico / econometrico.
Complessità della Strategia: utile soprattutto se si eseguono strategie econometriche, statistiche o di apprendimento automatico grazie alle librerie disponibili.
Minimizzazione dei Bias: le possibilità di aver bias è simile a quella di qualsiasi linguaggio ad alto livello, come Python o C++. E’ quindi necessario svolgere dei test.
Velocità di Sviluppo: Con R si possono scrivere rapidamente strategie basate su metodi statistici.
Velocità di esecuzione: R è più lento di C ++, ma è relativamente ottimizzato per le operazioni vettorializzate (come con MATLAB).
Costo: Gratis/Open Source
Alternative: SPSS, Stata
 
C++Descrizione: linguaggio maturo e di alto livello progettato per avere un’elevata velocità di esecuzione. Vasta gamma di librerie quantitative e numeriche. Debug più difficile da eseguire e spesso richiede più tempo di implementazione rispetto a Python o MATLAB. Estremamente prevalente sia in lato di acquisto che di vendita.
Esecuzione: la maggior parte delle API dei broker sono scritte in C ++ e Java. Quindi esistono molti plugin.
Personalizzazione C / C ++ consente l’accesso diretto alla memoria sottostante, quindi è possibile implementare strategie di altissima frequenza.
Complessità della Strategia: C++ STL offre un’ampia gamma di algoritmi ottimizzati. Quasi ogni specifico algoritmo matematico ha un’implementazione C / C++ gratuita e open-source sul web.
Minimizzazione dei Bias: il bias di Look-ahead può essere difficile da eliminare, ma non più difficile di altri linguaggi di alto livello. Possiede buoni strumenti di debug, ma bisogna stare attenti quando si ha a che fare con la memoria sottostante.
Velocità di Sviluppo:C++ è piuttosto lento rispetto a Python o MATLAB per lo stesso algoritmo. Più linee di codice (LOC) spesso portano a una maggiore probabilità di errori.
Velocità di Esecuzione: C/C++ ha una velocità di esecuzione estremamente elevata e può essere ottimizzato per specifiche architetture hardware. Questa è la ragione principale per utilizzarlo.
Costo:Costo: vari compilatori: Su Linux / GCC è gratuito, mentre MS Visual Studio ha diverse tipi di licenze.
Alternative: C#, Java, Scala

 

Diverse strategie richiedono quindi diversi pacchetti software. Le strategie HFT e UHFT sono scritte in C / C ++ (dato che spesso eseguite su GPU e FPGA), mentre le strategie direzionali a bassa frequenza sono facili da implementare in TradeStation, a causa della natura “tutto in uno” del software / broker.

La mia personale scelta è Python in quanto fornisce il giusto grado di personalizzazione, velocità di sviluppo, capacità di test e velocità di esecuzione per le mie esigenze e strategie. Se ho bisogno di qualcosa di più veloce, posso “entrare” in C++ direttamente dai miei programmi Python. Un metodo scelto da molti trader algoritmici consiste nel prototipare le loro strategie in Python e quindi convertire le sezioni di esecuzione più lente in C ++ in modo iterativo. Eventualmente l’intero algoritmo può essere scritto

Nei prossimi articoli sul backtesting descriverò alcune criticità relative all’implementazione di un sistema di backtesting per il trading algoritmico. Si evidenzierà come effettuare la misurazione delle prestazioni della strategia e si illustrerà una strategia esemplificativa.

 

Leggi il secondo articolo di questa serie: Il Backtesting di una Strategia di Trading Algoritmico – Parte II

 

Come identificare le Strategie di Trading Algoritmico

In questo articolo voglio parlare dei metodi con cui io stesso identifico le strategie di trading algoritmico più redditizie. L’obiettivo di oggi è capire in dettaglio come trovare, valutare e selezionare tali sistemi. Descrivere in che modo identificare le migliori strategie a seconda sia del nostro stile di trading che delle prestazioni strategiche, come determinare il tipo e la quantità di dati storici da testare, come valutare nel dettaglio una strategia di trading e infine come procedere verso la fase di backtesting e implementazione di una strategia.

Determinare il proprio stile di trading

Per essere un trader di successo – sia discrezionale che algoritmico – è necessario porsi onestamente alcune domande. Il trading ti offre la possibilità di perdere denaro a un ritmo allarmante, quindi è necessario “conoscerti” per capire quale strategia scegliere.

Direi che la considerazione più importante è essere consapevoli della propria personalità. Il trading, in particolare il trading algoritmico, richiede un grado significativo di disciplina, pazienza e distacco emotivo. Dato che stai lasciando che un algoritmo esegua il trading al tuo posto, è necessario essere determinati ad non interferire MAI con una strategia dopo che viene messa ad operare con denaro reale. Questo può essere estremamente difficile, specialmente nei periodi di forte drawdown. Tuttavia, molte strategie che hanno dimostrato di essere altamente redditizie in un backtest possono essere rovinate da semplici interferenze. Comprendi che se desideri entrare nel mondo del trading algoritmico sarai emotivamente messo alla prova e, che per avere successo, è necessario superare queste difficoltà!

La prossima considerazione riguarda il tempo. Hai un lavoro a tempo pieno? Lavori a tempo parziale? Lavori da casa o hai un lungo tragitto giornaliero? Queste domande aiuteranno a determinare la frequenza delle strategia che dovresti cercare. Per quelli di voi che lavorano a tempo pieno, una strategia di futures infragiornaliero potrebbe non essere appropriata (almeno fino a quando non sarà completamente automatizzata!). I tuoi limiti di tempo dettano anche la tipologia della strategia. Se la tua strategia prevede molti trade e dipende dai feed di notizie (come un terminale Bloomberg), devi chiaramente essere realista riguardo alla tua capacità di gestirlo con successo mentre sei in ufficio! Per quelli di voi con molto tempo a disposizione, o le abilità per automatizzare una strategia, potrebbe essere interessante implementare una strategia di trading ad alta frequenza (HFT).

 

In qualsiasi caso, il trading implica studi e ricerche continui sulle strategie da applicare per mantenere un portafoglio costantemente redditizio. Nessuna strategia rimane “redditizia” per sempre. Quindi parte significativa del tempo assegnato al trading è dedicato al ricerca di nuove strategie. Chiediti se sei pronto a farlo, in quanto può essere la differenza tra una forte redditività o un lento declino verso le perdite.

Devi anche considerare il capitale da dedicare al trading. L’importo minimo ideale generalmente previsto per una strategia quantitativa è di 50.000 USD. Se dovessi ricominciare, inizierei con un importo maggiore, probabilmente più vicino a 100.000 USD. Questo perché i costi di transazione possono essere estremamente costosi per le strategie di media e alta frequenza ed è necessario disporre di capitale sufficiente per assorbirli nei periodi drawdown. Se stai pensando di iniziare con meno di 10.000 USD, dovrai limitarti alle strategie a bassa frequenza, scambiando solo uno o due beni, altrimenti i costi di transazione annullerebbero i profitti.

Interactive Brokers, che è uno dei broker più usati dai trader con competenze di programmazione, grazie alla sua API, prevede un capitale iniziale minimo di 10.000 USD.

 

La programmazione è un fattore importante nella creazione di una strategia di trading algoritmica automatizzata. Essere competenti in un linguaggio di programmazione come C++, Java, C#, Python o R permette di creare autonomamente il gestore di archiviazione dei dati end-to-end, il motore di backtest e il sistema di esecuzione. Questo ha una serie di vantaggi, il principale è la completa comprensione di tutti gli aspetti dell’infrastruttura di trading. Permette anche di esplorare le strategie a più alta frequenza in quanto hai il pieno controllo del tuo “stack tecnologico”. Questo significa che puoi testare il tuo software ed eliminare i bug, ma significa anche dedicare più tempo alla codifica dell’infrastruttura e meno all’attuazione delle strategie, almeno nella prima parte della tua carriera da algotrader. Potresti scoprire di essere a tuo agio nel trading in Excel o MATLAB e puoi esternalizzare lo sviluppo di altri componenti. Cosa che comunque non consiglio, in particolare per i trader che operano ad alta frequenza.

Devi chiederti cosa speri di ottenere con il trading algoritmico. Ti interessa un reddito regolare, per cui speri di ricavare guadagni dal tuo conto di trading? Oppure, sei interessato a un guadagno di capitale a lungo termine e puoi permetterti di fare trading senza la necessità di prelevare i fondi? La dipendenza dal reddito determinerà la frequenza della tua strategia. Prelievi più regolari di reddito richiederanno una strategia di trading a frequenza più alta con una minore volatilità (cioè un SharpeRatio più alto). I trader a lungo termine possono permettersi una frequenza di trading più tranquilla.

Infine, non illuderti di diventare estremamente ricco in un breve lasso di tempo! Il trading di Algo NON è uno schema che permette di arricchirsi rapidamente – semmai può essere uno schema che ti fa diventare povero molto più velocemente. Ci vuole una notevole disciplina, ricerca, diligenza e pazienza per avere successo nel trading algoritmico. Possono essere necessari mesi, se non anni, per generare una redditività costante.

Dove trovare nuove idee di trading algoritmico

Nonostante si pensi il contrario, in realtà è abbastanza semplice individuare strategie di trading redditizie tra quele di pubblico dominio. Mai come ora, le idee di trading sono facilmente disponibili per chiunque. Le riviste di finanza accademica, i blog di trading, i forum di trading, le riviste settimanli di trading e i testi specialistici forniscono migliaia di strategie di trading su cui basare le proprie idee.

Il nostro obiettivo come ricercatori di trading quantitativo è stabilire una pipeline di strategie che ci fornirà un flusso di nuove idee di trading. Idealmente vogliamo creare un approccio metodico per la ricerca, la valutazione e l’implementazione delle strategie che incontriamo. Gli obiettivi della pipeline sono di generare una quantità consistente di nuove idee e di fornirci un quadro per respingere la maggior parte di queste idee con un approccio non emotivo.

E’ necessario stare estremamente attenti a non permettere che i bias cognitivi influenzino la nostra metodologia decisionale. Questo potrebbe essere semplice come avere una preferenza per un asset o un mercato rispetto ad un’altro (ad esempio, l’oro e altri metalli preziosi) perché sono percepiti come più esotici. L’obiettivo deve essere sempre quello di trovare strategie costantemente  redditizie, con aspettative positive. La scelta della classe di attività dovrebbe essere basata su altre considerazioni, quali vincoli di capitale, le commissioni di intermediazione e capacità di leva finanziaria.

Se non conosci il concetto di una strategia di trading, il primo posto da guardare sono i libri di testo. I testi classici offrono una vasta gamma di idee più semplici e dirette con cui familiarizzare con il trading quantitativo. Ecco una selezione che raccomando per coloro che sono nuovi nel trading quantitativo, che gradualmente diventano più sofisticati mentre si procede con la lista:

 

Il passo successivo per trovare idee di strategie più sofisticate consiste nel consultare blog e forum di trading. Tuttavia, una nota di cautela: molti blog di trading si basano sul concetto di analisi tecnica. L’analisi tecnica si basa sull’uso di indicatori stardard e sulla  psicologia del trader che  determina le trendline o schemi di inversione dei prezzi degli asset.

Nonostante sia estremamente popolare nello mondo del trading, l’analisi tecnica è considerata in qualche modo inefficace dal comunity dei trader  quantitativi. Alcuni hanno suggerito che non sia differente dal leggere un oroscopo o studiare i fondi del caffè, in termini di potere predittivo! In realtà ci sono individui di successo che fanno uso dell’analisi tecnica. Tuttavia, come quants abbiamo a disposizione una strumentazione matematica e statistica più sofisticata, possiamo facilmente valutare l’efficacia di tali strategie basate sull’AT e prendere decisioni basate sui dati piuttosto che basarci sulle considerazioni emotive o sui preconcetti.

Ecco una lista dei migliori blog e forum sul trading algoritmico:

Dopo aver testato e valutato alcun delle strategie più semplici, per strategie più sofisticate è tempo di guardare alla letteratura accademica. Alcune riviste accademiche sono difficili da consultare, senza prevedere elevati abbonamenti o costi una tantum, è possibile consultare specifici server, che sono un repository web dei lavori accademici non ancora pubblicati perchè sono oggetto di revisione. Poiché ci interessa solamente le strategie che possiamo replicare, testare e ottenere una discreta redditività, la revisione prima della pubblicazione è un aspetto meno importante. Il principale svantaggio delle strategie accademiche è che possono spesso essere obsolete, richiedere dati storici oscuri e costosi, operate su asset poco liquide o non tenere conto dei costi delle commissioni, slippage o spread. Può anche non essere chiaro se la strategia di trading deve essere eseguita con ordini di mercato, ordini limite o se contiene stop loss ecc. Quindi è assolutamente essenziale replicare la strategia da soli nel miglior modo possibile, effettuare il backtest e aggiungere costi di transazioni realistici, cioè che includa tutti gli aspetti degli strumenti che vogliamo negoziare.

Ecco un elenco dei più noti server e riviste finanziarie da cui è possibile trarre nuove idee:

Trove nuove strategie quantitative richiede generalmente (ma non solo) una buona esperienza in una o più delle seguenti categorie:

  • Market microstructure – In particolare per le strategie ad alta frequenza, è possibile utilizzare la market microstructure, ossia l’analisi delle dinamiche dell’order-book al fine di generare redditività. Mercati diversi avranno vari limiti tecnologici, regolamentazioni, partecipanti al mercato e vincoli, tutti aspetti che possono essere sfruttati a vantaggio del trader tramite strategie specifiche. Questa è una zona molto sofisticata e i trader retail hanno difficoltà ad essere competitivi in ​​questo spazio, in particolare perché i competor sono  hedge fund quantitativi e ben capitalizzati, con forti capacità tecnologiche.
  • Fund structure – I fondi d’investimento, come i fondi pensione, le partnership di investimento private (hedge fund), le società di consulenza e altri tipi di sono sottoposti sia ad una pesante regolamentazione che dall’enorme capitale da dover gestire. Questi vincoli causano un comportamento “prevedile” e possono essere sfruttati da trader più piccoli e quindi più agili. Ad esempio, i fondi di grandi dimensioni sono soggetti a limiti di capacità a causa delle loro dimensioni. Quindi, se hanno bisogno di scaricare rapidamente (vendere) una quantità di titoli, dovranno essere molto cauti altrimenti rischiano di “spostare il mercato”. Algoritmi sofisticati possono trarre vantaggio da questa e da altre inefficiente in un processo generale noto come fund structure arbitrage.
  • Machine learning/artificial intelligence – Negli ultimi anni gli algoritmi di apprendimento automatico si sono diffusi anche nei mercati finanziari. Approcci con reti neurali e algoritmi genetici sono stati tutti usati per prevedere i movimenti dei prezzi o per ottimizzare le strategie di trading. 
    Ci sono, naturalmente, molte altre aree per i quants da investigare. Discuteremo su come elaborare strategie personalizzate in dettaglio in un articolo successivo.

 

Continuando a monitorare questi siti su base settimanale o giornaliera, si inizia a ricevere un elenco quasi costante di strategie da una vasta gamma di fonti. Il prossimo passo consiste nel determinare come rifiutare un ampio sottoinsieme di queste strategie al fine di minimizzare lo spreco di tempo e risorse per il backtesting di strategie che potrebbero non essere redditizie.

 

Valutazione delle Strategie di Trading

La prima, e probabilmente la più ovvia, considerazione l’effettiva comprensione della strategia. Siamo in grado di spiegare la strategia in modo conciso o richiederà una serie di eventi e un elenco di parametri senza fine? Inoltre, la strategia ha buone e solide basi realistiche? Ad esempio, si potrebbe verificare qualche comportamento del mercato o qualche vincolo a cui i fondi sono soggetti che potrebbe causare i pattern che si sta tentando di sfruttare? Questo vincolo sarebbe ancora valido in caso di modifiche strutturali, come un’improvvisa e ampia modifica al contesto normativo? La strategia si basa su complesse regole statistiche o matematiche? Si applica a tutte le serie finanziare o è specifica per un determinato strumento o asset?

Si deve costantemente valutare tutti questi fattori quando si cercano nuovi metodi di trading, altrimenti si rischia di sprecare una notevole quantità di tempo nel tentativo di backtesting  e ottimizzare strategie non redditizie.

Una volta compresi i principi alla base della strategia si deve decidere se si adatta al proprio stile di trading. Questa non è una considerazione vaga come sembra! Le strategie differiranno sostanzialmente nelle loro caratteristiche di rendimento. Vi sono alcuni tipi di persone che possono gestire periodi di drawdown più significativi o che sono disposti ad accettare un rischio maggiore al fine di ottenere un rendimento maggiore. Nonostante il fatto che noi, come quants, proviamo ad eliminare il più possibile i bias cognitivi e dovremmo essere in grado di valutare oggettivamente una strategia, i bias si insinueranno sempre. Quindi abbiamo bisogno di mezzi meccanici e non emotivi attraverso i quali valutare le prestazioni delle strategie . Ecco l’elenco dei criteri che osservo quando valuto una potenziale nuova strategia:

  • Metodologia – La strategia è basata sul momentum, mean-reverting, market-neutral, direzionale? La strategia si basa su sofisticate (o complesse!) tecniche statistiche o machine learning difficili da comprendere e che richiedono un dottorato di ricerca in statistica? Queste tecniche introducono una quantità significativa di parametri, che potrebbero portare a errori di ottimizzazione? La strategia è in grado di resistere a un cambio di regime (vale a dire una potenziale nuova regolamentazione dei mercati finanziari)?
  • SharpeRatio – Caratterizza euristicamente il rapporto reward/risk della strategia. Quantifica quanto profitto è possibile ottenere con un accettabile livello di volatilità della curva equity. Naturalmente, è necessario determinare il periodo e la frequenza nei quali i rendimenti e la volatilità (cioè la deviazione standard) sono misurati. Una strategia di frequenza più alta richiederà una maggiore frequenza di campionamento della deviazione standard, ma, per esempio, un tempo complessivo più breve.
  • Leverage: la strategia richiede una leva significativa per essere redditizia? La strategia richiede l’uso di contratti derivati ​​con leva (futures, opzioni, swap) al fine di ottenere un rendimento? Questi contratti a leva possono avere una forte volatilità e quindi possono facilmente portare a margin call. Si dispone del capitale e del temperamento per sopportare tale volatilità?
  • Frequenza – La frequenza della strategia è intimamente legata allo stack tecnologico (e quindi alla competenza tecnologica), al SharpeRatio e al livello generale dei costi di transazione. In generale, le strategie ad alta frequenza richiedono più capitale, sono più sofisticate e più difficili da implementare. Tuttavia, supponendo che motore di backtesting sia performante e privo di bug, queste strategie hanno SharpeRatio molto alti.
  • Volatilità: la volatilità è strettamente correlata al “rischio” della strategia. E’ contraddistinto dal SharpeRatio lo contraddistingue. Una maggiore volatilità degli asset class sottostanti, se non controllata, causa spesso una maggiore volatilità della curva equity e quindi un SharpeRatio più basso. Naturalmente presumo che la volatilità positiva sia approssimativamente uguale alla volatilità negativa. Alcune strategie possono avere una maggiore volatilità al ribasso. Devi essere consapevole di queste caratteristiche.
  • Win / Loss, Average Profit / Loss – Le strategie differiscono nelle loro caratteristiche per il numero di vincite/perdite e per la media profitti/perdite. Si può avere una strategia molto redditizia, anche se il numero di trade  perdenti supera il numero di operazioni vincenti. Le strategie di momentum tendono ad avere questo schema in quanto si basano su un piccolo numero di “grandi successi” per essere redditizi. Le strategie di inversione della tendenza tendono ad avere profili opposti in cui ci molti trade “vincenti”, ma le operazioni in perdita possono incidere molto negativemente sul capitale.
  • Drawdown massimo: il drawdown massimo è Il drawdown è la discesa, la correzione, da un precedente massimo assoluto fino ad un minimo assoluto nella curva equity della strategia. Le strategie del momentum sono ben note per soffrire di periodi di estesi drawdown (a causa di una serie incrementale di molti trade perdenti). Molti trader si disperano nei periodi di drawdown estesi, nonostante i test storici hanno suggerito che questo comportamento è “tipico” per questa strategia. Si deve stabile quale percentuale di drawdown (e in quale periodo di tempo) sono accettabili prima di interrompere l’esecuzione della strategia. Questa è una decisione altamente personale e quindi deve essere valutata attentamente.
  • Capacità / Liquidità – A livello retail, a meno che non si stia tradando uno strumento altamente illiquido (come uno stock a ridotta capitalizzazione), non dovrete preoccuparvi molto della capacità strategica. La capacità determina la scalabilità della strategia a seconda del capitale. Gli hedge fund più grandi hanno notevoli problemi di capacità man mano che le loro strategie aumentano nella dotazione di capitale da gestire.
  • Parametri – Alcune strategie (in particolare quelle presenti nella comunity del Machine Learning) richiedono una grande quantità di parametri. Ogni parametro introdotto rende una strategia più vulnerabile al bias di ottimizzazione (noto come “curve-fitting”). E’ consigliato individuare le strategie con il minor numero di parametri possibile o assicurarsi  di disporre di una sufficiente quantità di dati con cui testare le strategie.
  • Benchmark – Quasi tutte le strategie (a meno che non siano  “absolute return”) sono misurate rispetto a un benchmark di performance. Il benchmark è solitamente un indice che caratterizza un ampio campione dello strumento o mercato su cui la strategia opera. Se la strategia negozia titoli azionari statunitensi a grande capitalizzazione, l’S&P500 sarebbe un benchmark naturale per misurare la propria strategia. I termini “alpha” e “beta”, applicati a strategie di questo tipo. Discuteremo questi coefficienti in modo approfondito negli articoli successivi.

 

Da notare che non abbiamo parlato dei rendimenti effettivi di una strategia. In realtà, i rendimenti forniscono informazioni limitate sull’efficacia della strategia. Non permettono di valutare la leva finanziaria, la volatilità, i benchmark o i requisiti patrimoniali. Pertanto le strategie vengono giudicate raramente solo in base ai loro rendimenti. Considerare sempre le prestazioni relative al rischio di una strategia, prima di esaminare i rendimenti.

In questa fase molte delle strategie trovate dalla nostra pipeline sono respinte immediatamente, dal momento che non soddisfano i requisiti di capitale, i vincoli di leva, la massima tolleranza di drawdown o la volatilità. Le strategie che rimangono possono ora essere utilizzate per il backtesting. Tuttavia, prima che sia possibile, è necessario considerare un criterio finale di esclusione, quello sulla disponibilità dei dati storici su cui testare queste strategie.

Ottenere i dati storici

Al giorno d’oggi, la complessità dei requisiti tecnici per l’archiviazione dei dati storici di tutti le asset class è notevole. Al fine di rimanere competitivi, sia i buy-side (fondi) che i sell-side (banche d’investimento) investono pesantemente nella loro infrastruttura tecnica. È imperativo considerare la sua importanza. In particolare, siamo interessati alla tempestività, all’accuratezza e ai requisiti di archiviazione. Si descrive ora i concetti basi per ottenere dati storici e come memorizzarli. Purtroppo questo è un argomento molto vasto e tecnico, quindi questo articolo non può essere esaustivo. Tuttavia, ho in programma di approfondire questo tema con articoli futuri. Nella sezione precedente si era impostata una pipeline strategica che permetteva di scartare determinate strategie sulla base di alcuni criteri.

In questa sezione filtreremo maggiormente le strategie in base agli approcci scelti per ottenere i dati storici. Le considerazioni principali (soprattutto a livello di trader retail) sono i costi dei dati, i requisiti di archiviazione e il livello di competenza tecnica. Dobbiamo anche discutere i diversi tipi di dati disponibili e le diverse considerazioni che ogni tipo comporta.

Iniziamo identificando i tipi di dati disponibili e le questioni chiave su cui dovremo riflettere:

  • Dati fondamentali – Include i dati sugli andamenti macroeconomici, come tassi di interesse, i dati sull’inflazione, le dinamiche societarie (dividendi, scissioni), i bilanci societari, dati sugli utili, i rapporti sulle colture, dati meteorologici, ecc. Questi dati vengono spesso usati per valutare le società o altre attività su aspetti di ambito fondamentale, ad esempio attraverso i valori attesi per i futuri flussi di cassa. Non include le serie dei prezzi delle azioni. Alcuni dati fondamentali sono disponibili gratuitamente dai siti web dei governi. Altri dati fondamentali storici a lungo termine possono essere estremamente costosi. I requisiti di archiviazione spesso non sono particolarmente ampi, a meno che non vengano studiate contemporaneamente migliaia di società.
  • Dati delle notizie – Questi dati sono spesso di natura qualitativa. Consiste in articoli, post di blog, post di microblog (“tweet”) ed editoriali. Le tecniche di apprendimento automatico sono spesso usate per interpretare il sentiment. Questi dati sono spesso anche liberamente disponibili o economici, tramite l’abbonamento ai media. I nuovi database di archiviazione dei documenti “NoSQL” sono progettati per archiviare questo tipo di dati qualitativi non strutturati.
  • Dati sui prezzi degli asset – Questo è il dominio dei dati tradizionalmente utilizzato dagli algotrader. Consiste in serie temporali dei prezzi degli strumenti finanziari. Azioni (titoli), prodotti a reddito fisso (obbligazioni), materie prime e valute sono tutti presenti all’interno di questa tipologia. Per gli asset più diffusi i dati storici giornalieri sono spesso semplici da ottenere, come per le azioni. Tuttavia, una volta che accuratezza e pulizia sono state incluse e le distorsioni statistiche rimosse, i dati possono diventare costosi. Inoltre, i dati delle serie temporali hanno spesso requisiti di archiviazione significativi, specialmente quando si considerano i dati intraday.
  • Strumenti finanziari – Le azioni, le obbligazioni, i futures e le opzioni derivate più esotiche hanno caratteristiche e parametri molto diversi. Quindi non esiste una struttura di database “universale” che possa gestirli. È necessario prestare particolare attenzione alla progettazione e alla realizzazione dei database per i vari strumenti finanziari utilizzati. Si approfondisce questo tema negli articoli relativi ai securities master database.
  • Frequenza – Maggiore è la frequenza dei dati, maggiori sono i costi e i requisiti di archiviazione. Per le strategie a bassa frequenza, i dati giornalieri sono spesso sufficienti. Per le strategie ad alta frequenza, potrebbe essere necessario ottenere dati a livello di tick e persino i dati storici di particolari order-book. L’implementazione di un motore di archiviazione per questo tipo di dati è molto intensa dal punto di vista tecnologico e adatta solo a coloro che hanno un forte background tecnico / di programmazione.
  • Benchmark – Le strategie sopra descritte saranno spesso confrontate con un benchmark. Questo di solito si manifesta come serie temporali finanziarie aggiuntive. Per le azioni, questo benchmark è spesso un indice azionario nazionale, come l’S&P500 (USA) o il FTSE100 (Regno Unito). Per un fondo a reddito fisso, è utile confrontarsi con un paniere delle obbligazioni o di prodotti a reddito fisso. Anche il “risk-free rate” (cioè il tasso di interesse appropriato) è un altro benchmark ampiamente accettato. Tutte le categorie di asset class possiedono un relativo benchmark di riferimento, quindi sarà necessario effettuare una ricerca in base alla propria particolare strategia, se si desidera ottenere un ritorno interessate per la propria strategia.
  • Tecnologia – Le tecnologie usate dietro un centro di archiviazione di dati finanziari sono complesse. Questo articolo può solo introdurre tutti gli aspetti coinvolti nella costruzione di centro di archiviazione. Tuttavia, si concentra su un motore di database, come un sistema di gestione di database relazionale (RDBMS), come MySQL, SQL Server, Oracle) e uno schema di archiviazione di documenti (ad esempio “NoSQL”). Questo è accessibile tramite il codice che implementa la  “business logic”, interrogando il database e fornendo  l’accesso a strumenti esterni, come MATLAB, R o Excel. Spesso questa logica è scritta in C ++, C #, Java o Python. Bisogna anche bisogno di ospitare questi dati da qualche parte, sul un personal computer, o su un server remoto. Prodotti come Amazon Web Services hanno reso più semplice ed economico questo lavoro, ma richiede comunque una notevole esperienza tecnica per ottenere risultati solidi.

 

Come si può vedere, una volta che una strategia è stata identificata tramite la pipeline, sarà necessario valutare la disponibilità, i costi, la complessità e i dettagli di implementazione di un particolare insieme di dati storici. Potresti scoprire che è necessario scartare una strategia basandosi esclusivamente su valutazioni dei dati storici. Questo è un tema molto ampio ed è oggetto di lavoro di squadre di ricercatori all’interno dei grandi fondi di investimento, assicurandosi che i prezzi siano accurati e tempestivi. Non sottovalutare le difficoltà di creare un robusto data center per i propri scopi di backtesting!

Voglio sottolineare, tuttavia, che molte piattaforme di backtesting possono fornire questi dati automaticamente, ad un determinato costo. Di conseguenza, si toglie molto lavoro alla gestione dei dati e ci si può concentrare esclusivamente sull’implementazione e l’ottimizzazione della strategia. Strumenti come TradeStation possiedono questa capacità. Tuttavia, la mia opinione personale è quella di implementare internamente il più possibile ed evitare di esternalizzare parti dello stack ai fornitori di software. Preferisco le strategie ad alta frequenza grazie ai loro SharpeRatio più attraenti, ma sono spesso strettamente collegate allo stack tecnologico, dove l’ottimizzazione avanzata è fondamentale.

Ora che abbiamo discusso le questioni relative ai dati storici, è ora di iniziare a implementare le nostre strategie in un motore di backtesting. Questo sarà oggetto di altri articoli, poiché si tratta di un’area di discussione altrettanto ampia!

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.