or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

data-retrieval.mddata-utilities.mdindex.mdpandas-extensions.mdperformance-analysis.mdportfolio-optimization.mdreturn-calculations.mdrisk-metrics.mdstatistical-analysis.md

risk-metrics.mddocs/

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

```