For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/

Run Calculations On Previous 100 day data



  • I want to calculate a series of variables that I can manipulate based on rolling 100 day data.

    lass SRT(bt.Indicator):
        params = dict(period_100d = 100)
    
        rolling_srt = ()
    
        def log(self, txt, dt=None):
            ''' Logging function for this strategy'''
            dt = dt or self.datas[0].datetime.date(0)
            print('%s, %s' % (dt.isoformat(), txt))
    
        def __init__(self):
            #Everything to calculate this beast!
            self.win = ()
            self.lose = ()
            self.dataclose = self.datas[0].close
            
            # calculate returns each day, and log them as a win if positive, and a loss if negative.
            for self.line in self.data.close.get(ago=-1, size = 100):
                if self.dataclose[0] > self.dataclose[-1]:
                    #store the values of the positive days.
                    self.win = float(self.dataclose[0] - self.dataclose[-1])
                else:
                    #store the values of the negative days
                    self.lose = float(self.dataclose[0] - self.dataclose[-1])
            # How many days were positive?
            self.avg_win = bt.ind.Average(self.win)
            # How many days were negative?
            self.avg_loss = bt.ind.Average(self.lose)
            # Average closing price over the 100days
            self.avg_risk = bt.ind.Average(self.dataclose, period = self.p.period_100d)
            # percentage of days resulting in win or loss.
            self.prob_w = len(self.win)/(self.p.period_100d)
            self.prob_l = (1 - self.prob_w)
    

    Errors:

    Traceback (most recent call last):
       line 23, in <module>
        cerebro.run()
      line 1127, in run
        runstrat = self.runstrategies(iterstrat)
     line 1217, in runstrategies
        strat = stratcls(*sargs, **skwargs)
     line 88, in __call__
        _obj, args, kwargs = cls.doinit(_obj, *args, **kwargs)
    line 78, in doinit
        _obj.__init__(*args, **kwargs)
     line 49, in __init__
        self.my_srt = SRT(self.data.get(ago=0, size=100))
     line 53, in __call__
        return super(MetaIndicator, cls).__call__(*args, **kwargs)
    line 88, in __call__
        _obj, args, kwargs = cls.doinit(_obj, *args, **kwargs)
    line 78, in doinit
        _obj.__init__(*args, **kwargs)
    line 19, in __init__
        self.dataclose = self.datas[0].close
    line 461, in __getattr__
        return getattr(self.lines, name)
    AttributeError: 'Lines_LineSeries_LineSeriesStub' object has no attribute 'close'
    

    Any help would seriously be appreciated. Veteran in finance, new to programming.



  • @dmeharchand said in Run Calculations On Previous 100 day data:

    self.my_srt = SRT(self.data.get(ago=0, size=100))

    Just guessing your strategy code from the exception, it seems the problem is with the indicator initialization. You may probably want to initialize the SRT indicator with the self.data instead.

    Probably worth to provide a full strategy code - so less guessing would be needed.



  • Slicing and [] can be used in the next() only. Check out Docs - Platform Concepts



  • In order to work with indicators you need to think of them as whole lines. Below I will create a new line by comparing data is greater at the current point less data shifted to the previous day.

    This will create a new line of boolean 1 for greater and 0 for not.

    To find how main wins over 100 days? Just add them up, all the wins are 1.

    Then subtact from 100 to get all the losses.

    Here's the code.

    class BuyAndHold_More(bt.Strategy):
    
        params = (
            ('period', 100),
        )
    
        def __init__(self):
            self.win = self.data > self.data(-1)
            self.win_100 = bt.ind.SumN(self.win, period=self.p.period)
            self.lose_100 = self.p.period - self.win_100
    

    I will set the period to 5 days so you can see the results here. Note that the starting period adjust to allow the indicator to get going.

    2005-01-10T00:00:00, o 13.32	h 13.45	l 13.17	c 13.19	v 47571800	win 0	win_100 3	lose_100 2
    2005-01-11T00:00:00, o 13.09	h 13.39	l 13.06	c 13.20	v 63973000	win 1	win_100 4	lose_100 1
    2005-01-12T00:00:00, o 13.26	h 13.49	l 13.24	c 13.48	v 53420800	win 1	win_100 4	lose_100 1
    2005-01-13T00:00:00, o 13.38	h 13.67	l 13.34	c 13.48	v 56987700	win 0	win_100 3	lose_100 2
    2005-01-14T00:00:00, o 13.56	h 13.76	l 13.49	c 13.63	v 42509100	win 1	win_100 3	lose_100 2
    2005-01-18T00:00:00, o 13.59	h 13.90	l 13.52	c 13.78	v 60758900	win 1	win_100 4	lose_100 1
    2005-01-19T00:00:00, o 13.66	h 13.80	l 13.45	c 13.47	v 51115100	win 0	win_100 3	lose_100 2
    2005-01-20T00:00:00, o 13.44	h 13.68	l 13.28	c 13.28	v 45253200	win 0	win_100 2	lose_100 3
    2005-01-21T00:00:00, o 13.35	h 13.49	l 13.28	c 13.31	v 40716100	win 1	win_100 3	lose_100 2
    2005-01-24T00:00:00, o 13.36	h 13.50	l 13.21	c 13.24	v 37540700	win 0	win_100 2	lose_100 3
    2005-01-25T00:00:00, o 13.49	h 13.74	l 13.46	c 13.59	v 48682500	win 1	win_100 2	lose_100 3
    2005-01-26T00:00:00, o 13.96	h 14.01	l 13.61	c 13.62	v 78543500	win 1	win_100 3	lose_100 2
    


  • @run-out this was a big help, much appreciated.
    From here I was able to extrapolate the average win amounts.

    class Win_Buy(bt.Strategy):
        params = (
            ('period', 100),
        )
    
        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):
            self.dataclose = self.datas[0].close
            # Win & Loss Data
            self.win_record = []
            self.win = self.data > self.data(-1)
            self.win_100 = bt.ind.SumN(self.win, period=self.p.period)
            self.lose_100 = self.p.period - self.win_100
            self.prob_win = self.win_100/100
            self.prob_lose = 1 - self.prob_win
            if self.dataclose[0] > self.dataclose[-1]:
                self.win_record = self.dataclose[0] - self.dataclose[-1]
            else:
                self.lose_record = self.dataclose[0] - self.dataclose[-1]
    
            #risk
            self.risk = bt.ind.Average(self.dataclose*.1, period=self.p.period)
    


  • @dmeharchand @run-out
    Second that I was not able to do that, everytime I try to store values it doesn't seem to work or the code will just freeze.

    class Win_Buy(bt.Strategy):
        params = (
            ('period', 100),
            ('test_period', 10),
        )
    
        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):
            # Win & Loss Data
            self.dataclose = self.datas[0].close
            self.win = self.data > self.data(-1)
            self.win_100 = bt.ind.SumN(self.win, period=self.p.period)
            self.lose_100 = self.p.period - self.win_100
            self.prob_win = self.win_100/100
            self.prob_lose = 1 - self.prob_win
            self.diff = self.dataclose[0] - self.dataclose[-1]
    #THIS IS WHERE THE PROBLEM IS
            if self.dataclose[0] > self.dataclose[-1]:
                self.win_record = (self.dataclose[0]-self.dataclose[-1])
            else:
                self.lose_record = (self.dataclose[0] - self.dataclose[-1])
    
            self.avg_win = bt.ind.Average(self.win_record, period=self.win_100[0])
    
        def next(self):
            self.log('Win, %.2f' % self.win_100[0])
            self.log('Close, %.2f' % self.dataclose[0])
            self.log('Yesterday Close, %.2f' % self.dataclose[-1])
    

    When I run it the code says

    AttributeError: 'Lines_LineSeries_LineIterator_DataAccessor_Strateg' object has no attribute 'win_record'
    

    Basically I am try to determine
    if the today > yesterday,
    store value
    average(stored values/ len(stored Values)

    Any help of reference you can provide would be amazing, again thank you so much for the input you have provided already



  • @dmeharchand said in Run Calculations On Previous 100 day data:

    if self.dataclose[0] > self.dataclose[-1]:
    self.win_record = (self.dataclose[0]-self.dataclose[-1])
    else:
    self.lose_record = (self.dataclose[0] - self.dataclose[-1])

    It seems you are trying to get the data[0].close values in __init__ method - where the data is not yet available. See the previous response from @ab_trader



  • @dmeharchand
    The way you have it, you've made a line out of the close value, which is good. self.datas[0] represents the first datas value, and close is the close line of that datas.

    self.dataclose = self.datas[0].close
    

    But you can't reference the values in init but using [0] and [-1]. These get used in next to say the value at the current date, and the value at the previous date. In next, there is a current date, in init, there isn't.

    Try using this in init for example:

    self.diff = self.dataclose(0) - self.dataclose(-1)
    

    Or if you like, you can drop the (0) as it's assumed.

    self.diff = self.dataclose - self.dataclose(-1)
    

    Then when you wish to recall the indicator in next, you would use:

    self.diff[0]
    

    Or if you want the previous value of self.diff in next:

    self.diff[-1]
    

    That's perfectly valid in next.

    If we look at your problem code, it might look something like:

    # The following line will result in a line object where wins are 1 and losses are 0
     self.win_record = self.dataclose(0) > self.dataclose(-1)
    
    # And I think this next line should work, change period.
    self.avg_win = bt.ind.Average(self.win_record, period=self.p.period)
    

    Let us know how you get on.



  • @run-out yes I've quickly discovered the () vs. [] delema.

    So i've been able to store the values appropriately with .append() in my self.win_record = pd.DataFrame({'Win': []}) and use them with the Average index ```self.avg_win = bt.ind.Average(self.win_record['Win'], period=self.p.period).

    When I print the index I get

    <backtrader.indicators.basicops.Average object at 0x000001DC76EDC808>
    

    The main problem now is that whenever try to manipulate this data I get an error

     line 163, in __getitem__
        return self.array[self.idx + ago]
    IndexError: array index out of range
    

    or the script wont run at all.

    Here is what i've come up with:

    class Win_Buy(bt.Strategy):
        params = (
            ('period', 100),
            ('test_period', 10),
        )
    
        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):
            # Win & Loss Data
            self.dataclose = self.datas[0].close
            self.win = self.data > self.data(-1)
            self.win_100 = bt.ind.SumN(self.win, period=self.p.period)
            self.lose_100 = self.p.period - self.win_100
            self.prob_win = self.win_100/100
            self.prob_loss = 1 - self.prob_win
            self.win_record = pd.DataFrame({'Win': []})
            self.loss_record = pd.DataFrame({'Loss': []})
    
            self.avg_risk = bt.ind.Average(self.dataclose, period=self.p.period)
    
            #self.avg_loss = bt.ind.Average(self.loss_record, period=self.p.period)
    
    
    
        def next(self):
    
            if self.dataclose[0] > self.dataclose[-1]:
                self.diff = self.dataclose - self.dataclose[-1]
                print(self.diff)
                self.df = pd.DataFrame({'Win':[self.diff]})
                self.win_record = self.win_record.append(self.df, ignore_index=True)
                print(self.win_record['Win'][0])
            else:
                self.df = pd.DataFrame({'Win':[0]})
                self.win_record = self.win_record.append(self.df, ignore_index=True)
    
            if self.dataclose[0] > self.dataclose[-1]:
                self.diff = self.dataclose[0] - self.dataclose[-1]
                self.df = pd.DataFrame({'Loss':[self.diff]})
                self.loss_record = self.loss_record.append(self.df, ignore_index=True)
            else:
                self.df = pd.DataFrame({'Loss':[0]})
                self.loss_record = self.loss_record.append(self.df, ignore_index=True)
    
            self.avg_win = bt.ind.Average(self.win_record, period=self.p.period)
    
            print(self.avg_win[0])
    
            self.dsharpe = self.avg_win/self.avg_risk
            print(self.dsharpe)
    


  • The code you've shared looks highly unorthadox for backtrader. Could we take a step back and you tell us what you are trying to accomplish in general, without any solutions? We can then tell you if maybe there's a better way to handle things.



  • @run-out said in Run Calculations On Previous 100 day data:

    unorthadox

    good polite definition. :)

    I would recommend for TS to go thru the docs and quickstart before compiling that complex scripts.



  • @run-out
    Each day of a backtest I want to use the following 100 Days to calcuate/create specific variables:

    • Prob_Win = How many days over the last 100 the market was up
    • Prob_Loss = (1 - Prob_W)
    • Avg_Win = The amount of each of the win days the market was up/ No. of days a win was recorded
    • Avg_Loss =The amount of each of the lose days the market was down/ No. of days a win was recorded
    • Avg_ risk = Average closing price across the 100 days * (a risk percentage)

    Finally, I want to create a calculation dsharpe:
    dsharpe = self.avg_win/self.avg_risk that recalculates everyday on past 100 days.

    Then use this value to determine whether I should buy or sell a security
    i.e If dsharpe > .4, purchase security

    Hope thats clear?
    Appologies for my unorthadox code, I am new to programming.
    I greatly appreciate any suggestions/ help @run-out or @ab_trader can provide.

    Please let me know if I can assist in anyway.
    Thanks,
    D



  • @dmeharchand CORRECTION each day of the backtest I want to use the PREVIOUS 100 days to calculate specific variable.



  • I've written out the bulk of the code for you. I'm going to leave you to calculate the dsharpe for practice.

    
    class Strategy(bt.Strategy):
    
        params = (("period", 5),)
    
        def __init__(self):
    
            self.diff = self.data - self.data(-1)
            self.win_record = self.data(0) > self.data(-1)
    
            self.prob_win = bt.ind.SumN(self.win_record, period=self.p.period)
            self.prob_loss = self.p.period - self.prob_win
    
            self.wins = bt.ind.SumN(
                bt.If(self.diff >= 0, self.diff, 0), period=self.p.period
            )
            self.losses = bt.ind.SumN(
                bt.If(self.diff < 0, -self.diff, 0), period=self.p.period
            )
    
            self.ave_win = bt.DivByZero(self.wins, self.prob_win, 0)
            self.ave_loss = bt.DivByZero(self.losses, self.prob_loss, 0)
            
            self.ave_close = bt.ind.Average(self.data.close, period=self.p.period)
    
        def log(self, txt, dt=None):
            """ Logging function fot this strategy"""
            dt = dt or self.data.datetime[0]
            if isinstance(dt, float):
                dt = bt.num2date(dt)
            print("%s, %s" % (dt.isoformat(), txt))
    
    
        def next(self):
    
            self.log(
                "c {:5.2f}\tdiff {:5.2f}, \twinr {:5.2f}, \tpwin {:5.2f}, \tploss {:5.2f}, \t"
                "wins {:5.2f}, \tplosses {:5.2f}, \tave_win {:5.2f}, \tave_loss {:5.2f},\tpav_close {:5.2f}".format(
                    self.datas[0].close[0],
                    self.diff[0],
                    self.win_record[0],
                    self.prob_win[0],
                    self.prob_loss[0],
                    self.wins[0],
                    self.losses[0],
                    self.ave_win[0],
                    self.ave_loss[0],
                    self.ave_close[0],
                )
            )
    
    


  • @run-out alright... I think I got it!

    class Win_Buy(bt.Strategy):
        params = (("period", 5),)
    
        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):
            self.risk = 0.01
            self.diff = self.data - self.data(-1)
            self.win_record = self.data(0) > self.data(-1)
    
            self.prob_win = bt.ind.SumN(self.win_record, period=self.p.period)
            self.prob_loss = self.p.period - self.prob_win
    
            self.wins = bt.ind.SumN(
                bt.If(self.diff >= 0, self.diff, 0), period=self.p.period)
    
            self.losses = bt.ind.SumN(
                bt.If(self.diff <0, -self.diff,0), period=self.p.period)
    
            self.avg_win = bt.DivByZero(self.wins, self.prob_win, 0)
            self.avg_loss = bt.DivByZero(self.losses, self.prob_loss, 0)
            self.avg_risk = bt.ind.Average(self.data.close*self.risk, period=self.p.period)
    
        def next(self):
    
            self.dsharpe = self.avg_win[0] / (self.avg_risk[0])
            if self.dsharpe > 1:
                # previous close less than the previous close
                # BUY, BUY, BUY!!! (with all possible default parameters)
                self.log('BUY CREATE, %.2f' % self.data[0])
                self.buy()
            else:
                pass
    

    With Prints...

    2018-01-09, c 24.37	diff  0.02, 	winr  1.00, 	pwin  3.00, 	ploss  2.00, 	wins  0.25, 	plosses  0.14, 	avg_win  0.08, 	avg_loss  0.07,	avg_risk  0.24,	DSHAREP  0.34
    2018-01-10, c 24.25	diff -0.12, 	winr  0.00, 	pwin  2.00, 	ploss  3.00, 	wins  0.12, 	plosses  0.26, 	avg_win  0.06, 	avg_loss  0.09,	avg_risk  0.24,	DSHAREP  0.25
    2018-01-11, c 24.32	diff  0.07, 	winr  1.00, 	pwin  2.00, 	ploss  3.00, 	wins  0.09, 	plosses  0.26, 	avg_win  0.05, 	avg_loss  0.09,	avg_risk  0.24,	DSHAREP  0.18
    2018-01-12, c 24.34	diff  0.02, 	winr  1.00, 	pwin  3.00, 	ploss  2.00, 	wins  0.11, 	plosses  0.18, 	avg_win  0.04, 	avg_loss  0.09,	avg_risk  0.24,	DSHAREP  0.15
    2018-01-15, c 24.40	diff  0.06, 	winr  1.00, 	pwin  4.00, 	ploss  1.00, 	wins  0.17, 	plosses  0.12, 	avg_win  0.04, 	avg_loss  0.12,	avg_risk  0.24,	DSHAREP  0.17
    2018-01-16, c 24.31	diff -0.09, 	winr  0.00, 	pwin  3.00, 	ploss  2.00, 	wins  0.15, 	plosses  0.21, 	avg_win  0.05, 	avg_loss  0.11,	avg_risk  0.24,	DSHAREP  0.21
    2018-01-17, c 24.35	diff  0.04, 	winr  1.00, 	pwin  4.00, 	ploss  1.00, 	wins  0.19, 	plosses  0.09, 	avg_win  0.05, 	avg_loss  0.09,	avg_risk  0.24,	DSHAREP  0.20
    

    Does this seem right?
    Also if I just wanted to use DSharpe as a bt.Indicator would i just change the class from Strategy to Indicator? and the purchase metrics?


Log in to reply
 

});