Skip to content

Commit f4d5376

Browse files
authored
修正了一些bug(例如report中计算mdd时的bug)
出现bug的原因是qlib里面也是这样写的,于是我想当然地以为那样就是对的(
1 parent cc07945 commit f4d5376

File tree

1 file changed

+33
-19
lines changed

1 file changed

+33
-19
lines changed

scutquant/report.py

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import matplotlib.pyplot as plt
22
import pandas as pd
33
import numpy as np
4-
from sklearn.linear_model import LinearRegression
54

65

76
def sharpe_ratio(ret: pd.Series | pd.DataFrame, rf: float = 0.03, freq: float = 1.0) -> float:
@@ -47,10 +46,20 @@ def information_ratio(ret: pd.Series | pd.DataFrame, benchmark: pd.Series | pd.D
4746

4847
def calculate_mdd(data: pd.Series) -> pd.Series:
4948
"""
50-
:param data: pd.Series
51-
:return: pd.Series
49+
:param data: 累计收益率序列
50+
:return: 从开始到目前的回撤
5251
"""
53-
return data - data.cummax()
52+
return (data - data.cummax()) / data.cummax()
53+
54+
55+
def annualized_return(data: pd.Series, freq: float = 1) -> float:
56+
# (1 + total_ret) ** (1/years) - 1
57+
return (1 + data.values[-1]) ** (252 / (len(data) * freq)) - 1
58+
59+
60+
def annualized_volatility(data: pd.Series, freq: float = 1) -> float:
61+
# ret.std()*(x **0.5), x为一年有多少tick
62+
return data.std() * ((252 / len(data) / freq) ** 0.5)
5463

5564

5665
def plot(data, label, title: str = None, xlabel: str = None, ylabel: str = None, figsize=None,
@@ -136,7 +145,7 @@ def report_all(user_account, benchmark, show_raw_value: bool = False, excess_ret
136145
ben_ret = []
137146
days = 0
138147
for i in range(len(acc_val)):
139-
acc_ret.append(acc_val[i] / init_val_acc - 1)
148+
acc_ret.append(acc_val[i] / init_val_acc - 1) # 当前净值相对于初始值的收益率
140149
ben_ret.append(ben_val[i] / init_val_ben - 1)
141150
excess_ret = []
142151

@@ -146,37 +155,42 @@ def report_all(user_account, benchmark, show_raw_value: bool = False, excess_ret
146155
days += 1
147156
days /= len(acc_ret)
148157

149-
acc_mdd = calculate_mdd(pd.Series(acc_ret))
150-
ben_mdd = calculate_mdd(pd.Series(ben_ret))
158+
acc_mdd = calculate_mdd(pd.Series(acc_ret) + 1)
159+
ben_mdd = calculate_mdd(pd.Series(ben_ret) + 1)
151160

152161
ret = pd.Series(acc_ret) # 累计收益率
153162
ben = pd.Series(ben_ret) # benchmark的累计收益率
154-
x = ben.values.reshape(-1, 1)
155-
model = LinearRegression().fit(x, ret)
163+
164+
ann_return = annualized_return(ret, freq=freq)
165+
ann_std = annualized_volatility(ret, freq=freq)
166+
ben_ann_return = annualized_return(ben, freq=freq)
167+
ben_ann_std = annualized_volatility(ben, freq=freq)
168+
169+
beta = ret.cov(ben) / ben.var()
170+
alpha = ret.mean() - beta * ben.mean()
171+
epsilon = pd.Series(ret - beta * ben - alpha).std()
156172

157173
sharpe = sharpe_ratio(acc_ret, rf=rf, freq=freq * 365)
158174
sortino = sortino_ratio(acc_ret, ben_ret)
159175
inf_ratio = information_ratio(acc_ret, ben_ret)
160176

161-
print('Annualized Return:', (1 + acc_ret[-1]) ** (252 / (len(ret) * freq)) - 1) # (1 + total_ret) ** (1/years) - 1
162-
print('Annualized Return:', (1 + acc_ret[-1]) ** (252 / (len(ret) * freq)) - 1) # (1 + total_ret) ** (1/years) - 1
177+
print('Annualized Return:', ann_return) # (1 + total_ret) ** (1/years) - 1
163178
# print("years:", 252 / len(ret))
164-
print('Annualized Volatility:', ret.std() * ((252 / len(ret) / freq) ** 0.5)) # ret.std()*(x **0.5), x为一年有多少tick
165-
print('Annualized Return(Benchmark):', (1 + ben_ret[-1]) ** (252 / (len(ret) * freq)) - 1)
166-
print('Annualized Return(Benchmark):', (1 + ben_ret[-1]) ** (252 / (len(ret) * freq)) - 1)
167-
print('Annualized Volatility(Benchmark):', ben.std() * ((252 / len(ret) / freq) ** 0.5), '\n')
179+
print('Annualized Volatility:', ann_std) # ret.std()*(x **0.5), x为一年有多少tick
180+
print('Annualized Return(Benchmark):', ben_ann_return)
181+
print('Annualized Volatility(Benchmark):', ben_ann_std, '\n')
168182
print('Cumulative Rate of Return:', acc_ret[-1])
169183
print('Cumulative Rate of Return(Benchmark):', ben_ret[-1])
170184
print('Cumulative Excess Rate of Return:', excess_ret[-1], '\n')
171185
print('Max Drawdown:', acc_mdd.min())
172186
print('Max Drawdown(Benchmark):', ben_mdd.min())
173-
print('Max Drawdown(Excess Return):', calculate_mdd(pd.Series(excess_ret)).min(), '\n')
187+
print('Max Drawdown(Excess Return):', calculate_mdd(pd.Series(excess_ret) + 1).min(), '\n')
174188
print('Sharpe Ratio:', sharpe)
175189
print('Sortino Ratio:', sortino)
176190
print('Information Ratio:', inf_ratio, '\n')
177-
print('Beta:', model.coef_[0])
178-
print("Alpha:", model.intercept_)
179-
print("Epsilon:", (ret - model.predict(x)).std())
191+
print('Beta:', beta)
192+
print("Alpha:", alpha)
193+
print("Epsilon:", epsilon)
180194
print('Profitable Days(%):', days)
181195

182196
if show_raw_value:

0 commit comments

Comments
 (0)