Advertisement
coloriot

Metrics

Apr 20th, 2025
18
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 2.82 KB | None | 0 0
  1. import numpy as np
  2. from scipy.stats import skew, kurtosis, anderson, shapiro
  3.  
  4. def compute_risk_metrics(portfolio_returns, portfolio_beta, rf_rate=0.0):
  5.     """
  6.    Compute various risk metrics for the portfolio.
  7.    Note, that it works for RF and CPI Adjusted returns.
  8.    """
  9.  
  10.     mean_return = portfolio_returns.mean() * 12
  11.     std_dev = portfolio_returns.std(ddof=1) * np.sqrt(12)
  12.     downside_returns = portfolio_returns[portfolio_returns < rf_rate]
  13.     downside_dev = downside_returns.std(ddof=1) * np.sqrt(12)
  14.  
  15.     cumulative = (1 + portfolio_returns).cumprod()
  16.     running_max = np.maximum.accumulate(cumulative)
  17.     drawdowns = (cumulative - running_max) / running_max
  18.  
  19.     max_drawdown = drawdowns.min()
  20.     cumulative_return = cumulative.iloc[-1] - 1
  21.     calmar_ratio = mean_return / abs(max_drawdown) if max_drawdown != 0 else np.nan
  22.  
  23.     VaR_95 = np.percentile(portfolio_returns, 5)
  24.     CVaR_95 = portfolio_returns[portfolio_returns <= VaR_95].mean()
  25.     omega_ratio = (portfolio_returns[portfolio_returns > rf_rate].sum() /
  26.                    abs(portfolio_returns[portfolio_returns < rf_rate].sum()))
  27.  
  28.     anderson_stat = anderson(portfolio_returns).statistic
  29.     shapiro_stat, shapiro_p = shapiro(portfolio_returns)
  30.  
  31.     neg_deviation = abs(portfolio_returns[portfolio_returns < rf_rate] - rf_rate)
  32.     sasr = mean_return / neg_deviation.mean() if not neg_deviation.empty else np.nan
  33.  
  34.     mad = np.mean(np.abs(portfolio_returns - portfolio_returns.mean()))
  35.     iqr = np.percentile(portfolio_returns, 75) - np.percentile(portfolio_returns, 25)
  36.  
  37.     ulcer_index = np.sqrt(np.mean(drawdowns[drawdowns < 0] ** 2))
  38.  
  39.     sorted_drawdowns = drawdowns.sort_values()
  40.     threshold_index = int(0.05 * len(sorted_drawdowns))
  41.     cdar_5 = sorted_drawdowns[:threshold_index].mean() if threshold_index > 0 else np.nan
  42.  
  43.     pain_ratio = mean_return / abs(drawdowns.mean()) if drawdowns.mean() != 0 else np.nan
  44.  
  45.     return {
  46.         'Annual Return': mean_return,
  47.         'Annual Volatility': std_dev,
  48.         'Sharpe Ratio': mean_return / std_dev,
  49.         'Sortino Ratio': mean_return / downside_dev if downside_dev != 0 else np.nan,
  50.         'Treynor Ratio': mean_return / portfolio_beta if portfolio_beta != 0 else np.nan,
  51.         'Calmar Ratio': calmar_ratio,
  52.         'Max Drawdown': max_drawdown,
  53.         'VaR 95%': VaR_95,
  54.         'CVaR 95%': CVaR_95,
  55.         'Downside Deviation': downside_dev,
  56.         'Omega Ratio': omega_ratio,
  57.         'Skewness': skew(portfolio_returns),
  58.         'Kurtosis': kurtosis(portfolio_returns),
  59.         'Anderson-Darling Stat': anderson_stat,
  60.         'Shapiro-Wilk Stat': shapiro_stat,
  61.         'Shapiro-Wilk p': shapiro_p,
  62.         'SASR': sasr,
  63.         'MAD': mad,
  64.         'IQR': iqr,
  65.         'Ulcer Index': ulcer_index,
  66.         'CDaR (5%)': cdar_5,
  67.         'Pain Ratio': pain_ratio,
  68.     }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement