策略回测收益指标计算

[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()  # 最大回撤