策略回测收益指标计算
[TOC]
参考
股票分析之——收益率(附完整代码和讲解) https://zhuanlan.zhihu.com/p/91948053
关于策略评价指标的计算公式 https://zhuanlan.zhihu.com/p/26082917
Python3对股票的收益和风险进行分析 https://blog.csdn.net/asialee_bird/article/details/89340563
史上最全的Python定量金融三方库汇总 https://zhuanlan.zhihu.com/p/259845886
和金融相关的python包汇总 https://www.cnblogs.com/end/p/16463523.html
策略收益计算的主思路
每笔订单收益(order_pnl) -> 日收益(pnl) -> 日收益率(returns) -> 所有指标
在dataframe里处理非常简单,当然不想折腾直接用quantstats包是最通用最标准的,折腾不是为了造轮子,而是看一遍quantstats源码,并且深刻理解一下整体计算思路。以下为个人整理经验。
收益 pnl
每日收益
关键词:pnl earn (去本金)
- 示例
操作 | 权益 | 初始资金 | 占用保证金 | 市值 | 持仓盈亏 | 平仓盈亏 | 费用 | 可用资金 |
---|---|---|---|---|---|---|---|---|
day1: 开户 | 10000 | 10000 | 0 | 0 | 0 | 0 | 0 | 10000 |
day2: 开仓AG | 9980 | 10000 | 1000 | 8000 | 0 | 0 | -20 | 8980 |
day3: 持有 | 10980 | 10000 | 1000 | 9000 | 1000 | 0 | -20 | 9980 |
day4: 平仓AG | 10960 | 10000 | 0 | 0 | 0 | 1000 | -40 | 10960 |
- 收益与权益
权益 = 初始 - 占用 + 持仓盈亏 + 平仓盈亏 - 交易税费 + 出入金 + 分红 - 红利税
init:初始资金
order_pnl:订单收益 = (price-open)*volume - 0.001印花税 - 0.0003佣金
pnl:收益 = order_pnl.resample('D').sum()
pnl:收益 = right - right.shift(1)
cum_pnl:累计收益 = pnl.cumsum()
right:权益 = pnl.cumsum() + init
收益率 returns
计算问题
- 单复利区别:
概念:simple interest 单利,compound interest 复利
累计收益:cum_sum()
平均收益:mean() - 去本金处理:收益率是去本金的百分比
- fillna:收益率需要做fillna(0)处理
- 年化处理:自然日为returns经历的自然天数与每年365天的处理,交易日为returns经历的交易日与252的处理
- 标准差:std与qart
numpy文档说明:std = sqrt(mean(x)), where x = abs(a - a.mean())**2
收益率-实际值
returns = earn_rate = pct_change = earnings_yield = 益本比
returns:每日收益率 = priceDf.pct_change()
returns:每日收益率 = right.pct_change()
month_returns:各月收益率 = priceDf.resample('M').last().pct_change()
returns_cum:累计收益率 = (returns + 1).cumprod() -1
returns_total:总收益率 = np.product(returns + 1) -1
returns_total:总收益率 = (returns + 1).prod() - 1
rf_daily: 无风险日收益率 = np.power(1 + rf, 1/252) -1
returns_risk_premium: 溢出收益率 = returns - (np.power(1 + rf, 1/252) -1)
收益率-理论值 Annualized_Returns
CAGR:复合年化收益率 = (Ending Value / Beginning Value) ^ (1 / number of years) - 1 # 公式
CAGR:复合年化收益率 = (returns + 1).prod() ** (returns_days/year_days) - 1 # 代码demo
CAGR:复合年化收益率 = Annualized_Returns
rf:无风险收益率 = y10 #十年国债
统计指标
思路
每笔订单收益(order_pnl) -> 日收益(pnl) -> 日收益率(returns) -> 所有指标
参考
股票分析之——收益率(附完整代码和讲解) https://zhuanlan.zhihu.com/p/91948053
关于策略评价指标的计算公式 https://zhuanlan.zhihu.com/p/26082917
Python3对股票的收益和风险进行分析 https://blog.csdn.net/asialee_bird/article/details/89340563
史上最全的Python定量金融三方库汇总 https://zhuanlan.zhihu.com/p/259845886
和金融相关的python包汇总 https://www.cnblogs.com/end/p/16463523.html
统计:均值方差...
log_returns: 对数收益率 = np.log(returns)
returns_range:日收益率极差 = returns.max() - returns.min()
returns_interquartile_range:日收益率四分位差 = returns.quantile(0.75) - returns.quantile(0.25)
returns_var:日收益率方差 = returns.var()
returns_std:日收益率标准差 = returns.std() = divisor
downside 下行风险 = np.sqrt((returns_risk_premium[returns_risk_premium < 0] ** 2).sum() / len(returns_risk_premium))
downside_volatility 下行风险标准差 = returns_risk_premium[returns_risk_premium < 0].std() * np.sqrt(252)
returns_mean_year:年化均值 = (1 + (returns.mean()) ** 252 - 1
returns_var_year: 年化方差 = returns.std() ** 2 * 252
returns_std_year:年化标准差 = returns.std() * np.sqrt(252)
returns_coefficient_year:年化离散系数 = returns_std_year / returns_mean_year
评价指标:夏普索提诺...
annual_volatility:年化波动率 = returns_std_year, volatility
Sharpe_Ratio:夏普比率 = (年化 - 起始日无风险利率) / 年化波动率
Sharpe_Ratio:夏普比率 = (cagr - rf)/std
Sharpe_Ratio:夏普比率 = returns_risk_premium / returns_std * np.sqrt(252) # rqrisk和qs算法
Calmar_Ratio:卡玛比率 = 年化/最大回撤
Sortino_Ratio:索提诺比率 = (年化 - 起始日无风险利率) / 下行标准差
Sortino_Ratio:索提诺比率 = (cagr - rf)/downside_volatility
Sortino_Ratio:索提诺比率 = returns_risk_premium.mean() / downside * np.sqrt(252) # qs算法
Information_Ratio:信息比率 = (策略每日收益 - 市场基准每日收益)的年化均值 / 年化标准差
Max_Drawdown:最大回撤比率 = max(Di-Dj) / Di
Max_Drawdown:最大回撤比率 = (returns_cum / returns_cum.expanding(min_periods=1).max() - 1).min()
win_rate:胜率 = 盈利次数/总交易次数
profit_and_coss_ratio:盈亏比 = 盈利单的盈利之和 / 亏损单的亏损之和 = (成功率×平均获利幅度)/(败率×平均亏损幅度)
class AtRisk:
def __init__(self, returns, rf=0):
self.returns = returns
self.rf = rf
self._nature_yearly = 365
self._trade_yearly = 252
@property # 年化收益率
def cagr_nature(self): return self._cagr('nature')
@property
def cagr_trade(self): return self._cagr('trade')
def _cagr(self, days_type='nature', cumprod=True):
# cagr = (Ending Value / Beginning Value) ^ (1 / number of years) - 1
# quantstats use nature
years_num = (self.returns.index[-1] - self.returns.index[0]).days / self._nature_yearly if days_type == 'nature' else len(self.returns) / self._trade_yearly
total_returns = (self.returns + 1).prod() - 1 if cumprod else self.returns.sum()
return ((total_returns + 1)/1) ** (1/years_num) - 1
@property # 年化波动率
def annual_volatility(self): return self.returns.std(ddof=1) * np.sqrt(self._trade_yearly) # ddof = 1 无偏差样本
@property # 夏普
def sharpe(self): return (self.cagr_trade - self.rf) / self.annual_volatility
@property # 夏普
def sharpe_qs(self):
divisor = self.returns.std(ddof=1)
return self.returns_risk_premium.mean() / divisor * np.sqrt(self._trade_yearly)
@property # 下行年化波动率
def downside(self): return self.returns_risk_premium[self.returns_risk_premium < 0].std(ddof=1) * np.sqrt(self._trade_yearly)
@property # 索提诺
def sortino(self): return (self.cagr_trade - self.rf) / self.downside
@property
def returns_risk_premium(self): return self.returns - (np.power(1 + self.rf, 1/self._trade_yearly) - 1)
@property # 下行风险
def downside_qs(self): return np.sqrt((self.returns_risk_premium[self.returns_risk_premium < 0] ** 2).sum() / len(self.returns_risk_premium))
@property # 索提诺
def sortino_qs(self): return self.returns_risk_premium.mean() / self.downside_qs * np.sqrt(self._trade_yearly)
@property # 最大回撤
def max_drawdown(self):
returns_cum = (self.returns+1).cumprod()
returns_cum_peak = returns_cum.expanding(min_periods=1).max() # 滚动高点
drawdown = (returns_cum / returns_cum_peak) - 1 # 回撤 = 每日净值与各自对应最高点的差比
return drawdown.min() # 最大回撤