Simulazione Monte Carlo con Python

Simulazione Monte Carlo con Python

Sommario

In questo articolo descriviamo come effettuare ed implementare una Simulazione Monte Carlo con Python. Wikipedia afferma che “i metodi Monte Carlo (o esperimenti Monte Carlo) sono un’ampia classe di algoritmi computazionali che si basano su ripetuti campionamenti casuali per ottenere risultati numerici. Si basa sull’idea di usare la casualità per risolvere problemi che in linea di principio potrebbero essere deterministici. I metodi Monte CarloSono spesso utilizzati in problemi fisici e matematici e sono particolarmente utili quando è difficile o impossibile utilizzare altri approcci. I metodi Monte Carlo sono principalmente  usati in tre distinte classi di problemi: ottimizzazione, integrazione numerica e generazione di estrazioni da una distribuzione di probabilità.

Il metodo Monte Carlo

Nella finanza possiamo usare una simulazione Monte Carlo per esaminare la potenziale evoluzione dei prezzi degli asset nel tempo, supponendo che siano soggetti a rendimenti giornalieri che seguono una distribuzione normale. Come descritto in un articolo precedente, i rendimenti dei prezzi degli asset di solito seguono una distribuzione più leptokurtica (coda grassa) rispetto a una distribuzione normale, ma nella simulazione Monte Carlo si considera spesso una distribuzione normale. Questo tipo di evoluzione dei prezzi è anche noto come “camminata casuale”.

Ad esempio se vogliamo acquistare uno specifico titolo azionario potrebbe essere utile guardare al futuro e tentare di prevedere che tipo di rendimenti possiamo aspettarci e con quale probabilità, oppure potrebbe essere interessante indagare quali potenziali risultati estremi potremmo ottenere e la potenziale esposizione al rischio di rovina o, al contrario, a rendimenti superiori.

 

Per impostare la simulazione, dobbiamo stimare i livelli attesi di rendimento (mu) e di volatilità (vol)  per il titolo in analisi. Questi dati possono essere stimati a partire dai prezzi storici tramite semplici metodi, ipotizzando che il rendimento medio e i livelli di volatilità del passato continueranno anche nel futuro. Possiamo anche adattare i dati storici per tenere conto delle opinioni degli investitori o dei cambiamenti del regime di mercato, ecc. Tuttavia, per mantenere le cose semplici e concentrarsi sul codice, impostiamo semplici livelli di rendimento e volatilità basati sui dati storici dei prezzi.

Simulazione singola

Iniziamo generando i dati iniziali di cui abbiamo bisogno come input per la nostra simulazione Monte Carlo. A scopo illustrativo diamo un’occhiata al titolo della Apple Inc….non molto originale, ma tant’è. 

				
					import numpy as np
from math import sqrt
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style('whitegrid')
from scipy.stats import norm
import yfinance as yf

# Scarica i dati dei prezzi di Apple in un dataframe
apple = yf.download('AAPL', start='2000-01-01', end='2020-01-01')

# Calcolo del CARG che è una stima dei redimenti medi (mu)
days = (apple.index[-1] - apple.index[0]).days
cagr = ((((apple['Adj Close'][-1]) / apple['Adj Close'][1])) ** (365.0 / days)) - 1
print('CAGR =', str(round(cagr, 4) * 100) + "%")

# crea una serie di rendimenti percentuali e calcola la volatilità annuale dei rendimenti
apple['Returns'] = apple['Adj Close'].pct_change()
volatility = apple['Returns'].std() * sqrt(252)
print("Annual Volatility =", str(round(volatility, 4) * 100) + "%")

				
			

Otteniamo un rendimento medio di 25,37% e una volatilità (vol) di 40,31% come paramentri per il metodo Monte Carlo.

				
					CAGR = 25.37%
Annual Volatility = 40.31%
				
			

Il codice per eseguire la simulazione Monte Carlo è il seguente:

				
					np.random.seed(1)
# Definizione delle Variabili
S = apple['Adj Close'][-1] # prezzo iniziale dell'asset (es. l'ultimo prezzo reale)
T = 252 # Numero di giorni di trading
mu = cagr # Redimenti medi
vol = volatility # Volatilità

# crea una lista di rendimenti giornalieri usando una distrubuzione normale casuale
daily_returns=np.random.normal((mu/T),vol/sqrt(T),T)+1

# imposta il prezzo iniziale e care una serie di prezzi a partire dai rendimenti giornalieri casuali
price_list = [S]
for x in daily_returns:
    price_list.append(price_list[-1]*x)

# Crea i gradici della serie dei prezzi e dell'istogramma dei rendimenti giornalieri
plt.plot(price_list)
plt.show()
plt.hist(daily_returns-1, 100)
plt.show()

				
			

Otteniamo i seguenti grafici

Simulazione Monte Carlo con Python
Simulazione Monte Carlo con Python

Il codice precedente esegue la simulazione di un anno di trading (252 giorni) di una singola possibile evoluzione della serie di prezzi, basata su un’estrazione di rendimenti giornalieri casuali normalmente distribuiti. La simulazione è rappresentata dalla serie a linea singola mostrata nel primo grafico. Il secondo grafico traccia un istogramma di quei rendimenti giornalieri casuali nel corso dell’anno.

Siamo riusciti a simulare con successo un anno di dati futuri per i prezzi giornalieri. Molto interessante ma in realtà non offre molte informazioni sulle caratteristiche di rischio e rendimento del titolo poiché abbiamo generato solo un percorso  casuale. La probabilità che il prezzo si evolva in futuro esattamente come  descritto dai grafici precedenti è praticamente pari a zero.

Simulazioni Multiple

Le vere informazioni si ottengo eseguendo migliaia, decine di migliaia o addirittura centinaia di migliaia di simulazioni, dove ogni simulazione produce una diversa possibile evoluzione del prezzo basata sulle stesse caratteristiche del titolo (mu e vol).

Possiamo semplicemente adattare il codice precedente per eseguire più simulazioni. Nel codice seguente notiamo un paio di cose: in primo luogo abbiamo rimosso il grafico dell’istogramma, inoltre prevediamo la visualizzazione di più serie di prezzi sullo stesso grafico in modo da mostrare le informazioni di ogni simulazione.

				
					

# Definizione delle Variabili
S = apple['Adj Close'][-1] # prezzo iniziale dell'asset (es. l'ultimo prezzo reale)
T = 252 # Numero di giorni di trading
mu = cagr # Redimenti medi
vol = volatility # Volatilità

# Specifica il numero di simulazioni
num_sims = 1000
for i in range(num_sims):
    # crea una lista di rendimenti giornalieri usando una distrubuzione normale casuale
    daily_returns = np.random.normal(mu / T, vol / sqrt(T), T) + 1

    # imposta il prezzo iniziale e care una serie di prezzi a partire dai rendimenti giornalieri casuali
    price_list = [S]

    for x in daily_returns:
        price_list.append(price_list[-1] * x)
    # grafico dei dati di un ogni singola simulazione che saranno visualizzate al termine
    plt.plot(price_list)

# grafico multiple delle serie dei prezzi 
plt.show()
				
			

Otteniamo il grafico di 1000 serie di prezzi simulati

Analisi-Dati-MonteCarlo-AAPL-1000-Simulazioni

Possiamo vedere i risultati generati da 1000 diverse simulazioni, tutte basate sugli stessi parametri iniziali, tenendo conto della casualità della serie dei rendimenti giornalieri. Notiamo che lo spread dei prezzi finali è piuttosto ampio, da circa 45$ a 500$!

In questo formato, un il grafico pieno di dati può essere difficile da interpretare e vedere chiaramente cosa sta succedendo, quindi è utile produrre anche l’istogramma che mostra la distribuzione dei valori finali della simulazione, invece che la distribuzione dei rendimenti giornalieri per una singola simulazione.

In questo esempio abbiamo simulato 10.000 serie di prezzi in modo da avere più dati.

				
					

# lista vuota per i valori finali di ogni simulazione
result = []

# Definizione delle Variabili
S = apple['Adj Close'][-1] # prezzo iniziale dell'asset (es. l'ultimo prezzo reale)
T = 252 # Numero di giorni di trading
mu = cagr # Redimenti medi
vol = volatility # Volatilità


# Specifica il numero di simulazioni
num_sims = 1000
for i in range(num_sims):
    # crea una lista di rendimenti giornalieri usando una distrubuzione normale casuale
    daily_returns = np.random.normal(mu / T, vol / sqrt(T), T) + 1

    # imposta il prezzo iniziale e care una serie di prezzi a partire dai rendimenti giornalieri casuali
    price_list = [S]

    for x in daily_returns:
        price_list.append(price_list[-1] * x)
    # grafico dei dati di un ogni singola simulazione che saranno visualizzate al termine
    plt.plot(price_list)

    # aggiunta del valore finale di ogni simulazione alla lista dei risultati
    result.append(price_list[-1])

# visualizzazione del grafico delle serie dei prezzi
plt.show()
# creazione dell'istrogramma dei prezzi finali di ogni simulazione
plt.hist(result, bins=50)
plt.show()
				
			

Otteniamo i seguenti grafici.

Analisi-Dati-MonteCarlo-AAPL-10000-Simulazioni
Analisi-Dati-MonteCarlo-AAPL-10000-Simulazioni-Istogramma

Possiamo quindi calcolare la media della distribuzione per ottenere il nostro “valore atteso”:

				
					# uso della funzione mean di numpy per calcolare la media dei risultati
print(round(np.mean(result),2))
				
			

La funzione restituisce un valore pari a 91,61. Ovviamente otterrai un risultato leggermente diverso dato che abbiamo simulazioni di prezzi giornalieri casuali. Più percorsi o “corse” includiamo in ogni simulazione, più il valore medio tende verso il rendimento medio che abbiamo utilizzato come input “mu”. Questo è il risultato della legge dei grandi numeri.

Possiamo anche dare un’occhiata a un paio di “quantili” della potenziale distribuzione dei prezzi, per avere un’idea della probabilità di rendimenti molto alti o molto bassi.

Usiamo semplicemente la funzione “percentile” numpy per calcolare i quantili del 5% e del 95%.

				
					print("5% quantile =",np.percentile(result,5))
print("95% quantile =",np.percentile(result,95))
				
			
				
					5% quantile = 42.87331484051217
95% quantile = 163.03431792455953
				
			

Vediamo che esiste una probabilità del 5% che il prezzo delle azioni finisca al di sotto di circa 42,87$ e una probabilità del 5% che finisca sopra 163,03$.

A questo punto possiamo iniziare a porci domande come “sono disposto a rischiare una probabilità del 5% di finire con un’azione che vale meno di  42,87$, al fine di inseguire un rendimento atteso di circa il 23%, per un prezzo medio di circa 91,61$?”

L’ultima cosa da possiamo fare è visualizzare sull’istogramma i due quantili che abbiamo appena calcolato per avere una rappresentazione visiva.

				
					plt.hist(result,bins=100)
plt.axvline(np.percentile(result,5), color='r', linestyle='dashed', linewidth=2)
plt.axvline(np.percentile(result,95), color='r', linestyle='dashed', linewidth=2)
plt.show()
				
			
Analisi-Dati-MonteCarlo-AAPL-10000-Simulazioni-Istogramma-quantile

Codice completo

In questo articolo abbiamo descritto come implementare una simulazione Monte Carlo con Python. Per il codice completo riportato in questo articolo, si può consultare il seguente repository di github:
https://github.com/datatrading-info/AnalisiDatiFinanziari

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...

Scroll to Top