Backtrader Community

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    1. Home
    2. exu
    For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/
    • Profile
    • Following 0
    • Followers 0
    • Topics 5
    • Posts 11
    • Best 1
    • Controversial 0
    • Groups 0

    exu

    @exu

    1
    Reputation
    326
    Profile views
    11
    Posts
    0
    Followers
    0
    Following
    Joined Last Online

    exu Unfollow Follow

    Best posts made by exu

    • Order modification feature request

      Hi,
      I know it's been requested already, but I'm just asking how it could be better achieved when backtesting with daily data.
      Suppose you are a position/swing trader and you're long in the market so, to close the position, you may issue two orders: OCO orders, one stop loss and one take profit. Every day you'd go to your home broker and adjust the limit price on your stop loss order till you are either stopped or the price goes in your TP direction. You can do that here where I live.

      To simulate that in BT, one would have to cancel the OCO orders and issue new ones with the recent limit price levels, but you lose one next() just for that as they would have to be accepted/submitted by the broker first, consuming one bar. I thought of adding a data1 with a lower resolution (minutes) and two rows for each day, the first just to cancel the orders and the next to submit new ones, all happening in the "same" data0 bar.

      Is that more efficient than speeding the notifications?

      posted in General Discussion
      exu
      exu

    Latest posts made by exu

    • Broker cash adjust delay

      Hi,
      How could I simulate the following behavior in BT:

      Once a position is closed (D+0) and there is profit, the broker will pay you only 3 days after, in D+3 (these are the rules in Brazil)

      I need to incorporate this in my backtest. I guess it has to do with checksubmit, but how could I delay the cash adjust?

      posted in General Discussion
      exu
      exu
    • RE: Order modification feature request

      thanks guys, I'll try and implement just that...

      posted in General Discussion
      exu
      exu
    • RE: Order modification feature request

      @ab_trader
      Yes, sure, but what I meant is that, to simulate an order update, they would have to be cancelled and new ones issued between current [0] and next [+1], can you do that with only daily data0?

      posted in General Discussion
      exu
      exu
    • Order modification feature request

      Hi,
      I know it's been requested already, but I'm just asking how it could be better achieved when backtesting with daily data.
      Suppose you are a position/swing trader and you're long in the market so, to close the position, you may issue two orders: OCO orders, one stop loss and one take profit. Every day you'd go to your home broker and adjust the limit price on your stop loss order till you are either stopped or the price goes in your TP direction. You can do that here where I live.

      To simulate that in BT, one would have to cancel the OCO orders and issue new ones with the recent limit price levels, but you lose one next() just for that as they would have to be accepted/submitted by the broker first, consuming one bar. I thought of adding a data1 with a lower resolution (minutes) and two rows for each day, the first just to cancel the orders and the next to submit new ones, all happening in the "same" data0 bar.

      Is that more efficient than speeding the notifications?

      posted in General Discussion
      exu
      exu
    • RE: Analyzers stats when datas have only trading days

      @ab_trader :
      Thanks, yes of course. The example above came from this guy's performance report:
      https://github.com/Oxylo/btreport/blob/master/report.py and it relies on ALL the days of the period, just checking on how the built-in analyzers deal with that...

      posted in Indicators/Strategies/Analyzers
      exu
      exu
    • Analyzers stats when datas have only trading days

      Hi there,
      I'd like to know if the fact that a line has only trading days (i.e 180 days instead of 365) in a year, could affect analyzers calculations such as annualized returns, because something like that would be wrong:

      dt = st.data._dataname['open'].index
      bt_period = dt[-1] - dt[0]
      bt_period_days = bt_period.days
      annual_return_pct = (1 + total_return)**(365.25 / bt_period_days) - 100)
      
      posted in Indicators/Strategies/Analyzers
      exu
      exu
    • RE: Can I use daily and 1-minute bars without replaydata?

      I think I've got it now after what you said about the signal being active during all minutes...

      So, I'd do something like that I guess, comparing the lower timeframe len as you suggested:

      class SMA_CrossOver(bt.Strategy):
          params = (('fast', 10), ('slow', 35))
          
          def __init__(self):
              sma_fast = btind.SMA(self.data0, period=self.p.fast)
              sma_slow = btind.SMA(self.data0, period=self.p.slow)
      
              self.buysig = btind.CrossOver(sma_fast, sma_slow)
      
              # To keep track of pending orders
              self.order = None
              # to prevent lower TF actions to happen more than one
              self.bar_previous = None
      
          def next(self):
              # lower timeframe becomes active during the whole higer timeframe
              # make sure the strategy does it's thing only once
              if self.bar_previous != len(self.data0):
                  self.bar_previous = len(self.data0)
      
                  if self.position.size:
                      if self.buysig < 0:
                          limit = self.data0.close[0] * 0.98
                          self.sell(data=self.data0, exectype=bt.Order.Limit, price=limit)
      
                  elif self.buysig > 0:
                      price = self.data0.close[0] * 1.02
                      limit = self.data0.close[0] * 1.04
                      self.buy(data=self.data0, exectype=bt.Order.StopLimit, price=price, plimit=limit)
      

      Thanks a lot, I think I can go from there!

      posted in General Code/Help
      exu
      exu
    • Can I use daily and 1-minute bars without replaydata?

      I apologize in advance for newbie confusion, but the code below that works as expected when fed only with a data0 (1-year of daily bars) line:

      from __future__ import (absolute_import, division, print_function, unicode_literals)
      import pandas as pd
      from sqlalchemy import create_engine
      import backtrader as bt
      import backtrader.indicators as btind
      
      # Create a Stratey
      class SMA_CrossOver(bt.Strategy):
      
          params = (('fast', 10), ('slow', 35))
      
          def __init__(self):
      
              sma_fast = btind.SMA(self.data0, period=self.p.fast)
              sma_slow = btind.SMA(self.data0, period=self.p.slow)
      
              self.buysig = btind.CrossOver(sma_fast, sma_slow)
      
          def next(self):
              if self.position.size:
                  if self.buysig < 0:
                      limit = self.data0.close[0] * 0.98
                      self.sell(data=self.data0, exectype=bt.Order.Limit, price=limit)
      
              elif self.buysig > 0:
                  price = self.data0.close[0] * 1.02
                  limit = self.data0.close[0] * 1.04
                  self.buy(data=self.data0, exectype=bt.Order.StopLimit, price=price, plimit=limit)
      
          def notify_order(self, order):
      
              if order.status in [order.Submitted, order.Accepted]:
                  return
              if order.status in [order.Completed]:
                  if order.isbuy():
                      txt = ','.join(
                          ['BUY EXECUTED: ', '%04d' % len(self),
                           '%04d' % len(self.data0),
                           # '%04d' % len(self.data1),
                           self.data.datetime.date(0).isoformat(),
                           '%.2f' % self.data0.close[0],
                           '%.2f' % self.buysig[0]])
                      print(txt)
                  else:  # sell
                      txt = ','.join(
                      ['SELL EXECUTED: ', '%04d' % len(self),
                       '%04d' % len(self.data0),
                       # '%04d' % len(self.data1),
                       self.data.datetime.date(0).isoformat(),
                       '%.2f' % self.data0.close[0],
                       '%.2f' % self.buysig[0]])
                      print(txt)
      
      
              elif order.status in [order.Canceled, order.Margin, order.Rejected]:
                  print('Order Canceled/Margin/Rejected')
              self.order = None
      
      if __name__ == '__main__':
          # Connecting to PostgreSQL by providing a sqlachemy engine
          DB_TYPE = 'postgresql'
          DB_DRIVER = 'psycopg2'
          DB_USER = 'postgres'
          DB_PASS = 'XXXX'
          DB_HOST = 'localhost'
          DB_PORT = 'XXXX'
          DB_NAME = 'tests'
          POOL_SIZE = 50
          TABLENAME = 'tests'
          SQLALCHEMY_DATABASE_URI = '%s+%s://%s:%s@%s:%s/%s' % (DB_TYPE, DB_DRIVER, DB_USER,
                                                                DB_PASS, DB_HOST, DB_PORT, DB_NAME)
          ENGINE = create_engine(SQLALCHEMY_DATABASE_URI, pool_size=POOL_SIZE, max_overflow=0)
      
          # Create a cerebro entity
          cerebro = bt.Cerebro()
      
          # Add a strategy
          cerebro.addstrategy(SMA_CrossOver)
      
          # Get a pandas dataframe
          PETR4i = pd.read_sql("select day_id, open, high, low, close, volume 
                from vista_minutes where  instrument_id = 1571 order by day_id",  con=ENGINE)
      
          PETR4 = pd.read_sql("select day_id::timestamp , open, high, low, close, ntl_fin_vol 
                 from bdins where  instrument_id = 1571 order by day_id ",  con=ENGINE)
      
          # Pass it to the backtrader datafeed and add it to the cerebro
          data1 = bt.feeds.PandasData(dataname=PETR4i,
                                     datetime=0,
                                     open=1,
                                     high=2,
                                     low=3,
                                     close=4,
                                     volume=5,
                                     timeframe=bt.TimeFrame.Minutes
                                     )
      
          # Pass it to the backtrader datafeed and add it to the cerebro
          data0 = bt.feeds.PandasData(dataname=PETR4,
                                     datetime=0,
                                     open=1,
                                     high=2,
                                     low=3,
                                     close=4,
                                     volume=5,
                                     timeframe=bt.TimeFrame.Days
                                     )
      
          # Add the Data Feed to Cerebro
          cerebro.adddata(data0)
          # cerebro.adddata(data1)
      
          # Set our desired cash start
          cerebro.broker.setcash(100000.0)
      
          cerebro.addsizer(bt.sizers.FixedSize, stake=100)
      
          # 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
          thestrats = cerebro.run(tradehistory=True)
          thestrat = thestrats[0]
      
          # Print out the final result
          print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
      

      Producing:

      Starting Portfolio Value: 100000.00
      BUY EXECUTED: ,0089,0089,2017-05-12,15.45,1.00
      SELL EXECUTED: ,0100,0100,2017-05-29,13.57,0.00
      BUY EXECUTED: ,0146,0146,2017-08-02,13.51,0.00
      BUY EXECUTED: ,0203,0203,2017-10-24,16.51,0.00
      SELL EXECUTED: ,0222,0222,2017-11-23,16.19,0.00
      Final Portfolio Value: 100018.86
      

      when I add a second line data1 to the cerebro

          cerebro.adddata(data0)
          cerebro.adddata(data1)
      

      and try to make the broker use data1 (1-year of 1-minute bars) to execute the orders:

          def next(self):
              if self.position.size:
                  if self.buysig < 0:
                      limit = self.data0.close[0] * 0.98
                      self.sell(data=self.data1, exectype=bt.Order.Limit, price=limit)
      
              elif self.buysig > 0:
                  price = self.data0.close[0] * 1.02
                  limit = self.data0.close[0] * 1.04
                  self.buy(data=self.data1, exectype=bt.Order.StopLimit, price=price, plimit=limit)
      

      the output is nowhere near as what I'd expect:

      Starting Portfolio Value: 100000.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      BUY EXECUTED: ,39855,0089,2017-05-12,15.45,1.00
      Order Canceled/Margin/Rejected
      Order Canceled/Margin/Rejected
      Order Canceled/Margin/Rejected
      Order Canceled/Margin/Rejected
      ...
      

      I am obviously missing something important here, but since I have both daily and intraday data feeds available at once, I'd like to have my swing trades strategies using the daily data0 and once the orders were generated, feed the intraday line to the broker to have the opportunity to issue then a couple hours after the usually volatile opening and more importantly, when using bracket orders, be able to know if I'll get stopped out or take my profit during the day. This is impossible to deduce having only daily OHLC.

      Can I do that without having to resampledata() or replaydata() since I have both feeds at once? Do I need to use coupling, or prenext or something else that I am missing entirely to somehow to avoid the orders to be fired like crazy when I add a second intraday line?

      posted in General Code/Help
      exu
      exu
    • RE: Different data timeframes for Strategy and Order Execution

      Thanks @sohail ! I should have started the question with a full example of what I'm trying to achieve, but i just don't have one yet. Oh well...

      posted in General Code/Help
      exu
      exu
    • RE: Different data timeframes for Strategy and Order Execution

      @ab_trader
      Thank you, but I looked at all the examples in the docs and also the blog entries, couldn't find anything similar. It does make sense though, to have your orders executed with intraday data since you can get a good idea of how your limits will fare... I'll keep looking into it...

      posted in General Code/Help
      exu
      exu