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.

La probabilità di rendimenti negativi, la probabilità di perdita in senso stretto, o sotto un certo valore può essere determinata sulla base dello storico come la frequenza relativa di giorni con rendimenti negativi o sotto quella certa soglia.
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)