Testing Statistico della Mean Reversion – Parte II

Nel precedente articolo abbiamo introdotto il test statistico della mean-reverting. Abbiamo esaminato un paio di tecniche che ci hanno aiutato a determinare se una serie temporale fosse o meno mean reverting. In particolare abbiamo esaminato il test Dickey-Fuller e l’esponente di Hurst. In questo articolo considereremo un altro metodo, ovvero il test Cointegrated Dickey Fuller (CADF).

Innanzitutto, si deve notare che è molto difficile ,in realtà, trovare un asset direttamente negoziabile che abbia un comportamento mean revering. Ad esempio, le azioni si comportano generalmente come un moto browniano geometrico e quindi rendono relativamente inutili le strategie mean-reverting. Tuttavia, non c’è nulla che ci impedisca di creare un portafoglio di serie di prezzi stazionarie e quindi applicare strategie di trading a medio termine.

La forma più semplice di strategie di trading di ritorno alla media è il classico “pairs trade”, che di solito implica una coppia long-short di azioni indipendente dal moneta fiat. Questa tecnica si basa sulla teoria che due società dello stesso settore sono probabilmente esposte a simili fattori di mercato, che influenzano le loro attività. Occasionalmente i loro relativi prezzi azionari divergono a causa di determinati eventi, ma nel lungo periodo tengono a tornare verso la media.

Consideriamo due titoli del settore energetico Approach Resources Inc (AREX) e Whiting Petroleum Corp (WLL). Entrambi sono esposti alle stesse condizioni di mercato e quindi sono collegati in modo stazionario. Possiamo studiare le relazioni tra questa coppia di titoli usando le librerie Pandas e Matplotlib. Il primo grafico (Figura 1) mostra i rispettivi storici dei prezzi dal 1 ° gennaio 2012 al 1 ° gennaio 2013.

Fig 1 - Grafico dei prezzi di AREX e WLL
Se creiamo il grafico di dispersione dei loro prezzi, si osserva una relazione sostanzialmente lineare (vedi Figura 2) per questo periodo.
Fig 2 - Grafico della dispersione dei prezzi di AREX e WILL

Il pairs trading si basa su un modello lineare per identificare la relazione tra i prezzi di due azioni:

\(\begin{eqnarray}
y(t) = \beta x(t) + \epsilon(t)
\end{eqnarray}\)

Dove y(t) è il prezzo del titolo AREX e x(t) è il prezzo del titolo WLL, entrambi per il giorno t.

Se tracciamo i residui ε(t) = y(t) – βx(t) (per un particolare valore di β che determineremo in seguito) si crea una nuova serie temporale che, a prima vista, sembra relativamente stazionaria. Il grafico è mostrato in Figura 3:

Fig 3 - Grafico dei Residui della Regressione Lineare tra AREX e WLL

Test di Dickey-Fuller Aumentato e Cointegrato

Al fine di confermare statisticamente se questa serie è di tipo mean-reverting, si può utilizzare uno dei metodi descritti nell’articolo precedente, vale a dire il test Dickey-Fuller aumentato o l’esponente di Hurst. Tuttavia, nessuno di questi due test può determinare β, il rapporto di coperta (Hedge Ratio) necessario per formare la combinazione lineare, quindi possono solo verificare se, per un particolare β, la combinazione lineare è stazionaria.

È qui che entra in gioco il test Cointegrated Dickey-Fuller (CADF). Questo metodo determina il rapporto di correlazione ottimale eseguendo una regressione lineare rispetto alle due serie temporali e quindi verifica la stazionarietà di una combinazione lineare.

Implementazione in Python

La verifica della relazione di cointegrazione tra AREX e WLL per il periodo dal 1 ° gennaio 2012 al 1 ° gennaio 2013 può essere effettuata facilmente sfruttando le librerie di Python. Come fonte di dati per questo esempio è sufficiente utilizzare Yahoo Finance, mentre per eseguire il test ADF, come sopra, si utilizza la libreria Statsmodels. Il primo passo è creare un nuovo file, cadf.py, e importare le librerie necessarie. Il codice utilizza NumPy, Matplotlib, Pandas e Statsmodels. Per poter etichettare correttamente gli assi e scaricare i dati da Yahoo Finance tramite panda, importiamo il modulo matplotlib.dates e il modulo pandas.io.data. Usiamo anche la funzione dei minimi quadrati ordinari (OLS) presente in pandas:
            #!/usr/bin/python
# -*- coding: utf-8 -*-

# cadf.py

import datetime
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import pandas as pd
import pandas.io.data as web
import pprint
import statsmodels.tsa.stattools as ts

from pandas.stats.api import ols
        

La prima funzione, plot_price_series, accetta un DataFrame panda come input, e due colonne identificate con stringhe “ts1” e “ts2”. Queste saranno le nostre coppie di azioni. Semplicemente la funzione traccia le due serie di prezzi sullo stesso grafico. Questo ci consente di verificare visivamente se esiste una probabile cointegrazione.

Usiamo il modulo dates di Matplotlib per ottenere i mesi dagli oggetti datetime. Quindi creiamo una figura e un insieme di assi su cui applicare l’etichettatura. Infine, stampiamo in output il grafico:

            # cadf.py

def plot_price_series(df, ts1, ts2):
    months = mdates.MonthLocator()  # every month
    fig, ax = plt.subplots()
    ax.plot(df.index, df[ts1], label=ts1)
    ax.plot(df.index, df[ts2], label=ts2)
    ax.xaxis.set_major_locator(months)
    ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))
    ax.set_xlim(datetime.datetime(2012, 1, 1), datetime.datetime(2013, 1, 1))
    ax.grid(True)
    fig.autofmt_xdate()

    plt.xlabel('Month/Year')
    plt.ylabel('Price ($)')
    plt.title('%s and %s Daily Prices' % (ts1, ts2))
    plt.legend()
    plt.show()
        

 

La seconda funzione, plot_scatter_series, traccia il grafico di dispersione dei due prezzi. Questo ci consente di verificare visivamente se esiste una relazione lineare tra le due serie e quindi se può essere un buon candidato per la procedura OLS e il successivo test ADF:

            # cadf.py

def plot_scatter_series(df, ts1, ts2):
    plt.xlabel('%s Price ($)' % ts1)
    plt.ylabel('%s Price ($)' % ts2)
    plt.title('%s and %s Price Scatterplot' % (ts1, ts2))
    plt.scatter(df[ts1], df[ts2])
    plt.show()
        

 

La terza funzione, plot_residuals, è progettata per tracciare i valori residui del modello lineare adattato delle due serie di prezzi. Questa funzione richiede che il DataFrame abbia una colonna “res”, che rappresenta i prezzi residui:

            # cadf.py

def plot_residuals(df):
    months = mdates.MonthLocator()  # every month
    fig, ax = plt.subplots()
    ax.plot(df.index, df["res"], label="Residuals")
    ax.xaxis.set_major_locator(months)
    ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))
    ax.set_xlim(datetime.datetime(2012, 1, 1), datetime.datetime(2013, 1, 1))
    ax.grid(True)
    fig.autofmt_xdate()

    plt.xlabel('Month/Year')
    plt.ylabel('Price ($)')
    plt.title('Residual Plot')
    plt.legend()

    plt.plot(df["res"])
    plt.show()
        

 

Infine, la procedura è racchiusa in una funzione __main__. Il primo compito è scaricare i dati OHLCV per AREX e WLL da Yahoo Finance. Quindi creiamo un DataFrame separato, df, utilizzando lo stesso indice del frame AREX per archiviare entrambi i valori di prezzo di chiusura corretti. Quindi tracciamo la serie di prezzi e il grafico di dispersione.

Dopo che i grafici sono completi, i residui vengono calcolati chiamando la funzione pandas ols sulle serie WLL e AREX. Questo ci consente di calcolare il rapporto β. Il rapporto di copertura viene quindi utilizzato per creare una colonna “res” tramite il calcolo della combinazione lineare di WLL e AREX.

Infine, i residui vengono tracciati e il test ADF viene eseguito sui residui calcolati. Quindi stampiamo i risultati del test ADF:

            # cadf.py

if __name__ == "__main__":
    start = datetime.datetime(2012, 1, 1)
    end = datetime.datetime(2013, 1, 1)

    arex = web.DataReader("AREX", "yahoo", start, end)
    wll = web.DataReader("WLL", "yahoo", start, end)

    df = pd.DataFrame(index=arex.index)
    df["AREX"] = arex["Adj Close"]
    df["WLL"] = wll["Adj Close"]

    # Plot the two time series
    plot_price_series(df, "AREX", "WLL")

    # Display a scatter plot of the two time series
    plot_scatter_series(df, "AREX", "WLL")

    # Calculate optimal hedge ratio "beta"
    res = ols(y=df['WLL'], x=df["AREX"])
    beta_hr = res.beta.x

    # Calculate the residuals of the linear combination
    df["res"] = df["WLL"] - beta_hr*df["AREX"]

    # Plot the residuals
    plot_residuals(df)

    # Calculate and output the CADF test on the residuals
    cadf = ts.adfuller(df["res"])
    pprint.pprint(cadf)
        

 

L’output del codice (ad esclusione dei grafici Matplotlib) è il seguente:

            (-2.9607012342275936,
 0.038730981052330332,
 0,
 249,
 {'1%': -3.4568881317725864,
  '10%': -2.5729936189738876,
  '5%': -2.8732185133016057},
 601.96849256295991)
        

Si può vedere che il test statistico di -2.96 è inferiore al valore critico del 5% di -2.87, il che significa che possiamo rifiutare l’ipotesi che non ci sia una relazione di cointegrazione al livello del 5%. Quindi possiamo concludere, con un ragionevole grado di certezza, che AREX e WLL possiedono una relazione di cointegrazione, almeno nel il campione temporale considerato.

Perchè fare il test statistico?

Fondamentalmente, per quanto riguarda il trading algoritmico, i test statistici descritti sopra sono utili per analizzare i profitti che generano quando sono applicati a strategie di trading. Quindi, ha sicuramente senso valutare semplicemente le prestazioni a livello di strategia, al contrario del livello di serie prezzo / tempo? Perché prendersi il disturbo di calcolare tutte le metriche di cui sopra quando possiamo semplicemente utilizzare l’analisi a livello di trade, le metriche di rischio/rendimento e le valutazioni del drawdown?

In primo luogo, qualsiasi strategia di trading implementata basata su una misura statistica delle serie temporali avrà un campione molto più ampio su cui lavorare, dato che quando si calcolano questi test statistici, stiamo facendo uso delle informazioni contenute in ogni candela OHLCV, invece che di ogni trade. Ci saranno molti meno trade round-trip rispetto alle barre e quindi la rilevanza statistica di qualsiasi metrica del livello di trading sarà molto più piccola.

In secondo luogo, qualsiasi strategia che implementiamo dipenderà da alcuni parametri, come i periodi di osservazione per il calcolo dell medie o le misure z-score per entrare / uscire da una operazione, in un contesto di mean-reverting. Quindi le metriche del livello di strategia sono più appropriate per questi parametri, mentre i test statistici sono validi per il campione di serie temporali sottostante.

In pratica, vogliamo calcolare entrambe le serie di statistiche. Python, tramite le librerie pandas e statsmodels, rende questo estremamente semplice. Lo sforzo aggiuntivo è in realtà piuttosto minimo!

Gli altri articoli di questa serie

Benvenuto su DataTrading!

Sono Gianluca, ingegnere software e data scientist. Sono appassionato di coding, finanza e trading. Leggi la mia storia.

Ho creato DataTrading per aiutare le altre persone ad utilizzare nuovi approcci e nuovi strumenti, ed applicarli correttamente al mondo del trading.

DataTrading vuole essere un punto di ritrovo per scambiare esperienze, opinioni ed idee.

SCRIVIMI SU TELEGRAM

Per informazioni, suggerimenti, collaborazioni...

Torna in alto
Scroll to Top