示例#1
0
    def compare_strategy_vs_benchmark(self, br, strategy_df, benchmark_df):
        """
        compare_strategy_vs_benchmark - Compares the trading strategy we are backtesting against a benchmark

        Parameters
        ----------
        br : BacktestRequest
            Parameters for backtest such as start and finish dates

        strategy_df : pandas.DataFrame
            Strategy time series

        benchmark_df : pandas.DataFrame
            Benchmark time series
        """

        include_benchmark = False
        calc_stats = False

        if hasattr(br, 'include_benchmark'): include_benchmark = br.include_benchmark
        if hasattr(br, 'calc_stats'): calc_stats = br.calc_stats

        if include_benchmark:
            ret_stats = RetStats()
            risk_engine = RiskEngine()
            filter = Filter()
            calculations = Calculations()

            # align strategy time series with that of benchmark
            strategy_df, benchmark_df = strategy_df.align(benchmark_df, join='left', axis = 0)

            # if necessary apply vol target to benchmark (to make it comparable with strategy)
            if hasattr(br, 'portfolio_vol_adjust'):
                if br.portfolio_vol_adjust is True:
                    benchmark_df = risk_engine.calculate_vol_adjusted_index_from_prices(benchmark_df, br = br)

            # only calculate return statistics if this has been specified (note when different frequencies of data
            # might underrepresent vol
            # if calc_stats:
            benchmark_df = benchmark_df.fillna(method='ffill')
            ret_stats.calculate_ret_stats_from_prices(benchmark_df, br.ann_factor)

            if calc_stats:
                benchmark_df.columns = ret_stats.summary()

            # realign strategy & benchmark
            strategy_benchmark_df = strategy_df.join(benchmark_df, how='inner')
            strategy_benchmark_df = strategy_benchmark_df.fillna(method='ffill')

            strategy_benchmark_df = filter.filter_time_series_by_date(br.plot_start, br.finish_date, strategy_benchmark_df)
            strategy_benchmark_df = calculations.create_mult_index_from_prices(strategy_benchmark_df)

            self._benchmark_pnl = benchmark_df
            self._benchmark_ret_stats = ret_stats

            return strategy_benchmark_df

        return strategy_df
示例#2
0
    def calculate_trading_PnL(self, br, asset_a_df, signal_df):
        """
        calculate_trading_PnL - Calculates P&L of a trading strategy and statistics to be retrieved later

        Parameters
        ----------
        br : BacktestRequest
            Parameters for the backtest specifying start date, finish data, transaction costs etc.

        asset_a_df : pandas.DataFrame
            Asset prices to be traded

        signal_df : pandas.DataFrame
            Signals for the trading strategy
        """

        calculations = Calculations()
        # signal_df.to_csv('e:/temp0.csv')
        # make sure the dates of both traded asset and signal are aligned properly
        asset_df, signal_df = asset_a_df.align(signal_df,
                                               join='left',
                                               axis='index')

        # only allow signals to change on the days when we can trade assets
        signal_df = signal_df.mask(numpy.isnan(
            asset_df.values))  # fill asset holidays with NaN signals
        signal_df = signal_df.fillna(method='ffill')  # fill these down
        asset_df = asset_df.fillna(method='ffill')  # fill down asset holidays

        returns_df = calculations.calculate_returns(asset_df)
        tc = br.spot_tc_bp

        signal_cols = signal_df.columns.values
        returns_cols = returns_df.columns.values

        pnl_cols = []

        for i in range(0, len(returns_cols)):
            pnl_cols.append(returns_cols[i] + " / " + signal_cols[i])

        # do we have a vol target for individual signals?
        if hasattr(br, 'signal_vol_adjust'):
            if br.signal_vol_adjust is True:
                risk_engine = RiskEngine()

                if not (hasattr(br, 'signal_vol_resample_type')):
                    br.signal_vol_resample_type = 'mean'

                if not (hasattr(br, 'signal_vol_resample_freq')):
                    br.signal_vol_resample_freq = None

                leverage_df = risk_engine.calculate_leverage_factor(
                    returns_df, br.signal_vol_target,
                    br.signal_vol_max_leverage, br.signal_vol_periods,
                    br.signal_vol_obs_in_year, br.signal_vol_rebalance_freq,
                    br.signal_vol_resample_freq, br.signal_vol_resample_type)

                signal_df = pandas.DataFrame(signal_df.values *
                                             leverage_df.values,
                                             index=signal_df.index,
                                             columns=signal_df.columns)

                self._individual_leverage = leverage_df  # contains leverage of individual signal (before portfolio vol target)

        _pnl = calculations.calculate_signal_returns_with_tc_matrix(signal_df,
                                                                    returns_df,
                                                                    tc=tc)
        _pnl.columns = pnl_cols

        # portfolio is average of the underlying signals: should we sum them or average them?
        if hasattr(br, 'portfolio_combination'):
            if br.portfolio_combination == 'sum':
                portfolio = pandas.DataFrame(data=_pnl.sum(axis=1),
                                             index=_pnl.index,
                                             columns=['Portfolio'])
            elif br.portfolio_combination == 'mean':
                portfolio = pandas.DataFrame(data=_pnl.mean(axis=1),
                                             index=_pnl.index,
                                             columns=['Portfolio'])
        else:
            portfolio = pandas.DataFrame(data=_pnl.mean(axis=1),
                                         index=_pnl.index,
                                         columns=['Portfolio'])

        portfolio_leverage_df = pandas.DataFrame(data=numpy.ones(
            len(_pnl.index)),
                                                 index=_pnl.index,
                                                 columns=['Portfolio'])

        # should we apply vol target on a portfolio level basis?
        if hasattr(br, 'portfolio_vol_adjust'):
            if br.portfolio_vol_adjust is True:
                risk_engine = RiskEngine()

                portfolio, portfolio_leverage_df = risk_engine.calculate_vol_adjusted_returns(
                    portfolio, br=br)

        self._portfolio = portfolio
        self._signal = signal_df  # individual signals (before portfolio leverage)
        self._portfolio_leverage = portfolio_leverage_df  # leverage on portfolio

        # multiply portfolio leverage * individual signals to get final position signals
        length_cols = len(signal_df.columns)
        leverage_matrix = numpy.repeat(
            portfolio_leverage_df.values.flatten()[numpy.newaxis, :],
            length_cols, 0)

        # final portfolio signals (including signal & portfolio leverage)
        self._portfolio_signal = pandas.DataFrame(data=numpy.multiply(
            numpy.transpose(leverage_matrix), signal_df.values),
                                                  index=signal_df.index,
                                                  columns=signal_df.columns)

        if hasattr(br, 'portfolio_combination'):
            if br.portfolio_combination == 'sum':
                pass
            elif br.portfolio_combination == 'mean':
                self._portfolio_signal = self._portfolio_signal / float(
                    length_cols)
        else:
            self._portfolio_signal = self._portfolio_signal / float(
                length_cols)

        self._pnl = _pnl  # individual signals P&L

        # TODO FIX very slow - hence only calculate on demand
        _pnl_trades = None
        # _pnl_trades = calculations.calculate_individual_trade_gains(signal_df, _pnl)
        self._pnl_trades = _pnl_trades

        self._ret_stats_pnl = RetStats()
        self._ret_stats_pnl.calculate_ret_stats(self._pnl, br.ann_factor)

        self._portfolio.columns = ['Port']
        self._ret_stats_portfolio = RetStats()
        self._ret_stats_portfolio.calculate_ret_stats(self._portfolio,
                                                      br.ann_factor)

        self._cumpnl = calculations.create_mult_index(
            self._pnl)  # individual signals cumulative P&L
        self._cumpnl.columns = pnl_cols

        self._cumportfolio = calculations.create_mult_index(
            self._portfolio)  # portfolio cumulative P&L
        self._cumportfolio.columns = ['Port']
示例#3
0
        contract_type='european-put',
        strike='10d-otm',
        position_multiplier=1.0)

    # Add transaction costs to the option index (bid/ask bp for the option premium and spot FX)
    df_cuemacro_option_put_tc = fx_options_curve.apply_tc_signals_to_total_return_index(
        cross, df_cuemacro_option_put_tot, option_tc_bp=5, spot_tc_bp=2)

    # Get total returns for spot
    df_bbg_tot = df_tot  # from earlier!
    df_bbg_tot.columns = [x + '-bbg' for x in df_bbg_tot.columns]

    # Calculate a hedged portfolio of spot + 2*options (can we reduce drawdowns?)
    calculations = Calculations()

    ret_stats = RetStats()

    df_hedged = calculations.join([
        df_bbg_tot[cross + '-tot.close-bbg'].to_frame(),
        df_cuemacro_option_put_tc[cross +
                                  '-option-tot-with-tc.close'].to_frame()
    ],
                                  how='outer')
    df_hedged = df_hedged.fillna(method='ffill')
    df_hedged = df_hedged.pct_change()

    df_hedged['Spot + 2*option put hedge'] = df_hedged[
        cross + '-tot.close-bbg'] + df_hedged[cross +
                                              '-option-tot-with-tc.close']

    df_hedged.columns = RetStats(returns_df=df_hedged,