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/

    I'm having trouble buying on open/ selling on close using daysteps

    Indicators/Strategies/Analyzers
    3
    7
    988
    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.
    • B
      brettelliot last edited by

      Hi everyone,

      I've been gone for a few months but I'm back at it! Glad to see the community is still as active as when I went dark around April!

      Anyway, I'm trying to make a strategy that buys on open using the open price and sells on close using the close price. I have daily data and i'm trying to make use of the daysteps.py example and the DayStepsFilter.

      I sort of have it working. My strategy buys on open using the open price, and it sells on close, BUT it sells using the open price instead of the close price. I'm not sure what's going on.

      First, here is a sample log (reproduced here as is, which is a bit out of order):

      2014-12-08, BUY EXECUTED, Price: 41.91, Cost: 41.91, Comm 0.04
      2014-12-08, SELL EXECUTED, Price: 41.91, Cost: 41.91, Comm 0.04
      2014-12-08, OPERATION PROFIT, GROSS 0.00, NET -0.08
      0236,0471,0236,0236,2014-12-08T23:59:59.999989,41.91,41.91
      2014-12-08, BUY CREATE, 41.91
      0236,0472,0236,0236,2014-12-08T23:59:59.999989,41.91,41.37
      2014-12-08, SELL CREATE, 41.37
      

      First, I issue a buy for $41.91 which is the open price. That is executed with the open price of $41.91.

      Then on the closing bar of the day I issue a sell using the closing price of $41.37 and BUT it's executed using the opening price of $41.91.

      I've tried futzing with the order types of the buy and sell but could not get it working.

      Here is the next() function in the strategy and hopefully someone could point out my error:

          def next(self):
              self.callcounter += 1
      
              txtfields = list()
              txtfields.append('%04d' % len(self.data))
              txtfields.append('%04d' % self.callcounter)
              txtfields.append('%04d' % len(self))
              txtfields.append('%04d' % len(self.data0))
              txtfields.append(self.data.datetime.datetime(0).isoformat())
              txtfields.append('%.2f' % self.data0.open[0])
              txtfields.append('%.2f' % self.data0.close[0])
              print(','.join(txtfields))
      
              if len(self.data) > self.lcontrol:
                  self.log('BUY CREATE, %.2f' % self.dataclose[0])
                  self.buy()
      
              else:
                  self.log('SELL CREATE, %.2f' % self.dataclose[0])
                  self.sell()
      
              self.lcontrol = len(self.data)
      

      Here's all the code (which is basically daysteps with the logging code and the code to buy/ sell on open and close.

      from __future__ import (absolute_import, division, print_function,
                              unicode_literals)
      
      import datetime  # For datetime objects
      import os.path  # To manage paths
      import sys  # To find out the script name (in argv[0])
      
      # Import the backtrader platform
      import backtrader as bt
      
      
      # Create a Stratey
      class TestStrategy(bt.Strategy):
      
          def log(self, txt, dt=None):
              ''' Logging function fot this strategy'''
              dt = dt or self.datas[0].datetime.date(0)
              print('%s, %s' % (dt.isoformat(), txt))
      
          def __init__(self):
              # Keep a reference to the "close" line in the data[0] dataseries
              self.dataclose = self.datas[0].close
              self.lcontrol = 0  # control if 1st or 2nd call
      
          def start(self):
              self.lcontrol = 0  # control if 1st or 2nd call
              self.callcounter = 0
      
          def notify_order(self, order):
              if order.status in [order.Submitted, order.Accepted]:
                  # Buy/Sell order submitted/accepted to/by broker - Nothing to do
                  return
      
              # Check if an order has been completed
              # Attention: broker could reject order if not enough cash
              if order.status in [order.Completed]:
                  if order.isbuy():
                      self.log(
                          'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                          (order.executed.price,
                           order.executed.value,
                           order.executed.comm))
      
                  else:  # Sell
                      self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                               (order.executed.price,
                                order.executed.value,
                                order.executed.comm))
      
                  self.bar_executed = len(self)
      
              elif order.status in [order.Canceled, order.Margin, order.Rejected]:
                  self.log('Order Canceled/Margin/Rejected')
      
          def notify_trade(self, trade):
              if not trade.isclosed:
                  return
      
              self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
                       (trade.pnl, trade.pnlcomm))
      
          def next(self):
              self.callcounter += 1
      
              txtfields = list()
              txtfields.append('%04d' % len(self.data))
              txtfields.append('%04d' % self.callcounter)
              txtfields.append('%04d' % len(self))
              txtfields.append('%04d' % len(self.data0))
              txtfields.append(self.data.datetime.datetime(0).isoformat())
              txtfields.append('%.2f' % self.data0.open[0])
              txtfields.append('%.2f' % self.data0.close[0])
              print(','.join(txtfields))
      
              if len(self.data) > self.lcontrol:
                  self.log('BUY CREATE, %.2f' % self.dataclose[0])
                  self.buy()
      
              else:
                  self.log('SELL CREATE, %.2f' % self.dataclose[0])
                  self.sell()
      
              self.lcontrol = len(self.data)
      
      
      if __name__ == '__main__':
          # Create a cerebro entity
          cerebro = bt.Cerebro()
      
          # Datas are in a subfolder of the samples. Need to find where the script is
          # because it could have been called from anywhere
          modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
          datapath = os.path.join(modpath, 'orcl_2014.txt')
      
          # Create a Data Feed
          data = bt.feeds.YahooFinanceCSVData(
              dataname=datapath,
              # Do not pass values before this date
              fromdate=datetime.datetime(2014, 1, 1),
              # Do not pass values before this date
              todate=datetime.datetime(2014, 12, 31),
              # Do not pass values after this date
              reverse=False)
      
          # Add the Data Feed to Cerebro
          #data.addfilter(DayStepsCloseFilter)
      
          data.addfilter(bt.filters.DayStepsFilter)
          cerebro.adddata(data)
      
          # Add a strategy
          cerebro.addstrategy(TestStrategy)
      
          # Set our desired cash start
          cerebro.broker.setcash(100000.0)
      
          # consider a bar with the same time as the end of session to be the end of the session
          cerebro.broker.set_eosbar(True)
      
          # Set the commission - 0.1% ... divide by 100 to remove the %
          cerebro.broker.setcommission(commission=0.001)
      
          # Print out the starting conditions
          print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
      
          # Run over everything
          cerebro.run()
      
          # Print out the final result
          print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
      

      Thanks everyone!

      B 1 Reply Last reply Reply Quote 0
      • B
        brettelliot @brettelliot last edited by

        I continued searching for answers and I came across cerebro.broker.set_coc(True).

        It sort of works but I'm still not sure I'm doing this right.

        This does let my sell order use the close price BUT it sells on the next day. The logs are in the right order here though and you can see it sells at the right price ($41.37) but on the 9th instead of the 8th.

        2014-12-05, SELL CREATE, 41.93
        2014-12-08, SELL EXECUTED, Price: 41.93, Cost: 42.02, Comm 0.04
        2014-12-08, OPERATION PROFIT, GROSS -0.09, NET -0.17
        0236,0471,0236,0236,2014-12-08T23:59:59.999989,41.91,41.91
        2014-12-08, BUY CREATE, 41.91
        2014-12-08, BUY EXECUTED, Price: 41.91, Cost: 41.91, Comm 0.04
        0236,0472,0236,0236,2014-12-08T23:59:59.999989,41.91,41.37
        2014-12-08, SELL CREATE, 41.37
        2014-12-09, SELL EXECUTED, Price: 41.37, Cost: 41.91, Comm 0.04
        2014-12-09, OPERATION PROFIT, GROSS -0.54, NET -0.62
        
        B 1 Reply Last reply Reply Quote 0
        • B
          backtrader administrators @brettelliot last edited by

          @brettelliot said in I'm having trouble buying on open/ selling on close using daysteps:

          It sort of works but I'm still not sure I'm doing this right.

          You were doing it wrong before. You cannot use the close price for selling unless you cheat (set_coc -> set_cheat_on_close), because when you issue the order you have already seen the price. And using a price which has been seen (and the price has already influenced the calculation of the indicators) is cheating

          @brettelliot said in I'm having trouble buying on open/ selling on close using daysteps:

          BUT it sells on the next day.

          The evaluation is done after the bar is gone.

          1 Reply Last reply Reply Quote 1
          • B
            brettelliot last edited by

            OK that makes sense. What's the right way to use daily data and make two trades... one buys at the open price and the other sells at the closing price?

            1 Reply Last reply Reply Quote 0
            • B
              backtrader administrators last edited by

              Imho: there is no right way because to really do that and not cheat yourself you need a smaller timeframe.

              But if you want to do it:

              • Use cheat-on-open and issue an order in next_open: it will be executed with the opening price. Issue the order adding coc=False (to avoid it being hit by cheat-on-close) to either buy / sell

                See: Docs - Cheat on Open

              • Activate also cheat-on-close and send a 2nd order during next (use no coc parameter for the order) and it will be executed with the close price)

              1 Reply Last reply Reply Quote 2
              • B
                brettelliot last edited by

                Thank you!!

                1 Reply Last reply Reply Quote 0
                • A
                  Amien Johaadien last edited by

                  Or use hourly data - then you can buy at 9am and sell at 5pm...

                  1 Reply Last reply Reply Quote 0
                  • 1 / 1
                  • First post
                    Last post
                  Copyright © 2016, 2017, 2018, 2019, 2020, 2021 NodeBB Forums | Contributors