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/

    Median calculation

    Indicators/Strategies/Analyzers
    2
    3
    48
    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.
    • carlrom
      carlrom last edited by

      Hello,

      I am new to backtrader and until today had managed to find all the answers in the Community. I can't find help on how to calculate the median (P50) of a line for the last 'period' bars (so basically a movav but for the median not the mean). I am creating an indicator and have managed to implement the calculation (sorting and retrieving the value in the middle) within next() but for efficiency and plotting I would like to have a class for the indicator and the calculation in init.

      Many thanks.

      run-out 1 Reply Last reply Reply Quote 0
      • run-out
        run-out @carlrom last edited by

        @carlrom
        I think I would be inclined to use the statistics package for python and then use their median method.

        import statistics
        

        You can create an indictor class using next in the indicator to create your line. It would look like this.

        class Median(bt.Indicator):
        
            lines = ("median", )
        
            params = (
                ("median_period", 10),
            )
        
            def __init__(self):
                self.addminperiod(self.p.median_period)
        
            def next(self):
                close_array = self.data.get(size=self.p.median_period)
                self.l.median[0] = statistics.median(close_array)
                # logging
                print(self.data.datetime.date(), close_array, self.l.median[0])
        

        Use the self.addminperiod to ensure there are no empty list for statistics and that you have the minimum period of data in your median calculation.

        Plotting can be controlled according to indicator.plots

        Your indicator can be called from your strategy init using:

        def __init__(self):
            self.median = Median(median_period=self.p.median_period)
        

        The logging looks like this for median_period=5:

        2020-11-06 array('d', [261.36, 265.3, 287.38, 294.68, 293.41]) 287.38
        2020-11-09 array('d', [265.3, 287.38, 294.68, 293.41, 278.77]) 287.38
        2020-11-10 array('d', [287.38, 294.68, 293.41, 278.77, 272.43]) 287.38
        2020-11-11 array('d', [294.68, 293.41, 278.77, 272.43, 276.48]) 278.77
        2020-11-12 array('d', [293.41, 278.77, 272.43, 276.48, 275.08]) 276.48
        

        The entire code:

        import datetime
        import backtrader as bt
        import statistics
        
        
        class Median(bt.Indicator):
        
            lines = ("median", )
        
            params = (
                ("median_period", 10),
            )
        
            def __init__(self):
                self.addminperiod(self.p.median_period)
        
            def next(self):
                close_array = self.data.get(size=self.p.median_period)
                self.l.median[0] = statistics.median(close_array)
                # Logging
                print(self.data.datetime.date(), close_array, self.l.median[0])
        
        
        class Strategy(bt.Strategy):
        
            params = (
                ("median_period", 5),
            )
        
            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.date(), txt))
        
            def print_signal(self):
                self.log(
                    f"o {self.datas[0].open[0]:7.2f} "
                    f"h {self.datas[0].high[0]:7.2f} "
                    f"l {self.datas[0].low[0]:7.2f} "
                    f"c {self.datas[0].close[0]:7.2f} "
                    f"v {self.datas[0].volume[0]:7.0f} "
                    f"median {self.median[0]:5.0f}"
                )
        
        
            def __init__(self):
                self.median = Median(median_period=self.p.median_period)
        
            def next(self):
                self.print_signal()
        
        
        if __name__ == "__main__":
        
            cerebro = bt.Cerebro()
        
            tickers = ['FB']
        
            for ticker in tickers:
                data = bt.feeds.YahooFinanceData(
                    dataname=ticker,
                    timeframe=bt.TimeFrame.Days,
                    fromdate=datetime.datetime(2020, 11, 1),
                    todate=datetime.datetime(2020, 12, 5),
                    reverse=False,
                )
                cerebro.adddata(data, name=ticker)
        
            cerebro.addstrategy(Strategy)
        
            # Execute
            cerebro.run()
        
        carlrom 1 Reply Last reply Reply Quote 2
        • carlrom
          carlrom @run-out last edited by

          @run-out It worked! I had tried running the next() within the indicator class but I wasn't including the subscript [0] in median[0].
          THANKS A LOT!

          1 Reply Last reply Reply Quote 1
          • 1 / 1
          • First post
            Last post
          Copyright © 2016, 2017, 2018 NodeBB Forums | Contributors
          $(document).ready(function () { app.coldLoad(); }); }