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 i seguenti 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):
    """
    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, è 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__":
    # 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 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.

Per il codice completo riportato in questo articolo utilizzando il modulo di backtesting vettoriale VectorBacktest si può consultare il seguente repository di github:
https://github.com/datatrading-info/VectorBacktest

Scroll to Top