Navigation

    Backtrader Community

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

    Crespecular

    @Crespecular

    0
    Reputation
    289
    Profile views
    11
    Posts
    0
    Followers
    0
    Following
    Joined Last Online

    Crespecular Unfollow Follow

    Latest posts made by Crespecular

    • RE: Shorting values are negative

      Thanks a lot!

      posted in General Code/Help
      C
      Crespecular
    • RE: Shorting values are negative

      Thank you for your quick response!

      Btw, when you say cash inflow, do you mean pnl? or is there any code that I can retrieve current cash balance + cash generate(or loss) from the transaction?

      :)

      posted in General Code/Help
      C
      Crespecular
    • Shorting values are negative

      Hi, it is me again. I really appreciate everyone's commitment to this community!

      While I was making a long&short strategy with coding helps from people here, I confronted another problem.

      My strategy is the following :

      For shorting strategy (Which is the code that I wrote below), within the universe, I short 2 stocks (maximum positions that I preset).

      If the price reaches a profit-realisation or loss-cut level then I cover the stocks then enter the market again with shorting 2 different positions within the universe.

      This portfolio will be rebalanced every Q ends regardless of the current stocks' prices.

      This is my code:

      #Short 
      class Short(bt.Strategy):
          
          params = (
          ('losscut', 0.15),  # Different params for losscut & profit realisation & maximum stock holdings 
          ('profit', 0.15),
          ('max_stock', 2), 
          ) 
          
      
              
          def notify_order(self, order):
      
              if order.status in [order.Submitted, order.Accepted]:
                  self.order = order
                  return
      
              if order.status in [order.Completed]:
                  if order.isbuy(): #Trying to track entry price for each stock position we entered
                      self.entry_order.update({order.data._name : order.executed.price})
      
                      print('Date : {}, Current Order is Buy : {}, Price : {}, Value : {}, Commission : {}'.format(
                          self.datetime.date(), order.data._name,order.executed.price, order.executed.value, order.executed.comm))
                      
                  else:
                      print('Date : {}, Current Order is Sell : {}, Price : {}, Value : {}, Commission : {}'.format(
                          self.datetime.date(), order.data._name,order.executed.price, order.executed.value, order.executed.comm))
      
              # Sentinel to None: new orders allowed
              self.order = None  
          
          def __init__(self):
              # __init__ = creates object defined by the Class. INITIALISES the object - sets all attributes
              # To keep track of pending orders, 현재 보유 중인 종목, 투자되었던 종목, Entry Price
              self.entry = {}
              self.entry_order = {}
              self.Short_list = []
              self.Shorted_list = []
              self.rebalanced = []
              self.order = None
              
          def next(self):
              
              for i, d in enumerate(self.datas):
                  dt, dn = self.datetime.date(), d._name
                  pos = self.getposition(d).size
                  offset = BMonthEnd()
      
                  if pos == 0:
                      if dn not in self.Shorted_list:
                          if len(self.Short_list) == self.params.max_stock:
                              return
                          
                          else:
                              self.order_target_percent(data=d, target = -(1/float(self.params.max_stock)))
                              self.Shorted_list.append(dn)
                              self.Short_list.append(dn)
                              self.entry.update({dn : d.close[0]})
                              
                  else:
                      if dt.month in [3,6,9,12]:
                          #Last day of current month
                          if dt.day == offset.rollforward(dt).day:
                              self.order_target_percent(data=d, target = 0)
                              self.rebalanced.append(dn)
                              self.Short_list.remove(dn)
                              return
                          #Last day of previous month
                          #print(offset.rollback(dt))
                          
                      if d.close[0] > self.entry[dn]*float(1 + self.params.losscut):
                          self.order_target_percent(data=d, target = 0.0)
                          self.Short_list.remove(dn)
                          
                      elif d.close[0] < self.entry[dn]*float(1 - self.params.profit):               
                          self.order_target_percent(data=d, target = 0.0)
                          self.Short_list.remove(dn)
                          
      
                          
      # Trade list similar to Amibroker output
      class open_list(bt.Analyzer):
      
          def get_analysis(self):
      
              return self.trades
      
      
          def __init__(self):
      
              self.trades = []
              self.cumprofit = 0.0
      
      
          def notify_trade(self, trade):
      
              if trade.isclosed:
      
                  dir = 'short'
                  if trade.history[0].event.size > 0: dir = 'Short'
      
                  pricein = trade.history[len(trade.history)-1].status.price
      
                  datein = bt.num2date(trade.history[0].status.dt)-timedelta(days=1)
                  
      
                  if trade.data._timeframe >= bt.TimeFrame.Days:
                      datein = datein.date()
      
                  pnl = trade.history[len(trade.history)-1].status.pnlcomm
                  barlen = trade.history[len(trade.history)-1].status.barlen
                  pbar = pnl / barlen
                  self.cumprofit += pnl
      
                  size = value = 0.0
                  for record in trade.history:
                      if abs(size) < abs(record.status.size):
                          size = record.status.size
                          value = record.status.value
      
                  highest_in_trade = max(trade.data.high.get(ago=0, size=barlen+1))
                  lowest_in_trade = min(trade.data.low.get(ago=0, size=barlen+1))
                  hp = 100 * (highest_in_trade - pricein) / pricein
                  lp = 100 * (lowest_in_trade - pricein) / pricein
                  if dir == 'long':
                      mfe = hp
                      mae = lp
                  if dir == 'short':
                      mfe = -lp
                      mae = -hp
      
                  self.trades.append({'ref': trade.ref, 'ticker': trade.data._name, 'dir': dir,
                       'datein': datein, 'pricein': pricein, 'pnl': pnl,
                       'size': size, 'value': value, 'cumpnl': self.cumprofit,
                       'nbars': barlen, 'pnl/bar': round(pbar, 2),
                       'mfe%': round(mfe, 2), 'mae%': round(mae, 2)})
                  
      
      # Trade list similar to Amibroker output
      class trade_list(bt.Analyzer):
      
          def get_analysis(self):
      
              return self.trades
      
      
          def __init__(self):
      
              self.trades = []
              self.cumprofit = 0.0
      
      
          def notify_trade(self, trade):
      
              if trade.isclosed:
                  offset = BMonthEnd()
                  dir = 'short'
                  if trade.history[0].event.size > 0: dir = 'Short'
      
                  pricein = trade.history[len(trade.history)-1].status.price
                  priceout = trade.history[len(trade.history)-1].event.price
                  
                  datein = bt.num2date(trade.history[0].status.dt)-timedelta(days=1)
                  
                  dateout = bt.num2date(trade.history[len(trade.history)-1].status.dt)-timedelta(days=1)
                  if dateout.weekday() == 5:      #if it's Saturday
                      dateout = dateout - timedelta(days = 1) #then make it Friday
                  elif dateout.weekday() == 6:      #if it's Sunday
                      dateout = dateout - timedelta(days = 2); #then make it Friday
          
                  if dateout.strftime("%Y-%m-%d") == offset.rollforward(bt.num2date(trade.history[len(trade.history)-1].status.dt)-timedelta(days=1)).strftime("%Y-%m-%d"):
                      status = 'Rebalanced'
                  else:
                      if pricein > priceout:
                          status = 'Loss-Cut'
                      else:
                          status = 'Profit-realisation'
      
                  if trade.data._timeframe >= bt.TimeFrame.Days:
                      datein = datein.date()
                      dateout = dateout.date()
      
                  pcntchange = 100 * priceout / pricein - 100
                  pnl = trade.history[len(trade.history)-1].status.pnlcomm
                  barlen = trade.history[len(trade.history)-1].status.barlen
                  pbar = pnl / barlen
                  self.cumprofit += pnl
      
                  size = value = 0.0
                  for record in trade.history:
                      if abs(size) < abs(record.status.size):
                          size = record.status.size
                          value = record.status.value
      
                  highest_in_trade = max(trade.data.high.get(ago=0, size=barlen+1))
                  lowest_in_trade = min(trade.data.low.get(ago=0, size=barlen+1))
                  hp = 100 * (highest_in_trade - pricein) / pricein
                  lp = 100 * (lowest_in_trade - pricein) / pricein
                  if dir == 'Short':
                      mfe = hp
                      mae = lp
                  if dir == 'short':
                      mfe = -lp
                      mae = -hp
      
                  self.trades.append({'ref': trade.ref, 'ticker': trade.data._name, 'dir': dir, 'Status' : status,
                       'datein': datein, 'pricein': pricein, 'dateout': dateout, 'priceout': priceout,
                       'chng%': round(pcntchange, 2), 'pnl': pnl,
                       'size': size, 'value': value, 'cumpnl': self.cumprofit,
                       'nbars': barlen, 'pnl/bar': round(pbar, 2),
                       'mfe%': round(mfe, 2), 'mae%': round(mae, 2)})   
               
              
      class EIKON_HLOC(bt.feeds.GenericCSVData):
          params = (
              ('nullvalue' , float('NaN')),
              ('dtformat', '%Y-%m-%d'),
              ('datetime', 0),
              ('time',-1),
              ('open', 1),
              ('high', 2),
              ('low', 3),
              ('close', 4),
              ('volume', 5),
              ('openinterest', -1),
          )
          
      
      #Variable for our starting cash
      startcash = 1000000000
      
      #Create an instance of cerebro
      cerebro = bt.Cerebro(cheat_on_open=False)
      
      #Add our strategy
      cerebro.addstrategy(Short)
      
      datalist = []
      for i in bascket:
          datalist.append(('{}.csv'.format(i), i))
      
      for i in range(len(datalist)):
          data = EIKON_HLOC(dataname = datalist[i][0])
          cerebro.adddata(data, name=datalist[i][1])
          
      # Set our desired cash start
      cerebro.broker.setcash(startcash)
      cerebro.broker.set_coc(True)
      cerebro.broker.setcommission(commission=0.005)
      
      # add analyzers
      cerebro.addanalyzer(trade_list, _name='trade_list')
      cerebro.addanalyzer(open_list, _name='open_list')
      # run backtest
      strats = cerebro.run(tradehistory=True)
      # get analyzers data
      trade_list = strats[0].analyzers.trade_list.get_analysis()
      open_list = strats[0].analyzers.open_list.get_analysis()
      print (tabulate(open_list, headers="keys"))
      print (tabulate(trade_list, headers="keys"))
      #Finally plot the end results
      cerebro.plot()
      

      And the result is following:

      Date : 2015-01-05, Current Order is Sell : 000060, Price : 12600.0, Value : -499993200.0, Commission : 2499966.0
      Date : 2015-01-05, Current Order is Sell : 000250, Price : 10050.0, Value : -499997550.0, Commission : 2499987.75
      Date : 2015-03-16, Current Order is Buy : 000250, Price : 11650.0, Value : -499997550.0, Commission : 2897995.75
      Date : 2015-03-16, Current Order is Sell : 005380, Price : 172000.0, Value : -470592000.0, Commission : 2352960.0
      Date : 2015-04-01, Current Order is Buy : 000060, Price : 11400.0, Value : -499993200.0, Commission : 2261874.0
      Date : 2015-04-02, Current Order is Sell : 005930, Price : 28460.0, Value : -488686660.0, Commission : 2443433.3000000003
      Date : 2015-06-03, Current Order is Buy : 005380, Price : 138500.0, Value : -470592000.0, Commission : 1894680.0
      Date : 2015-06-03, Current Order is Sell : 006730, Price : 18784.0, Value : -542951520.0, Commission : 2714757.6
      Date : 2015-07-01, Current Order is Buy : 005930, Price : 25360.0, Value : -488686660.0, Commission : 2177282.8000000003
      Date : 2015-07-02, Current Order is Sell : 034310, Price : 21850.0, Value : -546075200.0, Commission : 2730376.0
      Date : 2015-07-23, Current Order is Buy : 034310, Price : 25300.0, Value : -546075200.0, Commission : 3161488.0
      Date : 2015-07-23, Current Order is Sell : 034730, Price : 302500.0, Value : -477647500.0, Commission : 2388237.5
      Date : 2015-08-21, Current Order is Buy : 034730, Price : 256000.0, Value : -477647500.0, Commission : 2021120.0000000002
      Date : 2015-08-21, Current Order is Sell : 078340, Price : 111900.0, Value : -574158900.0, Commission : 2870794.5
      Date : 2015-08-25, Current Order is Buy : 006730, Price : 15713.0, Value : -542951520.0, Commission : 2270921.325
      Date : 2015-08-25, Current Order is Sell : 098460, Price : 32200.0, Value : -607742800.0, Commission : 3038714.0
      Date : 2015-09-01, Current Order is Buy : 098460, Price : 38000.0, Value : -607742800.0, Commission : 3586060.0
      Date : 2015-10-01, Current Order is Buy : 078340, Price : 116300.0, Value : -574158900.0, Commission : 2983676.5
      

      The question is... OK, so I take short positions at the beginning and cover them when stocks meet profit-realisation or loss-cut or even rebalancing conditions. Then there must be + values coming from shorting and - values from buying (if the values are calculated based on cash flows).

      But why do I get negative values for both sell & buy ? I was expecting a positive value from cash inflows of short selling but the figure here shows a negative value. Also, as you see below, even though prices are different, I still get the same absolute values from sell & buy. I don't know what I am missing here :(

      Date : 2015-01-05, Current Order is Sell : 000250, Price : 10050.0, Value : -499997550.0, Commission : 2499987.75
      Date : 2015-03-16, Current Order is Buy : 000250, Price : 11650.0, Value : -499997550.0, Commission : 2897995.75
      


      Also, as you can see the order transaction date from the result, sometimes the sell & buy order takes place within the same day but sometimes it doesn't.

      Could you guys help me why I am having these issues?

      Thank you!!!

      posted in General Code/Help
      C
      Crespecular
    • RE: To get multiple windows for plotting each multiple assets

      @backtrader Thanks! I will try reformatting by using matplotlib then :)

      posted in General Code/Help
      C
      Crespecular
    • To get multiple windows for plotting each multiple assets

      Hi, I am currently running a simple Long & Short model for multiple equities.

      This is my final part of the codes :

      #Variable for our starting cash
      startcash = 1000000000
      
      #Create an instance of cerebro
      cerebro = bt.Cerebro(cheat_on_open=False)
      
      #Add our strategy
      cerebro.addstrategy(Long)
      
      datalist = []
      for i in bascket:
          datalist.append(('{}.csv'.format(i), i))
      
      for i in range(len(datalist)):
          data = EIKON_HLOC(dataname = datalist[i][0])
          cerebro.adddata(data, name=datalist[i][1])
          
      # Set our desired cash start
      cerebro.broker.setcash(startcash)
      cerebro.broker.set_coc(True)
      cerebro.broker.setcommission(commission=0.005)
      
      # add analyzers
      cerebro.addanalyzer(trade_list, _name='trade_list')
      cerebro.addanalyzer(open_list, _name='open_list')
      # run backtest
      strats = cerebro.run(tradehistory=True)
      # get analyzers data
      trade_list = strats[0].analyzers.trade_list.get_analysis()
      open_list = strats[0].analyzers.open_list.get_analysis()
      print (tabulate(open_list, headers="keys"))
      print (tabulate(trade_list, headers="keys"))
      #Finally plot the end results
      cerebro.plot(numfigs=1)
      

      and I get this packed window

      0_1540218014012_45811dd8-da40-4ab3-a0e4-18b0500a300f-image.png

      What I want here is to get separate windows for each equity.
      I tried :

      cerebro.plot(numfigs=5)
      

      But it rather gave me 5 different windows for all equities with different time-horizon (ex: 2010-2011, 2011-2012 ...)

      I have lots of things to learn about backtrader and python while doing my UG degree and I would really appreciate you guys help!

      Thank you :D

      posted in General Code/Help
      C
      Crespecular
    • Rebalance quarterly

      Hi, I want to sell (cover for shorting) all the current long(short) holdings on my portfolio every quarter and replace them with the new stocks.

      class Short(bt.Strategy):
          
       
          params = (
          ('losscut', 0.05),
          ('profit', 0.05),
          ('max_stock', 5),
          )
      
          
          
          def __init__(self):
              self.inds = dict()
              self.entry = {}
              self.loss_cut = {}
              self.profit_realisation = {}
              self.shorting_list = []
              self.shorted_list = []
      
          def next(self):
              
              for i, d in enumerate(self.datas):
                  dt, dn = self.datetime.date(), d._name
                  pos = self.getposition(d).size
                  if pos == 0: 
                      if dn not in self.shorted_list:
                          if len(self.shorting_list) == self.params.max_stock:
                              return
                          else:
                              self.order_target_percent(data=d, target = -0.1)
                              self.entry.update({dn:d.close[0]})
                              self.shorted_list.append(dn)
                              self.shorting_list.append(dn)
                              
                  else:
                      if dt.month in [3,6,9,12]:
                         # I need the codes that clears the whole position in the beginning (or at the end) of every quarter (ex: 1st of March or 28th of March)'
                          pass
                      
                      if d.close[0] > self.entry[dn]*float(1 + self.params.losscut) == 1:
                          self.order_target_percent(data=d, target = 0)
                          self.loss_cut.update({dn:d.close[0]})
                          self.shorting_list.remove(dn)
                          
                      elif d.close[0]<self.entry[dn]*(1 - self.params.profit):
                          self.order_target_percent(data=d, target = 0)
                          self.profit_realisation.update({dn:d.close[0]})
                          self.shorting_list.remove(dn)
                          
      
      
                      pass
      

      If anyone knows something, please enlighten me!

      Thank you :D

      posted in General Code/Help
      C
      Crespecular
    • RE: Closed trade list (including MFE/MAE) analyzer

      @ab_trader said in Closed trade list (including MFE/MAE) analyzer:

      strats[0]

      Hi, I have a simple question

      On your final lines,

      # get analyzers data
          trade_list = strats[0].analyzers.trade_list.get_analysis()
          print (tabulate(trade_list, headers="keys"))
      

      What does this code indicate?

      strats[0]
      

      I am new here, so this question might be silly :p

      Thank you for sharing!

      posted in Indicators/Strategies/Analyzers
      C
      Crespecular
    • RE: Using self CSV data failed

      Thanks for your response!

      From now on, I will follow your instructions so that the others can contribute in the future.

      so I tried two different things:

      class EIKON_HLOC(bt.feeds.GenericCSVData):'''
          params = (
      ('nullvalue' , float('NaN')),
              ('dtformat', '%Y-%m-%d'),
              ('datetime', 0),
              ('open', 1),
              ('high', 2),
              ('low', 3),
              ('close', 4),
              ('volume', 5),
              ('openinterest', -1),
          )
      
      #This will load pre-made CSV (using Eikon) data
      datalist = []
      tickers = ['WMT','IBM','C','AAPL.O','MSFT.O']
      for ticker in ticker_list:
          datalist.append(('{}.csv'.format(ticker), ticker))
      
      #First option using class
      datalist = []
      for ticker in ticker_list:
          datalist.append(('{}.csv'.format(ticker), ticker))
      
      for i in range(len(datalist)):
          data = EIKON_HLOC(dataname = datalist[i][0])
          cerebro.adddata(data, name=datalist[i][1])
      
      #Second Option using Pandas dataframe
      datalist = []
      for ticker in ticker_list:
          datalist.append(('{}.csv'.format(ticker), ticker))
          
      for i in range(len(datalist)):
          # Create a Data Feed
          dataframe = pd.read_csv(datalist[i][0], index_col = 0)
          dataframe = dataframe.set_index(pd.DatetimeIndex(dataframe.index))
          data = bt.feeds.PandasData(dataname=dataframe)    
          # Add the Data Feed to Cerebro
          cerebro.adddata(data)
      

      After several trials, both worked. So finally I can create my own strategies to digest personal data (but still trying to understand the coding structures).

      Is there any example sources that I can refer to ?

      Thank you!

      posted in General Code/Help
      C
      Crespecular
    • RE: Using self CSV data failed

      @ab_trader yup just noticed that I mixed up my codes. Sorry for this confusion.

      This is it :

      0_1537158598324_cf688c10-f4c6-4350-bddd-dc16d077fb4a-image.png

      In fact, I solved this problem using Pandas DataFeed. But I still want to figure out what I am missing here.

      Thanks again :)

      posted in General Code/Help
      C
      Crespecular
    • RE: Using self CSV data failed

      Oops, wrong code! sorry guys
      This is the one that I tried :

      0_1537149614954_b9a23f08-2c5d-4fc6-ba4d-739ab237c619-image.png

      But still getting none :p

      posted in General Code/Help
      C
      Crespecular