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/

    valid= is not working for sell orders.

    General Code/Help
    1
    3
    71
    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.
    • Jinwook Jang
      Jinwook Jang last edited by

      Hello,

      I'm trying to submit LIMIT sell orders and I want them to be expired at the end of the day.
      I was trying to that by using this code. (I put "hours=1" to test code to see whether the order expires in one hour for now. )

      self.sell(price=self.position.price*1.1,exectype=bt.Order.Limit,valid=(datetime.datetime.now()+datetime.timedelta(hours=1)),size=self.position.size)
      

      But they are not expiring. Maybe it's related to TimeFrame. Maybe it's related to "runonce=False".

      Here is the full code.

      # -*- coding: utf-8 -*-
      """
      Spyder Editor
      
      This is a temporary script file.
      """
      
      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 math
      
      # Import the backtrader platform
      import backtrader as bt
      
      #Spyder plot problem. https://community.backtrader.com/topic/1911/cerebro-plot-does-not-work-with-anaconda-ipython/4
      #import backtrader.plot
      #import matplotlib
      #matplotlib.use('QT5Agg')
      # code
      #cerebro.plot(iplot=False)
      
      import backtrader.plot
      import matplotlib
      matplotlib.use('QT5Agg')
      
      # Create a Cash Pool Indicator
      # Dynamic indicators
      # https://www.backtrader.com/blog/posts/2018-02-06-dynamic-indicator/dynamic-indicator/
      class CashPool(bt.Indicator):
          lines = ('cash_pool',)
      
          def __init__(self):
              #print('CashPool Indicator: __init__')
              #setting to zero for now. 
              self._cashpool = 0
              self._tradeopen = False
      
          #Indicating whether there is a open position.
          def tradeopen(self,yesno):
              #print('CashBalance Indicator: tradeopen')
              self._tradeopen = yesno
              
          def setcashbalance(self,cashpool):
              #print('CashBalance Indicator: setting cash pool')
              self._cashpool = cashpool
      
          def next(self):
              #print('CashPool Indicator: next')
              if self._tradeopen:
                  self.lines.cash_pool[0] = self.lines.cash_pool[-1] 
                  #print('Cash Pool (tradeopen) %.2f' % self.lines.cash_pool[0])
              else :
                  self.lines.cash_pool[0] = self._cashpool
                  #print('Cash Pool (tradeopen=no) %.2f' % self.lines.cash_pool[0])
      
      
                  
                  
      # Create a Stratey
      
      class IB_v1(bt.Strategy):
          params = (
          ('division', 30.0),
          ('printlog', False),
          )
          
          def log(self, txt, dt=None):
              ''' Logging function fot this strategy'''
              dt = dt or self.datas[0].datetime.datetime(0)
              #vol = self.datas[0].volume
              print('%s, %s' % (dt.isoformat(), txt))
      
          def __init__(self):
              print('strategy init')
              # Keep a reference to the "close" line in the data[0] dataseries
              self.dataclose = self.datas[0].close
      
              # To keep track of pending orders
              self.order = None
      
              # creating CashBalance indicator. 
              # This keeps track of the balance. 
              self.cp = CashPool()
              
              #setting cash balance
              #The cash is set from the main program. 
              self.cp.setcashbalance(self.broker.getcash())
              
              #Two different buying target
              #one at less than average. 
              #one at below 1.05*average
              self.buytarget_avg = 0
              self.buytarget_aboveavg = 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
                  print("orders submitted/accepted")
                  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' % order.executed.price)
                      self.log('BUY EXECUTED size, %.2f' % order.executed.size)
                      self.log('Average price %.2f' % self.position.price)
                  elif order.issell():
                      self.log('SELL EXECUTED, %.2f' % order.executed.price)
                      self.log('SELL EXECUTED size, %.2f' % order.executed.size)
      
                  self.bar_executed = len(self)
      
              elif order.status in [order.Canceled, order.Margin, order.Rejected]:
                  self.log('Order Canceled/Margin/Rejected')
      
              # Write down: no pending order
              self.order = None
              if self.position.size == 0:
                  #print('%.2f' % self.position)
                  print('size %.2f' % self.position.size)
                  print('price %.2f' % self.position.price)
                  #self.cp.tradeopen(self.position.size, self.broker.getcash())
      
          def next(self):
              # Simply log the closing price of the series from the reference
              #self.log('Close, %.2f' % self.dataclose[0])
      
              # Check if an order is pending ... if yes, we cannot send a 2nd one
              if self.order:
                  return
      
              # Check if we are in the market
              if not self.position:
                  
                  #Check wether you can do the IB
                  if self.cp[0]/self.params.division/2 >= self.dataclose[-1]:
                      #print("yes! we can do this")
                      
                    
                      #At 3:00(15:00) EST check the price of the target. 
                      if self.data.datetime.time() == datetime.time(15,00):
                          print("yes! we are doing this.")
                          self.log('Close, %.2f' % self.dataclose[0])
                          #print('cnt %.2f' % math.ceil(self.cp[0]/self.dataclose[0]/self.params.division) )
                          self.log('BUY submitting, %.2f' % math.ceil(self.cp[0]/self.dataclose[0]/self.params.division))
                          #print('price %.2f' % self.dataclose[0])
                          self.buy(size=math.ceil(self.cp[0]/self.dataclose[0]/self.params.division))
                 
              # Ok. now we are starting. 
              else:
                 #At 3:00(15:00) EST check the price of the target. 
                 if self.data.datetime.time() == datetime.time(15,00): 
                     #Buy some shares.
                     if self.dataclose[0] <= self.position.price:
                         #buy x/2 amount if the price is below average
                         self.buy(size=math.ceil(self.cp[0]/self.dataclose[0]/self.params.division/2))
                         self.log('BUY submitting, %.2f' % math.ceil(self.cp[0]/self.dataclose[0]/self.params.division/2))
                     elif self.dataclose[0] <= self.position.price*1.05:
                         #buy x/2 amount if the price is below 1.05*average
                         self.buy(size=math.floor(self.cp[0]/self.dataclose[0]/self.params.division/2))
                         self.log('BUY submitting, %.2f' % math.floor(self.cp[0]/self.dataclose[0]/self.params.division/2))
                     else:
                         print("Not buying this time.")
                         
                 #Now we are selling shares if the price is above 10% of average purchase price. 
                 #I'm putting order at 9:30AM
                 #this is hourly data. 9:30 does not work. 
                 #It seems bt.Oder.DAY does not work. Use "0" instead. 
                 if self.data.datetime.time()  == datetime.time(9,00):
                     #print("putting a sell order")
                     #print("current position %d" % self.getposition().size)
                     if self.position.size > 0:
                         self.sell(price=self.position.price*1.1,exectype=bt.Order.Limit,valid=(datetime.datetime.now()+datetime.timedelta(hours=1)),size=self.position.size)
                         #self.log('SELL submitting, %.2f' % self.position.price*1.1)
                         self.log('SELL submitting, %.2f' % self.position.size)
                  
                          #if self.dataclose[0] <
                      #self.buy(size=math.ceil(self.cp[0]/self.dataclose[-1]/self.params.division/2),price=self.dataclose[0])
                      #self.buy(size=math.floor(self.cp[0]/self.dataclose[-1]/self.params.division/2),price=self.dataclose[-1]*1.05)
      """
                  # Not yet ... we MIGHT BUY if ...
                  if self.dataclose[0] < self.dataclose[-1]:
                          # current close less than previous close
      
                          if self.dataclose[-1] < self.dataclose[-2]:
                              # previous close less than the previous close
      
                              # BUY, BUY, BUY!!! (with default parameters)
                              self.log('BUY CREATE, %.2f' % self.dataclose[0])
      
                              # Keep track of the created order to avoid a 2nd order
                              self.order = self.buy()
      """
      #        else:
      """
                  # Already in the market ... we might sell
                  if len(self) >= (self.bar_executed + 5):
                      # SELL, SELL, SELL!!! (with all possible default parameters)
                      self.log('SELL CREATE, %.2f' % self.dataclose[0])
      
                      # Keep track of the created order to avoid a 2nd order
                      self.order = self.sell()
      """
              
      
      if __name__ == '__main__':
          # Create a cerebro entity
          # To create a dynamic indicator "runonce=Fale" is required. 
          cerebro = bt.Cerebro(runonce=False)
      
          # Add a strategy
          cerebro.addstrategy(IB_v1)
      
          # 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.getcwd()
          datapath = os.path.join(modpath, 'data/TQQQ/TQQQ_1min.txt')
          #datapath = 'C:/Users/jinwo/OneDrive/BackTrader/data/TQQQ/TQQQ_1hour_sample.txt'
          
          
          # Create a Data Feed
          data = bt.feeds.GenericCSVData(
              dataname=datapath,
              # Do not pass values before this date
              fromdate=datetime.datetime(2021, 8, 2),
              # Do not pass values before this date
              todate=datetime.datetime(2021, 9, 1),
              # Do not pass values after this date
              openinterest = -1,
              #This is required to have non-daily data.
              #There is no hourly time frame
              #https://community.backtrader.com/topic/309/hourly-bars/4?_=1639874160786
              timeframe=bt.TimeFrame.Minutes,
              #compression = 60,
              reverse=False)
      
          # Add the Data Feed to Cerebro
          cerebro.adddata(data)
      
          # Set our desired cash start
          cerebro.broker.setcash(10000.0)
      
          # Add a FixedSize sizer according to the stake
          cerebro.addsizer(bt.sizers.FixedSize, stake=10)
      
          # Set the commission
          cerebro.broker.setcommission(commission=0.0)
      
          # 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())
      
          # Plot the result
          cerebro.plot(iplot=False,style='candlestick')
          
          
      
      Jinwook Jang 1 Reply Last reply Reply Quote 0
      • Jinwook Jang
        Jinwook Jang @Jinwook Jang last edited by

        @jinwook-jang I think there is a bug. I copied the one of the example from the Quickstart. (It's plotting example.) I only changed a few lines to test Limit order with "valid=". It's clear the sell orders does not expire.

        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
        
        import backtrader.plot
        import matplotlib
        matplotlib.use('QT5Agg')
        
        # Create a Stratey
        class TestStrategy(bt.Strategy):
            params = (
                ('maperiod', 15),
            )
        
            def log(self, txt, dt=None):
                ''' Logging function fot this strategy'''
                dt = dt or self.datas[0].datetime.datetime(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
        
                # To keep track of pending orders and buy price/commission
                self.order = None
                self.buyprice = None
                self.buycomm = None
        
                # Add a MovingAverageSimple indicator
                self.sma = bt.indicators.SimpleMovingAverage(
                    self.datas[0], period=self.params.maperiod)
        
                # Indicators for the plotting show
                bt.indicators.ExponentialMovingAverage(self.datas[0], period=25)
                bt.indicators.WeightedMovingAverage(self.datas[0], period=25,
                                                    subplot=True)
                bt.indicators.StochasticSlow(self.datas[0])
                bt.indicators.MACDHisto(self.datas[0])
                rsi = bt.indicators.RSI(self.datas[0])
                bt.indicators.SmoothedMovingAverage(rsi, period=10)
                bt.indicators.ATR(self.datas[0], plot=False)
        
            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))
        
                        self.buyprice = order.executed.price
                        self.buycomm = 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')
        
                # Write down: no pending order
                self.order = None
        
            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):
                # Simply log the closing price of the series from the reference
                #self.log('Close, %.2f' % self.dataclose[0])
        
                # Check if an order is pending ... if yes, we cannot send a 2nd one
                if self.order:
                    return
        
                # Check if we are in the market
                if not self.position:
        
                    # Not yet ... we MIGHT BUY if ...
                    if self.dataclose[0] > self.sma[0]:
        
                        # BUY, BUY, BUY!!! (with all possible default parameters)
                        self.log('BUY CREATE, %.2f' % self.dataclose[0])
        
                        # Keep track of the created order to avoid a 2nd order
                        self.order = self.buy()
        
                else:
        
                    if self.dataclose[0] < self.sma[0]:
                        # SELL, SELL, SELL!!! (with all possible default parameters)
                        self.log('SELL CREATE, %.2f' % self.dataclose[0])
        
                        # Keep track of the created order to avoid a 2nd order
                        #self.order = self.sell()
                        self.order = self.sell(price=self.position.price*1.1,exectype=bt.Order.Limit,valid=datetime.datetime.now() + datetime.timedelta(minutes=30),size=self.position.size)
        
        
        if __name__ == '__main__':
            # Create a cerebro entity
            cerebro = bt.Cerebro()
        
            # Add a strategy
            cerebro.addstrategy(TestStrategy)
        
            # 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, 'data/yfinance/TQQQ/TQQQ.csv')
        
            # Create a Data Feed
            data = bt.feeds.YahooFinanceCSVData(
                dataname=datapath,
                # Do not pass values before this date
                fromdate=datetime.datetime(2021, 7, 2),
                # Do not pass values before this date
                todate=datetime.datetime(2021, 12, 10),
                # Do not pass values after this date
                sessionend=datetime.time(17, 30),
                reverse=False)
        
            # Add the Data Feed to Cerebro
            cerebro.adddata(data)
        
            # Set our desired cash start
            cerebro.broker.setcash(100000.0)
        
            # Add a FixedSize sizer according to the stake
            cerebro.addsizer(bt.sizers.FixedSize, stake=10)
        
            # Set the commission
            cerebro.broker.setcommission(commission=0.0)
        
            # 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())
        
            # Plot the result
            cerebro.plot(iplot=False,style='candlestick')
        
        Jinwook Jang 1 Reply Last reply Reply Quote 0
        • Jinwook Jang
          Jinwook Jang @Jinwook Jang last edited by

          @jinwook-jang I think I found the problem. I think it's my code. datetime.datetime.now() is giving wrong expiration date.

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