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.