Navigation

    Backtrader Community

    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/

    help on a customized strategy

    Indicators/Strategies/Analyzers
    1
    1
    95
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Zhangjt9317
      Zhangjt9317 last edited by

      Hi, I am new to bt and the algo trading, and I really need help with a strategy I am building.

      I have 1-min BTC data OHLCV + (day-based) forecast price predicted using the model. I am trying to run a backtest using 1min data. The data looks like this:

      ,date,time,open,high,low,close,volume,close_time,average,Forecast
      0,2017-08-17,2017-08-17 04:00:00,4261.48,4261.48,4261.48,4261.48,1.7751830000000002,1502942459999,4308.1725,4303.864329999999
      1,2017-08-17,2017-08-17 04:01:00,4261.48,4261.48,4261.48,4261.48,0.0,1502942519999,4308.1725,4303.864329999999
      2,2017-08-17,2017-08-17 04:02:00,4280.56,4280.56,4280.56,4280.56,0.261074,1502942579999,4308.1725,4303.864329999999
      3,2017-08-17,2017-08-17 04:03:00,4261.48,4261.48,4261.48,4261.48,0.012008,1502942639999,4308.1725,4303.864329999999
      4,2017-08-17,2017-08-17 04:04:00,4261.48,4261.48,4261.48,4261.48,0.140796,1502942699999,4308.1725,4303.864329999999
      5,2017-08-17,2017-08-17 04:05:00,4261.48,4261.48,4261.48,4261.48,0.0,1502942759999,4308.1725,4303.864329999999
      6,2017-08-17,2017-08-17 04:06:00,4261.48,4261.48,4261.48,4261.48,0.0,1502942819999,4308.1725,4303.864329999999
      7,2017-08-17,2017-08-17 04:07:00,4261.48,4261.48,4261.48,4261.48,0.0,1502942879999,4308.1725,4303.864329999999
      8,2017-08-17,2017-08-17 04:08:00,4261.48,4261.48,4261.48,4261.48,0.0,1502942939999,4308.1725,4303.864329999999
      9,2017-08-17,2017-08-17 04:09:00,4261.48,4261.48,4261.48,4261.48,0.0,1502942999999,4308.1725,4303.864329999999
      
      

      The forecast price varies by days, and I am trying to do backtest on minutes, that's why they look the same

      The strategy is:

      • Open trade at the daily open price.
      • Long if forecast > open price, short if forecast < open price.
      • Close trade at forecast price or at stop-loss (potential gain == potential loss), whichever reaches first.

      I have $10000 cash and trying to buy BTC as much as I can every time (not sure if this is called all-in)

      and I am trying to set the stop loss condition to just when the trade reaches back to the forecast price

      class Data_Extend(GenericCSVData):
          lines = ('Forecast',)  # add a 'Forecast' line to the inherited ones from the base class
          params = (('Forecast', 7),) # forecast column location
      
      # using a signal strategy here 
      class FStrategy(bt.SignalStrategy):
          params = dict(
              stake=1,
              printout=True,
              csvcross=True,
              period=7,
              startcash=10000,
              target_perc=1.0,
          )
      
          def log(self, txt, dt=None):
              if self.p.printout:
                  dt = dt or self.data.datetime[0]
                  dt = bt.num2date(dt)
                  print('%s, %s' % (dt.isoformat(), txt))
      
          def notify_order(self, order):
              if order.status in [bt.Order.Submitted, bt.Order.Accepted]:
                  return  # Await further notifications
      
              if order.status == order.Completed:
                  if order.isbuy():
                      buytxt = 'BUY COMPLETE, %.2f' % order.executed.price
                      self.log(buytxt, order.executed.dt)
      
                  else:
                      selltxt = 'SELL COMPLETE, %.2f' % order.executed.price
                      self.log(selltxt, order.executed.dt)
      
              elif order.status in [order.Expired, order.Canceled]:
                  self.log('%s ,' % order.Status[order.status])
                  pass  # Simply log
      
              # Allow new orders
              self.orderid = None
      
          def __init__(self):
              self.orderid = None
      
              # there is only 1 data feed, so it is the same anyway
              self.dataclose = self.datas[0].close
              self.dataopen = self.datas[0].open
              self.dataforecast = self.datas[0].Forecast
      
              # long condition (with exit)T
              self.signal_long = self.dataforecast > self.dataopen
              # multi head signal 1 ==> long when forecast > open
              self.signal_add(bt.SIGNAL_LONG, self.signal_long)
              # LONGEXIT: short indications are taken to exit long positions
              self.long_exit = self.dataforecast <= self.dataopen
              self.signal_add(bt.SIGNAL_LONGEXIT, self.long_exit)
      
              # short condition (with exit)
              self.signal_short = self.dataforecast < self.dataopen
              self.signal_add(bt.SIGNAL_SHORT, self.signal_short)
              # SHORTEXIT: long indications are taken to exit short positions
              self.short_exit = self.dataforecast <= self.dataopen
              self.signal_add(bt.SIGNAL_SHORTEXIT, self.short_exit)
      
          def next(self):
              if self.orderid:
                  return  # if an order is active, no new orders are allowed
      
              if not self.position:  # not yet in market
                  sizer = self.broker.get_cash() / self.datas[0].open[0]
                  if self.signal_long:  # cross upwards
                      self.log('BUY CREATE @CLOSE , %.2f, OPEN %.2f, FORECAST %.2f' % (self.data.close[0], self.data.open[0], self.dataforecast[0]))
                      print('Position Size: %.2f BTC' % sizer)
                      self.order = self.buy(size=sizer)
                  elif self.long_exit:
                      self.log('CLOSE LONG , %.2f, OPEN %.2f, FORECAST %.2f' % (self.data.close[0], self.data.open[0], self.dataforecast[0]))
                      self.order = self.close(size=sizer)
      
              else:  # in the market
                  sizer = self.broker.getposition(data=self.datas[0]).size
                  if self.signal_short:  # short signal
                      self.log('SELL CREATE @CLOSE , %.2f, OPEN %.2f, FORECAST %.2f' % (self.data.close[0], self.data.open[0], self.dataforecast[0]))
                      print('Position Size: %.2f BTC' % sizer)
                      self.order = self.sell(size=sizer)
                  elif self.short_exit:
                      self.log('CLOSE SHORT , %.2f, OPEN %.2f, FORECAST %.2f' % (self.data.close[0], self.data.open[0], self.dataforecast[0]))
                      self.order = self.close(size=sizer)
      
      
          def stop(self):
              print('==================================================')
              print('Starting Value - %.2f' % self.broker.startingcash)
              print('Ending   Value - %.2f' % self.broker.getvalue())
              print('==================================================')
      

      In the next and init I log some info I want, and when I run using the data, the log looks like this:

      2019-01-01T23:59:59.999989, BUY CREATE @CLOSE , 3797.14, OPEN 3701.23, FORECAST 3728.05
      Position Size: 2.70 BTC
      2019-01-01T23:59:59.999989, BUY COMPLETE, 3797.14
      2019-01-02T23:59:59.999989, SELL CREATE @CLOSE , 3858.56, OPEN 3796.45, FORECAST 3742.63
      Position Size: 2.63 BTC
      2019-01-02T23:59:59.999989, SELL COMPLETE, 3858.56
      2019-01-03T23:59:59.999989, BUY CREATE @CLOSE , 3766.78, OPEN 3857.57, FORECAST 3863.28
      Position Size: 2.63 BTC
      2019-01-03T23:59:59.999989, BUY COMPLETE, 3766.78
      2019-01-05T23:59:59.999989, SELL CREATE @CLOSE , 3770.96, OPEN 3790.09, FORECAST 3770.63
      Position Size: 2.70 BTC
      2019-01-05T23:59:59.999989, SELL COMPLETE, 3770.96
      2019-01-06T23:59:59.999989, BUY CREATE @CLOSE , 3987.60, OPEN 3771.12, FORECAST 3797.46
      Position Size: 2.70 BTC
      2019-01-06T23:59:59.999989, BUY COMPLETE, 3987.60
      2019-01-07T23:59:59.999989, SELL CREATE @CLOSE , 3975.45, OPEN 3987.62, FORECAST 3925.53
      Position Size: 2.55 BTC
      2019-01-07T23:59:59.999989, SELL COMPLETE, 3975.45
      2019-01-08T23:59:59.999989, BUY CREATE @CLOSE , 3955.13, OPEN 3976.76, FORECAST 4001.77
      Position Size: 2.55 BTC
      2019-01-08T23:59:59.999989, BUY COMPLETE, 3955.13
      2019-01-10T23:59:59.999989, SELL CREATE @CLOSE , 3585.88, OPEN 3966.06, FORECAST 3965.74
      Position Size: 2.56 BTC
      2019-01-10T23:59:59.999989, SELL COMPLETE, 3585.88
      2019-01-11T23:59:59.999989, BUY CREATE @CLOSE , 3601.31, OPEN 3585.88, FORECAST 3670.85
      Position Size: 2.56 BTC
      2019-01-11T23:59:59.999989, BUY COMPLETE, 3601.31
      2019-01-12T23:59:59.999989, SELL CREATE @CLOSE , 3583.13, OPEN 3601.31, FORECAST 3528.85
      Position Size: 2.55 BTC
      2019-01-12T23:59:59.999989, SELL COMPLETE, 3583.13
      2019-01-13T23:59:59.999989, BUY CREATE @CLOSE , 3476.81, OPEN 3584.10, FORECAST 3611.51
      Position Size: 2.55 BTC
      2019-01-13T23:59:59.999989, BUY COMPLETE, 3476.81
      2019-01-15T23:59:59.999989, SELL CREATE @CLOSE , 3553.06, OPEN 3626.08, FORECAST 3600.16
      Position Size: 2.63 BTC
      2019-01-15T23:59:59.999989, SELL COMPLETE, 3553.06
      2019-01-16T23:59:59.999989, BUY CREATE @CLOSE , 3591.84, OPEN 3553.06, FORECAST 3578.68
      Position Size: 2.63 BTC
      2019-01-16T23:59:59.999989, BUY COMPLETE, 3591.84
      2019-01-17T23:59:59.999989, SELL CREATE @CLOSE , 3616.21, OPEN 3591.84, FORECAST 3585.79
      Position Size: 2.60 BTC
      2019-01-17T23:59:59.999989, SELL COMPLETE, 3616.21
      2019-01-18T23:59:59.999989, CLOSE LONG , 3594.87, OPEN 3613.32, FORECAST 3597.20
      2019-01-19T23:59:59.999989, BUY CREATE @CLOSE , 3665.30, OPEN 3594.87, FORECAST 3599.16
      Position Size: 2.62 BTC
      2019-01-19T23:59:59.999989, BUY COMPLETE, 3665.30
      
      Position Size: 1.71 BTC
      2019-12-29T23:59:59.999989, BUY COMPLETE, 7388.24
      ==================================================
      Starting Value - 10000.00
      Ending   Value - 12191.66
      ==================================================
      ===============================================================================
      TimeReturn:
        - 2019-01-31 00:00:00: -0.10192223974656367
        - 2019-02-28 00:00:00: 0.12747463642130064
        - 2019-03-31 00:00:00: 0.07017082291549426
        - 2019-04-30 00:00:00: -0.032682599108764476
        - 2019-05-31 00:00:00: 0.10385189267487394
        - 2019-06-30 00:00:00: 0.10896888494902579
        - 2019-07-31 00:00:00: -0.002396698888080584
        - 2019-08-31 00:00:00: -0.020199229864290236
        - 2019-09-30 00:00:00: 0.024766766780242477
        - 2019-10-31 00:00:00: 0.056789760260889555
        - 2019-11-30 00:00:00: -0.10789694777986258
        - 2019-12-31 00:00:00: 0.006156163558002525
      ===============================================================================
      SharpeRatio:
        - sharperatio: 0.24941200165758853
      ===============================================================================
      SQN:
        - sqn: 0.5616557528887063
        - trades: 126
      

      b66c9eaf-e27f-4d35-86ba-324f62787cef-image.png

      I do not really know if this is working well, I am especially confused with 3 things

      • size ==> is the size correct
      if not self.position:  # not yet in market
                  sizer = self.broker.get_cash() / self.datas[0].open[0]
      else:  # in the market
                  sizer = self.broker.getposition(data=self.datas[0]).size
      
      • close ==> is my way the right way to do it? I close both positions when
                  elif self.long_exit:
                      self.log('CLOSE LONG , %.2f, OPEN %.2f, FORECAST %.2f' % (self.data.close[0], self.data.open[0], self.dataforecast[0]))
                      self.order = self.close(size=sizer)
                  elif self.short_exit:
                      self.log('CLOSE LONG , %.2f, OPEN %.2f, FORECAST %.2f' % (self.data.close[0], self.data.open[0], self.dataforecast[0]))
                      self.order = self.close(size=sizer)
      
      • result ==> does the result look correct? I read many blogs, just wanna check

      Thank you if anybody can help.

      1 Reply Last reply Reply Quote 0
      • 1 / 1
      • First post
        Last post
      Copyright © 2016, 2017, 2018 NodeBB Forums | Contributors
      $(document).ready(function () { app.coldLoad(); }); }