In questa lezione del corso sulle basi del trading algoritmico con Python descriviamo come usare i futures continuous nel backtesting. Analizziamo le caratteristiche dei contratti futures, che pongono diverse sfide nel contesto del backtesting.
In particolare, approfondiamo i concetti di “continuous contract” e “roll returns”. Illustriamo inoltre le principali criticità legate ai futures e una proposta di implementazione in Python con Pandas per gestire efficacemente tali problematiche.
Breve panoramica sui Futures
I futures sono contratti negoziati in borsa, stipulati tra due parti per acquistare o vendere un’attività sottostante a una data futura prefissata, chiamata scadenza. Alla scadenza, l’acquirente riceve il bene fisico (o l’equivalente in contanti) al prezzo concordato alla stipula. Questi strumenti derivati, standardizzati per quantità e qualità del sottostante, sono regolati giornalmente (“marked to market”) e offrono grande liquidità. Oggi esistono futures su una vasta gamma di asset, inclusi indici azionari, tassi di interesse e valute.
Una panoramica completa di codici e simboli per i contratti futures nelle principali borse è disponibile sul sito CSI Data: Futures Factsheet.
La differenza principale rispetto alle azioni è che i futures hanno una scadenza. Esistono quindi più contratti sullo stesso sottostante, ciascuno con una diversa data. Il contratto più vicino alla scadenza è detto “near contract”. Il trader quantitativo si trova quindi a operare su una serie temporale discontinua.
Questa lezione mira a presentare vari metodi per generare una serie di prezzi continua da più contratti e a valutarne vantaggi e svantaggi.
I contratti Futures Continuous
Il problema centrale nella creazione di contratti continui è la discontinuità nei prezzi tra i contratti, dovuta a fenomeni come contango (prezzi futuri maggiori) o backwardation (prezzi futuri minori).
Vediamo ora le principali tecniche di unione dei contratti.
Aggiustamento Back/Forward (“Panama”)
Questo metodo sposta ogni contratto in modo da far coincidere i prezzi di chiusura e apertura tra contratti adiacenti. Introduce però un bias di trend, che può generare prezzi irrealistici e rendimenti distorti.
Aggiustamento Proporzionale
Simile al metodo per gestire frazionamenti azionari. Utilizza il rapporto tra vecchio e nuovo prezzo per modificare proporzionalmente lo storico. Tuttavia, non è adatto a strategie che si basano su soglie di prezzo assoluto.
Rollover e Serie Perpetual
Consiste nel passare gradualmente da un contratto all’altro utilizzando una media ponderata tra i due. Per esempio, in una transizione su 5 giorni, il prezzo risultante sarà una combinazione progressiva dei prezzi dei due contratti, dal più distante a quello più vicino.
Il principale svantaggio è l’aumento delle operazioni durante il rollover, che comporta costi di transazione maggiori.
I futures continuous nel backtesting con Python e Pandas
Il resto della lezione è dedicato all’implementazione dei futures continuous per il backtesting di strategie.
L’obiettivo è unire i futures WTI Crude Oil “near” e “far” (simbolo CL) in un’unica serie. Ad esempio, i contratti CLF2014 (gennaio) e CLG2014 (febbraio).
Per scaricare i dati usiamo la libreria Quandl. È fondamentale configurare l’ambiente Python e installare Quandl eseguendo 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 la lezione precedente
Creiamo un nuovo file ed inseriamo le seguenti istruzioni:
import datetime
import numpy as np
import panda as pd
import quandl
Nell’implementazione dei futures continuous per il backtesting prevediamo che la logica principale è codifica nella funzione futures_rollover_weights
. La funzione 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 funzione:
def futures_rollover_weights(start_date, expiry_dates, contracts, rollover_days=5):
"""
Si costruisce un DataFrame pandas che contiene pesi (tra 0,0 e 1,0)
di posizioni contrattuali da mantenere per eseguire un rollover di rollover_days
prima della scadenza del primo contratto. La matrice può quindi essere
'moltiplicato' con un altro DataFrame contenente i prezzi di settle di ciascuno
contratto al fine di produrre una serie temporali per un contratto future
continuo.
"""
# Costruisci una sequenza di date a partire dalla data inizio del primo contratto
# alla data di fine del contratto finale
dates = pd.date_range(start_date, expiry_dates[-1], freq='B')
# Crea il DataFrame 'roll weights' che memorizzerà i moltiplicatori per
# ogni contratto (tra 0,0 e 1,0)
roll_weights = pd.DataFrame(np.zeros((len(dates), len(contracts))),
index=dates, columns=contracts)
prev_date = roll_weights.index[0]
# Si scorre ogni contratto e si crea i pesi specifiche per ogni
# contratto che dipende dalla data di settlement e dai 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')
# Crea una sequenza di pesi a finesta mobile (cioè [0.0,0.2, ...,
# 0.8,1.0] e si usano per regolare i pesi di ogni 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, possiamo 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__":
# Scarica gli attuali contratti future Front e Back (vicino e lontano)
# per il petrolio WTI, negoziato al NYMEX, da Quandl.com. Avrai bisogno di
# aggiustare i contratti per riflettere gli attuali contratti vicini / lontani
# a seconda del punto in cui leggi questo!
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)
# Crea un dizionario delle date di scadenza di ogni contratto
expiry_dates = pd.Series({'CLF2014': datetime.datetime(2013, 12, 19),
'CLG2014': datetime.datetime(2014, 2, 21)}).order()
# Calcolare la matrice (Dataframe) dei pesi di rollover
weights = futures_rollover_weights(wti_near.index[0], expiry_dates, wti.columns)
# Costruzione del future continuo dei contratti del petrolio WTI (CL)
wti_cts = (wti * weights).sum(1).dropna()
# Stammpa delle serie aggregate dei prezzi di settle dei contratti
wti_cts.tail(60)
Eseguendo questo script otteniamo il seguente output:

Da notare come ora la serie è continua tra i due contratti.
Conclusione
Il prossimo passo per implementare i futures continuous nel backtesting è eseguire il codice in scadenze multiple per un buon numero di anni, a seconda delle vostre esigenze.
Il codice completo presentato in questa lezione, parte del pacchetto python per il backtest TQBacktest, è disponibile nel seguente repository GitHub: https://github.com/tradingquant-it/TQBacktest.”