0
# Risk Metrics
1
2
Calculate comprehensive risk measures including drawdowns, volatility-based ratios, and downside risk metrics for portfolio evaluation. Provides essential risk assessment tools for quantitative finance.
3
4
## Capabilities
5
6
### Drawdown Analysis
7
8
Measure and analyze portfolio drawdowns from peak values.
9
10
```python { .api }
11
def to_drawdown_series(prices):
12
"""
13
Calculate drawdown series showing percentage decline from peak values.
14
15
Parameters:
16
- prices (pd.Series or pd.DataFrame): Price series
17
18
Returns:
19
pd.Series or pd.DataFrame: Drawdown series (negative values)
20
"""
21
22
def calc_max_drawdown(prices):
23
"""
24
Calculate maximum drawdown over the entire period.
25
26
Parameters:
27
- prices (pd.Series): Price series
28
29
Returns:
30
float: Maximum drawdown as positive decimal (e.g., 0.15 for 15% drawdown)
31
"""
32
33
def drawdown_details(drawdown, index_type=pd.DatetimeIndex):
34
"""
35
Detailed drawdown analysis including duration and recovery periods.
36
37
Parameters:
38
- drawdown (pd.Series): Drawdown series
39
- index_type: Index type for date handling
40
41
Returns:
42
pd.DataFrame: Detailed drawdown statistics
43
"""
44
```
45
46
### Volatility-Based Risk Ratios
47
48
Calculate risk-adjusted return metrics using volatility measures.
49
50
```python { .api }
51
def calc_sharpe(returns, rf=0.0, nperiods=None, annualize=True):
52
"""
53
Calculate Sharpe ratio (excess return per unit of volatility).
54
55
Parameters:
56
- returns (pd.Series): Return series
57
- rf (float): Risk-free rate (default: 0.0)
58
- nperiods (int): Periods for annualization (inferred if None)
59
- annualize (bool): Whether to annualize the ratio
60
61
Returns:
62
float: Sharpe ratio
63
"""
64
65
def calc_information_ratio(returns, benchmark_returns):
66
"""
67
Calculate Information ratio (active return per unit of tracking error).
68
69
Parameters:
70
- returns (pd.Series): Portfolio returns
71
- benchmark_returns (pd.Series): Benchmark returns
72
73
Returns:
74
float: Information ratio
75
"""
76
77
def calc_risk_return_ratio(returns):
78
"""
79
Calculate risk-return ratio (mean return divided by volatility).
80
81
Parameters:
82
- returns (pd.Series): Return series
83
84
Returns:
85
float: Risk-return ratio
86
"""
87
```
88
89
### Downside Risk Metrics
90
91
Risk measures focused on negative returns and downside volatility.
92
93
```python { .api }
94
def calc_sortino_ratio(returns, rf=0.0, nperiods=None, annualize=True):
95
"""
96
Calculate Sortino ratio (excess return per unit of downside deviation).
97
98
Parameters:
99
- returns (pd.Series): Return series
100
- rf (float): Risk-free rate (default: 0.0)
101
- nperiods (int): Periods for annualization (inferred if None)
102
- annualize (bool): Whether to annualize the ratio
103
104
Returns:
105
float: Sortino ratio
106
"""
107
108
def calc_calmar_ratio(prices):
109
"""
110
Calculate Calmar ratio (CAGR divided by maximum drawdown).
111
112
Parameters:
113
- prices (pd.Series): Price series
114
115
Returns:
116
float: Calmar ratio
117
"""
118
```
119
120
### Ulcer Index Metrics
121
122
Alternative risk measures based on drawdown depth and duration.
123
124
```python { .api }
125
def to_ulcer_index(prices):
126
"""
127
Calculate Ulcer Index (root-mean-square of drawdowns).
128
129
Parameters:
130
- prices (pd.Series): Price series
131
132
Returns:
133
float: Ulcer Index
134
"""
135
136
def to_ulcer_performance_index(prices, rf=0.0, nperiods=None):
137
"""
138
Calculate Ulcer Performance Index (excess return per unit of Ulcer Index).
139
140
Parameters:
141
- prices (pd.Series): Price series
142
- rf (float): Risk-free rate (default: 0.0)
143
- nperiods (int): Periods for annualization (inferred if None)
144
145
Returns:
146
float: Ulcer Performance Index
147
"""
148
```
149
150
### Statistical Risk Utilities
151
152
Supporting functions for excess returns and risk calculations.
153
154
```python { .api }
155
def to_excess_returns(returns, rf, nperiods=None):
156
"""
157
Calculate excess returns over risk-free rate.
158
159
Parameters:
160
- returns (pd.Series): Return series
161
- rf (float): Risk-free rate
162
- nperiods (int): Periods for annualization (inferred if None)
163
164
Returns:
165
pd.Series: Excess return series
166
"""
167
```
168
169
## Usage Examples
170
171
### Basic Risk Metrics
172
173
```python
174
import ffn
175
176
# Download price data
177
prices = ffn.get('AAPL', start='2020-01-01')['AAPL']
178
returns = ffn.to_returns(prices).dropna()
179
180
# Calculate key risk metrics
181
sharpe = ffn.calc_sharpe(returns, rf=0.02)
182
sortino = ffn.calc_sortino_ratio(returns, rf=0.02)
183
calmar = ffn.calc_calmar_ratio(prices)
184
max_dd = ffn.calc_max_drawdown(prices)
185
186
print(f"Sharpe Ratio: {sharpe:.2f}")
187
print(f"Sortino Ratio: {sortino:.2f}")
188
print(f"Calmar Ratio: {calmar:.2f}")
189
print(f"Max Drawdown: {max_dd:.2%}")
190
```
191
192
### Drawdown Analysis
193
194
```python
195
import ffn
196
197
# Analyze drawdowns
198
prices = ffn.get('AAPL', start='2020-01-01')['AAPL']
199
drawdowns = ffn.to_drawdown_series(prices)
200
201
# Get detailed drawdown statistics
202
dd_details = ffn.drawdown_details(drawdowns)
203
print("Drawdown Details:")
204
print(dd_details)
205
206
# Plot drawdown series
207
import matplotlib.pyplot as plt
208
drawdowns.plot(title='AAPL Drawdowns', figsize=(12, 6))
209
plt.ylabel('Drawdown %')
210
plt.show()
211
```
212
213
### Advanced Risk Analysis
214
215
```python
216
import ffn
217
218
# Multi-asset risk comparison
219
prices = ffn.get('AAPL,MSFT,GOOGL', start='2020-01-01')
220
returns = ffn.to_returns(prices).dropna()
221
222
# Calculate Ulcer Index for each asset
223
ulcer_indices = {}
224
for col in prices.columns:
225
ulcer_indices[col] = ffn.to_ulcer_index(prices[col])
226
227
print("Ulcer Index by Asset:")
228
for asset, ui in ulcer_indices.items():
229
print(f"{asset}: {ui:.2f}")
230
231
# Risk-adjusted performance comparison
232
risk_metrics = {}
233
for col in returns.columns:
234
risk_metrics[col] = {
235
'Sharpe': ffn.calc_sharpe(returns[col], rf=0.02),
236
'Sortino': ffn.calc_sortino_ratio(returns[col], rf=0.02),
237
'Calmar': ffn.calc_calmar_ratio(prices[col]),
238
'Max DD': ffn.calc_max_drawdown(prices[col])
239
}
240
241
import pandas as pd
242
risk_df = pd.DataFrame(risk_metrics).T
243
print("\nRisk Metrics Comparison:")
244
print(risk_df.round(3))
245
```
246
247
### Portfolio Risk Assessment
248
249
```python
250
import ffn
251
import pandas as pd
252
253
# Create equal-weight portfolio
254
prices = ffn.get('AAPL,MSFT,GOOGL', start='2020-01-01')
255
returns = ffn.to_returns(prices).dropna()
256
257
# Portfolio returns
258
weights = pd.Series([1/3, 1/3, 1/3], index=returns.columns)
259
portfolio_returns = (returns * weights).sum(axis=1)
260
portfolio_prices = ffn.to_price_index(portfolio_returns, start=100)
261
262
# Portfolio risk metrics
263
portfolio_sharpe = ffn.calc_sharpe(portfolio_returns, rf=0.02)
264
portfolio_sortino = ffn.calc_sortino_ratio(portfolio_returns, rf=0.02)
265
portfolio_max_dd = ffn.calc_max_drawdown(portfolio_prices)
266
267
print(f"Portfolio Sharpe: {portfolio_sharpe:.2f}")
268
print(f"Portfolio Sortino: {portfolio_sortino:.2f}")
269
print(f"Portfolio Max DD: {portfolio_max_dd:.2%}")
270
271
# Compare to individual assets
272
individual_sharpe = [ffn.calc_sharpe(returns[col], rf=0.02) for col in returns.columns]
273
avg_individual_sharpe = sum(individual_sharpe) / len(individual_sharpe)
274
275
print(f"Average Individual Sharpe: {avg_individual_sharpe:.2f}")
276
print(f"Portfolio Benefit: {portfolio_sharpe - avg_individual_sharpe:.2f}")
277
```