kernc / backtesting.py

:mag_right: :chart_with_upwards_trend: :snake: :moneybag: Backtest trading strategies in Python.
https://kernc.github.io/backtesting.py/
GNU Affero General Public License v3.0
5.06k stars 990 forks source link

Implementing Walkforward to a Strategy #1055

Open chemcoder-2020 opened 10 months ago

chemcoder-2020 commented 10 months ago

I have a strategy class called VWAPBounceStrategy, in which the buys and sells are signaled by self.longs, self.shorts, self.longXs, and self.shortXs, which are outputs of a method called vwapbounce_signal.

I would like to implement walkforward analysis on this strategy. I looked at the ML notebook and some conversations on updating the optimal parameters. Here's a basic implementation. I would like to ask if this looks right, or is there something else needed. The workflow is retraining every month. This code does run.

` class VWAPWalkForwardStrategy(VWAPBounceStrategy): def next(self): month = ( pd.Series(pd.DatetimeIndex(self.data.df.index).month) .diff() .abs().gt(0) .cumsum() .fillna(0) )

      if month.iloc[-1] < N_TRAIN:
          return

      if month.iloc[-1] == month.iloc[-2]:
          return super().next()

      data = self.data.df.reset_index()[month.gt(month.iloc[-1] - N_TRAIN - 1)]
      data = data.set_index("date")[:-1]
      support_rejection = [True, False]
      resistance_rejection = [True, False]
      combo_choices = list(
          itertools.product(
              *[
                  support_rejection,
                  resistance_rejection,
              ]
          )
      )

      rets = {}
      print(data.iloc[0])
      print(data.iloc[-1])
      for option in combo_choices:
          bt = Backtest(data, VWAPBounceStrategy, commission=0, cash=30000)
          output = bt.run(
              support_rejection=option[0],
              resistance_rejection=option[1],
          )
          _target = output["Win Rate [%]"] * output["Avg. Trade WL Ratio"]
          rets[option] = _target
      rets = pd.Series(rets)
      best = rets.idxmax()
      print(best)

      # use best result
      self.support_rejection = best[0]
      self.resistance_rejection = best[1]
      (
          self.longs,
          self.shorts,
          self.longXs,
          self.shortXs,
          self.ma_mid,
          self.rsi,
          self.eod,
      ) = self.I(
          self.vwapbounce_signal,
          self.data.df,
          plot=False,
      )
      super().next()`
chemcoder-2020 commented 10 months ago

More specifically, should super().next() be before the (self.longs, ...) = self.I(...) update, or after that, as it is now.