Ejemplo n.º 1
0
 def test_tail_ratio(self):
     res_a = empyrical.tail_ratio(ret['a'])
     res_b = empyrical.tail_ratio(ret['b'])
     res_c = empyrical.tail_ratio(ret['c'])
     assert isclose(ret['a'].vbt.returns.tail_ratio(), res_a)
     pd.testing.assert_series_equal(
         ret.vbt.returns.tail_ratio(),
         pd.Series([res_a, res_b, res_c], index=ret.columns))
Ejemplo n.º 2
0
 def test_tail_ratio(self):
     res_a = empyrical.tail_ratio(ret['a'])
     res_b = empyrical.tail_ratio(ret['b'])
     res_c = empyrical.tail_ratio(ret['c'])
     assert isclose(ret['a'].vbt.returns.tail_ratio(), res_a)
     pd.testing.assert_series_equal(
         ret.vbt.returns.tail_ratio(),
         pd.Series([res_a, res_b, res_c],
                   index=ret.columns).rename('tail_ratio'))
     pd.testing.assert_series_equal(
         ret.vbt.returns.rolling_tail_ratio(ret.shape[0], minp=1).iloc[-1],
         pd.Series([res_a, res_b, res_c],
                   index=ret.columns).rename(ret.index[-1]))
Ejemplo n.º 3
0
def compute_stats(portfolio, benchmark):
    '''Compute statistics for the current portfolio'''
    stats = {}

    grp_by_year = portfolio.groupby(lambda x: x.year)
    stats['1yr_highest'] = grp_by_year.max().iloc[-1]
    stats['1yr_lowest'] = grp_by_year.min().iloc[-1]

    portfolio_return = simple_returns(portfolio)
    # benchmark_return = simple_returns(benchmark)
    
    stats['wtd_return'] = aggregate_returns(portfolio_return, 'weekly').iloc[-1]
    stats['mtd_return'] = aggregate_returns(portfolio_return, 'monthly').iloc[-1]
    stats['ytd_return'] = aggregate_returns(portfolio_return, 'yearly').iloc[-1]
    stats['max_drawdown'] = max_drawdown(portfolio_return)
    # stats['annual_return'] = annual_return(portfolio_return, period='daily')
    stats['annual_volatility'] = annual_volatility(portfolio_return, period='daily', alpha=2.0)
    # stats['calmar_ratio'] = calmar_ratio(portfolio_return, period='daily')
    # stats['omega_ratio'] = omega_ratio(portfolio_return, risk_free=0.0)
    stats['sharpe_ratio_1yr'] = sharpe_ratio(portfolio_return, risk_free=0.0, period='daily')
    # stats['alpha'], stats['beta'] = alpha_beta(portfolio_return, benchmark_return, 
    #                                            risk_free=0.0, period='daily')
    stats['tail_ratio'] = tail_ratio(portfolio_return)
    # stats['capture_ratio'] = capture(portfolio_return, benchmark_return, period='daily')

    return stats
    def evaluation(self):
        ap.sound(f'entry: create_df')

        mdd = empyrical.max_drawdown(self.df.eac_stgy_rt)
        stgy_ret_an = empyrical.annual_return(self.df.eac_stgy_rt, annualization=self.cls.annualization)
        bcmk_ret_an = empyrical.annual_return(self.df.eac_bcmk_rt, annualization=self.cls.annualization)
        stgy_vlt_an = empyrical.annual_volatility(self.df.eac_stgy_rt, annualization=self.cls.annualization)
        bcmk_vlt_an = empyrical.annual_volatility(self.df.eac_bcmk_rt, annualization=self.cls.annualization)
        calmar = empyrical.calmar_ratio(self.df.eac_stgy_rt, annualization=self.cls.annualization)
        omega = empyrical.omega_ratio(self.df.eac_stgy_rt, risk_free=self.cls.rf, annualization=self.cls.annualization)
        sharpe = qp.sharpe_ratio(stgy_ret_an, self.df.cum_stgy_rt, self.cls.rf)
        sortino = empyrical.sortino_ratio(self.df.eac_stgy_rt, annualization=self.cls.annualization)
        dsrk = empyrical.downside_risk(self.df.eac_stgy_rt, annualization=self.cls.annualization)
        information = empyrical.information_ratio(self.df.eac_stgy_rt, factor_returns=self.df.eac_bcmk_rt)
        beta = empyrical.beta(self.df.eac_stgy_rt, factor_returns=self.df.eac_bcmk_rt, risk_free=self.cls.rf)
        tail_rt = empyrical.tail_ratio(self.df.eac_stgy_rt)
        alpha = qp.alpha_ratio(stgy_ret_an, bcmk_ret_an, self.cls.rf, beta)

        stgy_ttrt_rt = (self.cls.yd.ttas[-1] - self.cls.yd.ttas[0]) / self.cls.yd.ttas[0]
        bcmk_ttrt_rt = (self.cls.pc.close[-1] - self.cls.pc.close[0]) / self.cls.pc.close[0]
        car_rt = stgy_ttrt_rt - bcmk_ttrt_rt
        car_rt_an = stgy_ret_an - bcmk_ret_an

        self.cls.df_output = pd.DataFrame(
            {'sgty_ttrt_rt': [stgy_ttrt_rt], 'bcmk_ttrt_rt': [bcmk_ttrt_rt], 'car_rt': [car_rt],
             'stgy_ret_an': [stgy_ret_an], 'bcmk_ret_an': [bcmk_ret_an], 'car_rt_an': [car_rt_an],
             'stgy_vlt_an': [stgy_vlt_an], 'bcmk_vlt_an': [bcmk_vlt_an], 'mdd': [mdd],
             'sharpe': [sharpe], 'alpha': [alpha], 'beta': [beta], 'information': [information],
             'tail_rt': [tail_rt], 'calmar': [calmar], 'omega': [omega], 'sortino': [sortino], 'dsrk': [dsrk]})
        print(f'feedback: \n{self.cls.df_output.T}')
Ejemplo n.º 5
0
def tail_ratio(close):
    try:
        rets = daily_returns(close)
        tr_data = empyrical.tail_ratio(rets)
        return tr_data
    except Exception as e:
        raise (e)
Ejemplo n.º 6
0
def tail_ratio(daily_returns):
    """Tail Ratio"""
    try:
        logger.info("Calculating Tail Ratio...")
        tr_data = empyrical.tail_ratio(daily_returns)
        return tr_data
    except Exception as exception:
        logger.error('Oops! An Error Occurred ⚠️')
        raise exception
Ejemplo n.º 7
0
def tail_ratio(returns):
    """
    Determines the ratio between the right (95%) and left tail (5%).

    For example, a ratio of 0.25 means that losses are four times
    as bad as profits.

    Parameters
    ----------
    returns : pd.Series
        Daily returns of the strategy, noncumulative.
         - See full explanation in :func:`~pyfolio.timeseries.cum_returns`.

    Returns
    -------
    float
        tail ratio
    """

    return ep.tail_ratio(returns)
Ejemplo n.º 8
0
def tail_ratio(returns):
    """
    Determines the ratio between the right (95%) and left tail (5%).

    For example, a ratio of 0.25 means that losses are four times
    as bad as profits.

    Parameters
    ----------
    returns : pd.Series
        Daily returns of the strategy, noncumulative.
         - See full explanation in :func:`~pyfolio.timeseries.cum_returns`.

    Returns
    -------
    float
        tail ratio
    """

    return empyrical.tail_ratio(returns)
Ejemplo n.º 9
0
def common_sense_ratio(returns):
    """
    Common sense ratio is the multiplication of the tail ratio and the
    Gain-to-Pain-Ratio -- sum(profits) / sum(losses).

    See http://bit.ly/1ORzGBk for more information on motivation of
    this metric.


    Parameters
    ----------
    returns : pd.Series
        Daily returns of the strategy, noncumulative.
         - See full explanation in tears.create_full_tear_sheet.

    Returns
    -------
    float
        common sense ratio
    """

    return ep.tail_ratio(returns) * (1 + ep.annual_return(returns))
Ejemplo n.º 10
0
def common_sense_ratio(returns):
    """
    Common sense ratio is the multiplication of the tail ratio and the
    Gain-to-Pain-Ratio -- sum(profits) / sum(losses).

    See http://bit.ly/1ORzGBk for more information on motivation of
    this metric.


    Parameters
    ----------
    returns : pd.Series
        Daily returns of the strategy, noncumulative.
         - See full explanation in tears.create_full_tear_sheet.

    Returns
    -------
    float
        common sense ratio
    """

    return empyrical.tail_ratio(returns) * \
        (1 + empyrical.annual_return(returns))
Ejemplo n.º 11
0
def plot_function(epoch_weights):
    ew = np.concatenate(epoch_weights).reshape(-1, No_Channels)
    comm = np.sum(np.abs(ew[1:] - ew[:-1]), axis=1)
    ret = np.sum(np.multiply(ew, y_test.numpy()), axis=1)[1:]
    ind = pd.date_range("20180101", periods=len(ret), freq='H')
    ret = pd.DataFrame(ret - comm * cost, index = ind)
    exp = np.exp(ret.resample('1D').sum()) - 1.0
    ggg = 'Drawdown:', emp.max_drawdown(exp).values[0], 'Sharpe:', emp.sharpe_ratio(exp)[0], \
    'Sortino:', emp.sortino_ratio(exp).values[0], 'Stability:', emp.stability_of_timeseries(exp), \
    'Tail:', emp.tail_ratio(exp), 'ValAtRisk:', emp.value_at_risk(exp)
    ttt = ' '.join(str(x) for x in ggg)
    print(ttt)
    plt.figure()
    np.exp(ret).cumprod().plot(figsize=(48, 12), title=ttt)
    plt.savefig('cumulative_return')
    plt.close()
    ret = ret.resample('1W').sum()
    plt.figure(figsize=(48, 12))
    pal = sns.color_palette("Greens_d", len(ret))
    rank = ret.iloc[:,0].argsort()
    ax = sns.barplot(x=ret.index.strftime('%d-%m'), y=ret.values.reshape(-1), palette=np.array(pal[::-1])[rank])
    ax.text(0.5, 1.0, ttt, horizontalalignment='center', verticalalignment='top', transform=ax.transAxes)
    plt.savefig('weekly_returns')
    plt.close()
    ew_df = pd.DataFrame(ew)
    plt.figure(figsize=(48, 12))
    ax = sns.heatmap(ew_df.T, cmap=cmap, center=0, xticklabels=False, robust=True)
    ax.text(0.5, 1.0, ttt, horizontalalignment='center', verticalalignment='top', transform=ax.transAxes)
    plt.savefig('portfolio_weights')
    plt.close()
    tr = np.diff(ew.T, axis=1)
    plt.figure(figsize=(96, 12))
    ax = sns.heatmap(tr, cmap=cmap, center=0, robust=True, yticklabels=False, xticklabels=False)
    ax.text(0.5, 1.0, ttt, horizontalalignment='center', verticalalignment='top', transform=ax.transAxes)
    plt.savefig('transactions')
    plt.close()
Ejemplo n.º 12
0
def report_metrics(strategy_rets, benchmark_rets, factor_returns=0):
    """使用 `empyrical`_ 库计算各种常见财务风险和绩效指标。

    Args:
        strategy_rets (:py:class:`pandas.Series`): 策略收益。
        benchmark_rets (:py:class:`pandas.Series`): 基准收益。
        factor_returns : 计算 excess_sharpe 时使用,策略计算时使用`strategy_rets`作为`factor_returns`,
            当不存在`strategy_rets`时使用`factor_returns`。
            `factor_returns`参考 :py:func:`empyrical.excess_sharpe` 中的`factor_returns`参数的解释。

    Examples:
        >>> from finance_tools_py._jupyter_helper import report_metrics
        >>> import pandas as pd
        >>> rep = report_metrics(pd.Series([-0.01,0.04,0.03,-0.02]),
                                 pd.Series([0.04,0.05,0.06,0.07]))
        >>> print(rep)
                                  基准         策略
        最大回撤                0.000000  -0.020000
        年化收益           713630.025679  10.326756
        年度波动性               0.204939   0.467333
        夏普比率               67.629875   5.392302
        R平方                 0.994780   0.614649
        盈利比率                1.650602   2.081081
        excess_sharpe       4.260282  -1.317465
        年复合增长率         713630.025679  10.326756


    Returns:
        :py:class:`pandas.DataFrame`:

    .. _empyrical:
        http://quantopian.github.io/empyrical/

    """
    if not benchmark_rets.empty:
        max_drawdown_benchmark = empyrical.max_drawdown(benchmark_rets)
        annual_return_benchmark = empyrical.annual_return(benchmark_rets)
        annual_volatility_benchmark = empyrical.annual_volatility(
            benchmark_rets)
        sharpe_ratio_benchmark = empyrical.sharpe_ratio(benchmark_rets)
        stability_of_timeseries_benchmark = empyrical.stability_of_timeseries(
            benchmark_rets)
        tail_ratio_benchmark = empyrical.tail_ratio(benchmark_rets)
        excess_sharpe_benchmark = empyrical.excess_sharpe(
            benchmark_rets, factor_returns)
        cagr_benchmark = empyrical.cagr(benchmark_rets)
    else:
        max_drawdown_benchmark = None
        annual_return_benchmark = None
        annual_volatility_benchmark = None
        sharpe_ratio_benchmark = None
        stability_of_timeseries_benchmark = None
        tail_ratio_benchmark = None
        excess_sharpe_benchmark = None
        cagr_benchmark = None
    max_drawdown_strategy = empyrical.max_drawdown(strategy_rets)
    annual_return_strategy = empyrical.annual_return(strategy_rets)
    annual_volatility_strategy = empyrical.annual_volatility(strategy_rets)
    sharpe_ratio_strategy = empyrical.sharpe_ratio(strategy_rets)
    stability_of_timeseries_strategy = empyrical.stability_of_timeseries(
        strategy_rets)
    tail_ratio_strategy = empyrical.tail_ratio(strategy_rets)
    excess_sharpe_strategy = empyrical.excess_sharpe(
        strategy_rets,
        benchmark_rets if not benchmark_rets.empty else factor_returns)
    cagr_strategy = empyrical.cagr(strategy_rets)

    return pd.DataFrame(
        {
            '基准': [
                max_drawdown_benchmark, annual_return_benchmark,
                annual_volatility_benchmark, sharpe_ratio_benchmark,
                stability_of_timeseries_benchmark, tail_ratio_benchmark,
                excess_sharpe_benchmark, cagr_benchmark
            ],
            '策略': [
                max_drawdown_strategy, annual_return_strategy,
                annual_volatility_strategy, sharpe_ratio_strategy,
                stability_of_timeseries_strategy, tail_ratio_strategy,
                excess_sharpe_strategy, cagr_strategy
            ]
        },
        index=[
            '最大回撤', '年化收益', '年度波动性', '夏普比率', 'R平方', '盈利比率', 'excess_sharpe',
            '年复合增长率'
        ])
Ejemplo n.º 13
0
    max_drawdown,
    sharpe_ratio,
    sortino_ratio,
    calmar_ratio,
    omega_ratio,
    tail_ratio
)
import pandas as pd
returns = pd.Series(
    index=pd.date_range('2017-03-10', '2017-03-19'),
    data=(-0.012143, 0.045350, 0.030957, 0.004902, 0.002341, -0.02103, 0.00148, 0.004820, -0.00023, 0.01201)
)

benchmark_returns = pd.Series(
    index=pd.date_range('2017-03-10', '2017-03-19'),
    data=(-0.031940, 0.025350, -0.020957, -0.000902, 0.007341, -0.01103, 0.00248, 0.008820, -0.00123, 0.01091)
)
creturns = cum_returns(returns)
max_drawdown(returns)
annual_return(returns)
annual_volatility(returns, period='daily')
calmar_ratio(returns)
omega_ratio(returns=returns, risk_free=0.01)
sharpe_ratio(returns=returns, risk_free=0.01)
sortino_ratio(returns=returns)
downside_risk(returns=returns)
alpha(returns=returns, factor_returns=benchmark_returns, risk_free=0.01)
beta(returns=returns, factor_returns=benchmark_returns, risk_free=0.01)
tail_ratio(returns=returns)

Ejemplo n.º 14
0
def analyze(context, perf):
    #print("perf.max_drawdown=", perf.max_drawdown)
    empyrical_max_drawdown = max_drawdown(perf.algorithm_period_return)
    print("empyrical_max_drawdown = ", empyrical_max_drawdown)

    empyrical_tail_ratio = tail_ratio(perf.algorithm_period_return)
    print("empyrical_tail_ratio = ", empyrical_tail_ratio)

    empyrical_sharpe_ratio = sharpe_ratio(perf.algorithm_period_return)
    print("empyrical_sharpe_ratio = ", empyrical_sharpe_ratio)

    empyrical_alpha_beta = alpha_beta(perf.algorithm_period_return,
                                      perf.benchmark_period_return)
    print("empyrical_alpha_beta = ", empyrical_alpha_beta)

    #cum_returns(perf)
    # Save results in CSV file
    filename = "csvoutput"
    perf.to_csv(filename + '.csv')

    filename_orders = "orders_output"

    perf0 = perf[['orders']]
    perf1 = perf0[perf0['orders'].apply(len) > 0]

    perf2 = pd.DataFrame(perf1['orders'])
    #perf2 = pd.DataFrame([x for x in perf1['orders']])
    #print(perf1[['orders',-1]].head(n=5))
    #convert list of dictionaries to dictionary
    perf2["ordersd"] = pd.Series(perf2["orders"].str[0])
    print(perf2["ordersd"].head(70))

    #extracting orders dictionary to multiple columns
    perf2 = pd.DataFrame([x for x in perf2['ordersd']])

    #unpack(perf2, 'ordersd')
    perf2.to_csv(filename_orders + '.csv')

    exchange = list(context.exchanges.values())[0]
    base_currency = exchange.base_currency.upper()

    axl = plt.subplot(411)
    perf.loc[:, ['portfolio_value']].plot(ax=axl)
    axl.legend_.remove()
    axl.set_ylabel('Portfolio Value\n({})'.format(base_currency))
    start, end = axl.get_ylim()
    axl.yaxis.set_ticks(np.arange(start, end, (end - start) / 5))

    ax2 = plt.subplot(412, sharex=axl)
    perf.loc[:, ['price', 'short_ema', 'long_ema']].plot(ax=ax2, label='Price')
    ax2.legend_.remove()
    ax2.set_ylabel('{asset}\n({base})'.format(asset=context.asset.symbol,
                                              base=base_currency))
    start, end = ax2.get_ylim()
    ax2.yaxis.set_ticks(np.arange(start, end, (end - start) / 5))

    transaction_df = extract_transactions(perf)
    if not transaction_df.empty:
        buy_df = transaction_df[transaction_df['amount'] > 0]
        sell_df = transaction_df[transaction_df['amount'] < 0]
        ax2.scatter(buy_df.index.to_pydatetime(),
                    perf.loc[buy_df.index, 'price'],
                    marker='^',
                    s=100,
                    c='green',
                    label='')
        ax2.scatter(sell_df.index.to_pydatetime(),
                    perf.loc[sell_df.index, 'price'],
                    marker='v',
                    s=100,
                    c='red',
                    label='')

    ax3 = plt.subplot(413, sharex=axl)
    perf.loc[:, ['algorithm_period_return', 'price_change']].plot(ax=ax3)
    ax3.legend_.remove()
    ax3.set_ylabel('Percent Change')
    start, end = ax3.get_ylim()
    ax3.yaxis.set_ticks(np.arange(0, end, (end - start) / 5))

    ax4 = plt.subplot(414, sharex=axl)
    perf.cash.plot(ax=ax4)
    ax4.set_ylabel('Cash\n({})'.format(base_currency))
    start, end = ax4.get_ylim()
    ax4.yaxis.set_ticks(np.arange(0, end, end / 5))

    plt.show()
Ejemplo n.º 15
0
 def test_tail_ratio(self, returns, expected):
     assert_almost_equal(empyrical.tail_ratio(returns), expected, 1)
Ejemplo n.º 16
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
Ejemplo n.º 17
0
def fin_funcs_port(df):
    """
    Financial calculations taken from Quantopians Empirical Library.
    :param df: dataframe containing daily returns calculated for a portfolio and as well for the related accounts.
    :return: Dictionary of financial ratios both for percent change returns and log returns.
    """
    returns_port = df["portfolio"]
    returns_acct = df["account"]

    risk_free_rate = 0.0

    annual_return_port = ep.annual_return(returns_port,
                                          period="daily",
                                          annualization=None)
    annual_return_acct = ep.annual_return(returns_acct,
                                          period="daily",
                                          annualization=None)

    cumm_return_port = ep.cum_returns(returns_port, starting_value=0).iloc[-1]
    cumm_return_acct = ep.cum_returns(returns_acct, starting_value=0).iloc[-1]

    cagr_port = ep.cagr(returns_port, period="daily", annualization=None)
    cagr_acct = ep.cagr(returns_acct, period="daily", annualization=None)

    sharpe_port = ep.sharpe_ratio(returns_port,
                                  risk_free=risk_free_rate,
                                  period="daily",
                                  annualization=None)
    sharpe_acct = ep.sharpe_ratio(returns_acct,
                                  risk_free=risk_free_rate,
                                  period="daily",
                                  annualization=None)

    annual_volatility_port = ep.annual_volatility(returns_port,
                                                  period="daily",
                                                  alpha=2.0,
                                                  annualization=None)
    annual_volatility_acct = ep.annual_volatility(returns_acct,
                                                  period="daily",
                                                  alpha=2.0,
                                                  annualization=None)
    max_drawdown_port = ep.max_drawdown(returns_port)
    max_drawdown_acct = ep.max_drawdown(returns_acct)

    calmar_port = ep.calmar_ratio(returns_port,
                                  period="daily",
                                  annualization=None)
    calmar_acct = ep.calmar_ratio(returns_acct,
                                  period="daily",
                                  annualization=None)

    sortino_port = ep.sortino_ratio(
        returns_port,
        required_return=0,
        period="daily",
        annualization=None,
        _downside_risk=None,
    )
    sortino_acct = ep.sortino_ratio(
        returns_acct,
        required_return=0,
        period="daily",
        annualization=None,
        _downside_risk=None,
    )

    tail_ratio_port = ep.tail_ratio(returns_port)
    tail_ratio_acct = ep.tail_ratio(returns_acct)

    financials = {
        ("return_portfolio", "annual_return"): annual_return_port,
        ("return_portfolio", "cumm_return"): cumm_return_port,
        ("return_portfolio", "cagr"): cagr_port,
        ("return_portfolio", "sharpe"): sharpe_port,
        ("return_portfolio", "annual_volatility"): annual_volatility_port,
        ("return_portfolio", "max_drawdown"): max_drawdown_port,
        ("return_portfolio", "calmar"): calmar_port,
        ("return_portfolio", "sortino"): sortino_port,
        ("return_portfolio", "tail_ratio"): tail_ratio_port,
        ("return_account", "annual_return"): annual_return_acct,
        ("return_account", "cumm_return"): cumm_return_acct,
        ("return_account", "cagr"): cagr_acct,
        ("return_account", "sharpe"): sharpe_acct,
        ("return_account", "annual_volatility"): annual_volatility_acct,
        ("return_account", "max_drawdown"): max_drawdown_acct,
        ("return_account", "calmar"): calmar_acct,
        ("return_account", "sortino"): sortino_acct,
        ("return_account", "tail_ratio"): tail_ratio_acct,
    }

    return financials
Ejemplo n.º 18
0
def analyze(context, perf): 
    #print("perf.max_drawdown=", perf.max_drawdown)
    empyrical_max_drawdown = max_drawdown(perf.algorithm_period_return)
    print("empyrical_max_drawdown = ", empyrical_max_drawdown)
    
    empyrical_tail_ratio = tail_ratio(perf.algorithm_period_return)
    print("empyrical_tail_ratio = ", empyrical_tail_ratio)
    
    empyrical_sharpe_ratio = sharpe_ratio(perf.algorithm_period_return)
    print("empyrical_sharpe_ratio = ", empyrical_sharpe_ratio)
    
    empyrical_alpha_beta = alpha_beta(perf.algorithm_period_return, perf.benchmark_period_return)
    print("empyrical_alpha_beta = ", empyrical_alpha_beta)    
    
    
    
    #cum_returns(perf)
    # Save results in CSV file
    filename = "csvoutput"
    perf.to_csv(filename + '.csv')      
    
    exchange = list(context.exchanges.values())[0]
    base_currency = exchange.base_currency.upper()
    
    axl = plt.subplot(411)
    perf.loc[:, ['portfolio_value']].plot(ax = axl)
    axl.legend_.remove()
    axl.set_ylabel('Portfolio Value\n({})'.format(base_currency))
    start, end = axl.get_ylim()
    axl.yaxis.set_ticks(np.arange(start, end, (end-start) / 5))
    
    ax2 = plt.subplot(412, sharex = axl)
    perf.loc[:,['price','short_ema','long_ema']].plot(ax = ax2, label = 'Price')
    ax2.legend_.remove()
    ax2.set_ylabel('{asset}\n({base})'.format(asset = context.asset.symbol, base = base_currency))
    start, end = ax2.get_ylim()
    ax2.yaxis.set_ticks(np.arange(start, end, (end-start) / 5))
    
    transaction_df = extract_transactions(perf)
    if not transaction_df.empty:
        buy_df = transaction_df[transaction_df['amount'] > 0]
        sell_df = transaction_df[transaction_df['amount'] < 0]
        ax2.scatter(buy_df.index.to_pydatetime(), perf.loc[buy_df.index, 'price'], marker = '^', s = 100, c = 'green', label = '')
        ax2.scatter(sell_df.index.to_pydatetime(), perf.loc[sell_df.index, 'price'], marker = 'v', s = 100, c = 'red', label = '')    
    
    
    ax3 = plt.subplot(413, sharex = axl)
    perf.loc[:,['algorithm_period_return', 'price_change']].plot(ax = ax3)
    ax3.legend_.remove()
    ax3.set_ylabel('Percent Change')
    start, end = ax3.get_ylim()
    ax3.yaxis.set_ticks(np.arange(0, end, (end-start) / 5))
    
    
    
    
    ax4 = plt.subplot(414, sharex = axl)
    perf.cash.plot(ax = ax4)
    ax4.set_ylabel('Cash\n({})'.format(base_currency))
    start, end = ax4.get_ylim()
    ax4.yaxis.set_ticks(np.arange(0, end, end / 5))
    
    
    
    plt.show()
Ejemplo n.º 19
0
def fin_funcs(df):
    """
    Financial calculations taken from Quantopians Empirical Library.

    :param df: dataframe containing daily returns calculated on a percentage change and also by log scale.
    :return: Dictionary of financial ratios both for percent change returns and log returns.
    """
    returns_pct = df["pct_change"]

    risk_free_rate = 0.0

    annual_return_pct = ep.annual_return(returns_pct,
                                         period="daily",
                                         annualization=None)
    cumm_return_pct = ep.cum_returns(returns_pct, starting_value=0).iloc[-1]
    cagr_pct = ep.cagr(returns_pct, period="daily", annualization=None)
    sharpe_pct = ep.sharpe_ratio(returns_pct,
                                 risk_free=risk_free_rate,
                                 period="daily",
                                 annualization=None)
    annual_volatility_pct = ep.annual_volatility(returns_pct,
                                                 period="daily",
                                                 alpha=2.0,
                                                 annualization=None)
    max_drawdown_pct = ep.max_drawdown(returns_pct)
    calmar_pct = ep.calmar_ratio(returns_pct,
                                 period="daily",
                                 annualization=None)
    sortino_pct = ep.sortino_ratio(
        returns_pct,
        required_return=0,
        period="daily",
        annualization=None,
        _downside_risk=None,
    )
    tail_ratio_pct = ep.tail_ratio(returns_pct)

    financials = {
        "annual_return": annual_return_pct,
        "cumm_return": cumm_return_pct,
        "cagr": cagr_pct,
        "sharpe": sharpe_pct,
        "annual_volatility": annual_volatility_pct,
        "max_drawdown": max_drawdown_pct,
        "calmar": calmar_pct,
        "sortino": sortino_pct,
        "tail_ratio": tail_ratio_pct,
    }

    # Originally set up program to analyse both pct_change and log returns, but the difference between log and
    # pct_change was not material to the final analysis. Consequently pct_change used exclusively. The code below
    # is left in tact should log returns at the account level be desired.

    # returns_log = df["log_ret"]
    # Log returns not used in final scenario.
    # annual_return_log = ep.annual_return(
    #     returns_log, period="daily", annualization=None
    # )
    # cumm_return_log = ep.cum_returns(returns_log, starting_value=0).iloc[-1]
    # cagr_log = ep.cagr(returns_log, period="daily", annualization=None)
    # sharpe_log = ep.sharpe_ratio(
    #     returns_log, risk_free=risk_free_rate, period="daily", annualization=None
    # )
    # annual_volatility_log = ep.annual_volatility(
    #     returns_log, period="daily", alpha=2.0, annualization=None
    # )
    # max_drawdown_log = ep.max_drawdown(returns_log)
    # calmar_log = ep.calmar_ratio(returns_log, period="daily", annualization=None)
    # sortino_log = ep.sortino_ratio(
    #     returns_log,
    #     required_return=0,
    #     period="daily",
    #     annualization=None,
    #     _downside_risk=None,
    # )
    # tail_ratio_log = ep.tail_ratio(returns_log)

    # financials = {
    #     ("return_percent_change", "annual_return"): annual_return_pct,
    #     ("return_percent_change", "cumm_return"): cumm_return_pct,
    #     ("return_percent_change", "cagr"): cagr_pct,
    #     ("return_percent_change", "sharpe"): sharpe_pct,
    #     ("return_percent_change", "annual_volatility"): annual_volatility_pct,
    #     ("return_percent_change", "max_drawdown"): max_drawdown_pct,
    #     ("return_percent_change", "calmar"): calmar_pct,
    #     ("return_percent_change", "sortino"): sortino_pct,
    #     ("return_percent_change", "tail_ratio"): tail_ratio_pct,
    #     ("return_log", "annual_return"): annual_return_log,
    #     ("return_log", "cumm_return"): cumm_return_log,
    #     ("return_log", "cagr"): cagr_log,
    #     ("return_log", "sharpe"): sharpe_log,
    #     ("return_log", "annual_volatility"): annual_volatility_log,
    #     ("return_log", "max_drawdown"): max_drawdown_log,
    #     ("return_log", "calmar"): calmar_log,
    #     ("return_log", "sortino"): sortino_log,
    #     ("return_log", "tail_ratio"): tail_ratio_log,
    #     }

    return financials
Ejemplo n.º 20
0
    def describer(self):
        tot_cnt = len(self.df)

        missed = self.df[
            (np.sign(self.df['return_close']) != self.df['signal'])
            & (self.df['signal'] == 0)]['strat_return'].describe()
        wrong = self.df[
            (np.sign(self.df['return_close']) == self.df['signal'] *
             (-1)) & (self.df['signal'] != 0)]['strat_return'].describe()
        jackpot = self.df[(np.sign(
            self.df['return_close']) == self.df['signal']
                           )]['strat_return'].describe()

        desc = pd.DataFrame([jackpot, wrong, missed],
                            index=['jackpot', 'wrong',
                                   'missed']).T.to_markdown()

        trans_fee_tot = self.df['transfee'].sum()

        return_ret, return_bench = {}, {}
        self.df['date'] = self.df.index.date
        for (k, v) in self.df.groupby('date'):
            return_ret[k] = v['strat_return'].sum()
            return_bench[k] = v['bench_return'].sum()
        rret = pd.Series(list(return_ret.values()),
                         index=list(return_ret.keys()),
                         name='rret')
        rbench = pd.Series(list(return_bench.values()),
                           index=list(return_bench.keys()),
                           name='rbench')
        (alpha, beta) = alpha_beta(rret, rbench, period='daily')
        sharpe = sharpe_ratio(rret, period='daily')
        max_down = max_drawdown(rret)
        ann_return_strat = annual_return(rret, period='daily')
        ann_return_bench = annual_return(rbench, period='daily')
        t_r = tail_ratio(rret)

        returns = f"Strategy Return: {round(self.pnl * 100, 2)}% | " \
                  f"Strategy Annualized Return: {round(ann_return_strat * 100, 2)}%. \n" \
                  f"BenchMark return: {round(self.df['bench_return'].sum() * 100, 2)}% | " \
                  f"BenchMark Annualized Return: {round(ann_return_bench * 100, 2)}%.\n"

        desc_ = f"Strategy: {self.func} \n" \
                f"Transaction Fee Percentage: {self.trans_fee_pctg}\n" \
                f"Intraday Closing Time: {self.trade_flag}\n" \
                f"Params: {self.signal_params}\n" \
                f"Test Period: {self.start_dt} - {self.end_dt}\n" \
                f"-- {self.id} --\n" \
                f"-- {self.timeframe} -- \n" \
                f"-- Position: {self.pos_} --\n" \
                f"-- Barly Stoploss: {self.stop_loss} --\n" \
                f"-- Action on Sig0: {self.action_on_0} --\n" \
                f"-- Signal Shift: {self.sig_shift} --\n" \
                f"Transaction Fee Total: {round(trans_fee_tot * 100, 2)}%\n" \
                f"Signal Ratio: {round(self.signal_ratio * 100, 2)}%\n" \
                f"Open Position: {self.open_t} times; Close Position: {self.close_t} times\n" \
                f"Sharpe Ratio: {round(sharpe, 2)} \n" \
                f"Tail Ratio: {round(t_r, 2)}\n" \
                f"Alpha: {round(alpha * 100, 2)}% | Beta: {round(beta * 100, 2)}% \n" \
                f"Max Drawdown: {round(max_down * 100, 2)}% \n" \
                f"Max Daily Drawdown: {round(rret.min() * 100, 2)}% \n" \
                f"Total Win: {self.winner} | Total Loss: {self.loser} | " \
                f"W/L Ratio: {round(self.winner / self.loser, 2) if self.loser != 0 else 0}\n"

        source_code = "\n\n".join([inspect.getsource(f) for f in self.func
                                   ]) if self.func is not None else " "
        source_code_neut = "\n\n".join(
            [inspect.getsource(f)
             for f in self.neut_func]) if self.neut_func is not None else " "

        t_stamp = datetime.now().strftime("%Y-%m-%d_%H:%M:%S")
        logger.info(f"-- {t_stamp} --\n{desc_}{returns}\n")

        # 所有回测都值得记录
        path_ = os.path.join(
            fp, f"../../docs/backtest/"
            f"{self.id}-{self.timeframe}-Sharpe{round(sharpe, 2)}-{datetime.now().strftime('%y%m%d-%H:%M:%S')}/"
        )
        os.mkdir(path_)

        plot = self.df[['close', 'strat_ret_cumsum',
                        'bench_ret_cumsum']].plot(figsize=(16, 9),
                                                  secondary_y='close')
        fig = plot.get_figure()
        fig_path = os.path.join(path_, f"return_curve.png")
        fig.savefig(fig_path)

        rec_path = os.path.join(path_, "trade_record.csv")
        self.df.to_csv(rec_path)

        desc_path = os.path.join(path_, "desc.txt")
        with open(desc_path, mode="w+", encoding='utf8') as f:
            f.write(
                desc_ + returns + '\n' + f'\nTotal Bars: {tot_cnt} \n' +
                '\nStatistics Desc: \n' + desc +
                '\n* NOTE: THIS DESCRIPTION DIFFERS FROM W/L RATIO ABOVE '
                'BECAUSE ONLY SIGNAL DIRECTION CORRECTNESS IS CONSIDERED HERE.\n'
                + '\n\nBias_factors: \n' + source_code + '\nNeut_factors: \n' +
                source_code_neut)

        # 但只有高夏普低回撤的回测才配拥有高级可视化
        if (sharpe > 1.5) & (max_down >= -0.2 * self.pos_):
            rich_visual_path = os.path.join(path_, "rich_visual.html")
            kline = rv.draw_kline_with_yield_and_signal(self.df)
            scatters_fr = rv.draw_factor_return_eval(self.bias_factor_df)
            scatters_ff = rv.draw_factor_eval(self.bias_factor_df)
            res_charts = [kline, *scatters_fr, *scatters_ff]
            if self.neut_factor_df is not None:
                sca_neut_fr = rv.draw_factor_return_eval(self.neut_factor_df)
                sca_neut_ff = rv.draw_factor_eval(self.neut_factor_df)
                res_charts += [*sca_neut_fr, *sca_neut_ff]
            rv.form_page(res_charts, rich_visual_path)
Ejemplo n.º 21
0
def get_report(my_portfolio,
               rf=0.0,
               sigma_value=1,
               confidence_value=0.95,
               filename: str = "report.pdf"):
    try:
        # we want to get the dataframe with the dates and weights
        rebalance_schedule = my_portfolio.rebalance

        columns = []
        for date in rebalance_schedule.columns:
            date = date[0:10]
            columns.append(date)
        rebalance_schedule.columns = columns

        # then want to make a list of the dates and start with our first date
        dates = [my_portfolio.start_date]

        # then our rebalancing dates into that list
        dates = dates + rebalance_schedule.columns.to_list()

        datess = []
        for date in dates:
            date = date[0:10]
            datess.append(date)
        dates = datess
        # this will hold returns
        returns = pd.Series()

        # then we want to be able to call the dates like tuples
        for i in range(len(dates) - 1):
            # get our weights
            weights = rebalance_schedule[str(dates[i + 1])]

            # then we want to get the returns

            add_returns = get_returns(
                my_portfolio.portfolio,
                weights,
                start_date=dates[i],
                end_date=dates[i + 1],
            )

            # then append those returns
            returns = returns.append(add_returns)

    except AttributeError:
        try:
            returns = get_returns_from_data(my_portfolio.data,
                                            my_portfolio.weights)
        except AttributeError:
            returns = get_returns(
                my_portfolio.portfolio,
                my_portfolio.weights,
                start_date=my_portfolio.start_date,
                end_date=my_portfolio.end_date,
            )

    creturns = (returns + 1).cumprod()

    # risk manager
    try:
        if list(my_portfolio.risk_manager.keys())[0] == "Stop Loss":

            values = []
            for r in creturns:
                if r <= 1 + my_portfolio.risk_manager["Stop Loss"]:
                    values.append(r)
                else:
                    pass

            try:
                date = creturns[creturns == values[0]].index[0]
                date = str(date.to_pydatetime())
                my_portfolio.end_date = date[0:10]
                returns = returns[:my_portfolio.end_date]

            except Exception as e:
                pass

        if list(my_portfolio.risk_manager.keys())[0] == "Take Profit":

            values = []
            for r in creturns:
                if r >= 1 + my_portfolio.risk_manager["Take Profit"]:
                    values.append(r)
                else:
                    pass

            try:
                date = creturns[creturns == values[0]].index[0]
                date = str(date.to_pydatetime())
                my_portfolio.end_date = date[0:10]
                returns = returns[:my_portfolio.end_date]

            except Exception as e:
                pass

        if list(my_portfolio.risk_manager.keys())[0] == "Max Drawdown":

            drawdown = qs.stats.to_drawdown_series(returns)

            values = []
            for r in drawdown:
                if r <= my_portfolio.risk_manager["Max Drawdown"]:
                    values.append(r)
                else:
                    pass

            try:
                date = drawdown[drawdown == values[0]].index[0]
                date = str(date.to_pydatetime())
                my_portfolio.end_date = date[0:10]
                returns = returns[:my_portfolio.end_date]

            except Exception as e:
                pass

    except Exception as e:
        pass

    fig1, ax1 = plt.subplots()
    fig1.set_size_inches(5, 5)

    #defining colors for the allocation pie
    cs = [
        "#ff9999",
        "#66b3ff",
        "#99ff99",
        "#ffcc99",
        "#f6c9ff",
        "#a6fff6",
        "#fffeb8",
        "#ffe1d4",
        "#cccdff",
        "#fad6ff",
    ]

    wts = copy.deepcopy(my_portfolio.weights)
    port = copy.deepcopy(my_portfolio.portfolio)
    indices = [i for i, x in enumerate(wts) if x == 0.0]

    while 0.0 in wts:
        wts.remove(0.0)

    for i in sorted(indices, reverse=True):
        del port[i]

    ax1.pie(wts, labels=port, autopct="%1.1f%%", shadow=False, colors=cs)
    ax1.axis(
        "equal")  # Equal aspect ratio ensures that pie is drawn as a circle.
    plt.rcParams["font.size"] = 12
    plt.close(fig1)
    fig1.savefig("allocation.png")

    pdf = FPDF()
    pdf.add_page()
    pdf.set_font("arial", "B", 14)
    pdf.image(
        "https://user-images.githubusercontent.com/61618641/120909011-98f8a180-c670-11eb-8844-2d423ba3fa9c.png",
        x=None,
        y=None,
        w=45,
        h=5,
        type="",
        link="https://github.com/ssantoshp/Empyrial",
    )
    pdf.cell(20, 15, f"Report", ln=1)
    pdf.set_font("arial", size=11)
    pdf.image("allocation.png", x=135, y=0, w=70, h=70, type="", link="")
    pdf.cell(20, 7, f"Start date: " + str(my_portfolio.start_date), ln=1)
    pdf.cell(20, 7, f"End date: " + str(my_portfolio.end_date), ln=1)

    benchmark = get_returns(
        my_portfolio.benchmark,
        wts=[1],
        start_date=my_portfolio.start_date,
        end_date=my_portfolio.end_date,
    )

    CAGR = cagr(returns, period='daily', annualization=None)
    # CAGR = round(CAGR, 2)
    # CAGR = CAGR.tolist()
    CAGR = str(round(CAGR * 100, 2)) + "%"

    CUM = cum_returns(returns, starting_value=0, out=None) * 100
    CUM = CUM.iloc[-1]
    CUM = CUM.tolist()
    CUM = str(round(CUM, 2)) + "%"

    VOL = qs.stats.volatility(returns, annualize=True)
    VOL = VOL.tolist()
    VOL = str(round(VOL * 100, 2)) + " %"

    SR = qs.stats.sharpe(returns, rf=rf)
    SR = np.round(SR, decimals=2)
    SR = str(SR)

    empyrial.SR = SR

    CR = qs.stats.calmar(returns)
    CR = CR.tolist()
    CR = str(round(CR, 2))

    empyrial.CR = CR

    STABILITY = stability_of_timeseries(returns)
    STABILITY = round(STABILITY, 2)
    STABILITY = str(STABILITY)

    MD = max_drawdown(returns, out=None)
    MD = str(round(MD * 100, 2)) + " %"
    """OR = omega_ratio(returns, risk_free=0.0, required_return=0.0)
    OR = round(OR,2)
    OR = str(OR)
    print(OR)"""

    SOR = sortino_ratio(returns, required_return=0, period='daily')
    SOR = round(SOR, 2)
    SOR = str(SOR)

    SK = qs.stats.skew(returns)
    SK = round(SK, 2)
    SK = SK.tolist()
    SK = str(SK)

    KU = qs.stats.kurtosis(returns)
    KU = round(KU, 2)
    KU = KU.tolist()
    KU = str(KU)

    TA = tail_ratio(returns)
    TA = round(TA, 2)
    TA = str(TA)

    CSR = qs.stats.common_sense_ratio(returns)
    CSR = round(CSR, 2)
    CSR = CSR.tolist()
    CSR = str(CSR)

    VAR = qs.stats.value_at_risk(returns,
                                 sigma=sigma_value,
                                 confidence=confidence_value)
    VAR = np.round(VAR, decimals=2)
    VAR = str(VAR * 100) + " %"

    alpha, beta = alpha_beta(returns, benchmark, risk_free=rf)
    AL = round(alpha, 2)
    BTA = round(beta, 2)

    def condition(x):
        return x > 0

    win = sum(condition(x) for x in returns)
    total = len(returns)
    win_ratio = win / total
    win_ratio = win_ratio * 100
    win_ratio = round(win_ratio, 2)

    IR = calculate_information_ratio(returns, benchmark.iloc[:, 0])
    IR = round(IR, 2)

    data = {
        "": [
            "Annual return",
            "Cumulative return",
            "Annual volatility",
            "Winning day ratio",
            "Sharpe ratio",
            "Calmar ratio",
            "Information ratio",
            "Stability",
            "Max Drawdown",
            "Sortino ratio",
            "Skew",
            "Kurtosis",
            "Tail Ratio",
            "Common sense ratio",
            "Daily value at risk",
            "Alpha",
            "Beta",
        ],
        "Backtest": [
            CAGR,
            CUM,
            VOL,
            f"{win_ratio}%",
            SR,
            CR,
            IR,
            STABILITY,
            MD,
            SOR,
            SK,
            KU,
            TA,
            CSR,
            VAR,
            AL,
            BTA,
        ],
    }

    # Create DataFrame
    df = pd.DataFrame(data)
    df.set_index("", inplace=True)
    df.style.set_properties(**{
        "background-color": "white",
        "color": "black",
        "border-color": "black"
    })

    empyrial.df = data

    y = []
    for x in returns:
        y.append(x)

    arr = np.array(y)
    # arr
    # returns.index
    my_color = np.where(arr >= 0, "blue", "grey")
    ret = plt.figure(figsize=(30, 8))
    plt.vlines(x=returns.index, ymin=0, ymax=arr, color=my_color, alpha=0.4)
    plt.title("Returns")
    plt.close(ret)
    ret.savefig("ret.png")

    pdf.cell(20, 7, f"", ln=1)
    pdf.cell(20, 7, f"Annual return: " + str(CAGR), ln=1)
    pdf.cell(20, 7, f"Cumulative return: " + str(CUM), ln=1)
    pdf.cell(20, 7, f"Annual volatility: " + str(VOL), ln=1)
    pdf.cell(20, 7, f"Winning day ratio: " + str(win_ratio), ln=1)
    pdf.cell(20, 7, f"Sharpe ratio: " + str(SR), ln=1)
    pdf.cell(20, 7, f"Calmar ratio: " + str(CR), ln=1)
    pdf.cell(20, 7, f"Information ratio: " + str(IR), ln=1)
    pdf.cell(20, 7, f"Stability: " + str(STABILITY), ln=1)
    pdf.cell(20, 7, f"Max drawdown: " + str(MD), ln=1)
    pdf.cell(20, 7, f"Sortino ratio: " + str(SOR), ln=1)
    pdf.cell(20, 7, f"Skew: " + str(SK), ln=1)
    pdf.cell(20, 7, f"Kurtosis: " + str(KU), ln=1)
    pdf.cell(20, 7, f"Tail ratio: " + str(TA), ln=1)
    pdf.cell(20, 7, f"Common sense ratio: " + str(CSR), ln=1)
    pdf.cell(20, 7, f"Daily value at risk: " + str(VAR), ln=1)
    pdf.cell(20, 7, f"Alpha: " + str(AL), ln=1)
    pdf.cell(20, 7, f"Beta: " + str(BTA), ln=1)

    qs.plots.returns(returns,
                     benchmark,
                     cumulative=True,
                     savefig="retbench.png",
                     show=False)
    qs.plots.yearly_returns(returns,
                            benchmark,
                            savefig="y_returns.png",
                            show=False),
    qs.plots.monthly_heatmap(returns, savefig="heatmap.png", show=False)
    qs.plots.drawdown(returns, savefig="drawdown.png", show=False)
    qs.plots.drawdowns_periods(returns, savefig="d_periods.png", show=False)
    qs.plots.rolling_volatility(returns, savefig="rvol.png", show=False)
    qs.plots.rolling_sharpe(returns, savefig="rsharpe.png", show=False)
    qs.plots.rolling_beta(returns, benchmark, savefig="rbeta.png", show=False)

    pdf.image("ret.png", x=-20, y=None, w=250, h=80, type="", link="")
    pdf.cell(20, 7, f"", ln=1)
    pdf.image("y_returns.png", x=-20, y=None, w=200, h=100, type="", link="")
    pdf.cell(20, 7, f"", ln=1)
    pdf.image("retbench.png", x=None, y=None, w=200, h=100, type="", link="")
    pdf.cell(20, 7, f"", ln=1)
    pdf.image("heatmap.png", x=None, y=None, w=200, h=80, type="", link="")
    pdf.cell(20, 7, f"", ln=1)
    pdf.image("drawdown.png", x=None, y=None, w=200, h=80, type="", link="")
    pdf.cell(20, 7, f"", ln=1)
    pdf.image("d_periods.png", x=None, y=None, w=200, h=80, type="", link="")
    pdf.cell(20, 7, f"", ln=1)
    pdf.image("rvol.png", x=None, y=None, w=190, h=80, type="", link="")
    pdf.cell(20, 7, f"", ln=1)
    pdf.image("rsharpe.png", x=None, y=None, w=190, h=80, type="", link="")
    pdf.cell(20, 7, f"", ln=1)
    pdf.image("rbeta.png", x=None, y=None, w=190, h=80, type="", link="")

    pdf.output(dest="F", name=filename)
Ejemplo n.º 22
0
def empyrial(my_portfolio, rf=0.0, sigma_value=1, confidence_value=0.95):
    try:
        # we want to get the dataframe with the dates and weights
        rebalance_schedule = my_portfolio.rebalance

        columns = []
        for date in rebalance_schedule.columns:
            date = date[0:10]
            columns.append(date)
        rebalance_schedule.columns = columns

        # then want to make a list of the dates and start with our first date
        dates = [my_portfolio.start_date]

        # then our rebalancing dates into that list
        dates = dates + rebalance_schedule.columns.to_list()

        datess = []
        for date in dates:
            date = date[0:10]
            datess.append(date)
        dates = datess
        # this will hold returns
        returns = pd.Series()

        # then we want to be able to call the dates like tuples
        for i in range(len(dates) - 1):
            # get our weights
            weights = rebalance_schedule[str(dates[i + 1])]

            # then we want to get the returns

            add_returns = get_returns(
                my_portfolio.portfolio,
                weights,
                start_date=dates[i],
                end_date=dates[i + 1],
            )

            # then append those returns
            returns = returns.append(add_returns)

    except AttributeError:
        try:
            returns = get_returns_from_data(my_portfolio.data,
                                            my_portfolio.weights)
        except AttributeError:
            returns = get_returns(
                my_portfolio.portfolio,
                my_portfolio.weights,
                start_date=my_portfolio.start_date,
                end_date=my_portfolio.end_date,
            )

    creturns = (returns + 1).cumprod()

    # risk manager
    try:
        if list(my_portfolio.risk_manager.keys())[0] == "Stop Loss":

            values = []
            for r in creturns:
                if r <= 1 + my_portfolio.risk_manager["Stop Loss"]:
                    values.append(r)
                else:
                    pass

            try:
                date = creturns[creturns == values[0]].index[0]
                date = str(date.to_pydatetime())
                my_portfolio.end_date = date[0:10]
                returns = returns[:my_portfolio.end_date]

            except Exception as e:
                pass

        if list(my_portfolio.risk_manager.keys())[0] == "Take Profit":

            values = []
            for r in creturns:
                if r >= 1 + my_portfolio.risk_manager["Take Profit"]:
                    values.append(r)
                else:
                    pass

            try:
                date = creturns[creturns == values[0]].index[0]
                date = str(date.to_pydatetime())
                my_portfolio.end_date = date[0:10]
                returns = returns[:my_portfolio.end_date]

            except Exception as e:
                pass

        if list(my_portfolio.risk_manager.keys())[0] == "Max Drawdown":

            drawdown = qs.stats.to_drawdown_series(returns)

            values = []
            for r in drawdown:
                if r <= my_portfolio.risk_manager["Max Drawdown"]:
                    values.append(r)
                else:
                    pass

            try:
                date = drawdown[drawdown == values[0]].index[0]
                date = str(date.to_pydatetime())
                my_portfolio.end_date = date[0:10]
                returns = returns[:my_portfolio.end_date]

            except Exception as e:
                pass

    except Exception as e:
        pass

    print("Start date: " + str(my_portfolio.start_date))
    print("End date: " + str(my_portfolio.end_date))

    benchmark = get_returns(
        my_portfolio.benchmark,
        wts=[1],
        start_date=my_portfolio.start_date,
        end_date=my_portfolio.end_date,
    )

    CAGR = cagr(returns, period='daily', annualization=None)
    # CAGR = round(CAGR, 2)
    # CAGR = CAGR.tolist()
    CAGR = str(round(CAGR * 100, 2)) + "%"

    CUM = cum_returns(returns, starting_value=0, out=None) * 100
    CUM = CUM.iloc[-1]
    CUM = CUM.tolist()
    CUM = str(round(CUM, 2)) + "%"

    VOL = qs.stats.volatility(returns, annualize=True)
    VOL = VOL.tolist()
    VOL = str(round(VOL * 100, 2)) + " %"

    SR = qs.stats.sharpe(returns, rf=rf)
    SR = np.round(SR, decimals=2)
    SR = str(SR)

    empyrial.SR = SR

    CR = qs.stats.calmar(returns)
    CR = CR.tolist()
    CR = str(round(CR, 2))

    empyrial.CR = CR

    STABILITY = stability_of_timeseries(returns)
    STABILITY = round(STABILITY, 2)
    STABILITY = str(STABILITY)

    MD = max_drawdown(returns, out=None)
    MD = str(round(MD * 100, 2)) + " %"
    """OR = omega_ratio(returns, risk_free=0.0, required_return=0.0)
    OR = round(OR,2)
    OR = str(OR)
    print(OR)"""

    SOR = sortino_ratio(returns, required_return=0, period='daily')
    SOR = round(SOR, 2)
    SOR = str(SOR)

    SK = qs.stats.skew(returns)
    SK = round(SK, 2)
    SK = SK.tolist()
    SK = str(SK)

    KU = qs.stats.kurtosis(returns)
    KU = round(KU, 2)
    KU = KU.tolist()
    KU = str(KU)

    TA = tail_ratio(returns)
    TA = round(TA, 2)
    TA = str(TA)

    CSR = qs.stats.common_sense_ratio(returns)
    CSR = round(CSR, 2)
    CSR = CSR.tolist()
    CSR = str(CSR)

    VAR = qs.stats.value_at_risk(returns,
                                 sigma=sigma_value,
                                 confidence=confidence_value)
    VAR = np.round(VAR, decimals=2)
    VAR = str(VAR * 100) + " %"

    alpha, beta = alpha_beta(returns, benchmark, risk_free=rf)
    AL = round(alpha, 2)
    BTA = round(beta, 2)

    def condition(x):
        return x > 0

    win = sum(condition(x) for x in returns)
    total = len(returns)
    win_ratio = win / total
    win_ratio = win_ratio * 100
    win_ratio = round(win_ratio, 2)

    IR = calculate_information_ratio(returns, benchmark.iloc[:, 0])
    IR = round(IR, 2)

    data = {
        "": [
            "Annual return",
            "Cumulative return",
            "Annual volatility",
            "Winning day ratio",
            "Sharpe ratio",
            "Calmar ratio",
            "Information ratio",
            "Stability",
            "Max Drawdown",
            "Sortino ratio",
            "Skew",
            "Kurtosis",
            "Tail Ratio",
            "Common sense ratio",
            "Daily value at risk",
            "Alpha",
            "Beta",
        ],
        "Backtest": [
            CAGR,
            CUM,
            VOL,
            f"{win_ratio}%",
            SR,
            CR,
            IR,
            STABILITY,
            MD,
            SOR,
            SK,
            KU,
            TA,
            CSR,
            VAR,
            AL,
            BTA,
        ],
    }

    # Create DataFrame
    df = pd.DataFrame(data)
    df.set_index("", inplace=True)
    df.style.set_properties(**{
        "background-color": "white",
        "color": "black",
        "border-color": "black"
    })
    display(df)

    empyrial.df = data

    y = []
    for x in returns:
        y.append(x)

    arr = np.array(y)
    # arr
    # returns.index
    my_color = np.where(arr >= 0, "blue", "grey")
    plt.figure(figsize=(30, 8))
    plt.vlines(x=returns.index, ymin=0, ymax=arr, color=my_color, alpha=0.4)
    plt.title("Returns")

    empyrial.returns = returns
    empyrial.creturns = creturns
    empyrial.benchmark = benchmark
    empyrial.CAGR = CAGR
    empyrial.CUM = CUM
    empyrial.VOL = VOL
    empyrial.SR = SR
    empyrial.win_ratio = win_ratio
    empyrial.CR = CR
    empyrial.IR = IR
    empyrial.STABILITY = STABILITY
    empyrial.MD = MD
    empyrial.SOR = SOR
    empyrial.SK = SK
    empyrial.KU = KU
    empyrial.TA = TA
    empyrial.CSR = CSR
    empyrial.VAR = VAR
    empyrial.AL = AL
    empyrial.BTA = BTA

    try:
        empyrial.orderbook = make_rebalance.output
    except Exception as e:
        OrderBook = pd.DataFrame({
            "Assets": my_portfolio.portfolio,
            "Allocation": my_portfolio.weights,
        })

        empyrial.orderbook = OrderBook.T

    wts = copy.deepcopy(my_portfolio.weights)
    indices = [i for i, x in enumerate(wts) if x == 0.0]

    while 0.0 in wts:
        wts.remove(0.0)

    for i in sorted(indices, reverse=True):
        del my_portfolio.portfolio[i]

    return (
        qs.plots.returns(returns, benchmark, cumulative=True),
        qs.plots.yearly_returns(returns, benchmark),
        qs.plots.monthly_heatmap(returns),
        qs.plots.drawdown(returns),
        qs.plots.drawdowns_periods(returns),
        qs.plots.rolling_volatility(returns),
        qs.plots.rolling_sharpe(returns),
        qs.plots.rolling_beta(returns, benchmark),
        graph_opt(my_portfolio.portfolio, wts, pie_size=7, font_size=14),
    )