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

Price: nan with custom data feed



  • Hello community! I’m quite new to backtrader and struggeling since two days with the following problem:

    I have a simple RSI strategy (originally from https://backtest-rookies.com/) which works fine with data from bt.feeds.YahooFinanceCSVData. In my custom data feed I can see all OHLCV values, but after a buy or sell the Price attribute in self.position is always nan:

    2015-02-10 23:00:00, buy, 16.81
    --- Position Begin
    - Size: 1
    - Price: nan
    - Price orig: 0.0
    - Closed: 0
    - Opened: 1
    - Adjbase: 16.85
    --- Position End
    

    Custom data feed class (DataFeeds/MyJSONFeed.py):

    from backtrader.feed import DataBase
    from backtrader import date2num, TimeFrame
    
    
    class MyJSONFeed(DataBase):
        params = (('dataname', None),
                  ('fromdate', datetime.datetime.min),
                  ('todate', datetime.datetime.max),
                  ('symbol', None),
                  ('name', ''),
                  ('compression', 1),
                  ('timeframe', TimeFrame.Days),
                  ('sessionend', None))
    
        def __init__(self, *args, **kwargs):
            super(DataBase).__init__()
            self.server = "http://urlToAPI.json"
            self.timeframe = 'D'
            self.granularity = self.timeframe
            if self.p.timeframe == TimeFrame.Minutes:
                self.timeframe = 'M'
                self.granularity = self.timeframe + str(self.p.compression)
    
            postParameter = {
                'data[instrument]': self.p.symbol,
                'data[granularity]': self.granularity,
                'data[from]': str(self.p.fromdate),
                'data[to]': str(self.p.todate),
            }
            self.payload = urllib.parse.urlencode(postParameter)
            self.headers = {
                'Content-Type': 'application/x-www-form-urlencoded'
            }
    
            self.index = 0
            self.result = None
    
        def start(self):
            response = requests.request("POST", self.server, headers=self.headers, data=self.payload)
            self.result = json.loads(response.text.encode('utf8'))
            print(self.result)
            self.index = 0
    
        def _load(self):
            if self.index >= len(self.result):
                return False
            one_row = self.result[self.index]
            self.lines.datetime[0] = date2num(datetime.datetime.strptime(one_row["time"], '%Y-%m-%d %H:%M:%S.%f'))
            self.lines.high[0] = float(one_row["high"])
            self.lines.low[0] = float(one_row["low"])
            self.lines.close[0] = float(one_row["close"])
            self.lines.volume[0] = int(float(one_row["volume"]))
            self.lines.openinterest[0] = -1
            self.index += 1
            return True
    
    

    The JSON looks like

    [
        {
            "time": "2015-01-01 23:00:00.000000",
            "open": 15.74,
            "high": 16.07,
            "low": 15.54,
            "close": 15.76,
            "volume": 9606
        },
        {
            "time": "2015-01-04 23:00:00.000000",
            "open": 15.8,
            "high": 16.27,
            "low": 15.65,
            "close": 16.19,
            "volume": 21820
        },
        {
            "time": "2015-01-05 23:00:00.000000",
            "open": 16.2,
            "high": 16.73,
            "low": 16.12,
            "close": 16.54,
            "volume": 24917
        },
        {
            "time": "2015-01-06 23:00:00.000000",
            "open": 16.53,
            "high": 16.66,
            "low": 16.3,
            "close": 16.53,
            "volume": 22613
        },
        {
            "time": "2015-01-07 23:00:00.000000",
            "open": 16.55,
            "high": 16.66,
            "low": 16.31,
            "close": 16.36,
            "volume": 15618
        },
        {
            "time": "2015-01-08 23:00:00.000000",
            "open": 16.38,
            "high": 16.63,
            "low": 16.2,
            "close": 16.5,
            "volume": 25345
        },
        {
            "time": "2015-01-11 23:00:00.000000",
            "open": 16.49,
            "high": 16.68,
            "low": 16.43,
            "close": 16.58,
            "volume": 14853
        },
        {
            "time": "2015-01-12 23:00:00.000000",
            "open": 16.57,
            "high": 17.2,
            "low": 16.57,
            "close": 17.06,
            "volume": 25565
        },
        {
            "time": "2015-01-13 23:00:00.000000",
            "open": 17.04,
            "high": 17.09,
            "low": 16.55,
            "close": 16.85,
            "volume": 36333
        },
        {
            "time": "2015-01-14 23:00:00.000000",
            "open": 16.86,
            "high": 17.23,
            "low": 16.7,
            "close": 16.96,
            "volume": 53752
        },
        {
            "time": "2015-01-15 23:00:00.000000",
            "open": 16.94,
            "high": 17.84,
            "low": 16.89,
            "close": 17.8,
            "volume": 56762
        },
        {
            "time": "2015-01-18 23:00:00.000000",
            "open": 17.81,
            "high": 18.01,
            "low": 17.61,
            "close": 17.7,
            "volume": 21925
        },
        {
            "time": "2015-01-19 23:00:00.000000",
            "open": 17.7,
            "high": 18.03,
            "low": 17.63,
            "close": 18,
            "volume": 23881
        },
        {
            "time": "2015-01-20 23:00:00.000000",
            "open": 17.99,
            "high": 18.49,
            "low": 17.9,
            "close": 18.13,
            "volume": 48124
        },
        {
            "time": "2015-01-21 23:00:00.000000",
            "open": 18.12,
            "high": 18.48,
            "low": 17.9,
            "close": 18.33,
            "volume": 48511
        },
        {
            "time": "2015-01-22 23:00:00.000000",
            "open": 18.34,
            "high": 18.4,
            "low": 18.11,
            "close": 18.29,
            "volume": 30369
        },
        {
            "time": "2015-01-25 23:00:00.000000",
            "open": 18.31,
            "high": 18.48,
            "low": 17.86,
            "close": 17.92,
            "volume": 26849
        },
        {
            "time": "2015-01-26 23:00:00.000000",
            "open": 17.94,
            "high": 18.21,
            "low": 17.49,
            "close": 18.07,
            "volume": 23625
        },
        {
            "time": "2015-01-27 23:00:00.000000",
            "open": 18.06,
            "high": 18.16,
            "low": 17.93,
            "close": 18,
            "volume": 20791
        },
        {
            "time": "2015-01-28 23:00:00.000000",
            "open": 17.98,
            "high": 18.04,
            "low": 16.74,
            "close": 16.95,
            "volume": 44092
        },
        {
            "time": "2015-01-29 23:00:00.000000",
            "open": 16.97,
            "high": 17.32,
            "low": 16.82,
            "close": 17.24,
            "volume": 28803
        },
        {
            "time": "2015-02-01 23:00:00.000000",
            "open": 17.25,
            "high": 17.34,
            "low": 17,
            "close": 17.21,
            "volume": 21213
        },
        {
            "time": "2015-02-02 23:00:00.000000",
            "open": 17.21,
            "high": 17.75,
            "low": 17.08,
            "close": 17.29,
            "volume": 28780
        },
        {
            "time": "2015-02-03 23:00:00.000000",
            "open": 17.32,
            "high": 17.67,
            "low": 17.17,
            "close": 17.38,
            "volume": 28170
        },
        {
            "time": "2015-02-04 23:00:00.000000",
            "open": 17.39,
            "high": 17.5,
            "low": 16.93,
            "close": 17.27,
            "volume": 20148
        },
        {
            "time": "2015-02-05 23:00:00.000000",
            "open": 17.25,
            "high": 17.38,
            "low": 16.56,
            "close": 16.73,
            "volume": 33005
        },
        {
            "time": "2015-02-08 23:00:00.000000",
            "open": 16.8,
            "high": 17.17,
            "low": 16.7,
            "close": 17,
            "volume": 13733
        },
        {
            "time": "2015-02-09 23:00:00.000000",
            "open": 17.02,
            "high": 17.11,
            "low": 16.74,
            "close": 16.93,
            "volume": 13049
        },
        {
            "time": "2015-02-10 23:00:00.000000",
            "open": 16.92,
            "high": 17.1,
            "low": 16.74,
            "close": 16.81,
            "volume": 11512
        },
        {
            "time": "2015-02-11 23:00:00.000000",
            "open": 16.81,
            "high": 17.07,
            "low": 16.63,
            "close": 16.85,
            "volume": 14217
        },
        {
            "time": "2015-02-12 23:00:00.000000",
            "open": 16.86,
            "high": 17.45,
            "low": 16.85,
            "close": 17.35,
            "volume": 13400
        },
        {
            "time": "2015-02-15 23:00:00.000000",
            "open": 17.35,
            "high": 17.42,
            "low": 17.28,
            "close": 17.31,
            "volume": 5308
        },
        {
            "time": "2015-02-16 23:00:00.000000",
            "open": 17.29,
            "high": 17.34,
            "low": 16.3,
            "close": 16.53,
            "volume": 22112
        },
        {
            "time": "2015-02-17 23:00:00.000000",
            "open": 16.51,
            "high": 16.61,
            "low": 16.27,
            "close": 16.5,
            "volume": 16961
        },
        {
            "time": "2015-02-18 23:00:00.000000",
            "open": 16.51,
            "high": 16.8,
            "low": 16.36,
            "close": 16.39,
            "volume": 17057
        },
        {
            "time": "2015-02-19 23:00:00.000000",
            "open": 16.38,
            "high": 16.59,
            "low": 16.2,
            "close": 16.24,
            "volume": 14596
        },
        {
            "time": "2015-02-22 23:00:00.000000",
            "open": 16.27,
            "high": 16.62,
            "low": 16.1,
            "close": 16.33,
            "volume": 16215
        },
        {
            "time": "2015-02-23 23:00:00.000000",
            "open": 16.32,
            "high": 16.5,
            "low": 16.09,
            "close": 16.29,
            "volume": 17707
        },
        {
            "time": "2015-02-24 23:00:00.000000",
            "open": 16.28,
            "high": 16.74,
            "low": 16.28,
            "close": 16.57,
            "volume": 11301
        },
        {
            "time": "2015-02-25 23:00:00.000000",
            "open": 16.58,
            "high": 16.9,
            "low": 16.52,
            "close": 16.57,
            "volume": 9227
        },
        {
            "time": "2015-02-26 23:00:00.000000",
            "open": 16.58,
            "high": 16.7,
            "low": 16.42,
            "close": 16.59,
            "volume": 11486
        }
    ]
    

    My Cerebro class (Cerebros/OptimizerCerebro.py)

    from __future__ import (absolute_import, division, print_function,
                            unicode_literals)
    import backtrader as bt
    import datetime  # For datetime objects
    from DataFeeds.MyJSONFeed import MyJSONFeed
    
    
    class OptimizerCerebro(bt.Cerebro):
        options = {
            "fromdate": datetime.datetime(2010, 3, 1, 19, 0, 0),
            "todate": datetime.datetime(2020, 3, 1, 20, 0, 0),
            "endvalue": 0,
            "symbol": 'EUR_USD',
            "timeframe": bt.TimeFrame.Days,
            "compression": 1
        }
    
        def __init__(self, options):
            super().__init__()
            self.options.update(options)
    
        @property
        def run(self):
            # Create a cerebro entity
            cerebro = bt.Cerebro()
            startcash = 100000
    
            # Add a strategy
            if "strategy" not in self.options:
                return False
    
            cerebro.optstrategy(self.options["strategy"], period=range(14, 21))
    
            # Create a Data Feed
            data = MyJSONFeed(
                dataname='XAU_USD',
                fromdate=self.options["fromdate"],
                todate=self.options["todate"],
                symbol=self.options["symbol"],
                timeframe=self.options["timeframe"],
                compression=1,
            )
    
            '''modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
            datapath = os.path.join(modpath, 'datas/orcl-1995-2014.txt')
    
            # Create a Data Feed
            data = bt.feeds.YahooFinanceCSVData(
                dataname=datapath,
                # Do not pass values before this date
                fromdate=datetime.datetime(2000, 1, 1),
                # Do not pass values after this date
                todate=datetime.datetime(2000, 12, 31),
                reverse=False)'''
    
            # Add the data to Cerebro
            cerebro.adddata(data)
    
            # Set our desired cash start
            cerebro.broker.setcash(startcash)
            cerebro.run()
    

    The strategy (Strategies/RSIBorderBounce.py)

    import backtrader as bt
    
    
    class RSIBorderBounce(bt.Strategy):
        params = (
            ('period', 21),
        )
    
        def log(self, txt, dt=None):
            """ Logging function for this strategy"""
            dt = dt or self.datas[0].datetime.datetime(0)
            print('%s, %s' % (dt, txt))
    
        def __init__(self):
            self.startcash = self.broker.getvalue()
            self.dataclose = self.datas[0].close
            self.rsi = bt.indicators.RSI_SMA(self.data.close, period=self.params.period, safediv=True)
    
        def next(self):
            if self.position:
                print(self.position)
            if not self.position:
    
                if self.rsi < 30:
                    self.log('buy, %.2f' % self.dataclose[0])
                    self.buy(size=1)
            else:
                if self.rsi > 70:
                    self.log('sell, %.2f' % self.dataclose[0])
                    self.sell(size=1)
    
        def stop(self):
            pnl = round(self.broker.getvalue() - self.startcash, 2)
            print('RSI Period: {} Final PnL: {}'.format(
                self.params.period, pnl))
    
    

    And my main script is

    import datetime  # For datetime objects
    import backtrader as bt
    
    from Cerebros.OptimizerCerebro import OptimizerCerebro
    from Strategies.RSIBorderBounce import RSIBorderBounce
    
    if __name__ == '__main__':
        testStrategy = OptimizerCerebro({
            "strategy": RSIBorderBounce,
            "fromdate": datetime.datetime(2015, 1, 1),
            "todate": datetime.datetime(2015, 3, 1),
            "endvalue": 0,
            "symbol": 'XAG_USD',
            "timeframe": bt.TimeFrame.Days,
            "compression": 1
        })
        testStrategy.run
    

    Can someone give me an advice?
    Thanks!



});