Backtrader Community

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

    stevenm100

    @stevenm100

    2
    Reputation
    1
    Profile views
    9
    Posts
    0
    Followers
    0
    Following
    Joined Last Online

    stevenm100 Unfollow Follow

    Best posts made by stevenm100

    • RE: Custom Dynamic trailstop indicator stops after updating tradeopen parameter

      @run-out
      Thanks for the detailed reply. Agree there was some clumsiness about the setup, but I had mirrored what I had in a single data example which was working. I have streamlined it now though per your suggestion.
      Anyway, after some banging of my head on the desk, I found what was wrong with my multi-data version and it was simple.....the magical runonce=False, which i hadnt copied over from my single-data example.

      # Run over everything
      cerebro.run(runonce=False, maxcpus=1) # runonce = False is required
      

      now it all works as expected. Thanks for helping me though it. Hopefully the Dynamic Trailstop indicator is useful to other folks too now that its shared. Cheers!

      posted in Indicators/Strategies/Analyzers
      S
      stevenm100
    • RE: Custom Dynamic trailstop indicator stops after updating tradeopen parameter

      @run-out
      Yes, that works. Thank you very much for your input, very much appreciated.

      posted in Indicators/Strategies/Analyzers
      S
      stevenm100

    Latest posts made by stevenm100

    • List of active indicators into an OR() to make a global indicator

      My backtesting system generates a list of values external to BT that would be the levels of interest to generate a CrossUp or CrossDown signal. There can be a varying number of levels on a given day as the levels are generated from a machine learning output.
      I would like to do something like this:

      # Dictionary of levels, structure is somewhat arbitrary
      levels = {'level1': 100.00, 'level2' : 101.00}
      

      In BT init, I would like to instantiate a CrossUp for each of the levels:

      for level in levels:
          indicator_name = 'primary_ind_' + levels.index(level)
          indicator_name = CrossUp(self.data.Close, level)
      

      and i suspect this will work.

      However, my real goal is to then combine all of the indicators together into an ANY or OR indicator, but i fail for two reasons:

      1. I cant find a way to get a list from BT of the instantiated indicators
      2. I cant see how to pass a list to the backtrader OR function
      # This is what i want to do, but cant do
      secondary_ind = bt.OR('list of indictors')
      

      Has anyone managed this or something similar? The challenge seems to be the changing number of indicators from run to run, if it was the same each time I think I could do it with my smooth brain by hardcoding each one in the OR function.

      posted in Indicators/Strategies/Analyzers
      S
      stevenm100
    • RE: How to pass a data-specific parameter for each data in a multidata strategy

      oops missed a bit of the code:

      cerebro = bt.Cerebro()
        
      cerebro.addstrategy(spy_0DTE_strat_OPTIONS,  entry_time = spy_dict)
      posted in General Code/Help
      S
      stevenm100
    • RE: How to pass a data-specific parameter for each data in a multidata strategy

      FYI folks,
      It works to pass a dictionary as a parameter to the strategy with the caveat that the dataname cannot be an integer. I got around this by passing as an str()

      spy_dict = spy_orders.to_dict('dict')
      
      for n in spy_orders.index:
          print(n)
          dataframe = pdr.get_data_yahoo(spy_dict['dataname'][n], period='1d', interval='1m', prepost=False)
      
          data = bt.feeds.PandasData(dataname=dataframe, tz=pytz.timezone('US/Eastern'))
      
          cerebro.adddata(data, name=str(n)) # <--- LOOK HERE name = str(n)
      

      Then when trying to reference it within the strategy you need to convert back to int() like this:

      if tm == self.p.entry_time['entry_time'][int(d._name)]: #<--LOOK HERE int(d._name)
      #do some trading stuff
      

      Cheers.

      posted in General Code/Help
      S
      stevenm100
    • RE: How to pass a data-specific parameter for each data in a multidata strategy

      @run-out
      Thanks, that looks interesting.

      just generally speaking for backtrader , is there a way to pass a separate parameter in for each data loaded? For instance, can a strategy's parameter actually be a dict or a list? (i'll try this next).

      One other related question, if opting for the "load all the data" method, is there a way to only turn on plotting for the companion data that gets traded (like setting the plot = True/False in the notify trade method)? (i'll also try this out soon).

      I havent found either on the community pages yet :)

      posted in General Code/Help
      S
      stevenm100
    • How to pass a data-specific parameter for each data in a multidata strategy

      Hey all,
      I searched for a while but couldnt find anyone trying to do this exactly, so quick question for you:

      I run through SPY in an instance of cerebro to create a list of signals for trading 0DTE options which includes: contract type, strike, entry time.

      on a single data, this can be done by passing in the entry time as a parameter, but i get stuck on a multidata setup.

      When I load all of the datasets to multidata cerebro instance, I'd ideally like to pass the entry time as a parameter "specific to each data", but I can only see how to pass a "global parameter".

      Does anyone have a way to pass a data-specific parameter?

      My table of signals from the first run on SPY looks like:
      index, dataname entry_time
      0 SPY220225P00432000 10:46:00
      1 SPY220225P00435000 11:19:00
      2 SPY220225P00436000 11:55:00

      The entry signal in the options trades looks like this:

      for i, d in enumerate(self.datas):
                  
                  dt, tm, dn = self.datetime.date(tz=pytz.timezone('US/Eastern')), self.datetime.time(tz=pytz.timezone('US/Eastern')), d._name
      
                  if tm == self.p.entry_time: # <-- this parameter is global.
                  #do some trade stuff
      

      I remain keen to run the options trades as a multidata strategy rather than looping through each one, and if i wanted to load all of the data along with SPY, i would be loading a lot of options data that wont be used.

      Thanks!!

      posted in General Code/Help
      S
      stevenm100
    • RE: Custom Dynamic trailstop indicator stops after updating tradeopen parameter

      @run-out
      Thanks for the detailed reply. Agree there was some clumsiness about the setup, but I had mirrored what I had in a single data example which was working. I have streamlined it now though per your suggestion.
      Anyway, after some banging of my head on the desk, I found what was wrong with my multi-data version and it was simple.....the magical runonce=False, which i hadnt copied over from my single-data example.

      # Run over everything
      cerebro.run(runonce=False, maxcpus=1) # runonce = False is required
      

      now it all works as expected. Thanks for helping me though it. Hopefully the Dynamic Trailstop indicator is useful to other folks too now that its shared. Cheers!

      posted in Indicators/Strategies/Analyzers
      S
      stevenm100
    • RE: Custom Dynamic trailstop indicator stops after updating tradeopen parameter

      @run-out
      Yes, that works. Thank you very much for your input, very much appreciated.

      posted in Indicators/Strategies/Analyzers
      S
      stevenm100
    • RE: Custom Dynamic trailstop indicator stops after updating tradeopen parameter

      @run-out

      sorry for not posting the whole thing.....Ive cut it down to something easier to understand and follow.

      Ive added some debugging print statements to follow along.

      The code attempts to use the custom indicator (copying the idea in the dynamic highest example, setting the param to True/False, 0 otherwise), and also calculating the stop price in the strategy next to show that it works outside of the custom indicator.
      note that if you copy/paste it directly after one week, you'll need to update the dates in the yfinance statement as it only goes back 7 days.

      I learned that the indicator runs through all of the data before the strategy does (printing len shows 388 from next in the indicator before the strategy next begins printing), and so the title is probably now incorrect

      but the thing I was trying to achieve still remains...how do I set the indicator parameter to True from the strategy? it doesnt seem to work for me as it does in the Dynamic Indicator example.

      I understand that I cant see the self.l.trailstop line on the chart because it is zero throughout (according to strategy print, it is nan according to indicator print), and so outside of the plotted area. I had wanted to see the trailstop appear in the visible range during the period that the param is set to True.

      Any thoughts or suggestions on why the custom indicator isnt working?

      representative output:

      ***INDICATOR NEXT self.l.trailatr[0] = nan, tradeopen param = False, at len: 385***
      ***INDICATOR NEXT self.l.trailatr[0] = nan, tradeopen param = False, at len: 386***
      ***INDICATOR NEXT self.l.trailatr[0] = nan, tradeopen param = False, at len: 387***
      ***INDICATOR NEXT self.l.trailatr[0] = nan, tradeopen param = False, at len: 388***
      ***STRAT NEXT self.TrailStop.trailstop[0] = 0.0, at len: 22***
      ******BUYING, SET PARAM TRUE*****
      stop price = 142.62
      ***STRAT NEXT self.TrailStop.trailstop[0] = 0.0, at len: 23***
      stop price updated = 142.78
      ***STRAT NEXT self.TrailStop.trailstop[0] = 0.0, at len: 24***
      stop price updated = 142.89
      ***STRAT NEXT self.TrailStop.trailstop[0] = 0.0, at len: 25***
      ***STRAT NEXT self.TrailStop.trailstop[0] = 0.0, at len: 26***
      
      import backtrader.feeds as btfeed
      import pandas as pd
      import backtrader as bt
      from pandas_datareader import data as pdr
      import yfinance as yf
      
      yf.pdr_override()
      
      class TrailStop(bt.Indicator):
          lines = ('trailatr','trailstop',)
          params = ( ('tradeopen',False),('atr_period', 10),('trail_mult', 4),)
          plotinfo = dict(subplot=False)
          plotlines = dict(trailstop=dict(color='blue', ls='--',_plotskip=False,),  
                              trailatr=dict(color='black', ls='-', _plotskip=False),
                          )         
      
          def init(self):
              self.l.trailatr = bt.indicators.AverageTrueRange(period=self.p.atr_period)
      
          def next(self):
              print('***INDICATOR NEXT self.l.trailatr[0] = {}, tradeopen param = {}, at len: {}***'.format(self.l.trailatr[0], self.p.tradeopen, len(self.l.trailatr)))
              
              if self.p.tradeopen == True: # using "if True:" this gets accessed and result is self.l.trailstop[0] = 1. suggests uim using the wrong way to access param?
                  self.l.trailstop[0] = max(self.dataclose[0] - self.p.trail_mult * self.atr[0], self.l.trailstop[-1])
                  #print('inside indicator if statement')
      
              else:
                  self.l.trailstop[0] = min(0,1)
                  #print('in indicator else statement')
      
       
      # Create a Stratey
      class TestStrategy(bt.Strategy):
          params = (
              ('fast_ma',20),
              ('trail_mult', 4),
          )
      
          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)) # #-out to turn logging off
      
          def __init__(self):
              # Keep a reference to the "close" line in the data[0] dataseries
              self.dataclose = self.datas[0].close
      
              self.atr = bt.indicators.AverageTrueRange(self.datas[0])    # using for manually calculating exit in strategy next
              self.stopprice = 0                                          # for manually working out the stop in strategy next
      
              self.TrailStop = TrailStop(self.datas[0])                   # instantiate the TrailStop Class
      
              # 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.ExponentialMovingAverage(self.datas[0].close, period=self.p.fast_ma)
      
              self.buysig = bt.indicators.AllN(self.datas[0].low > self.sma, period=3)
              
      
          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))
      
                  # Keep track of which bar execution took place
                  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])
      
              print('***STRAT NEXT self.TrailStop.trailstop[0] = {}, at len: {}***'.format(self.TrailStop.trailstop[0], len(self.datas[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.buysig:
                      # BUY, BUY, BUY!!! (with 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()
                      self.TrailStop.p.tradeopen = True
                      print('******BUYING, SET PARAM TRUE*****')
                      self.stopprice = self.data.close[0] - self.p.trail_mult * self.atr[0]
                      print('stop price = {:.2f}'.format(self.stopprice))
      
              elif self.data.close[0] <  self.stopprice :
                  self.close()
                  self.stopprice = 0
                  self.TrailStop.p.tradeopen = False
                  print('******SELLING, SET PARAM FALSE*****')
      
      
              if self.stopprice < self.dataclose[0] - self.p.trail_mult * self.atr[0]:    # if old price < new price
                  self.stopprice = self.dataclose[0] - self.p.trail_mult * self.atr[0]    # assign new price
                  print('stop price updated = {:.2f}'.format(self.stopprice))
      
      
      starting_balance=50000
      
      
      if __name__ == '__main__':
          
          ticker_id       =   'AAPL'
          
          dataframe = pdr.get_data_yahoo(ticker_id, period='1d', interval='1m', start='2021-07-20',end='2021-07-21',prepost=False)
      
          dataframe = dataframe[dataframe.index.strftime('%H:%M') < '20:00']
          data = bt.feeds.PandasData(dataname=dataframe)
      
          # Create a cerebro entity, add strategy
          cerebro = bt.Cerebro()
          cerebro.addstrategy(TestStrategy)
      
          # Add the Data Feed to Cerebro, set desired cash, set desired commission
          cerebro.adddata(data)
          cerebro.broker.setcash(starting_balance)
          cerebro.broker.setcommission(commission=0.0)
      
          # Add a FixedSize sizer according to the stake
          cerebro.addsizer(bt.sizers.PercentSizer, percents=10)
      
          # Run over everything
          cerebro.run()
          cerebro.plot(style='candlestick')
      
      
      posted in Indicators/Strategies/Analyzers
      S
      stevenm100
    • Custom Dynamic trailstop indicator stops after updating tradeopen parameter

      Hi,
      I had originally written this for a multidata strategy, and it didnt work as expected, so i wrote a single data strategy and it still didnt work, so wondering if anyone else has run into this...

      I have wanted an ATR trailing stop for a while, and can make it work using the strategy 'next', but wanted a custom indicator because then I can make it print the line on the plot. I notice that nobody has shared a custom trailingATR indicator anywhere, so had a go myself.

      I added some print() statements to help me debug:

      class TrailStop(bt.Indicator):
          lines = ('trailatr','trailstop',)
          params = ( ('tradeopen',False),('atr_period', 10),('trail_mult', 4),)
          plotinfo = dict(subplot=False)
          plotlines = dict(trailstop=dict(color='red', ls='--',_plotskip=False,),  
                              trailatr=dict(color='red', ls='-', _plotskip=True),
                          )         
      
          def init(self):
              self.l.trailatr = bt.ind.AverageTrueRange(period=self.p.atr_period)#self.p.atr_period)
      
          def next(self):
              print('from inside indicator next, tradeopen param = {}'.format(self.p.tradeopen)) # unable to access next when changing the param????
              if self.p.tradeopen == True:
                  self.lines.trailstop[0] = max(self.data.close[0], self.lines.trailstop[-1])
                  print('inside indicator if statement')
                  print(self.l.trailstop[0])
              else:
                  self.lines.trailstop[0] = 0
                  print('in indicator else statement')
                  print(self.l.trailstop[0])
      
      

      I instatiate the indicator in the strategy 'init':

      self.trailstop = TrailStop()
      self.l.trailatr = self.trailstop.trailstop
      

      note that the max statement is just a dummy to help me understand what was going on, the value is zero (i know from printing that too). The story continues....
      When I change the indicators parameter from the strategy 'next' like this:

      self.trailstop.p.tradeopen = True
      

      The printing stops and i get nothing from the indicator 'next' or from the strategy 'next' but the strategy runs through the data, reaches 'stop' and plots the chart at the end.

      In particular, I am puzzled why the indicator seems not to be accessing the IF statement or the ELSE statement after the parameter update.

      A snippet of the many lines of output looks like this:

      from inside indicator next, tradeopen param = False
      in indicator else statement
      from inside indicator next, tradeopen param = False
      in indicator else statement
      Trailstop parameter set to True (inside strategy next statement)
      15:15:00, NIO, BUY EXECUTED, Price: 43.12, Size: 383.00, Cost: 16514.96

      and that is where the printing stops.

      Any ideas what is going on, or has anyone else seen this? This is my first go at a dynamic indicator, and if i cant get it to work i'll have to live without seeing the trailstop on the chart.
      Failing that, has anyone got a trailingATR custom indicator to work?

      posted in Indicators/Strategies/Analyzers
      S
      stevenm100