Esempio n. 1
0
 def test_conditional_value_at_risk(self, test_cutoff):
     # empyrical can't tolerate NaNs here
     res_a = empyrical.conditional_value_at_risk(ret['a'].iloc[1:], cutoff=test_cutoff)
     res_b = empyrical.conditional_value_at_risk(ret['b'].iloc[1:], cutoff=test_cutoff)
     res_c = empyrical.conditional_value_at_risk(ret['c'].iloc[1:], cutoff=test_cutoff)
     assert isclose(ret['a'].vbt.returns.conditional_value_at_risk(cutoff=test_cutoff), res_a)
     pd.testing.assert_series_equal(
         ret.vbt.returns.conditional_value_at_risk(cutoff=test_cutoff),
         pd.Series([res_a, res_b, res_c], index=ret.columns).rename('conditional_value_at_risk')
     )
Esempio n. 2
0
    def calculate_statistics(self, df: DataFrame = None, output=True):
        """"""
        self.output("开始计算策略统计指标")

        # Check DataFrame input exterior
        if df is None:
            df = self.daily_df

        # Check for init DataFrame
        if df is None:
            # Set all statistics to 0 if no trade.
            start_date = ""
            end_date = ""
            total_days = 0
            profit_days = 0
            loss_days = 0
            end_balance = 0
            max_drawdown = 0
            max_ddpercent = 0
            max_drawdown_duration = 0
            max_drawdown_end = 0
            total_net_pnl = 0
            daily_net_pnl = 0
            total_commission = 0
            daily_commission = 0
            total_slippage = 0
            daily_slippage = 0
            total_turnover = 0
            daily_turnover = 0
            total_trade_count = 0
            daily_trade_count = 0
            total_return = 0
            annual_return = 0
            daily_return = 0
            return_std = 0
            sharpe_ratio = 0
            sortino_info = 0
            win_ratio = 0
            return_drawdown_ratio = 0
            tail_ratio_info = 0
            stability_return = 0
            win_loss_pnl_ratio = 0
            pnl_medio = 0
            duration_medio = 0
            calmar_ratio = 0
        else:
            # Calculate balance related time series data
            df["balance"] = df["net_pnl"].cumsum() + self.capital
            df["return"] = np.log(df["balance"] /
                                  df["balance"].shift(1)).fillna(0)
            df["highlevel"] = (df["balance"].rolling(min_periods=1,
                                                     window=len(df),
                                                     center=False).max())
            df["drawdown"] = df["balance"] - df["highlevel"]
            df["ddpercent"] = df["drawdown"] / df["highlevel"] * 100

            # Calculate statistics value
            start_date = df.index[0]
            end_date = df.index[-1]

            total_days = len(df)
            profit_days = len(df[df["net_pnl"] > 0])
            loss_days = len(df[df["net_pnl"] < 0])

            end_balance = df["balance"].iloc[-1]
            max_drawdown = df["drawdown"].min()
            max_ddpercent = df["ddpercent"].min()
            max_drawdown_end = df["drawdown"].idxmin()

            if isinstance(max_drawdown_end, date):
                max_drawdown_start = df["balance"][:max_drawdown_end].idxmax()
                max_drawdown_duration = (max_drawdown_end -
                                         max_drawdown_start).days
            else:
                max_drawdown_duration = 0

            total_net_pnl = df["net_pnl"].sum()
            daily_net_pnl = total_net_pnl / total_days

            win = df[df["net_pnl"] > 0]
            win_amount = win["net_pnl"].sum()
            win_pnl_medio = win["net_pnl"].mean()
            # win_duration_medio = win["duration"].mean().total_seconds()/3600
            win_count = win["trade_count"].sum()
            pnl_medio = df["net_pnl"].mean()
            # duration_medio = df["duration"].mean().total_seconds()/3600

            loss = df[df["net_pnl"] < 0]
            loss_amount = loss["net_pnl"].sum()
            loss_pnl_medio = loss["net_pnl"].mean()
            # loss_duration_medio = loss["duration"].mean().total_seconds()/3600

            total_commission = df["commission"].sum()
            daily_commission = total_commission / total_days

            total_slippage = df["slippage"].sum()
            daily_slippage = total_slippage / total_days

            total_turnover = df["turnover"].sum()
            daily_turnover = total_turnover / total_days

            total_trade_count = df["trade_count"].sum()
            win_ratio = (win_count / total_trade_count) * 100
            win_loss_pnl_ratio = -win_pnl_medio / loss_pnl_medio
            daily_trade_count = total_trade_count / total_days

            total_return = (end_balance / self.capital - 1) * 100
            annual_return = total_return / total_days * 240
            daily_return = df["return"].mean() * 100
            return_std = df["return"].std() * 100

            if return_std:
                sharpe_ratio = daily_return / return_std * np.sqrt(240)
            else:
                sharpe_ratio = 0

            return_drawdown_ratio = -total_return / max_ddpercent

            #calmar_ratio:年化收益率与历史最大回撤率之间的比率
            calmar_ratio = annual_return / abs(max_ddpercent)

            #sortino_info
            sortino_info = sortino_ratio(df['return'])
            omega_info = omega_ratio(df['return'])
            #年化波动率
            annual_volatility_info = annual_volatility(df['return'])
            #年化复合增长率
            cagr_info = cagr(df['return'])
            #年化下行风险率
            annual_downside_risk = downside_risk(df['return'])
            """CVaR即条件风险价值,其含义为在投资组合的损失超过某个给定VaR值的条件下,该投资组合的平均损失值。"""
            c_var = conditional_value_at_risk(df['return'])
            """风险价值(VaR)是对投资损失风险的一种度量。它估计在正常的市场条件下,在设定的时间段(例如一天)中,
            一组投资可能(以给定的概率)损失多少。金融业中的公司和监管机构通常使用VaR来衡量弥补可能损失所需的资产数量"""
            var_info = value_at_risk(df['return'])

            #收益稳定率
            stability_return = stability_of_timeseries(df['return'])
            #尾部比率0.25 == 1/4,收益1,风险4
            tail_ratio_info = tail_ratio(df['return'])

        # Output
        if output:
            self.output("-" * 30)
            self.output(f"首个交易日:\t{start_date}")
            self.output(f"最后交易日:\t{end_date}")

            self.output(f"总交易日:\t{total_days}")
            self.output(f"盈利交易日:\t{profit_days}")
            self.output(f"亏损交易日:\t{loss_days}")

            self.output(f"起始资金:\t{self.capital:,.2f}")
            self.output(f"结束资金:\t{end_balance:,.2f}")

            self.output(f"总收益率:\t{total_return:,.2f}%")
            self.output(f"年化收益:\t{annual_return:,.2f}%")
            self.output(f"最大回撤: \t{max_drawdown:,.2f}")
            self.output(f"百分比最大回撤: {max_ddpercent:,.2f}%")
            self.output(f"最长回撤天数: \t{max_drawdown_duration}")

            self.output(f"总盈亏:\t{total_net_pnl:,.2f}")
            self.output(f"总手续费:\t{total_commission:,.2f}")
            self.output(f"总滑点:\t{total_slippage:,.2f}")
            self.output(f"总成交金额:\t{total_turnover:,.2f}")
            self.output(f"总成交笔数:\t{total_trade_count}")

            self.output(f"日均盈亏:\t{daily_net_pnl:,.2f}")
            self.output(f"日均手续费:\t{daily_commission:,.2f}")
            self.output(f"日均滑点:\t{daily_slippage:,.2f}")
            self.output(f"日均成交金额:\t{daily_turnover:,.2f}")
            self.output(f"日均成交笔数:\t{daily_trade_count}")

            self.output(f"日均收益率:\t{daily_return:,.2f}%")
            self.output(f"收益标准差:\t{return_std:,.2f}%")
            self.output(f"胜率:\t{win_ratio:,.2f}")
            self.output(f"盈亏比:\t\t{win_loss_pnl_ratio:,.2f}")

            self.output(f"平均每笔盈亏:\t{pnl_medio:,.2f}")
            self.output(f"calmar_ratio:\t{calmar_ratio:,.3f}")
            # self.output(f"平均持仓小时:\t{duration_medio:,.2f}")
            self.output(f"Sharpe Ratio:\t{sharpe_ratio:,.2f}")
            self.output(f"sortino Ratio:\t{sortino_info:,.3f}")
            self.output(f"收益回撤比:\t{return_drawdown_ratio:,.2f}")

        statistics = {
            "start_date": start_date,
            "end_date": end_date,
            "total_days": total_days,
            "profit_days": profit_days,
            "loss_days": loss_days,
            "capital": self.capital,
            "end_balance": end_balance,
            "max_drawdown": max_drawdown,
            "max_ddpercent": max_ddpercent,
            "max_drawdown_end": max_drawdown_end,
            "max_drawdown_duration": max_drawdown_duration,
            "total_net_pnl": total_net_pnl,
            "daily_net_pnl": daily_net_pnl,
            "total_commission": total_commission,
            "daily_commission": daily_commission,
            "total_slippage": total_slippage,
            "daily_slippage": daily_slippage,
            "total_turnover": total_turnover,
            "daily_turnover": daily_turnover,
            "total_trade_count": total_trade_count,
            "daily_trade_count": daily_trade_count,
            "total_return": total_return,
            "annual_return": annual_return,
            "daily_return": daily_return,
            "return_std": return_std,
            "sharpe_ratio": sharpe_ratio,
            'sortino_info': sortino_info,
            "win_ratio": win_ratio,
            "return_drawdown_ratio": return_drawdown_ratio,
            "tail_ratio_info": tail_ratio_info,
            "stability_return": stability_return,
            "win_loss_pnl_ratio": win_loss_pnl_ratio,
            "pnl_medio": pnl_medio,
            "calmar_ratio": calmar_ratio
        }

        # Filter potential error infinite value
        for key, value in statistics.items():
            if value in (np.inf, -np.inf):
                value = 0
            statistics[key] = np.nan_to_num(value)

        self.output("策略统计指标计算完成")
        return statistics
Esempio n. 3
0
def expected_shortfall(portfolio_daily_returns, probability=0.05):
    return ep.conditional_value_at_risk(portfolio_daily_returns, probability)