Hello there,
I'm trying to backtest on multiple assets that have 'prediction' and 'signal' columns.
I'm having some troubles to keep track of orders as i try to use manual bracket orders (1 market buy, 1 limit take profit, 1 limit stop loss).
class custom_data_loader(btfeeds.PandasData):
lines = ("signal","prediction",)
params = (("signal", -1),("prediction", -1))
datafields = btfeeds.PandasData.datafields + (["prediction", "signal",])
class custom_Sizer(bt.Sizer):
def __init__(self):
self.size_per_asset = {
'bnbusd': 0.05,
'vetusd': 0.05,
'iotusd': 0.05,
'xlmusd': 0.05,
'ontusd': 0.05,
'trxusd': 0.05,
'xrpusd': 0.05,
'adausd': 0.05,
'ltcusd': 0.05,
'eosusd': 0.05,
'ethusd': 0.10,
'btcusd': 0.20
}
def _getsizing(self, comminfo, cash, data, isbuy):
if isbuy == True:
size = math.floor((cash * self.size_per_asset[data._name]) / data[0])
else:
size = self.broker.getposition(d).size
return size
class custom_Strategy(bt.Strategy):
def log(self, txt, dt=None, vlm=None):
dt = dt or self.datas[0].datetime.datetime(0)
print('%s, %s' % (dt.isoformat(), txt))
def __init__(self):
self.inds = dict()
self.o = dict()
for i, d in enumerate(self.datas):
self.o[d] = None
self.inds[d] = dict()
self.inds[d]['close'] = d.close
self.inds[d]['prediction'] = d.prediction
self.inds[d]['signal'] = d.signal
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
return
self.last_executed_price = order.executed.price
if order.status in [order.Completed]:
if order.isbuy():
self.log(
'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
self.buyprice = order.executed.price
self.buycomm = order.executed.comm
self.last_executed_price = order.executed.price
else: # Sell
self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
self.last_executed_price = order.executed.price
self.bar_executed = len(self)
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log('Order Canceled/Margin/Rejected')
# Write down: no pending order
self.order = None
def notify_trade(self, trade):
if not trade.isclosed:
return
self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
(trade.pnl, trade.pnlcomm))
def next(self):
for i, d in enumerate(self.datas):
if not self.getposition(d).size: #and not self.order[d]:
if self.inds[d]['signal'][0]>0:
print('%s : close: %.2f, prediction: %.2f, signal: %.2f'% (d._name, self.inds[d]['close'][0], self.inds[d]['prediction'][0], self.inds[d]['signal'][0]))
self.log('%s: BUY MARKET CREATE, at %.2f' % (d._name, d.close[0]))
o1 = self.buy(data=d, exectype=bt.Order.Market, transmit=False)
print('%s : close: %.2f, prediction: %.2f, signal: %.2f'% (d._name, self.inds[d]['close'][0], self.inds[d]['prediction'][0], self.inds[d]['signal'][0]))
self.log('%s: SELL STOP CREATE, at %.2f' % (d._name, d.close[0]*0.90))
o2 = self.buy(data=d, price=d.close[0]*0.90, exectype=bt.Order.Stop, transmit=False, parent=o1)
print('%s : close: %.2f, prediction: %.2f, signal: %.2f'% (d._name, self.inds[d]['close'][0], self.inds[d]['prediction'][0], self.inds[d]['signal'][0]))
self.log('%s: SELL LIMIT CREATE, at %.2f' % (d._name, d.prediction[0]))
o3 = self.buy(data=d, price=d.prediction[0], exectype=bt.Order.Limit, transmit=True, parent=o1)
self.o[d] = [o1, o2, o3]
# Create cerebro entity
cerebro = bt.Cerebro()
# Add data
#pairs = ['bnbusd','vetusd','iotusd','xlmusd','ontusd','trxusd','xrpusd','adausd','ltcusd','btcusd','eosusd','ethusd']
pairs = ['bnbusd','ethusd', 'ltcusd']
for pair in pairs:
pathname = 'data/' + pair + '.csv'
df = pd.read_csv(pathname, infer_datetime_format=True,
parse_dates=['datetime'],
dtype={
"open" : "float",
"high" : "float",
"low" : "float",
"close" : "float",
"volume": "float"
, "prediction": "float"
, "signal": "float"
},
index_col=0)
df.index = pd.to_datetime(df.datetime, format='%Y-%m-%dT%H:%M:%S.%fZ')
df = df[['open', 'high', 'low', 'close', 'volume', 'prediction', 'signal']]
bt_data = custom_data_loader(dataname=df)
cerebro.adddata(bt_data, name=pair)
# Add sizer
cerebro.addsizer(custom_Sizer)
# Add analyzer
cerebro.addanalyzer(bt.analyzers.PyFolio)
# Add a strategy
cerebro.addstrategy(custom_Strategy3)
# Add broker fees
cerebro.broker.setcommission(commission=0.00075)
# Set our desired cash start
cerebro.broker.setcash(10000.0)
# Print out the starting conditions
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
# Run over everything
strat = cerebro.run()
# Print out the final result
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
It seems i'm printing some stuff twice... but the main problem is that only the first market buy is trigger...
Starting Portfolio Value: 10000.00
bnbusd : close: 32.94, prediction: 33.10, signal: 1.00
2020-12-26T09:26:00, bnbusd: BUY MARKET CREATE, at 32.94
bnbusd : close: 32.94, prediction: 33.10, signal: 1.00
2020-12-26T09:26:00, bnbusd: SELL STOP CREATE, at 29.65
bnbusd : close: 32.94, prediction: 33.10, signal: 1.00
2020-12-26T09:26:00, bnbusd: SELL LIMIT CREATE, at 33.10
2020-12-26T09:27:00, BUY EXECUTED, Price: 32.94, Cost: 494.09, Comm 0.37
2020-12-26T09:28:00, BUY EXECUTED, Price: 32.92, Cost: 493.87, Comm 0.37
2020-12-26T09:28:00, Order Canceled/Margin/Rejected
ethusd : close: 622.26, prediction: 624.22, signal: 1.00
2020-12-26T10:13:00, ethusd: BUY MARKET CREATE, at 622.26
ethusd : close: 622.26, prediction: 624.22, signal: 1.00
2020-12-26T10:13:00, ethusd: SELL STOP CREATE, at 560.03
ethusd : close: 622.26, prediction: 624.22, signal: 1.00
2020-12-26T10:13:00, ethusd: SELL LIMIT CREATE, at 624.22
2020-12-26T10:14:00, BUY EXECUTED, Price: 622.25, Cost: 622.25, Comm 0.47
2020-12-26T10:15:00, BUY EXECUTED, Price: 621.40, Cost: 621.40, Comm 0.47
2020-12-26T10:15:00, Order Canceled/Margin/Rejected
ltcusd : close: 124.39, prediction: 124.98, signal: 1.00
2020-12-30T12:27:00, ltcusd: BUY MARKET CREATE, at 124.39
ltcusd : close: 124.39, prediction: 124.98, signal: 1.00
2020-12-30T12:27:00, ltcusd: SELL STOP CREATE, at 111.95
ltcusd : close: 124.39, prediction: 124.98, signal: 1.00
2020-12-30T12:27:00, ltcusd: SELL LIMIT CREATE, at 124.98
2020-12-30T12:28:00, BUY EXECUTED, Price: 124.38, Cost: 373.14, Comm 0.28
2020-12-30T12:29:00, BUY EXECUTED, Price: 124.23, Cost: 372.69, Comm 0.28
2020-12-30T12:29:00, Order Canceled/Margin/Rejected
Final Portfolio Value: 11154.97
If someone could help me figure out what i'm doing wrong... I tried to adapt the script from the multi-exemple on the blog but with no success so far.
Thanks!!