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/

    Assigning independent value for multi assets

    General Discussion
    2
    4
    462
    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.
    • J
      jabbarabdullah last edited by

      Hi all,

      I have a multiple data feed strategy and I want to assign the starting cash and value for each and every data in the strategy to be independent in order for my Stop loss/Take Profit strategy to work (Something like
      data_d_cash = portfolio_cash * (1/len(datalist)).

      I need each data to have its own independent proportion for the stop loss/ take profit to work for each d in the strategy below.

      from __future__ import (absolute_import, division, print_function, unicode_literals)
      
      import backtrader as bt
      import datetime as dt
      import pytz
      import math
      
      cn1='XXX'
      cn2='YYY'
      datalist = [cn1,cn2]
      csh=100000
      stdt=dt.datetime(2019,07,2,9,30)
      enddt=dt.datetime(2019,07,2,16,00)
      SL=0.01
      TP=2*SL
      SU=0.005
      SUpct=SU*100
      prop=1/len(datalist) #proportion of portfolio
      batch=10 #rounding
      entrytime = dt.time(9,45)
      exittime = dt.time(15,55)
      stakesize=10
      lwbnd=40
      upbnd=90
      commis=0.05
      TPpct=TP*100
      SLpct=SL*100
      
      def rounddown(x):
          return int(math.floor(x / 1)) * 1
      
      class IBCommission(bt.CommInfoBase):
      
          """A :class:`IBCommision` charges the way interactive brokers does.
          """
      
          params = (('stocklike', True), ('commtype', bt.CommInfoBase.COMM_FIXED),)
      
          def _getcommission(self, size, price, pseudoexec):
              return self.p.commission
      
      class PropSizer(bt.Sizer):
          """A position sizer that will buy as many stocks as necessary for a certain proportion of the portfolio
             to be committed to the position, while allowing stocks to be bought in batches (say, 100)"""
          params = {"prop": prop, "batch": batch}
       
          def _getsizing(self, comminfo, cash, data, isbuy):
              """Returns the proper sizing"""
              
              for i, d in enumerate(self.datas):
                  if isbuy:    # Buying
                      target = csh * self.params.prop    # Ideal total value of the position
                      price = self.data.close[0]
                      shares_ideal = target / price    # How many shares are needed to get target
                      batches = int(shares_ideal / self.params.batch)    # How many batches is this trade?
                      shares = batches * self.params.batch    # The actual number of shares bought
           
                      if shares * price > cash:
                          return 0    # Not enough money for this trade
                      else:
                          return shares
           
                  else:    # Selling
                      return self.broker.getposition(d).size    # Clear the position
                  
      class MainSt(bt.Strategy):
          
      
          data_live = False
          def notify_data(self, data, status, *args, **kwargs):
              print('*' * 5, 'DATA NOTIF:', data._getstatusname(status),
                *args)
              if status == data.LIVE:
                  self.data_live = True
                  
          def log(self, txt, dt=None, vlm=None):
              dt = dt or self.datas[0].datetime.datetime(0)
              vlm = vlm or self.data.volume[0]
              print('%s) %s, %s' % (len(self), dt.isoformat(), txt))
      
      
          def __init__(self):
              self.order = {}
              self.buyprice = {}
              self.buycomm = {}
              self.bar_executed = {}
              self.inds = dict()
              self.o = dict()
              self.lendata = dict()
              
              for i, d in enumerate(self.datas):
                  self.order[d] = None
                  self.buyprice[d] = None
                  self.buycomm[d] = None   
                  self.bar_executed[d] = None
                  self.inds[d] = dict()
                  self.inds[d]['sma'] = bt.indicators.SimpleMovingAverage(d.close, period=54)
                  self.inds[d]['rsi'] = bt.indicators.RelativeStrengthIndex(d.close, period=14,safediv=True, upperband=upbnd,lowerband=lwbnd)
      
          def notify_order(self, order):
              if order.status in [order.Submitted, order.Accepted]:
                  return    
      
              
              if order.status in [order.Completed]:
                  
                  if order.isbuy():
                          
                      self.log('%s: BUY EXECUTED, Price: %.2f, Cost: %.2f, Size: %.2f, Comm %.2f' %
                          (order.data._name,
                           order.executed.price,
                           order.executed.value,
                           order.executed.size,
                           order.executed.comm))
      
                      self.buyprice = order.executed.price
                      self.buycomm = order.executed.comm
                      self.last_executed_price = order.executed.price
                      self.order = None
                      self.bar_executed = len(self)
                  else:
      
                      # Sell
                      self.log('%s: SELL EXECUTED, Price: %.2f, Cost: %.2f, Size: %.2f, Comm %.2f' %
                               (order.data._name,
                                order.executed.price,
                                order.executed.value,
                                order.executed.size,
                                order.executed.comm))
                     
                      self.last_executed_price = order.executed.price
                      self.order = None           
                      self.bar_executed = len(self)
      
                  self.bar_executed = len(self)
          
              elif order.status in [order.Canceled, order.Margin, order.Rejected]:
                  self.log('%s Order Canceled/Margin/Rejected' % (order.data._name))
      
              # Write down: no pending order[order.data._name]
              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):
              for i, d in enumerate(self.datas):
                  pv = self.broker.get_value(None)
                  pvd = self.broker.get_value()*prop
                  cshd = self.broker.get_cash()*prop
                  self.stakes = abs(rounddown(pv/d.close[0]))
                  target = csh * prop    # Ideal total value of the position
                  price = d.close[0]
                  shares_ideal = pvd / price    # How many shares are needed to get target
                  batches = int(shares_ideal / batch)    # How many batches is this trade?
                  shares = batches * batch         
                  
                  if not self.getposition(d).size:
                      
                      if (pv >= csh*(1+TP)):
                          return
                              
                      
                      if (pv <= csh*(1-SL)):
                          return
                      
                      if (d.datetime.time(-1) >= exittime):
                          return
                      
                      if ((self.inds[d]['rsi'][0] <= lwbnd)and(d.datetime.time(0) >= entrytime)):
                          print('%s pv: %.2f, cash:%.2f, rsi: %.2f'% (d._name, pvd, cshd, self.inds[d]['rsi'][0]))
                          self.log('%s: BUY CREATE, %.2f, VLM BOUGHT: %.2f' % (d._name, d.close[0], shares))
                          self.order = self.buy(data=d, size=shares)
                          
                  else:
                      
                      if (self.inds[d]['rsi'][0] >= upbnd):
                          print('%s pv: %.2f, cash:%.2f, rsi: %.2f'% (d._name, pvd, cshd, self.inds[d]['rsi'][0]))
                          self.log('%s: SELL CREATE (RSI>Upbnd), %.2f, VLM BOUGHT: %.2f' % (d._name, d.close[0], self.getposition(d).size))
                          self.order = self.sell(data=d, size = abs(self.getposition(d).size))       
                          
                      else:
                          if (d.close[0] >= ((self.getposition(d).price*(1+SU)))):
                              print('%s pv: %.2f, cash:%.2f, rsi: %.2f'% (d._name, pvd, cshd, self.inds[d]['rsi'][0]))
                              self.log('%s: SELL CREATE (>%.2fpct), %.2f, VLM BOUGHT: %.2f' % (d._name, SUpct, d.close[0], self.getposition(d).size))
                              self.order = self.sell(data=d, size = abs(self.getposition(d).size))
      
                          else:
                              if (d.close[0] <= ((self.getposition(d).price*(1-SU)))):
                                  print('%s pv: %.2f, cash:%.2f, rsi: %.2f'% (d._name, pvd, cshd, self.inds[d]['rsi'][0]))
                                  self.log('%s: SELL CREATE (<%.2fpct), %.2f, VLM BOUGHT: %.2f' % (d._name, SUpct, d.close[0], self.getposition(d).size))
                                  self.order = self.sell(data=d, size = abs(self.getposition(d).size))
                                  '''
                                      else:
                                          if (pvd >= target*(1+TP)):
                                              print('%s pv: %.2f, cash:%.2f, rsi: %.2f'% (d._name, pvd, cshd, self.inds[d]['rsi'][0]))
                                              self.log('%s: TAKE PROFIT VAL CREATE (>%.2fpct), %.2f, VLM BOUGHT: %.2f' % (d._name, TPpct, d.close[0], self.getposition(d).size))
                                              self.order = self.sell(data=d, size=abs(self.getposition(d).size))
                                          else:
                                              if (pvd <= target*(1-SL)):
                                                  print('%s pv: %.2f, cash:%.2f, rsi: %.2f'% (d._name, pvd, cshd, self.inds[d]['rsi'][0]))
                                                  self.log('%s: STOPLOSS VAL CREATE (<%.2fpct), %.2f, VLM BOUGHT: %.2f' % (d._name, SLpct, d.close[0], self.getposition(d).size))
                                                  self.order = self.sell(data=d, size=abs(self.getposition(d).size))
                                   '''            
                              else:
                                  if (d.datetime.time(0) >= exittime):
                                      print('%s pv: %.2f, cash:%.2f, rsi: %.2f'% (d._name, pvd, cshd, self.inds[d]['rsi'][0]))
                                      self.log('%s: EOD STOP, %.2f, VLM BOUGHT: %.2f' % (d._name, d.close[0], self.getposition(d).size))
                                      self.order = self.sell(data=d, size=abs(self.getposition(d).size))  
      
      def runstrat():
      
          
          # Get a pandas dataframe
          cerebro = bt.Cerebro()
          ibstore = bt.stores.IBStore(port=7497, host='127.0.0.1', clientId=12345)
          #create our data list
          is_first = True
          #Loop through the list adding to cerebro.
          for i in datalist:
              data = ibstore.getdata(dataname=i,fromdate=stdt, historical =True, useRTH=True, tz = pytz.timezone('US/Eastern'),
                                   todate=enddt, timeframe=bt.TimeFrame.Seconds, compression=15)
              '''
              if i in datalist:
                  if is_first:
                      data_main_plot = data
                      is_first = False
                  else:
                      data.plotinfo.plotmaster = data_main_plot
              else:
                  data.plotinfo.plot = False
              '''
              cerebro.adddata(data)   
              
          cerebro.broker.setcash(csh)    
          comminfo = IBCommission(commission=commis)
          cerebro.broker.addcommissioninfo(comminfo)
          cerebro.addwriter(bt.WriterFile, csv=True, rounding=2, out="C:\\Users\\User\\Desktop\\Backtest Library\\TestResults.csv")
          
          start_value = cerebro.broker.getvalue()
      
          cerebro.addstrategy(MainSt)
          # Run over everything
          cerebro.run()  
          
          # Plot the result
          cerebro.plot(volume=False)
          
          # Print out the starting conditions
          print(' ')
          print('--','Summary','--')
          print('Start capital: %.2f' % start_value)
          # Print out the final result
          print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
          print('Final Portfolio Cash: %.2f' % cerebro.broker.getcash())    
          print('Final PnL Value: %.2f' % (cerebro.broker.getvalue()-start_value))
      
      if __name__ == '__main__':
      
          runstrat()
          
      

      Appreciate your help. Thanks!

      B 1 Reply Last reply Reply Quote 0
      • B
        backtrader administrators @jabbarabdullah last edited by

        @jabbarabdullah said in Assigning independent value for multi assets:

        data_d_cash = portfolio_cash * (1/len(datalist)).

        Apply then that in your loop.

        1 Reply Last reply Reply Quote 0
        • J
          jabbarabdullah last edited by

          But that will simply divide the total portfolio amount with number of data feeds right? I was thinking of something like
          self.broker.get_value(data=d) for each asset because my take profit is based on the increase in value of data d
          (ie. self.getposition(d).size*d.close[0] >= self.broker.get_cash(d)*(1+take_profit)). However, we cannot simply assign data=d to the get_value, so I am quite lost now/ Really appreciate your help please.

          Thank you.

          1 Reply Last reply Reply Quote 0
          • B
            backtrader administrators last edited by

            Sincerely: you need to make up your mind as to what you want. What you post makes no sense. You probably have an idea about the direction in which you want to go.

            If your take-profit/stop-loss is based on the value increase/decrease of an asset you have acquired, record the initial value and exit when the value has gone beyond your limits.

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