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/

    Drawdown analysis appears to be incorrect

    Indicators/Strategies/Analyzers
    2
    2
    2033
    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
      advandenoord last edited by

      Hi,

      I am having an issue with the DrawDown Analyzer. I have added a DrawDown Analyzer to one of the examples on the backtrader website (more specifically, to the 'Visual Inspection: Plotting' example from https://www.backtrader.com/docu/quickstart/quickstart.html#our-first-strategy).

      To add the Drawdown Analyzer, I have adjusted a code snippets from https://backtest-rookies.com/2017/06/11/using-analyzers-backtrader/.

      I have also commented some of the log calls, to make the example more clear.

      The resulting code is as follows:

      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):
          params = (
              ('maperiod', 15),
          )
      
          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
      
              # 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()
      
      def printTradeAnalysis(analyzer):
          '''
          Function to print the Technical Analysis results in a nice format.
          '''
          #Get the results we are interested in
          total_open = analyzer.total.open
          total_closed = analyzer.total.closed
          total_won = analyzer.won.total
          total_lost = analyzer.lost.total
          win_streak = analyzer.streak.won.longest
          lose_streak = analyzer.streak.lost.longest
          pnl_net = round(analyzer.pnl.net.total,2)
          strike_rate = (total_won / total_closed) * 100
          #Designate the rows
          h1 = ['Total Open', 'Total Closed', 'Total Won', 'Total Lost']
          h2 = ['Strike Rate','Win Streak', 'Losing Streak', 'PnL Net']
          r1 = [total_open, total_closed, total_won, total_lost]
          r2 = [strike_rate, win_streak, lose_streak, pnl_net]
          #Check which set of headers is the longest.
          if len(h1) > len(h2):
              header_length = len(h1)
          else:
              header_length = len(h2)
          #Print the rows
          print_list = [h1,r1,h2,r2]
          row_format ="{:<15}" * (header_length + 1)
          print("Trade Analysis Results:")
          for row in print_list:
              print(row_format.format('',*row))
      
      def printDrawDownAnalysis(analyzer):
          '''
          Function to print the Technical Analysis results in a nice format.
          '''
          #Get the results we are interested in
          drawdown = round(analyzer.drawdown, 2)
          moneydown = round(analyzer.moneydown, 2)
          length = analyzer.len
          max_dd = round(analyzer.max.drawdown, 2)
          max_md = round(analyzer.max.moneydown, 2)
          max_len = analyzer.max.len
      
          #Designate the rows
          h1 = ['Drawdown', 'Moneydown', 'Length']
          h2 = ['Max drawdown','Max moneydown', 'Max len']
          r1 = [drawdown, moneydown,length]
          r2 = [max_dd, max_md, max_len]
          #Check which set of headers is the longest.
          if len(h1) > len(h2):
              header_length = len(h1)
          else:
              header_length = len(h2)
          #Print the rows
          print_list = [h1,r1,h2,r2]
          row_format ="{:<15}" * (header_length + 1)
          print("Drawdown Analysis Results:")
          for row in print_list:
              print(row_format.format('',*row))
      
      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, './datas/orcl-1995-2014.txt')
      
          # Create a Data Feed
          data = bt.feeds.YahooFinanceCSVData(
              dataname=datapath,
              # Do not pass values before this date
              fromdate=datetime.datetime(2000, 1, 1),
              # Do not pass values before this date
              todate=datetime.datetime(2000, 12, 31),
              # Do not pass values after this date
              reverse=False)
      
          # Add the Data Feed to Cerebro
          cerebro.adddata(data)
      
          # Set our desired cash start
          cerebro.broker.setcash(1000.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())
      
          # Add the analyzers we are interested in
          cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name="ta")
          cerebro.addanalyzer(bt.analyzers.DrawDown, _name="dd")
      
          # Run over everything
          strategies = cerebro.run()
          firstStrat = strategies[0]
      
          # print the analyzers
          printTradeAnalysis(firstStrat.analyzers.ta.get_analysis())
          printDrawDownAnalysis(firstStrat.analyzers.dd.get_analysis())
      
          # Print out the final result
          print('Ending Portfolio Value: %.2f' % cerebro.broker.getvalue())
      

      This creates the following output:

      Starting Portfolio Value: 1000.00
      2000-03-31, OPERATION PROFIT, GROSS 100.00, NET 100.00
      2000-04-12, OPERATION PROFIT, GROSS -28.70, NET -28.70
      2000-04-20, OPERATION PROFIT, GROSS -24.00, NET -24.00
      2000-05-05, OPERATION PROFIT, GROSS -22.50, NET -22.50
      2000-05-09, OPERATION PROFIT, GROSS -8.20, NET -8.20
      2000-05-19, OPERATION PROFIT, GROSS -28.10, NET -28.10
      2000-06-23, OPERATION PROFIT, GROSS 37.90, NET 37.90
      2000-06-28, OPERATION PROFIT, GROSS -1.60, NET -1.60
      2000-06-30, OPERATION PROFIT, GROSS -8.40, NET -8.40
      2000-07-05, OPERATION PROFIT, GROSS -21.50, NET -21.50
      2000-07-24, OPERATION PROFIT, GROSS -1.60, NET -1.60
      2000-07-28, OPERATION PROFIT, GROSS 1.50, NET 1.50
      2000-08-02, OPERATION PROFIT, GROSS -10.90, NET -10.90
      2000-09-11, OPERATION PROFIT, GROSS 38.70, NET 38.70
      2000-10-02, OPERATION PROFIT, GROSS -8.00, NET -8.00
      2000-10-31, OPERATION PROFIT, GROSS -35.00, NET -35.00
      2000-11-21, OPERATION PROFIT, GROSS 5.00, NET 5.00
      2000-12-15, OPERATION PROFIT, GROSS 30.60, NET 30.60
      2000-12-21, OPERATION PROFIT, GROSS -21.90, NET -21.90
      Trade Analysis Results:
                     Total Open     Total Closed   Total Won      Total Lost     
                     1              19             6              13             
                     Strike Rate    Win Streak     Losing Streak  PnL Net        
                     31.57894736842105 2              5              -6.7           
      Drawdown Analysis Results:
                     Drawdown       Moneydown      Length         
                     14.12          161.2          193            
                     Max drawdown   Max moneydown  Max len        
                     15.04          171.7          193            
      Ending Portfolio Value: 980.10
      

      As can be seen in the output, according to the DrawDown Analyzer, the maximum money down is 171.7 (and the money down is 161.2). However, on the basis of the operating profit, the maximum drawdown is 111.5.

      Furthermore, the length and maximum length of the drawdown is reported to be 193 days.

      Can anyone explain what I am doing wrong or what is going on?

      Many thanks in advance,

      Ad

      1 Reply Last reply Reply Quote 0
      • A
        ab_trader last edited by ab_trader

        Drawdown analyzer works with the historical broker value curve. You calculate your results based in closed trades equity, no in-trade drawdowns are considered. The results should be different in most of the cases.

        • If my answer helped, hit reputation up arrow at lower right corner of the post.
        • Python Debugging With Pdb
        • New to python and bt - check this out
        1 Reply Last reply Reply Quote 1
        • 1 / 1
        • First post
          Last post
        Copyright © 2016, 2017, 2018, 2019, 2020, 2021 NodeBB Forums | Contributors