def update_bars(self): """get data from exchange""" if len(self.bars) < 10: self.bars = self.exchange.get_bars(self.settings.MINUTES_PER_BAR, 0) else: new_bars = self.exchange.recent_bars(self.settings.MINUTES_PER_BAR, 0) for b in reversed(new_bars): if b.tstamp < self.bars[0].tstamp: continue elif b.tstamp == self.bars[0].tstamp: # merge? if b.subbars[-1].tstamp == self.bars[0].subbars[-1].tstamp: b.bot_data = self.bars[ 0].bot_data # merge bot data to not loose it self.bars[0] = b else: # merge! first = self.bars[0].subbars[-1] newBar = Bar(tstamp=b.tstamp, open=first.open, high=first.high, low=first.low, close=first.close, volume=first.volume, subbars=[first]) for sub in reversed(self.bars[0].subbars[:-1]): if sub.tstamp < b.subbars[-1].tstamp: newBar.add_subbar(sub) else: break for sub in reversed(b.subbars): if sub.tstamp > newBar.subbars[0].tstamp: newBar.add_subbar(sub) else: continue newBar.bot_data = self.bars[ 0].bot_data # merge bot data to not loose it self.bars[0] = newBar else: # b.tstamp > self.bars[0].tstamp self.bars.insert(0, b) del self.bars[400:] for bar in self.bars[3:]: # remove minute data from older bars bar.subbars = []
def run(self): self.reset() self.logger.info("starting backtest with " + str(len(self.bars)) + " bars and " + str(self.account.equity) + " equity") for i in range(self.bot.min_bars_needed(), len(self.bars)): if i == len(self.bars) - 1 or i < self.bot.min_bars_needed(): continue # ignore last bar and first x # slice bars. TODO: also slice intrabar to simulate tick self.current_bars = self.bars[-(i + 1):] # add one bar with 1 tick on open to show to bot that the old one is closed next_bar = self.bars[-i - 2] forming_bar = Bar(tstamp=next_bar.tstamp, open=next_bar.open, high=next_bar.open, low=next_bar.open, close=next_bar.open, volume=0, subbars=[]) self.current_bars.insert(0, forming_bar) self.current_bars[0].did_change = True self.current_bars[1].did_change = True self.do_funding() # self.bot.on_tick(self.current_bars, self.account) for subbar in reversed(next_bar.subbars): # check open orders & update account self.handle_open_orders(subbar) open = len(self.account.open_orders) forming_bar.add_subbar(subbar) self.bot.on_tick(self.current_bars, self.account) if open != len(self.account.open_orders): self.handle_open_orders(subbar) # got new ones self.current_bars[1].did_change = False next_bar.bot_data = forming_bar.bot_data for b in self.current_bars: if b.did_change: b.did_change = False else: break # no need to go further if self.account.open_position.quantity != 0: self.send_order( Order(orderId="endOfTest", amount=-self.account.open_position.quantity)) self.handle_open_orders(self.bars[0].subbars[-1]) if len(self.bot.position_history) > 0: daysInPos = 0 maxDays = 0 minDays = self.bot.position_history[0].daysInPos() if len( self.bot.position_history) > 0 else 0 for pos in self.bot.position_history: if pos.status != PositionStatus.CLOSED: continue if pos.exit_tstamp is None: pos.exit_tstamp = self.bars[0].tstamp daysInPos += pos.daysInPos() maxDays = max(maxDays, pos.daysInPos()) minDays = min(minDays, pos.daysInPos()) daysInPos /= len(self.bot.position_history) profit = self.account.equity - self.initialEquity uw_updates_per_day = 1440 # every minute total_days = (self.bars[0].tstamp - self.bars[-1].tstamp) / (60 * 60 * 24) rel = profit / (self.maxDD if self.maxDD > 0 else 1) rel_per_year = rel / (total_days / 365) self.logger.info( "finished | closed pos: " + str(len(self.bot.position_history)) + " | open pos: " + str(len(self.bot.open_positions)) + " | profit: " + ("%.2f" % (100 * profit / self.initialEquity)) + " | HH: " + ("%.2f" % (100 * (self.hh / self.initialEquity - 1))) + " | maxDD: " + ("%.2f" % (100 * self.maxDD / self.initialEquity)) + " | maxExp: " + ("%.2f" % (self.maxExposure / self.initialEquity)) + " | rel: " + ("%.2f" % (rel_per_year)) + " | UW days: " + ("%.1f" % (self.max_underwater / uw_updates_per_day)) + " | pos days: " + ("%.1f/%.1f/%.1f" % (minDays, daysInPos, maxDays))) else: self.logger.info("finished with no trades") #self.write_results_to_files() return self