Probabilità di perdita
Il rischio come probabilità di perdita è probabilmente la definizione più intuitiva e più vicina al modo di ragionare degli investitori, che si pongono domande come le seguenti.
- Qual è la probabilità che il mio investimento perda valore entro un anno?
- Quanto è probabile perdere almeno il 10%?
- Quante volte, storicamente, il FTSE MIB ha chiuso l'anno in perdita?
import yfinance as yf
import numpy as np
import matplotlib.pyplot as plt
df = yf.download(
"FTSEMIB.MI",
start="2010-01-01",
auto_adjust=True,
progress=False
)
returns = df.Close.pct_change().dropna() # Rendimenti giornalieri
def p_loss(returns, threshold=0.0):
"""
Restituisce la probabilità empirica che il rendimento
sia inferiore alla soglia annuale percentuale.
"""
return (returns < threshold/100).mean().item()*100
threshold = -2
plt.figure(figsize=(10,5))
plt.hist(returns[returns < threshold/100],bins=60, label=f"P(R < {threshold}%) = {p_loss(returns, threshold):.1f}%")
plt.hist(returns[returns > threshold/100],bins=60)
plt.title(f"Distribuzione dei rendimenti giornalieri")
plt.legend()
plt.xlabel("Rendimenti R")
plt.ylabel("Densità")
plt.grid(True)
plt.show()
Può comunque essere interessante determinare anche la probabilità di avere rendimenti al di sopra di un certo valore.
def p_loss_gain(returns, threshold=0.0, inf=True):
"""
Restituisce la probabilità empirica che il rendimento
sia inferiore/superiore alla soglia annuale percentuale.
"""
if inf:
return (returns < threshold/100).mean().item()*100
else:
return (returns > threshold/100).mean().item()*100
print()
print("Numero osservazioni:", len(returns))
print()
for threshold, inf in [(-2,True),(-1,True),(0,True),(0,False),(2,False)]:
print(f"P(R {'<' if inf else '>'} {threshold}%) = {p_loss_gain(returns, threshold, inf):.1f}%")
Numero osservazioni: 4196
P(R < -2%) = 6.8%
P(R < -1%) = 18.0%
P(R < 0%) = 46.7%
P(R > 0%) = 53.2%
P(R > 2%) = 6.4%
Di seguito possiamo anche visualizzare la probabilità di perdere oltre una certa soglia o di guadagnare al di sopra di una certa soglia al variare della soglia.
thresholds = np.linspace(-50,50,200)
risk = [ p_loss_gain(returns, t, t<0) for t in thresholds]
plt.figure(figsize=(8,5))
plt.plot(thresholds, risk)
plt.xlabel("Soglia (%)")
plt.ylabel("Probabilità (%)")
plt.title("Probabilità di perdere sotto la soglia se negativa, di guadagnare se positiva")
plt.grid(True)
plt.show()
Possiamo considerare i rendimenti in un intervallo di tempo maggiore di un giorno.
prices = df["Close"]
def p_loss(prices, N, threshold):
"""
Calcola la probabilità che il rendimento su N giorni sia inferiore a threshold.
"""
# Calcola il rendimento su N giorni (shift di N periodi)
returns_N = prices.pct_change(periods=N).dropna() * 100 # in percentuale
return (returns_N < threshold).mean().item() * 100
N = 21 # un mese di trading (21 giorni)
threshold = -5 # -5%
print(f"Probabilità di perdere più del {abs(threshold)}% in {N} giorni: {p_loss(prices, N, threshold):.1f}%")
Probabilità di perdere più del 5% in 21 giorni: 14.5%
Confrontiamo per diversi N e soglie
horizons = [5, 10, 21, 63, 252] # 1 settimana, 2 settimane, 1 mese, 3 mesi, 1 anno
thresholds = [-2, -5, -10, -20] # soglie in percentuale
pd.DataFrame([[f"{p_loss(prices, N, th):.1f}%" for th in thresholds] for N in horizons],index= horizons, columns=thresholds)
-2
-5
-10
-20
5
19.7%
5.0%
0.7%
0.0%
10
25.4%
9.5%
1.8%
0.3%
21
29.6%
14.5%
4.5%
0.6%
63
28.8%
20.4%
10.8%
3.5%
252
32.9%
29.0%
21.6%
12.1%
Potrebbe avere interesse vedere come varia la probabilità di perdere al di sotto di una certa soglia in relazione alla data di investimento.
def p_loss_t(tickers, threshold=0.0):
"""
Restituisce la probabilità empirica che oggi il rendimento
sia inferiore alla soglia annuale percentuale supponendo
di aver investito a partire da un certo giorno.
"""
plt.figure(figsize=(10,5))
plt.title(f"Probabilità di perdita sotto {threshold}% ad oggi avendo investito tempo fa")
for ticker in tickers:
returns = yf.download(ticker,start="2010-01-01",auto_adjust=True,progress=False)["Close"].pct_change().dropna()
plt.plot(returns.index,[(returns[t:] < threshold/100).mean()*100 for t in range(len(returns))], label=ticker)
plt.legend()
plt.xlabel("Data alla quale si è investito")
plt.ylabel("Probabilità (%)")
plt.grid(True)
tickers = ["FTSEMIB.MI","^SPX","AAPL"]
threshold = -2
p_loss_t(tickers, threshold)