コード例 #1
0
 def output_all_metrics_as_df(self, split=None, split_type=None):
     output_df = TimeSeriesSplit(self.data.copy()).split_selector(
         split, split_type)
     output_df['Trades'] = self._trade_count_series(split, split_type)
     output_df['EntryPrice'] = self._entry_price_series(split, split_type)
     output_df['PL'] = self._pnl_series(split, split_type)
     output_df['InTradePL_Accum'] = output_df['PL'].cumsum()
     output_df['ClosedPL'] = self.closed_trade_pnl_series(split, split_type)
     output_df['ClosedPL_Accum'] = output_df['ClosedPL'].cumsum()
     output_df['Closed_Pnl_CumSum'] = output_df['ClosedPL'].cumsum()
     output_df['Closed_Pnl_CumMax'] = output_df['Closed_Pnl_CumSum'].cummax(
     )
     output_df['ClosedTradeDrawDown'] = output_df[
         'Closed_Pnl_CumSum'] - output_df['Closed_Pnl_CumMax']
     output_df['MaxClosedDraw'] = self._closed_max_draw_series(
         split, split_type)
     output_df['Max'] = output_df['PL'].cummax()
     output_df['MaxDrawPerPeriod'] = np.where(
         output_df['Max'] != output_df['Max'].shift(-1),
         output_df['MaxClosedDraw'], 0)
     output_df['AverageWin'] = self._average_win_series(split, split_type)
     output_df['AverageLoss'] = self._average_loss_series(split, split_type)
     output_df.drop(['Max', 'Closed_Pnl_CumSum', 'Closed_Pnl_CumMax'],
                    axis=1,
                    inplace=True)
     return output_df
コード例 #2
0
 def _trade_count_series(self, split=None, split_type=None):
     trade_series = TimeSeriesSplit(self.data).split_selector(
         split, split_type)
     trade_series['Trades'] = np.logical_and(
         trade_series['Position'] != trade_series['Position'].shift(1),
         trade_series['Position'] != 0).cumsum()
     trade_series.loc[trade_series['Position'] == 0, 'Trades'] = 0
     return trade_series['Trades']
コード例 #3
0
 def _closed_max_draw_series(self, split=None, split_type=None):
     max_draw = TimeSeriesSplit(self.data).split_selector(split, split_type)
     max_draw['PL_Accum'] = self._pnl_series(split, split_type)
     max_draw['Max'] = max_draw['PL_Accum'].cummax()
     max_draw['Diff'] = max_draw['PL_Accum'] - max_draw['Max']
     max_draw['PeriodDraw_Closed'] = max_draw.groupby(max_draw['Diff'].eq(0).cumsum())['PL_Accum'] \
                                          .transform(lambda x: x.cummin()) - max_draw['Max']
     return max_draw['PeriodDraw_Closed']
コード例 #4
0
 def short_trades(self, split=None, split_type=None):
     shorts = TimeSeriesSplit(self.data.copy()).split_selector(
         split, split_type)
     shorts = np.logical_and(
         shorts['Position'] == -1,
         np.logical_not(shorts['Position'].shift(1) == -1)).sum()
     return shorts
コード例 #5
0
 def long_trades(self, split=None, split_type=None):
     longs = TimeSeriesSplit(self.data.copy()).split_selector(
         split, split_type)
     longs = np.logical_and(
         longs['Position'] == 1,
         np.logical_not(longs['Position'].shift(1) == 1)).sum()
     return longs
コード例 #6
0
    def __init__(self,
                 data: pd.DataFrame,
                 position_logic_range: PositionLogicRange,
                 split: Tuple[int, int],
                 timeseries_split: Tuple[str, str] = None,
                 timeseries_split_type: str = None,
                 tick_size=.01,
                 tick_value=10,
                 lot_size=5):
        self.data = data
        self.no_split = split[0]
        self.initial_split_multiple = split[1]
        self.time_series_split = timeseries_split
        self.time_series_split_type = timeseries_split_type
        self.position_logic_range = position_logic_range
        self.position_logic_combos = list(
            position_logic_range.position_logic_generator())
        self.tick_size = tick_size
        self.tick_value = tick_value
        self.lot_size = lot_size

        if split is None:
            pass
        else:
            self.data = TimeSeriesSplit(self.data).split_selector(
                self.time_series_split, self.time_series_split_type)
コード例 #7
0
    def tabular_trades(self,
                       group_by_col='Trades',
                       split=None,
                       split_type=None):
        grouping_df = TimeSeriesSplit(self.data).split_selector(
            split, split_type)
        grouping_df['Trades'] = self._trade_count_series()
        grouping_df['EntryPrice'] = self._entry_price_series()

        compact_results = grouping_df.groupby([group_by_col]).apply(
            self._grouping_function)

        compact_results['PL'] = (compact_results['ExitPrice'] - compact_results['EntryPrice']) * compact_results['TradeType']\
                                /self.tick_size * self.tick_value * self.lot_size
        compact_results['TotPL'] = compact_results['PL'].cumsum()
        compact_results['Max'] = compact_results['TotPL'].cummax()
        compact_results[
            'Diff'] = compact_results['TotPL'] - compact_results['Max']
        df = compact_results.reset_index()
        m = []
        for i in df.index:
            if df.iloc[i, df.columns.get_loc('TotPL')] == df.iloc[
                    i, df.columns.get_loc('Max')]:
                m.append(df.iloc[i, df.columns.get_loc('Diff')])
            elif i == 0:
                m.append(0)
            else:
                m.append(min(m[i - 1], df.iloc[i, df.columns.get_loc('Diff')]))

        compact_results['PeriodDraw'] = m
        compact_results['MaxDraw'] = compact_results['PeriodDraw'].cummin()
        compact_results['PMD'] = compact_results['TotPL'] / compact_results[
            'MaxDraw'].abs()
        compact_results['PercentWin'] = ((compact_results['PL'] > 0).cumsum() /
                                         compact_results.index.values) * 100
        compact_results['Day'] = compact_results['EntryTime'].dt.day
        compact_results['Month'] = compact_results['EntryTime'].dt.month
        compact_results['Year'] = compact_results['EntryTime'].dt.year
        return compact_results
コード例 #8
0
    def walk_forward(self,
                     optimzation_window_type='RollingOptimizationWindow'):
        optimization_results = {}
        walk_forward_results = {}

        for e, position_logic in enumerate(self.position_logic_combos):
            start = time.time()
            param_list = tuple(signal_function.function_parameters_tuple for signal_function in position_logic.signal_func_list)\
                         + position_logic.entry.function_parameters_tuple + position_logic.exit.function_parameters_tuple
            backtest_instance = Backtest(self.data,
                                         position_logic,
                                         tick_size=self.tick_size,
                                         tick_value=self.tick_value,
                                         lot_size=self.lot_size)
            opt_loop_results = {}
            wf_loop_results = {}
            for i in range(1, self.no_split + 1):
                opt_loop_results[i] = backtest_instance.profit_to_max_draw(
                    split=(self.no_split, self.initial_split_multiple, i),
                    split_type=optimzation_window_type)
                wf_loop_results[i] = backtest_instance.agg_stats(
                    split=(self.no_split, self.initial_split_multiple, i),
                    split_type='WalkForwardWindow')
                print(f"Finished window {i} of {self.no_split}")
            optimization_results[str(param_list)] = opt_loop_results
            walk_forward_results[str(param_list)] = wf_loop_results

            end = time.time()
            print(f"Run {e+1}/{len(self.position_logic_combos)} ")
            print(
                f"{round(((len(self.position_logic_combos)-e+1)*(end-start))/60,2)}min Remaining"
            )
            print(round(end - start, 3))

        opt_df = pd.DataFrame.from_dict(optimization_results, orient='index')
        wf_df = pd.DataFrame.from_dict(walk_forward_results, orient='index')

        testdict = {}
        for i in opt_df.columns:
            test_dates = TimeSeriesSplit(self.data).walk_forward_dates(
                (self.no_split, self.initial_split_multiple, i))
            top_param = opt_df[i].idxmax(axis=0)
            dict_val = wf_df.at[top_param, i]
            testdict[i] = dict_val
            testdict[i]['Params'] = top_param
            testdict[i]['WindowStart'] = test_dates[0]
            testdict[i]['WindowEnd'] = test_dates[1]
        final_df = pd.DataFrame.from_dict(
            testdict, orient='index')  # This should be outside the loop

        return final_df
コード例 #9
0
    def _pnl_series(self, split=None, split_type=None):
        pnl_series = TimeSeriesSplit(self.data).split_selector(
            split, split_type)
        if self.price == 'O':
            pnl_series['TickChange'] = pnl_series['O'] - pnl_series['O'].shift(
                1)
        elif self.price == 'C':
            pnl_series['TickChange'] = pnl_series['C'] - pnl_series['C'].shift(
                1)

        pnl_series['PL'] = (
            pnl_series['Position'] * pnl_series['TickChange']
        ) / self.tick_size * self.tick_value * self.lot_size
        pnl_series['PL_Accum'] = pnl_series['PL'].cumsum()
        return pnl_series['PL_Accum']
コード例 #10
0
    def __init__(self,
                 data,
                 position_logic_function,
                 split: Tuple[any] = None,
                 split_type: str = None,
                 process_data=True,
                 initial_split_method=None,
                 sort_data: str = True,
                 price='O',
                 tick_size=.01,
                 tick_value=10,
                 lot_size=5):
        self.data = data
        self.process_data = process_data
        self.position_logic_function = position_logic_function
        self.price = price
        self.split = split
        self.split_type = split_type
        self.sort_data = sort_data
        self.tick_size = tick_size
        self.tick_value = tick_value
        self.lot_size = lot_size

        if np.logical_or(
                np.logical_and(self.split is None, initial_split_method
                               is not None),
                np.logical_and(self.split is not None,
                               initial_split_method is None)):
            raise ValueError('Please provide Initial Split Type')

        if sort_data:
            if sorted(self.data.index) is not self.data.index:
                self.data.sort_index(ascending=True, inplace=True)

        if self.process_data:
            df = self.data.copy()

            if split_type is None:
                df = position_logic_function.apply(df)
                df['Position'] = list(position_handler(df))
            else:
                if initial_split_method == 'PreSignalCalc':
                    df = TimeSeriesSplit(df).split_selector(split, split_type)
                    df = position_logic_function.apply(df)
                    df['Position'] = list(position_handler(df))
                elif initial_split_method == 'PostSignalCalc':
                    df = position_logic_function.apply(df)
                    df = TimeSeriesSplit(df).split_selector(split, split_type)
                    df['Position'] = list(position_handler(df))
                elif initial_split_method is None:
                    pass
            df.drop(columns=['EntrySignal', 'ExitSignal'], inplace=True)
            self.data = df
        elif not self.process_data:
            self.data = self.data.copy()
コード例 #11
0
 def _entry_price_series(self, split=None, split_type=None):
     entry_price = TimeSeriesSplit(self.data).split_selector(
         split, split_type)
     if self.price == 'O':
         entry_price['EntryPrice'] = np.where(
             np.logical_and((entry_price['Position'] !=
                             entry_price['Position'].shift(1)),
                            entry_price['Position'] != 0),
             entry_price['O'].shift(1), 0)
     elif self.price == 'C':
         entry_price['EntryPrice'] = np.where(
             np.logical_and((entry_price['Position'] !=
                             entry_price['Position'].shift(1)),
                            entry_price['Position'] != 0),
             entry_price['C'].shift(1), 0)
     return entry_price['EntryPrice']
コード例 #12
0
 def closed_trade_pnl_series(self, split=None, split_type=None):
     closed_trade_pnl = TimeSeriesSplit(self.data).split_selector(
         split, split_type)
     closed_trade_pnl['PL'] = self._pnl_series(split, split_type)
     closed_trade_pnl['P&L Values'] = np.where(
         closed_trade_pnl['Position'] -
         closed_trade_pnl['Position'].shift(-1) != 0,
         closed_trade_pnl['PL'], np.nan)
     closed_trade_pnl['P&L Values'] = closed_trade_pnl['P&L Values'].fillna(
         method='ffill')
     closed_trade_pnl['Closed P&L'] = np.where(
         closed_trade_pnl['Position'] -
         closed_trade_pnl['Position'].shift(-1) != 0,
         closed_trade_pnl['P&L Values'] -
         closed_trade_pnl['P&L Values'].shift(1), 0)
     return closed_trade_pnl['Closed P&L']
コード例 #13
0
 def open_max_draw(self, split=None, split_type=None):
     open_draw = TimeSeriesSplit(self.data.copy()).split_selector(
         split, split_type)
     open_draw['Trades'] = self._trade_count_series(split, split_type)
     conditions = [open_draw['Position'] == 1, open_draw['Position'] == -1]
     long = open_draw.groupby('Trades')['L'].cummin() - open_draw.groupby(
         'Trades')['EntryPrice'].cummax()
     short = open_draw.groupby('Trades')['EntryPrice'].cummax(
     ) - open_draw.groupby('Trades')['H'].cummax()
     values = [long, short]
     open_draw['MaxOpenDraw_Pd'] = np.select(conditions, values, 0)
     return open_draw['MaxOpenDraw_Pd'].min(
     ) / self.tick_size * self.lot_size * self.tick_value
コード例 #14
0
    def __init__(self,
                 data,
                 split=None,
                 split_type=None,
                 tick_size=.01,
                 tick_value=10,
                 lot_size=5):
        self.data = data
        self.split = split
        self.split_type = split_type
        self.tick_size = tick_size
        self.tick_value = tick_value
        self.lot_size = lot_size

        if isinstance(self.data, Backtest):
            self.data = data.data
        else:
            if split is None:
                pass
            else:
                self.data = TimeSeriesSplit(self.data).split_selector(
                    self.split, self.split_type)