thank you all for the response, I solve the problem by following the documentation, just make sure you count the column number correctly (start from 0) and follow the extending data section in doc
Latest posts made by Zhangjt9317
-
RE: extending data feed ==> list index out of range issue
-
help on a customized strategy
Hi, I am new to bt and the algo trading, and I really need help with a strategy I am building.
I have 1-min BTC data OHLCV + (day-based) forecast price predicted using the model. I am trying to run a backtest using 1min data. The data looks like this:
,date,time,open,high,low,close,volume,close_time,average,Forecast 0,2017-08-17,2017-08-17 04:00:00,4261.48,4261.48,4261.48,4261.48,1.7751830000000002,1502942459999,4308.1725,4303.864329999999 1,2017-08-17,2017-08-17 04:01:00,4261.48,4261.48,4261.48,4261.48,0.0,1502942519999,4308.1725,4303.864329999999 2,2017-08-17,2017-08-17 04:02:00,4280.56,4280.56,4280.56,4280.56,0.261074,1502942579999,4308.1725,4303.864329999999 3,2017-08-17,2017-08-17 04:03:00,4261.48,4261.48,4261.48,4261.48,0.012008,1502942639999,4308.1725,4303.864329999999 4,2017-08-17,2017-08-17 04:04:00,4261.48,4261.48,4261.48,4261.48,0.140796,1502942699999,4308.1725,4303.864329999999 5,2017-08-17,2017-08-17 04:05:00,4261.48,4261.48,4261.48,4261.48,0.0,1502942759999,4308.1725,4303.864329999999 6,2017-08-17,2017-08-17 04:06:00,4261.48,4261.48,4261.48,4261.48,0.0,1502942819999,4308.1725,4303.864329999999 7,2017-08-17,2017-08-17 04:07:00,4261.48,4261.48,4261.48,4261.48,0.0,1502942879999,4308.1725,4303.864329999999 8,2017-08-17,2017-08-17 04:08:00,4261.48,4261.48,4261.48,4261.48,0.0,1502942939999,4308.1725,4303.864329999999 9,2017-08-17,2017-08-17 04:09:00,4261.48,4261.48,4261.48,4261.48,0.0,1502942999999,4308.1725,4303.864329999999
The forecast price varies by days, and I am trying to do backtest on minutes, that's why they look the same
The strategy is:
- Open trade at the daily open price.
- Long if forecast > open price, short if forecast < open price.
- Close trade at forecast price or at stop-loss (potential gain == potential loss), whichever reaches first.
I have $10000 cash and trying to buy BTC as much as I can every time (not sure if this is called all-in)
and I am trying to set the stop loss condition to just when the trade reaches back to the forecast price
class Data_Extend(GenericCSVData): lines = ('Forecast',) # add a 'Forecast' line to the inherited ones from the base class params = (('Forecast', 7),) # forecast column location # using a signal strategy here class FStrategy(bt.SignalStrategy): params = dict( stake=1, printout=True, csvcross=True, period=7, startcash=10000, target_perc=1.0, ) def log(self, txt, dt=None): if self.p.printout: dt = dt or self.data.datetime[0] dt = bt.num2date(dt) print('%s, %s' % (dt.isoformat(), txt)) def notify_order(self, order): if order.status in [bt.Order.Submitted, bt.Order.Accepted]: return # Await further notifications if order.status == order.Completed: if order.isbuy(): buytxt = 'BUY COMPLETE, %.2f' % order.executed.price self.log(buytxt, order.executed.dt) else: selltxt = 'SELL COMPLETE, %.2f' % order.executed.price self.log(selltxt, order.executed.dt) elif order.status in [order.Expired, order.Canceled]: self.log('%s ,' % order.Status[order.status]) pass # Simply log # Allow new orders self.orderid = None def __init__(self): self.orderid = None # there is only 1 data feed, so it is the same anyway self.dataclose = self.datas[0].close self.dataopen = self.datas[0].open self.dataforecast = self.datas[0].Forecast # long condition (with exit)T self.signal_long = self.dataforecast > self.dataopen # multi head signal 1 ==> long when forecast > open self.signal_add(bt.SIGNAL_LONG, self.signal_long) # LONGEXIT: short indications are taken to exit long positions self.long_exit = self.dataforecast <= self.dataopen self.signal_add(bt.SIGNAL_LONGEXIT, self.long_exit) # short condition (with exit) self.signal_short = self.dataforecast < self.dataopen self.signal_add(bt.SIGNAL_SHORT, self.signal_short) # SHORTEXIT: long indications are taken to exit short positions self.short_exit = self.dataforecast <= self.dataopen self.signal_add(bt.SIGNAL_SHORTEXIT, self.short_exit) def next(self): if self.orderid: return # if an order is active, no new orders are allowed if not self.position: # not yet in market sizer = self.broker.get_cash() / self.datas[0].open[0] if self.signal_long: # cross upwards self.log('BUY CREATE @CLOSE , %.2f, OPEN %.2f, FORECAST %.2f' % (self.data.close[0], self.data.open[0], self.dataforecast[0])) print('Position Size: %.2f BTC' % sizer) self.order = self.buy(size=sizer) elif self.long_exit: self.log('CLOSE LONG , %.2f, OPEN %.2f, FORECAST %.2f' % (self.data.close[0], self.data.open[0], self.dataforecast[0])) self.order = self.close(size=sizer) else: # in the market sizer = self.broker.getposition(data=self.datas[0]).size if self.signal_short: # short signal self.log('SELL CREATE @CLOSE , %.2f, OPEN %.2f, FORECAST %.2f' % (self.data.close[0], self.data.open[0], self.dataforecast[0])) print('Position Size: %.2f BTC' % sizer) self.order = self.sell(size=sizer) elif self.short_exit: self.log('CLOSE SHORT , %.2f, OPEN %.2f, FORECAST %.2f' % (self.data.close[0], self.data.open[0], self.dataforecast[0])) self.order = self.close(size=sizer) def stop(self): print('==================================================') print('Starting Value - %.2f' % self.broker.startingcash) print('Ending Value - %.2f' % self.broker.getvalue()) print('==================================================')
In the next and init I log some info I want, and when I run using the data, the log looks like this:
2019-01-01T23:59:59.999989, BUY CREATE @CLOSE , 3797.14, OPEN 3701.23, FORECAST 3728.05 Position Size: 2.70 BTC 2019-01-01T23:59:59.999989, BUY COMPLETE, 3797.14 2019-01-02T23:59:59.999989, SELL CREATE @CLOSE , 3858.56, OPEN 3796.45, FORECAST 3742.63 Position Size: 2.63 BTC 2019-01-02T23:59:59.999989, SELL COMPLETE, 3858.56 2019-01-03T23:59:59.999989, BUY CREATE @CLOSE , 3766.78, OPEN 3857.57, FORECAST 3863.28 Position Size: 2.63 BTC 2019-01-03T23:59:59.999989, BUY COMPLETE, 3766.78 2019-01-05T23:59:59.999989, SELL CREATE @CLOSE , 3770.96, OPEN 3790.09, FORECAST 3770.63 Position Size: 2.70 BTC 2019-01-05T23:59:59.999989, SELL COMPLETE, 3770.96 2019-01-06T23:59:59.999989, BUY CREATE @CLOSE , 3987.60, OPEN 3771.12, FORECAST 3797.46 Position Size: 2.70 BTC 2019-01-06T23:59:59.999989, BUY COMPLETE, 3987.60 2019-01-07T23:59:59.999989, SELL CREATE @CLOSE , 3975.45, OPEN 3987.62, FORECAST 3925.53 Position Size: 2.55 BTC 2019-01-07T23:59:59.999989, SELL COMPLETE, 3975.45 2019-01-08T23:59:59.999989, BUY CREATE @CLOSE , 3955.13, OPEN 3976.76, FORECAST 4001.77 Position Size: 2.55 BTC 2019-01-08T23:59:59.999989, BUY COMPLETE, 3955.13 2019-01-10T23:59:59.999989, SELL CREATE @CLOSE , 3585.88, OPEN 3966.06, FORECAST 3965.74 Position Size: 2.56 BTC 2019-01-10T23:59:59.999989, SELL COMPLETE, 3585.88 2019-01-11T23:59:59.999989, BUY CREATE @CLOSE , 3601.31, OPEN 3585.88, FORECAST 3670.85 Position Size: 2.56 BTC 2019-01-11T23:59:59.999989, BUY COMPLETE, 3601.31 2019-01-12T23:59:59.999989, SELL CREATE @CLOSE , 3583.13, OPEN 3601.31, FORECAST 3528.85 Position Size: 2.55 BTC 2019-01-12T23:59:59.999989, SELL COMPLETE, 3583.13 2019-01-13T23:59:59.999989, BUY CREATE @CLOSE , 3476.81, OPEN 3584.10, FORECAST 3611.51 Position Size: 2.55 BTC 2019-01-13T23:59:59.999989, BUY COMPLETE, 3476.81 2019-01-15T23:59:59.999989, SELL CREATE @CLOSE , 3553.06, OPEN 3626.08, FORECAST 3600.16 Position Size: 2.63 BTC 2019-01-15T23:59:59.999989, SELL COMPLETE, 3553.06 2019-01-16T23:59:59.999989, BUY CREATE @CLOSE , 3591.84, OPEN 3553.06, FORECAST 3578.68 Position Size: 2.63 BTC 2019-01-16T23:59:59.999989, BUY COMPLETE, 3591.84 2019-01-17T23:59:59.999989, SELL CREATE @CLOSE , 3616.21, OPEN 3591.84, FORECAST 3585.79 Position Size: 2.60 BTC 2019-01-17T23:59:59.999989, SELL COMPLETE, 3616.21 2019-01-18T23:59:59.999989, CLOSE LONG , 3594.87, OPEN 3613.32, FORECAST 3597.20 2019-01-19T23:59:59.999989, BUY CREATE @CLOSE , 3665.30, OPEN 3594.87, FORECAST 3599.16 Position Size: 2.62 BTC 2019-01-19T23:59:59.999989, BUY COMPLETE, 3665.30
Position Size: 1.71 BTC 2019-12-29T23:59:59.999989, BUY COMPLETE, 7388.24 ================================================== Starting Value - 10000.00 Ending Value - 12191.66 ================================================== =============================================================================== TimeReturn: - 2019-01-31 00:00:00: -0.10192223974656367 - 2019-02-28 00:00:00: 0.12747463642130064 - 2019-03-31 00:00:00: 0.07017082291549426 - 2019-04-30 00:00:00: -0.032682599108764476 - 2019-05-31 00:00:00: 0.10385189267487394 - 2019-06-30 00:00:00: 0.10896888494902579 - 2019-07-31 00:00:00: -0.002396698888080584 - 2019-08-31 00:00:00: -0.020199229864290236 - 2019-09-30 00:00:00: 0.024766766780242477 - 2019-10-31 00:00:00: 0.056789760260889555 - 2019-11-30 00:00:00: -0.10789694777986258 - 2019-12-31 00:00:00: 0.006156163558002525 =============================================================================== SharpeRatio: - sharperatio: 0.24941200165758853 =============================================================================== SQN: - sqn: 0.5616557528887063 - trades: 126
I do not really know if this is working well, I am especially confused with 3 things
- size ==> is the size correct
if not self.position: # not yet in market sizer = self.broker.get_cash() / self.datas[0].open[0] else: # in the market sizer = self.broker.getposition(data=self.datas[0]).size
- close ==> is my way the right way to do it? I close both positions when
elif self.long_exit: self.log('CLOSE LONG , %.2f, OPEN %.2f, FORECAST %.2f' % (self.data.close[0], self.data.open[0], self.dataforecast[0])) self.order = self.close(size=sizer) elif self.short_exit: self.log('CLOSE LONG , %.2f, OPEN %.2f, FORECAST %.2f' % (self.data.close[0], self.data.open[0], self.dataforecast[0])) self.order = self.close(size=sizer)
- result ==> does the result look correct? I read many blogs, just wanna check
Thank you if anybody can help.
-
extending data feed ==> list index out of range issue
Hi, I am new to backtrader, and I have a minute-based OHLCV dataset for BTC price, but with an extra 'Forecast' column. I read the
extending data feeds
post and made my own datafeed class as shown below:class datafeed(GenericCSVData): lines = ('Forecast',) # add a 'Forecast' line to the inherited ones from the base class params = ( ('nullvalue', float('NaN')), ('dtformat', '%Y-%m-%d %H:%M:%S'), ('tmformat', '%H:%M:%S'), ('datetime', 1), ('open', 2), ('high', 3), ('low', 4), ('close', 5), ('volume', 6), ('Forecast', 11) # 11th col )
and I am trying to compare the open price and forecast price as a signal
def __init__(self): # To control operation entries self.orderid = None # Create a CrossOver Signal from close an moving average self.signal = self.data.Forecast - self.data.open
But I have the error:
Traceback (most recent call last): File "D:/DailyTradeForecast.py", line 228, in <module> runstrategy() File "D:/DailyTradeForecast.py", line 159, in runstrategy cerebro.run() File "C:\Users\anaconda3\lib\site-packages\backtrader\cerebro.py", line 1127, in run runstrat = self.runstrategies(iterstrat) File "C:\Users\anaconda3\lib\site-packages\backtrader\cerebro.py", line 1212, in runstrategies data.preload() File "C:\Users\anaconda3\lib\site-packages\backtrader\feed.py", line 688, in preload while self.load(): File "C:\Users\anaconda3\lib\site-packages\backtrader\feed.py", line 479, in load _loadret = self._load() File "C:\Users\anaconda3\lib\site-packages\backtrader\feed.py", line 710, in _load return self._loadline(linetokens) File "C:\Users\anaconda3\lib\site-packages\backtrader\feeds\csvgeneric.py", line 148, in _loadline csvfield = linetokens[csvidx] IndexError: list index out of range Process finished with exit code 1
I am not so sure what is wrong with my datafeed, or it is the error from the signal, can someone help?