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/

    TimeReturn doesn't seem to work

    General Code/Help
    2
    4
    146
    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.
    • A
      adamb032 last edited by

      I am trying to get a basic buy and hold strategy working, but TimeReturn does not seem to be working... my code:

      start_date = datetime(2019,9,30)
      end_date = datetime(2019,10,5)
      data_df = get_eod_data('SPY', start_date, end_date)
      data_df['close_change'] = data_df['close'].pct_change()
      print(data_df[['close', 'close_change']])
      
      
      class BuyAndHold(bt.Strategy):
      
          def __init__(self):
              self.logger = logging.getLogger(__name__)
              self.logger.info(f'Initializing strategy: BuyAndHold')
              
          def nextstart(self):
              self.logger.info(f"BUY {self.data.datetime.date(0)}: {self.data.close[0]:.2f}")
              self.order_target_value(target=self.broker.get_cash())
      
      
          def stop(self):
              self.logger.info(f"starting value: {self.broker.startingcash}")
              self.logger.info(f"ending value  : {self.broker.getvalue()}")
              roi = (self.broker.get_value() / self.broker.startingcash) - 1.0
              self.logger.info(f"roi: {roi}")
      
      
      data = bt.feeds.PandasData(dataname=data_df, openinterest=None)
      
      cerebro = bt.Cerebro(stdstats=False)
      cerebro.adddata(data)
      
      cerebro.addanalyzer(btanalyzers.TimeReturn, _name='returns')
      cerebro.addobserver(bt.observers.Broker)
      cerebro.addobserver(bt.observers.DrawDown)
      cerebro.addsizer(bt.sizers.PercentSizer, percents=100)
      cerebro.addstrategy(BuyAndHold)
      
      results = cerebro.run()  # run it all
      
      #cerebro.plot(style='bar')  # and plot it with a single command
      
      returns_in_ordered_dict = results[0].analyzers.getbyname('returns').get_analysis()
      returns_df = pd.DataFrame([returns_in_ordered_dict]).T.rename(columns={0: 'return'})
      print(returns_df)
      print(286.601471 /288.957764-1)
      

      the output:

                       close  close_change
      trade_date                          
      2019-09-30  288.957764           NaN
      2019-10-01  285.520630     -0.011895
      2019-10-02  280.477051     -0.017664
      2019-10-03  282.774933      0.008193
      2019-10-04  286.601471      0.013532
      13:41:53 [                      buy_hold]:INFO: Initializing strategy: BuyAndHold
      13:41:53 [                      buy_hold]:INFO: BUY 2019-09-30: 288.96
      13:41:53 [                      buy_hold]:INFO: starting value: 10000.0
      13:41:53 [                      buy_hold]:INFO: ending value  : 9887.776226027483
      13:41:53 [                      buy_hold]:INFO: roi: -0.011222377397251737
                    return
      2019-09-30  0.000000
      2019-10-01 -0.014897
      2019-10-02 -0.017407
      2019-10-03  0.008071
      2019-10-04  0.013333
      
      -0.008154454711242876
      

      since this is a buy and hold, and it buys on the first bar (288.96) (the close on 2019-9-30), over these five days, the buy and hold should return -81.544bps, however using hte TimeReturn, it says my return is -112.22bps. also the daily returns are wrong. is there something I am not understanding here ?

      run-out 1 Reply Last reply Reply Quote 0
      • run-out
        run-out @adamb032 last edited by

        @adamb032 I believe what is happening, is in your dataframe, you are calculating the pct_change of the price. TimeReturn in backtrader is working off the value of the account.

        Try adding this into your analyzer.

        cerebro.addanalyzer(bt.analyzers.TimeReturn, **dict(_name='returns', data=data))
        

        By telling the analyzer to use data, you will get identical outputs to your dataframe.

        Here are the outputs on my end:

        Original data with percent change. 
                                   Close      close_change
        Date                                
        2019-09-30  296.769989           NaN
        2019-10-01  293.239990     -0.011895
        2019-10-02  288.059998     -0.017665
        2019-10-03  290.420013      0.008193
        2019-10-04  294.350006      0.013532
        
        TimeReturn using the data as parameter in the analyzer: 
                              return
        2019-09-30  0.002703
        2019-10-01 -0.011895
        2019-10-02 -0.017665
        2019-10-03  0.008193
        2019-10-04  0.013532
        
        Date/Close/Cash/Value of the account: 
        2019-10-01 close: 293.24, cash: 174.58, value: 9851.50
        2019-10-02 close: 288.06, cash: 174.58, value: 9680.56
        2019-10-03 close: 290.42, cash: 174.58, value: 9758.44
        2019-10-04 close: 294.35, cash: 174.58, value: 9888.13
        

        RunBacktest.com

        A 1 Reply Last reply Reply Quote 1
        • A
          adamb032 @run-out last edited by

          @run-out ah i see, i have another question, it looks like the first bar's returns are close/open-1 (ie, the open->close) returns, is there a way to force TimeReturn to only use close->close (so the first value is nan/0) or would that require basically writing my own wrapper around TimeReturn ?

          run-out 1 Reply Last reply Reply Quote 0
          • run-out
            run-out @adamb032 last edited by

            @adamb032 When I was reading the docs today it says that the first bar will be open to close if open is available, otherwise not data exist. Not sure if this works but try setting firstopen to False.

            - ``firstopen`` (default: ``True``)
                    When tracking the returns of a ``data`` the following is done when
                    crossing a timeframe boundary, for example ``Years``:
                      - Last ``close`` of previous year is used as the reference price to
                        see the return in the current year
                    The problem is the 1st calculation, because the data has** no
                    previous** closing price. As such and when this parameter is ``True``
                    the *opening* price will be used for the 1st calculation.
                    This requires the data feed to have an ``open`` price (for ``close``
                    the standard [0] notation will be used without reference to a field
                    price)
                    Else the initial close will be used.
            

            RunBacktest.com

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