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!