Modelli di Markov nascosti per determinare il Regime di Mercato

Modelli di Markov nascosti per determinare il Regime di Mercato

Sommario

Nell’articolo precedente della serie abbiamo introdotto i Hidden Markov Models. Sono stati descritti nel contesto della più ampia classe dei modelli di Markov. Questi modelli sono usati dai trader quantitativi per determinare i regimi di mercato in modo da adattare la gestione delle loro strategie quantitative.

In particolare è stato menzionato che “la variazione dei regimi  causano aggiustamenti dei ritorni degli asset tramite la variazione delle loro medie, varianze/volatilità, correlazioni seriali e covarianze, influenzando l’efficacia dei metodi di analisi delle serie temporali che si basano sulla stazionarietà“. Questo ha un significativo impatto nel modo in cui le strategie di trading subiscono variazioni durante il loro ciclo di vita.

In questo articolo descriviamo come implementare i modelli di Markov Nascosti tramite il linguaggio di programmazione Python. L’interfaccia Python più semplice per i modelli markov nascosti è la libreiria hmmlearn. Descriviamo come usare i HMM per analizzare  i vari regimi in cui si trovano i mercati azionari statunitensi. Negli articoli successivi descriviamo come usare queste analisi di regime in una sottoclasse del modulo RiskManager di DataTrader al fine di regolare i segnali di trading per alcune strategie.

All’interno dell’articolo effettuiamo una simulazione di una serie di rendimenti del mercato in due diversi regimi – “rialzista” e “ribassista”. Quindi applichiamo un modello di Markov alla serie dei ritorni per identificare la probabilità di trovarsi in un particolare regime di mercato.

Dopo aver delineato la procedura sui dati simulati, applichiamo il modello Hidden Markov ai dati azionari statunitensi al fine di determinare i regimi a due stati sottostanti.

Regimi di mercato

Applicare i Hidden Markov Models al rilevamento del regime è complicato poiché si tratta in realtà di una forma di apprendimento non supervisionato. Cioè, non esiste una “verità di base” o dati etichettati su cui “addestrare” il modello. In particolare non è chiaro quanti stati di regime esistano a priori. Esistono due, tre, quattro o più “veri” regimi di mercato nascosti?

Le risposte a queste domande dipendono fortemente dalla classe di asset che si vuole analizzare, dalla scelta del periodo di tempo e dalla natura dei dati utilizzati. Ad esempio, i dati sui rendimenti giornalieri nei mercati azionari mostrano spesso periodi di calma e volatilità più bassa, anche per un certo numero di anni, con periodi eccezionali di elevata volatilità in momenti di “panico” o di “correzione”. È quindi sufficiente modellare gli indici azionari con due stati? Potrebbe esserci un terzo stato intermedio che rappresenta più volatilità del solito ma non è un vero e proprio panico?

L’utilizzo di Hidden Markov Models come componente di un gestore del rischio che può interferire con gli ordini generati dalla strategia richiede un’analisi di ricerca attenta e una solida comprensione delle classi di asset modellati. Nei prossimi articoli le performance di varie strategie di trading sono analizzate per diversi gestori del rischio basati sul modello Hidden Markov.

Dati simulati

In questa sezione vediamo come generare dati simulati dei rendimenti a partire da distribuzioni gaussiane separate, ciascuna delle quali rappresenta il regime di mercato “rialzista” o “ribassista”. I rendimenti rialzisti attingono da una distribuzione guassiana con media positiva e varianza bassa, mentre i rendimenti ribassisti attingono da una distribuzione gaussiana con media leggermente negativa ma varianza più elevata.

Cinque periodi di regime di mercato separati sono simulati e “cuciti” insieme con Python. Il flusso di rendimenti complessivo è quindi usato in un modello di Markov nascosto per dedurre le probabilità a posteriori degli stati di regime, a partire la sequenza di osservazioni.

Il primo compito è installare le librerie yfinance e hmmlearn e quindi importarle in Python. Impostiamo anche il seed casuale in modo da consentire una replica coerente dei risultati:

				
					import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import yfinance
import hmmlearn

np.random.seed(1)
				
			

In questo esempio simuliamo un mercato con due regimi, assumendo che i rendimenti di mercato siano normalmente distribuiti.  Simuliamo regimi separati, ciascuno composto da \(N_k\) giorni di  rendimenti. Ognuno di questi k regimi può essere rialzista o ribassista. L’obiettivo del modello Hidden Markov è identificare quando il regime passa da rialzista a ribassista e viceversa.

Consideriamo \(k=5\) e \(N_k \in [50, 150]\). Il mercato rialzista è distribuito come \(\mathcal{N}(0.1, 0.1)\) mentre il mercato ribassista è distribuito come \(\mathcal{N}(-0.05, 0.2)\). I parametri sono impostati tramite il seguente codice:

				
					# Create the parameters for the bull and bear market returns distributions
Nk_lower = 50
Nk_upper = 150
bull_mean = 0.1
bull_var = 0.1
bear_mean = -0.05
bear_var = 0.2
				
			
I valori \(N_k\) sono scelti casualmente:
				
					# Create the list of durations (in days) for each regime
days = np.random.randint(Nk_lower, Nk_upper, 5)
				
			
I rendimenti per ciascuno k-esimo periodo sono estratti casualmente:
				
					# Create the various bull and bear markets returns
market_bull_1 = np.random.normal(loc=bull_mean, scale=bull_var, size=days[1])
market_bear_2 = np.random.normal(loc=bear_mean, scale=bear_var, size=days[2])
market_bull_3 = np.random.normal(loc=bull_mean, scale=bull_var, size=days[3])
market_bear_4 = np.random.normal(loc=bear_mean, scale=bear_var, size=days[4])
market_bull_5 = np.random.normal(loc=bull_mean, scale=bull_var, size=days[5])
				
			
Il codice Python per creare gli stati del regime (1 per rialzista o 2 per ribassista) e l’elenco finale dei rendimenti è il seguente:
				
					# Create the list of true regime states and full returns list
true_regimes=np.r_[np.repeat(1, days[0]), np.repeat(2,days[1]), np.repeat(1, days[2]), np.repeat(2,days[3]), np.repeat(1,days[4])]

returns = np.r_[market_bull_1, market_bear_2, market_bull_3, market_bear_4, market_bull_5]
				
			
Il grafico dei rendimenti mostra chiaramente le variazioni nella media e nella varianza tra i cambi di regime:
trading-quantitativo-hmm-returns
In questa fase è possibile specificare e adattare il modello di Markov nascosto utilizzando l’algoritmo di massimizzazione delle aspettative, i cui dettagli esulano dallo scopo di questo articolo. A tale scopo usiamo la funzione GaussianHMM della libreria hmmlearn, impostando il numero di stati pari a 2:
				
					from hmmlearn.hmm import GaussianHMM
rets = np.column_stack([returns])
# Create the Gaussian Hidden markov Model and fit it
# to the returns data, outputting a score
hmm_model = GaussianHMM(
    n_components=2, covariance_type="full", n_iter=1000
).fit(rets)
				
			
Dopo il fitting del modello è possibile tracciare il grafico delle probabilità a posteriori di trovarsi in un particolare stato di regime. La variabile post_probs contiene le probabilità a posteriori. Queste probabilità sono confrontati con i veri stati sottostanti. Da notare che il modello Hidden Markov fa un buon lavoro nell’identificare correttamente i regimi, anche se con un certo ritardo:
				
					fig, axs = plt.subplots(2) # Definezione di due grafici
axs[0].plot(hidden_states)
axs[0].set(ylabel='Regimes')
axs[1].plot(post_prob, label=['Bull','Bear'])
axs[1].set(ylabel='Probability')
axs[1].legend()
plt.show()
				
			
trading-quantitativo-hmm-simulate
Vediamo ora come applicare il modello Hidden Markov a dati finanziari storici del mondo reale.

Dati finanziari

Nella sezione precedente è stato semplice nascosto determinare i regimi con il modello di Markov dato che gli stati erano simulati da un insieme pre-specificato di gaussiane. Come affermato in precedenza, il problema del Regime Detection è in realtà una sfida di apprendimento non supervisionato poiché il numero di stati non è noto a priori, cioè non esiste nessuna “verità di base” con cui “addestrare” l’HMM. In questa sezione effettuiamo due attività di modellazione separate. La prima esegue il fitting dell’HMM con due stati di regime ai rendimenti dell’S&P500, mentre la seconda utilizza tre stati. Quindi confrontiamo i risultati tra i due modelli. In questo esempio, il processo di applicazione del modello Hidden Markov è simile a quello eseguito per i dati simulati. Invece di generare il flusso dei rendimenti da due distribuzioni gaussiane, scarichiamo i dati storici reali utilizzando la libreria yfinance:
				
					import matplotlib.pyplot as plt
import yfinance

# Obtain S&P500 data from 2004 and
# create the returns stream from this
data = yfinance.download("^GSPC", start="2004-01-01", end="2017-11-01")
data = data.asfreq('b').fillna(method='ffill')

Return = data['Adj Close'].pct_change()
LogRet = np.log(data['Adj Close']).diff().dropna()

plt.plot(LogRet)
plt.show()
				
			
trading-quantitativo-hmm-gspcrets
Ora applichiamo un modello di Markov nascosto a due stati utilizzando l’algoritmo EM. Visualizziamo quindi i grafici dei rendimenti e le probabilità a posteriori di ciascun regime:
				
					from hmmlearn.hmm import GaussianHMM
rets = np.column_stack([LogRet])
# Create the Gaussian Hidden markov Model and fit it to the returns data
hmm_model = GaussianHMM(
    n_components=2, covariance_type="full", n_iter=1000
).fit(rets)
post_prob = hmm_model.predict_proba(rets)

fig, axs = plt.subplots(2) # Definezione di due grafici
axs[0].plot(rets)
axs[0].set(ylabel='Returns')
axs[1].plot(post_prob, label=['Regime 1','Regime 2'])
axs[1].set(ylabel='Probability')
axs[1].legend()
plt.show()
				
			
trading-quantitativo-hmm-market-regime-two-state

Possiamo vedere che dal 2004 al 2007 i mercati erano più calmi e quindi il modello Hidden Markov ha dato un’elevata probabilità a posteriori di regime n. 1 per questo periodo. Tuttavia, tra il 2007 e il 2009 i mercati sono stati incredibilmente volatili a causa della crisi dei mutui subprime. Questo causa inizialmente un rapido cambio delle probabilità a posteriori tra i due stati, ma resta comunque abbastanza coerentemente nel regime n. 2 durante tutto il 2008.

I mercati sono diventati più calmi nel 2010, ma nel 2011 si è verificata un’ulteriore volatilità, portando ancora una volta l’HMM a dare un’elevata probabilità a posteriori per il regime 2. Dopo il 2011 i mercati sono tornati a calmarsi e l’HMM restituisce costantemente alte probabilità per il regime 1. Nel 2015 i mercati sono diventati ancora una volta più instabili e ciò si riflette nell’HMM con una maggiore frequenza di passaggi da un regime all’altro.

Lo stesso processo verrà ora eseguito per un HMM a tre stati. ci sono poche modifiche da fare nel codice, ad eccezione della modifica del parametro n_components=3 e della della legenda del grafico:

				
					hmm_model = GaussianHMM(
    n_components=3, covariance_type="full", n_iter=1000
).fit(rets)
post_prob = hmm_model.predict_proba(rets, None)

fig, axs = plt.subplots(2) # Definezione di due grafici
axs[0].plot(rets)
axs[0].set(ylabel='Returns')
axs[1].plot(post_prob, '--', label=['Regime 1','Regime 2', 'Regime 3'])
axs[1].set(ylabel='Probability')
axs[1].legend()
plt.show()
				
			
trading-quantitativo-hmm-market-regime-three-state

La lunghezza dei dati rende il grafico delle probabilità a posteriori un po’ più complicato da interpretare. Dato che il modello è costretto a considerare tre regimi separati, otteniamo a un comportamento di frequente passaggio tra il Regime #1 e il Regime #2 nel periodo più calmo dal 2004 al 2007. Tuttavia, nei periodi volatili del 2008, 2010 e 2011, il regime n. 3 domina la probabilità a posteriori indicando uno stato altamente volatile. Dopo il 2011 il modello torna al passaggio tra il Regime #1 e il Regime #2.

È chiaro che la scelta del numero iniziale di stati da applicare a un flusso di rendimenti reali è un elemento critico. Dipende dalla tipologia di asset utilizzata, da come viene eseguita la negoziazione di questo asset e dal periodo temporale considerato.

Prossimi passi

Negli articoli successivi descriviamo come utilizzare il modello di Markov nascosto sarà utilizzato in una sottoclasse di RiskManager nel motore di backtesting e trading live di DataTrader. In particolare vediamo come determinare quando applicare una strategia trend following nel tentativo di migliorare la redditività in caso di assenza di gestione del rischio.

SCRIVIMI SU TELEGRAM

Per informazioni, suggerimenti, collaborazioni...

Se è la prima volta che atterri su DataTrading, BENVENUTO!

Lascia che mi presenti. Sono Gianluca, ingegnere, trekker e 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.

TUTORIAL

TOOLBOX

Gli altri articoli di questa serie

Torna su