Analisi di Cointegrazione delle Serie Temporali per il Trading Mean-Reverting

Analisi delle Serie Temporali Cointegrate per il Trading Mean-Reverting

In un precedente articolo abbiamo descritto una strategia di trading basata sull’applicazione dei modelli ARIMA e GARCH delle serie temporali ai dati giornalieri dell’S&P500 e abbiamo preannunciato, così come negli altri articoli della serie sull’analisi delle serie temporali, l’uso di questi modelli nelle strategie di trading mean-reverting e come costruirli.

In questo articolo introduciamo un tema chiamato cointegrazione, un concetto delle serie temporale che consente di determinare se possiamo formare una coppia di asset  mean-reverting, cioè di ritorno medio. Descriviamo la teoria delle serie temporali relativa alla cointegrazione e successivamente vediamo come applicarla a reali strategie di trading utilizzando il nostro framework di backtesting open source: DataTrader.

Procediamo descrivendo il ritorno alla media nel tradizionale quadro del “pairs-trading”.  Inoltre introduciamo il concetto di stazionarietà di una combinazione lineare di asset, arrivando infine a definire la cointegrazione e ai test di radice unitaria. Dopo aver delineato questi test, simuliamo varie serie temporali con Python e applichiamo i test per valutare la cointegrazione.

Strategie di trading di reversione media

L’idea base di un mean-reverting “pairs-trading” è quella di aprire contemporaneamente posizioni long e short su due diversi asset. Questi due asset devono condividere fattori sottostanti che influenzano i loro movimenti. Un esempio dal mondo azionario potrebbe essere long su McDonald’s (NYSE:MCD) e short su Burger King (NYSE:BKW – prima della fusione con Tim Horton’s).

La logica di questo approccio consiste nell’elevata probabilità che i prezzi dei due asset tornano all’equilibrio nel lungo periodo a causa degli ampi fattori di mercato che influenzano la produzione e il consumo di hamburger. Un disequilibrio a breve termine di un asset della coppia, come un’interruzione della catena di approvvigionamento che colpisce esclusivamente McDonald’s, porterebbe a una temporanea dislocazione dei loro prezzi relativi. Un’operazione long-short effettuata in questo punto di disequilibrio potrebbe diventare redditizia poiché i due asset tornano al loro valore di equilibrio una volta risolta l’interruzione. Questa è l’essenza del classico “pairs-trading”.

Come quants, siamo interessati a fare trading di mean-reverting non solo su una coppia di azioni, ma anche su panieri di azioni che sono separatamente interconnesse.

Per raggiungere questo obiettivo abbiamo bisogno di un solido quadro matematico per identificare coppie o panieri di azioni che rientrano nel comportamento sopra descritto. È qui che nasce il concetto di serie temporali cointegrate.

L’idea è quella di considerare una coppia di serie temporali non stazionarie, come le attività simili alla camminata casuale di MCD e BKW, e formare una combinazione lineare di ciascuna serie per produrre una serie stazionaria, che abbia una media e una varianza fisse.

Questa serie stazionaria può avere interruzioni a breve termine in cui il valore si allontana dalla media, ma a causa della sua stazionarietà questo valore alla fine tornerà alla media. Le strategie di trading possono farne uso allungando/cortocircuitando la coppia nel punto di interruzione appropriato e scommettendo su un ritorno a lungo termine della serie alla sua media.

Le strategie di mean-reverting consentono di creare serie temporali stazionarie “sintetiche” per un’ampia gamma di strumenti. Non siamo certamente limitati alle azioni “vanilla”. Ad esempio, possiamo utilizzare gli Exchange Traded Fund (ETF) che tracciano i prezzi delle materie prime, come il petrolio greggio, e i panieri delle società produttrici di petrolio. Ci sono molte opportuna per per identificare tali sistemi di mean-reverting.

Prima di approfondire gli effettivi meccanismi delle strategie di trading, che sono oggetto di articoli successivi, dobbiamo capire come identificare statisticamente le serie cointegrate. A tale scopo usiamo le tecniche dell’analisi delle serie temporali, continuando l’uso del linguaggio Python come nei precedenti articoli sull’argomento.

Cointegrazione

Dopo aver motivato la necessità di un framework quantitativo per effettuare il mean reversion trading, possiamo definire il concetto di cointegrazione. Consideriamo una coppia di serie temporali, entrambe non stazionarie, allora esiste una particolare combinazione lineare di queste due serie che può portare ad una serie stazionaria. Tale coppia di serie è definita cointegrata .

La definizione matematica è data da:

Cointegrazione
Date  due serie temporali non stazionarie, con \(a, b \in \mathbb\(\{x_t\}\) e \(\{y_t\}\){R}\) costanti. Se la serie combinata \(a x_t + b y_t\) è stazionaria, allora \(\{x_t\}\) e \(\{y_t\}\) sono cointegrati.

 
Sebbene la definizione sia utile, non ci fornisce direttamente un meccanismo per determinare i valori di a e b, e verificare se la combinazione sia statisticamente stazionaria. Per quest’ultima verifica dobbiamo utilizzare i test per le radici unitarie.

Test di radice unitaria

Nel precedente articolo sui modelli autoregressivi AR(p) abbiamo descritto il ruolo dell’equazione caratteristica. Abbiamo notato che si trattava semplicemente di un modello autoregressivo impostato a zero, scritto in forma di spostamento all’indietro. Risolvere questa equazione fornisce un insieme di radici .

Affinché il modello sia considerato stazionario, tutte le radici dell’equazione devono superare l’unità. Un modello AR(p) con una radice uguale ad una unità – una radice unitaria – non è stazionario. Le passeggiate casuali sono processi AR(1) con radici unitarie e quindi sono non stazionarie.

Pertanto, al fine di rilevare se una serie temporale è stazionaria o meno, possiamo costruire un test di ipotesi statistica per la presenza di una radice unitaria in un campione di serie temporali.

Consideriamo tre test separati per le radici unitarie: Augmented Dickey-Fuller (AFD), Phillips-Perron e Phillips-Ouliaris. Vediamo che si basano su ipotesi diverse, ma tutti e tre effettuato verifiche per lo stesso problema, cioè la stazionarietà del campione di serie temporali.

Descriviamo brevemente gli approcci di questi metodi.

Test di Dickey-Fuller aumentato

Dickey e Fuller [2] hanno introdotto il seguente test per la presenza di una radice unitaria. Il test originale considera una serie temporale \(z_t = \alpha z_{t-1} + w_t\), dove \(w_t\) è un rumore bianco discreto. L’ipotesi nulla è \(\alpha = 1\), mentre l’ipotesi alternativa è \(\alpha < 1\).

Said e Dickey [6] hanno migliorato il test originale Dickey-Fuller arrivando alla definizione del test Augmented Dickey-Fuller (ADF), in cui la serie \(z_t\) viene modificato in un modello AR(p) a partire da un modello AR(1). abbiamo descritto questo test in un articolo dove abbiamo usato Python per calcolare il test ADF. In questo articolo effettuiamo lo stesso test.

Test di Phillips-Perron

Il test ADF presuppone un modello AR(p) come approssimazione per il campione di serie temporali e lo utilizza per considerare le autocorrelazioni di ordine superiore. Il test di Phillips-Perron [5] non prevede un’approssimazione del modello AR(p). In questo caso un metodo non parametrico di smoothing del kernel viene applicato sul processo stazionario \(w_t\), che gli consente di tenere conto dell’autocorrelazione e dell’eteroscedasticità non specificate.

Phillips-Ouliaris Test

The Phillips-Ouliaris test[4] is different from the previous two tests in that it is testing for evidence of cointegration among the residuals between two time series. The main idea here is that tests such as ADF, when applied to the estimated cointegrating residuals, do not have the Dickey-Fuller distributions under the null hypothesis where cointegration isn’t present. Instead, these distributions are known as Phillips-Ouliaris distributions and hence this test is more appropriate.

Difficoltà con gli Unit Root Test

Nonostante il test ADF e Phillips-Perron sono asintoticamente equivalenti, possono produrre risposte molto diverse su campioni finiti [7] . Questo si verifica perché gestiscono l’autocorrelazione e l’eteroscedasticità in modo diverso. È necessario avere molto chiari quali ipotesi si vogliono verificate quando si applicano questi test e non applicarli semplicemente alla cieca su serie arbitrarie.

Inoltre, i test di radice unitaria non sono adattati a distinguere processi stazionari altamente persistenti da processi non stazionari. Bisogna stare molto attenti quando si usano questi test su determinate forme di serie temporali finanziarie. Ciò può essere particolarmente problematico quando la relazione sottostante che è stata modellata (ad es, il ritorno alla media di due coppie simili) si interrompe naturalmente a causa di un cambio di regime o di altri cambiamenti strutturali nei mercati finanziari.

Simulazione di serie temporali cointegrate con Python

Applichiamo ora i precedenti unit root test ad alcuni dati simulati che sappiamo essere cointegrati. Possiamo utilizzare la definizione di cointegrazione per creare artificialmente due serie temporali non stazionarie che condividono una tendenza stocastica sottostante, ma con una combinazione lineare stazionaria.
Il nostro primo compito è definire una passeggiata casuale \(z_t = z_{t-1} + w_t\), dove \(w_t\) è un rumore bianco discreto.
Per rispolverare questi concetti si può dare un’occhiata al precedente articolo sul rumore bianco e le passeggiate casuali

Con la passeggiata casuale \(z_t\) creiamo due nuove serie temporali \(x_t\) e \(y_t\) che condividono lo stesso sottostante trend stocastico di \(z_t\), anche se in quantità diverse:

\(\begin{eqnarray}x_t &=& p z_t + w_{x,t} \\
y_t &=& q z_t + w_{y,t}\end{eqnarray}\)

Se consideriamo una combinazione lineare \(a x_t + b y_t\), otteniamo:

\(\begin{eqnarray}a x_t + b y_t &=& a (p z_t + w_{x,t}) + b (q z_t + w_{y,t}) \\
&=& (ap + bq) z_t + a w_{x,t} + b w_{y,t}\end{eqnarray}\)

Da notare che otteniamo una serie stazionaria (che è una combinazione di termini di rumore bianco) nel caso \(ap + bq = 0\). Possiamo valorizzare numericamente le variabili per renderlo più concreto, ad esempio supponiamo \(p=0.3\) e \(q=0.6\). Dopo qualche semplice operazione algebrica otteniamo \(ap + bq = 0\) per \(a=2\) e \(b=-1\), portando a una combinazione di serie stazionaria. Quindi \(x_t\) e \(y_t\) sono cointegrati quando \(a=2\) e \(b=-1\).

Vediamo ora come simulare queste serie temporali con Python e visualizzare la combinazione stazionaria. In primo luogo, desideriamo creare e tracciare la serie di passeggiate casuali sottostante, \(z_t\):

				
					import numpy as np

np.random.seed(1)
steps = np.random.standard_normal(1000)
steps[0] = 0
random_walk = np.cumsum(steps)

import matplotlib.pyplot as plt
plt.figure(figsize=[10, 7.5]); # Dimensioni del grafico
plt.plot(random_walk)
plt.title("Simulated Random Walk")
plt.show()

				
			
trading-quantitativo-cointegrated-time-series-random-walks

Se tracciamo il correlogramma della serie e quello delle sue differenze possiamo vedere poche evidenze di autocorrelazione:

				
					from statsmodels.graphics.tsaplots import plot_acf

plot_acf(random_walk, alpha=0.05, lags=30)
plot_acf(np.diff(random_walk), alpha=0.05, lags=30)
plt.show()

				
			
trading-quantitativo-cointegrated-time-series-random-walks-acf
Da quanto sopra questa realizzazione di \(z_t\) sembra chiaramente una passeggiata casuale. Il prossimo passo è creare \(x_t\) e \(y_t\) a partire da \(z_t\), usando \(p=0.3\) e \(q=0.6\), quindi tracciare i grafici di entrambe le serie:
				
					p=0.3
q=0.6

x_t = p*random_walk + np.random.standard_normal(1000)
y_t = q*random_walk + np.random.standard_normal(1000)

fig, axs = plt.subplots(2) # Definezione di due grafici
axs[0].plot(x_t)
axs[0].set(ylabel='x_t')
axs[1].plot(y_t)
axs[1].set(ylabel='y_t')

plt.show()

				
			
trading-quantitativo-cointegrated-time-series-x_t_y_t

Possiamo notare come grafici sono molto simili. Ovviamente lo sono per definizione: condividono la stessa struttura sottostrante di camminata casuale di \(z_t\).
Calcoliamo ora la combinazione lineare usando \(p=2\) e \(q=-1\) ed esaminiamo la struttura di autocorrelazione:

				
					
comb = 2*x_t - y_t

fig, axs = plt.subplots(2) # Definezione di due grafici
axs[0].plot(comb)
axs[0].set(ylabel='comb')
plot_acf(comb, alpha=0.05, lags=30, ax=axs[1])
axs[1].set(ylabel='acf')

plt.show()
				
			
trading-quantitativo-cointegrated-time-series-comb-lin-x-y

È evidente che la serie combinata comb assomiglia molto a una serie stazionaria. Questo era prevedibile dalla sua definizione.

Proviamo ad applicare i tre test della radice unitaria alla serie di combinazioni lineari. In primo luogo, il test Augmented Dickey-Fuller:

				
					from arch.unitroot import ADF

adf = ADF(comb)
print(adf.summary())
				
			
				
					Augmented Dickey-Fuller Results   
=====================================
Test Statistic                -31.547
P-value                         0.000
Lags                                0
-------------------------------------

Trend: Constant
Critical Values: -3.44 (1%), -2.86 (5%), -2.57 (10%)
Null Hypothesis: The process contains a unit root.
Alternative Hypothesis: The process is weakly stationary.
				
			

Il P-value è piccolo e quindi abbiamo prove per rifiutare l’ipotesi nulla che la serie possieda una radice unitaria. 

Proviamo il test di Phillips-Perron:

				
					from arch.unitroot import PhillipsPerron

php = PhillipsPerron(comb)
print(php.summary())
				
			
				
					Phillips-Perron Test (Z-tau)    
=====================================
Test Statistic                -31.584
P-value                         0.000
Lags                               22
-------------------------------------

Trend: Constant
Critical Values: -3.97 (1%), -3.41 (5%), -3.13 (10%)
Null Hypothesis: The process contains a unit root.
Alternative Hypothesis: The process is weakly stationary.
				
			

Anche in questo caso  abbiamo un piccolo P-value quindi abbiamo prove per rifiutare l’ipotesi nulla di una radice unitaria.

Infine, proviamo il test di Phillips-Ouliaris (questo test richiede in input la matrice dei costituenti della serie sottostante):

				
					from arch.unitroot.cointegration import phillips_ouliaris

po = phillips_ouliaris(2*x_t, -1*y_t)
print(po.summary())
				
			
				
					Phillips-Ouliaris Zt Cointegration Test
=====================================
Test Statistic                -31.129
P-value                         0.000
Kernel                       Bartlett
Bandwidth                       8.186
-------------------------------------

Trend: Constant
Critical Values: -3.51 (10%), -3.79 (5%), -4.35 (1%)
Null Hypothesis: No Cointegration
Alternative Hypothesis: Cointegration
Distribution Order: 4
				
			

Ancora una volta vediamo un P-vaule piccolo e quindi possiamo rifiutare l’ipotesi nulla. E’ sufficiente per dimostrare che abbiamo una coppia di serie cointegrate.

Prossimi passi

In questo articolo abbiamo esaminato alcuni test di radice unitaria per valutare se una combinazione lineare di serie temporali sia stazionaria, cioè se le due serie siano cointegrate.

Negli articoli futuri prenderemo in considerazione l’implementazione completa delle strategie di trading di ritorno alla media per i dati giornalieri di azioni ed ETF utilizzando DataTrader sulla base di questi test di cointegrazione.

Inoltre estenderemo la nostra analisi alla cointegrazione a più di due asset, ottenendo strategie di trading che sfruttano portafogli cointegrati.

Torna in alto
Scroll to Top