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.

Recommended Posts