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/

    Bracket Order on Portfolio testing

    General Code/Help
    2
    3
    85
    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.
    • M
      marketwizard last edited by

      Hello,

      I'm trying to backtest a Strategy on a whole portfolio using bracket orders.
      I don't get any error or something similar, but the trades are not executed in the right way.

      When an order is placed, it's allways only for one stock unit instead of a calculated amount.
      In my case I want to place an order with a target amount of up to 10% of my portfolio.

      It works with other methods, but when I use the Bracket order, it's not working.

      Here is the code:

      import backtrader as bt
      import pandas as pd
      import os.path
      import sys
      from datetime import datetime, timedelta, date
      import math
      import quantstats as qs
      from backtrader_plotting import Bokeh
      from backtrader_plotting.schemes import Blackly
      import warnings
      from csv import reader
      import csv
      from IB_commission import IBCommision
      
      period = 2000
      icap = 100000
      risk = 0.03
      pos_percent = 0.03
      
      warnings.simplefilter(action='ignore', category=FutureWarning)
      
      benchmark_path = XXX
      project_path = XXX
      
      strategy_name = "Stochastic_V1_PF"
      
      data_path = XXX
      list_path =  XXX
      
      datalist = []
      
      # load the datalist
      with open(list_path, 'r') as read_obj:
          tickers = reader(read_obj)
      
          for ticker in tickers:
              tick = str(ticker[0])
              filename = '%s_DAILY_ALPHA_VANTAGE.txt' % tick
              datapath = os.path.join(data_path, filename)
              datalist.append((datapath, tick))
      
      
      class ConstantValue(bt.Indicator):
          lines = ('constant',)
          params = (('constant', float('NaN')),)
      
          def next(self):
              self.lines.constant[0] = self.p.constant
      
      class RSIStack(bt.Strategy):
          # Define the parameters of the strategy
          params = (
              ('oneplot', True),
              ("mtrade", False),
              ("percents", 0.1)
          )
      
          def notify_order(self, order):
              if order.status == order.Completed:
                  pass
      
              if not order.alive():
                  self.order = None  # indicate no order is pending
      
          def __init__(self):
      
              self.log_pnl = []
              # Initialize the elements which are needed for the strategy (indicators, etc...)
              self.inds = dict()
      
              for i, d in enumerate(self.datas):
      
                  # Define the indicators
                  self.inds[d] = dict()
      
                  self.inds[d]['ema200'] = bt.indicators.EMA(d.close, period=200, plot=False, subplot=False)
                  self.inds[d]['ema300'] = bt.indicators.EMA(d.close, period=300, plot=False, subplot=False)
      
                  self.inds[d]['stoch'] = bt.indicators.Stochastic(d, period=70, period_dfast=10, period_dslow=10, plot=False, safediv=True)
                  self.inds[d]['percK'] = self.inds[d]['stoch'].percK
                  self.inds[d]['percD'] = self.inds[d]['stoch'].percD
      
                  self.inds[d]['rsi1'] = bt.indicators.RSI(d, period=2, safediv=True)
                  self.inds[d]['atr'] = bt.indicators.ATR(d, period=14, plot=False, subplot=False)
      
                  # Define the crossover signals
                  self.inds[d]['stoch_cross'] = bt.indicators.CrossOver(self.inds[d]['percK'], self.inds[d]['percD'], plot=False, subplot=False)
      
                  self.inds[d]['doji'] = bt.talib.CDLDOJI(d.open, d.high, d.low, d.close, plot=False)
      
                  self.inds[d]['curpos'] = None
      
                  self.stoch_oversold = 70.0
                  self.stoch_overbought = 30.0
      
                  if i > 0:  # Check we are not on the first loop of data feed:
                      if self.p.oneplot == True:
                          d.plotinfo.plotmaster = self.datas[0]
      
          def start(self):
              self.order = None  # sentinel to avoid operations on pending order
      
          def prenext(self):
              self.next()
      
          def next(self):
      
              for i, d in enumerate(self.datas):
      
                  pos = self.getposition(d).size
      
                  # Get the Amount of cash in the Portfolio
                  cash = self.broker.get_cash()
      
                  if self.order:
                      return  # pending order execution
      
                  if not pos:  # check if we already have an open position on the stock
      
                      long_tp = d.close[0] + 5 * self.inds[d]['atr']
                      long_stop = d.close[0] - 2 * self.inds[d]['atr']
      
                      if self.inds[d]['ema200'] > self.inds[d]['ema300']:
      
                          if self.inds[d]['stoch'] < self.stoch_oversold and (self.inds[d]['doji'] == 100 or self.inds[d]['doji'] == -100):
      
                              buy_ord = self.buy_bracket(data=d, limitprice=long_tp, stopprice=long_stop, exectype=bt.Order.Market)
                              self.inds[d]['curpos'] = 'long'
      
      def printSQN(analyzer):
          sqn = round(analyzer.sqn, 2)
          print('SQN: {}'.format(sqn))
      
      
      def pretty_print(format, *args):
          print(format.format(*args))
      
      
      def exists(object, *properties):
          for property in properties:
              if not property in object: return False
              object = object.get(property)
          return True
      
      def printTradeAnalysis(cerebro, analyzers):
          format = "  {:<24} : {:<24}"
          NA = '-'
      
          print('Backtesting Results')
          if hasattr(analyzers, 'ta'):
              ta = analyzers.ta.get_analysis()
      
              openTotal = ta.total.open if exists(ta, 'total', 'open') else None
              closedTotal = ta.total.closed if exists(ta, 'total', 'closed') else None
              wonTotal = ta.won.total if exists(ta, 'won', 'total') else None
              lostTotal = ta.lost.total if exists(ta, 'lost', 'total') else None
      
              streakWonLongest = ta.streak.won.longest if exists(ta, 'streak', 'won', 'longest') else None
              streakLostLongest = ta.streak.lost.longest if exists(ta, 'streak', 'lost', 'longest') else None
      
              pnlNetTotal = ta.pnl.net.total if exists(ta, 'pnl', 'net', 'total') else None
              pnlNetAverage = ta.pnl.net.average if exists(ta, 'pnl', 'net', 'average') else None
      
              pretty_print(format, 'Open Positions', openTotal or NA)
              pretty_print(format, 'Closed Trades', closedTotal or NA)
              pretty_print(format, 'Winning Trades', wonTotal or NA)
              pretty_print(format, 'Loosing Trades', lostTotal or NA)
              print('\n')
      
              pretty_print(format, 'Longest Winning Streak', streakWonLongest or NA)
              pretty_print(format, 'Longest Loosing Streak', streakLostLongest or NA)
              pretty_print(format, 'Strike Rate (Win/closed)',
                           (wonTotal / closedTotal) * 100 if wonTotal and closedTotal else NA)
              print('\n')
      
              pretty_print(format, 'Inital Portfolio Value', '${}'.format(icap))
              pretty_print(format, 'Final Portfolio Value', '${}'.format(cerebro.broker.getvalue()))
              pretty_print(format, 'Net P/L', '${}'.format(round(pnlNetTotal, 2)) if pnlNetTotal else NA)
              pretty_print(format, 'P/L Average per trade', '${}'.format(round(pnlNetAverage, 2)) if pnlNetAverage else NA)
              print('\n')
      
          if hasattr(analyzers, 'drawdown'):
              pretty_print(format, 'Drawdown', '${}'.format(analyzers.drawdown.get_analysis()['drawdown']))
          if hasattr(analyzers, 'sharpe'):
              pretty_print(format, 'Sharpe Ratio:', analyzers.sharpe.get_analysis()['sharperatio'])
          if hasattr(analyzers, 'vwr'):
              pretty_print(format, 'VRW', analyzers.vwr.get_analysis()['vwr'])
          if hasattr(analyzers, 'sqn'):
              pretty_print(format, 'SQN', analyzers.sqn.get_analysis()['sqn'])
          print('\n')
      
          print('Transactions')
          format = "  {:<24} {:<24} {:<16} {:<8} {:<8} {:<16}"
          pretty_print(format, 'Date', 'Amount', 'Price', 'SID', 'Symbol', 'Value')
          for key, value in analyzers.txn.get_analysis().items():
              pretty_print(format, key.strftime("%Y/%m/%d %H:%M:%S"), value[0][0], value[0][1], value[0][2], value[0][3],
                           value[0][4])
      
      
      if __name__ == '__main__':
      
          NA = '-'
      
          analysis_df = pd.DataFrame(columns=['Portfolio', 'Initial portfolio value', 'Final portfolio value',
                                              'Gross P/L', 'Net P/L', 'Open P/L',
                                              'Net P/L average', 'Net total wins', 'Net total lost',
                                              'Net average wins', 'Net average lost',
                                              'Profit factor', 'Payoff ratio',
                                              'Max drawdown', 'total trades',
                                              'Total closed trades', 'Total open trades',
                                              'Total winning trades', 'Total losing trades',
                                              'Strike rate', 'Sharpe ratio', 'VRW', 'SQN',
                                              'Average trade duration',
                                              'Average winning trade duration', 'Average losing trade duration',
                                              'Longest losing Streak', 'Longest winning Streak',
                                              'Total long trades', 'Total long win trades', 'Total long lost trades',
                                              'Total long PnL', 'Long average PnL', 'Max long PnL',
                                              'Total long win trades', 'Long average win', 'Long Max win',
                                              'Total long lost trades', 'Long average lost', 'Long Max lost',
                                              'Average long duration', 'Average long win duration',
                                              'Average long lost duration',
                                              'Total short trades', 'Total short win trades', 'Total short lost trades',
                                              'Total short PnL', 'Short average PnL', 'Max short PnL',
                                              'Total short win trades', 'Short average win', 'Short Max win',
                                              'Total short lost trades', 'Short average lost', 'Short max lost',
                                              'Average short duration', 'Average short win duration',
                                              'Average short lost duration'
                                              ])
      
          cerebro = bt.Cerebro(stdstats=False)
      
          endDate = date(2021, 1, 17)
          fromDate = (endDate - timedelta(days=period))
          startDate = fromDate.strftime('%Y-%m-%d')
      
          for i in range(len(datalist)):
              data = bt.feeds.GenericCSVData(
      
                  dataname=datalist[i][0],
                  # Do not pass values before this date
                  fromdate=datetime(fromDate.year, fromDate.month, fromDate.day),
                  # Do not pass values before this date
                  todate=datetime(datetime.today().year, datetime.today().month, datetime.today().day),
      
                  nullvalue=0.0,
      
                  dtformat=('%Y-%m-%d'),
      
                  datetime=0,
                  open=1,
                  high=2,
                  low=3,
                  close=4,
                  volume=5,
                  openinterest=-1
              )
      
              cerebro.adddata(data, name=datalist[i][1])
      
          # Set the Cash for the Strategy
          cerebro.broker.setcash(icap)
          # Set the comissions
          comminfo = IBCommision()
          cerebro.broker.addcommissioninfo(comminfo)
      
          # Add the benchmark Observer
          # cerebro.addobserver(bt.observers.Benchmark, data=data, timeframe=bt.TimeFrame.NoTimeFrame)
          # Add the analyzers we are interested in
          cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='ta')
          cerebro.addanalyzer(bt.analyzers.DrawDown, _name='drawdown')
          cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe', riskfreerate=0.0, annualize=True,
                              timeframe=bt.TimeFrame.Days)
          cerebro.addanalyzer(bt.analyzers.VWR, _name='vwr')
          cerebro.addanalyzer(bt.analyzers.SQN,
                              _name='sqn')  # The sqn value should be deemed reliable when the number of trades >= 30
          '''
          1.6 - 1.9 Below average
          2.0 - 2.4 Average
          2.5 - 2.9 Good
          3.0 - 5.0 Excellent
          5.1 - 6.9 Superb
          7.0 - Holy Grail?
          '''
          cerebro.addanalyzer(bt.analyzers.Transactions, _name='txn')
          cerebro.addanalyzer(bt.analyzers.PyFolio, _name='pyfolio')
      
          cerebro.addstrategy(RSIStack, oneplot=False)
      
          # Run over everything
          strategies = cerebro.run(runonce=False)
          Sta = strategies[0]
      
          # print the analyzers
          printTradeAnalysis(cerebro, Sta.analyzers)
      
          # Generate the chart
          b = Bokeh(style='bar', scheme=Blackly(), output_mode='show')
          cerebro.plot(b)
      
          # ---- Format the benchmark from SPY.csv ----
          with open(benchmark_path, mode='r') as infile:
              next(infile)
              for line in infile:
                  reader = csv.reader(infile)
                  mydict = {
                      datetime.strptime(rows[0], "%Y-%m-%d"): float(rows[4]) for rows in reader
                  }
      
          benchmark = (
              pd.DataFrame.from_dict(mydict, orient="index")
          )
          returns_bm = qs.utils.to_returns(benchmark)
          returns_bm.index = pd.to_datetime(returns_bm.index)
          # -------------------------------------------
      
          # Generate the Quantstats HTML Report
          portfolio_stats = Sta.analyzers.getbyname('pyfolio')
          returns, positions, transactions, gross_lev = portfolio_stats.get_pf_items()
          returns.index = returns.index.tz_convert(None)
          report_name = '%s_%s_%s.html' % (strategy_name, startDate, endDate)
          qs.extend_pandas()
          qs.reports.html(returns, returns_bm, output=report_name, title=report_name)
          # returns.to_csv('returns.csv')
      
          # fill the CSV with the strategy KPIs for every stock tested
          CSV_name = os.path.join(project_path, "Test_report_%s.csv" % (strategy_name))
          analysis_df.to_csv(CSV_name, index=False, header=True)
      
      

      And here are the (transactions) results:

      Transactions
        Date                     Amount                   Price            SID      Symbol   Value           
        2016/10/04 23:59:59      1                        7.0              21       AMD      -7.0            
        2016/10/05 23:59:59      1                        37.3984          7        ADM      -37.3984        
        2016/10/06 23:59:59      1                        79.3908          8        ADP      -79.3908        
        2016/10/07 23:59:59      1                        32.4279          13       AFL      -32.4279        
        2016/10/10 23:59:59      1                        39.7096          4        ABT      -39.7096        
        2016/10/11 23:59:59      -1                       38.20808104887775 4        ABT      38.20808104887775
        2016/10/12 23:59:59      1                        63.9792          67       CHRW     -63.9792        
        2016/10/13 23:59:59      1                        37.7296          4        ABT      -37.7296        
        2016/10/14 23:59:59      1                        6.92             21       AMD      -6.92           
        2016/10/17 23:59:59      1                        318.671          50       BLK      -318.671        
        2016/10/18 23:59:59      1                        57.0478          6        ADI      -57.0478        
        2016/10/19 23:59:59      1                        105.2816         24       AMT      -105.2816       
        2016/10/20 23:59:59      1                        31.8171          13       AFL      -31.8171        
        2016/10/21 23:59:59      1                        43.9882          0        A        -43.9882        
        2016/10/24 23:59:59      1                        45.2669          28       AOS      -45.2669        
        2016/10/25 23:59:59      -1                       42.949516258290714 0        A        42.949516258290714
        2016/10/26 23:59:59      -1                       68.3121          37       AVY      68.3121         
        2016/10/27 23:59:59      1                        42.3508          0        A        -42.3508        
        2016/10/28 23:59:59      1                        31.8895          13       AFL      -31.8895        
        2016/10/31 23:59:59      -1                       36.408354838686556 4        ABT      36.408354838686556
        2016/11/01 23:59:59      -1                       40.61960193537223 7        ADM      40.61960193537223
        2016/11/02 23:59:59      1                        104.97           3        ABMD     -104.97         
        2016/11/03 23:59:59      1                        31.0932          13       AFL      -31.0932        
        2016/11/04 23:59:59      1                        21.49            53       BSX      -21.49          
        2016/11/07 23:59:59      1                        79.5919          17       ALB      -79.5919        
        2016/11/08 23:59:59      1                        42.4337          28       AOS      -42.4337        
        2016/11/09 23:59:59      -1                       75.03601062560966 17       ALB      75.03601062560966
        2016/11/10 23:59:59      -1                       51.98551251106071 11       AEP      51.98551251106071
        2016/11/11 23:59:59      -1                       36.3981          51       BLL      36.3981         
        2016/11/14 23:59:59      1                        96.762           24       AMT      -96.762         
        2016/11/15 23:59:59      -1                       62.27067399669613 6        ADI      62.27067399669613
        2016/11/16 23:59:59      -1                       45.76131070490641 0        A        45.76131070490641
        2016/11/17 23:59:59      1                        46.3494          15       AJG      -46.3494        
        2016/11/18 23:59:59      1                        25.9753          1        AAPL     -25.9753        
        2016/11/21 23:59:59      1                        8.87             21       AMD      -8.87           
        2016/11/22 23:59:59      1                        105.85           5        ADBE     -105.85         
        2016/11/23 23:59:59      1                        100.2953         33       ASML     -100.2953       
        2016/11/25 23:59:59      1                        78.5715          17       ALB      -78.5715        
        2016/11/28 23:59:59      1                        38.2937          7        ADM      -38.2937        
        2016/11/29 23:59:59      1                        36.3534          35       ATVI     -36.3534        
        2016/11/30 23:59:59      -1                       131.14740123065323 30       APD      131.14740123065323
        2016/12/01 23:59:59      -1                       101.50838500709875 5        ADBE     101.50838500709875
        2016/12/02 23:59:59      -1                       34.24753895416149 35       ATVI     34.24753895416149
        2016/12/05 23:59:59      -1                       61.486834217216085 29       APA      61.486834217216085
        2016/12/06 23:59:59      1                        31.2918          13       AFL      -31.2918        
        2016/12/07 23:59:59      -1                       110.2229904459513 27       AON      110.2229904459513
        2016/12/08 23:59:59      -1                       279.33752616104323 46       BIIB     279.33752616104323
        2016/12/09 23:59:59      1                        104.0            5        ADBE     -104.0          
        2016/12/12 23:59:59      1                        112.1827         55       BXP      -112.1827       
        2016/12/13 23:59:59      1                        44.6046          0        A        -44.6046        
        2016/12/14 23:59:59      1                        288.21           46       BIIB     -288.21         
        2016/12/15 23:59:59      1                        97.4704          24       AMT      -97.4704        
        2016/12/16 23:59:59      -1                       10.967195448987663 21       AMD      10.967195448987663
        2016/12/19 23:59:59      1                        25.51            63       CDNS     -25.51          
        2016/12/20 23:59:59      1                        174.9558         79       COO      -174.9558       
        2016/12/21 23:59:59      -1                       64.4191713757161 91       D        64.4191713757161
        2016/12/22 23:59:59      -1                       31.747025613949187 20       AMAT     31.747025613949187
        2016/12/23 23:59:59      -1                       78.53153413751777 99       DLTR     78.53153413751777
        2016/12/27 23:59:59      1                        13.3937          158      HPQ      -13.3937        
        2016/12/28 23:59:59      1                        75.93            9        ADSK     -75.93          
        2016/12/29 23:59:59      -1                       90.43504287233867 216      MCO      90.43504287233867
        2016/12/30 23:59:59      1                        44.7989          28       AOS      -44.7989        
        2017/01/03 23:59:59      1                        96.5157          116      EOG      -96.5157        
        2017/01/04 23:59:59      1                        107.4254         27       AON      -107.4254       
        2017/01/05 23:59:59      1                        179.4013         129      FDX      -179.4013       
        2017/01/06 23:59:59      -1                       157.6728484681152 36       AVB      157.6728484681152
        2017/01/10 23:59:59      -1                       22.97554089153823 53       BSX      22.97554089153823
        2017/01/11 23:59:59      -1                       87.65672724153029 17       ALB      87.65672724153029
        2017/01/12 23:59:59      -1                       65.84221944952445 69       CINF     65.84221944952445
        2017/01/13 23:59:59      1                        32.6533          123      EW       -32.6533        
        2017/01/17 23:59:59      1                        57.3981          29       APA      -57.3981        
        2017/01/18 23:59:59      -1                       96.40142596702519 162      HSY      96.40142596702519
        2017/01/19 23:59:59      1                        109.2384         86       CTAS     -109.2384       
        2017/01/20 23:59:59      -1                       766.5184904139112 39       AZO      766.5184904139112
        2017/01/23 23:59:59      -1                       19.409222458171516 174      IPG      19.409222458171516
        2017/01/24 23:59:59      -1                       113.79884365794422 5        ADBE     113.79884365794422
        2017/01/25 23:59:59      -1                       28.816271955453356 1        AAPL     
      
      
      Maybe someone can help ?
      1 Reply Last reply Reply Quote 0
      • M
        marketwizard last edited by

        Can anyone help ?

        1 Reply Last reply Reply Quote 0
        • Elis Moer
          Elis Moer last edited by

          Differences between men's and women's hoodies. Contrary to popular belief Jesus is king hoodie, men's hoodies usually have much larger hoods than women's hoodies.

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