class BotStrategy(object): def __init__(self, backtest=True, forwardtest=True): self.output = BotLog() self.pair = shared.exchange['pair'] self.coinsInOrder = shared.exchange['coinsInOrder'] self.marketInOrder = shared.exchange['marketInOrder'] self.trades = [] self.currentPrice = "" self.currentClose = "" self.lowestAsk = 0.00 self.highestBid = 0.00 self.simultaneousTrades = 4 self.tradeMultiplier = 0.1 self.ticker = {} self.backTest = backtest self.forwardTest = forwardtest self.indicators = BotIndicators() self.candlesticks = [] self.movingAverages = [] self.movingAveragePeriod = shared.strategy['movingAverageLength'] self.trueRanges = [] self.averageTrueRanges = [] # portfolio self.openOrders = [] #api self.api = BotApi() def tick(self, candlestick): #strategy works on closed candles only if not candlestick.isClosed(): return else: self.candlesticks.append(candlestick) self.currentPrice = candlestick.currentPrice ma = self.indicators.sma(self.candlesticks, shared.strategy['movingAverageLength'], 'close') self.movingAverages.append(ma) tr = self.indicators.trueRange(self.candlesticks) self.trueRanges.append(tr) atr = self.indicators.averageTrueRange(self.trueRanges, 5) self.averageTrueRanges.append(atr) self.ticker = self.getTicker(self.pair) portfolioUpdated = self.updatePortfolio() # If live and portfolio not updated, we may run into some unpleasant issues. # Better stop here for now if not portfolioUpdated: return # Strategy needs at least 2 candles to work if len(self.candlesticks) > 1 and candlestick.isClosed(): self.updateOpenTrades(self.pair) self.evaluatePositions() def evaluatePositions(self): openOrders = self.getOpenOrders(self.pair) ''' Go Long (buy) if all of these are met: Previous price is lower movingAverage Current price is higher than moving average Go short (sell) if: Previous price is higher than moving average Current Price is lower than moving average ''' golong1 = self.candlesticks[-2].close < self.movingAverages[-1] golong2 = self.currentPrice > self.movingAverages[-1] goshort1 = self.candlesticks[-2].close > self.movingAverages[-1] goshort2 = self.currentPrice < self.movingAverages[-1] if golong1 and golong2 and len(openOrders) < self.simultaneousTrades: rate = float(self.ticker['lowestAsk']) total = (shared.exchange['nbMarket'] - shared.exchange['marketInOrder']) * self.tradeMultiplier stoploss = self.currentPrice - self.averageTrueRanges[-1] takeprofit = self.currentPrice + (2 * self.averageTrueRanges[-1]) self.buy(rate, total, self.candlesticks[-1].date, stoploss, takeprofit) if goshort1 and goshort2 and len(openOrders) < self.simultaneousTrades: rate = float(self.ticker['highestBid']) amount = (shared.exchange['nbCoin'] - shared.exchange['coinsInOrder']) * self.tradeMultiplier stoploss = self.currentPrice + self.averageTrueRanges[-1] takeprofit = self.currentPrice - (2 * self.averageTrueRanges[-1]) self.sell(rate, amount, self.candlesticks[-1].date, stoploss, takeprofit) def updateOpenTrades(self, pair): openOrders = self.getOpenOrders(pair) # TODO: implement not backtest for trade in openOrders: trade.tick(self.candlesticks[-1], self.candlesticks[-1].date) def getOpenOrders(self, pair): if not self.backTest and not self.forwardTest: orders = self.api.returnOpenOrders(pair) return orders else: openOrders = [] for order in self.trades: if order.status == 'OPEN': openOrders.append(order) return openOrders def getCurrentPrice(self, pair): if not self.backTest: return self.api.returnTicker(pair)['last'] else: return self.candlesticks[-1].close def getTicker(self, pair): if not self.backTest: return self.api.returnTicker(pair) else: return { 'last': self.currentPrice, 'highestBid': self.currentPrice - self.currentPrice * shared.exchange['spreadPercentage'], 'lowestAsk': self.currentPrice + self.currentPrice * shared.exchange['spreadPercentage'] } def updatePortfolio(self): if not self.backTest and not self.forwardTest: try: portfolio = self.api.returnBalances() if shared.exchange['market'] in portfolio: shared.exchange['nbMarket'] = float( portfolio[shared.exchange['market']]) else: shared.exchange['nbMarket'] = 0.00 if shared.exchange['coin'] in portfolio: shared.exchange['nbCoin'] = float( portfolio[shared.exchange['coin']]) else: shared.exchange['nbCoin'] = 0.00 return True except Exception as e: self.output.warning("Error updating portfolio") print(e) return False else: return True def showPortfolio(self): if not self.backTest and not self.forwardTest: self.updatePortfolio() self.output.log( str(shared.exchange['nbMarket']) + " " + str(shared.exchange['market']) + ' - ' + str(shared.exchange['nbCoin']) + " " + str(shared.exchange['coin'])) def buy(self, rate, total, date, stopLoss=0, takeProfit=0): amount = total / rate order = BotTrade('BUY', rate=rate, amount=amount, total=total, date=date, stopLoss=stopLoss, takeProfit=takeProfit, backtest=self.backTest, forwardtest=self.forwardTest) self.trades.append(order) def sell(self, rate, amount, date, stopLoss=0, takeProfit=0): total = rate * amount order = BotTrade('SELL', rate=rate, amount=amount, total=total, date=date, stopLoss=stopLoss, takeProfit=takeProfit, backtest=self.backTest, forwardtest=self.forwardTest) self.trades.append(order)
class BotTrade(object): def __init__(self,direction,amount,rate,total,date,stopLoss=0, takeProfit=0, backtest=True, live=False): global orderNb self.amount = amount self.backTest = backtest self.date = date self.direction = direction self.exitDate = 0.0 self.exitRate = 0.0 self.fee = float(shared.exchange['fee']) self.filledOn = "" self.live = live self.orderNumber = orderNb self.output = BotLog() self.rate = rate self.status = "OPEN" self.stopLoss = stopLoss self.takeProfit = takeProfit self.total = total # API self.api = BotApi() orderSuccess = True if self.direction == "BUY": if not self.backTest and self.live: try: order = self.api.exchange.createLimitBuyOrder(shared.exchange['pair'], amount, rate) self.orderNumber = order['id'] except Exception as e: self.output.fail(str(e)) orderSuccess = False self.output.fail("Buy order failed") elif self.total < 0.00001: orderSuccess = False self.output.fail("Not enough funds to place Buy Order") if orderSuccess: shared.exchange['nbMarket'] -= self.total self.amount -= self.amount*self.fee self.output.info(str(time.ctime(date)) + " - Order "+str(self.orderNumber)+": Buy "+str(self.amount)+' '+shared.exchange['asset']+' ('+str(self.fee*100)+'% fees) at '+str(self.rate)+' for '+str(self.total)+' '+shared.exchange['market']+' - stopLoss: '+str(self.stopLoss)+' - takeProfit: '+str(self.takeProfit)) elif self.direction == "SELL": if not self.backTest and self.live: try: order = self.api.exchange.createLimitSellOrder(shared.exchange['pair'], amount, rate) self.orderNumber = order['id'] except Exception as e: print(e) orderSuccess = False self.output.warning("Sell order failed") elif self.total < 0.00001: orderSuccess = False self.output.fail("Not enough funds to place Sell Order") if orderSuccess: shared.exchange['nbAsset'] -= self.amount self.total -= self.total*self.fee self.output.info(str(time.ctime(date)) + " - Order "+str(self.orderNumber)+": Sell "+str(self.amount)+' '+shared.exchange['asset']+' at '+str(self.rate)+' for '+str(self.total)+' ('+str(self.fee*100)+'% fees) '+shared.exchange['market']+' - stopLoss: '+str(self.stopLoss)+' - takeProfit: '+str(self.takeProfit)) orderNb+=1 if not orderSuccess: self.status = 'FAILED' def __setitem__(self, key, value): setattr(self, key, value) def __getitem__(self, key): return getattr(self, key) def toDict(self): return { 'date': self.date, 'direction': self.direction, 'amount': self.amount, 'rate': self.rate, 'total': self.total, 'stopLoss': self.stopLoss, 'takeProfit': self.takeProfit, 'exitRate': self.exitRate, 'filledOn': self.filledOn, 'exitDate': self.exitDate, 'orderNumber': self.orderNumber } def tick(self, candlestick, date): if not self.backTest and self.live: date = float(time.time()) # TODO: implement not backtest pass if not self.filledOn: if (self.direction == 'BUY' and candlestick.high > self.rate) or (self.direction == 'SELL' and candlestick.low < self.rate): self.filledOn = date if self.direction == 'BUY': shared.exchange['nbAsset'] += self.amount elif self.direction == 'SELL': shared.exchange['nbMarket'] += self.total if not self.stopLoss and not self.takeProfit: self.status = 'CLOSED' else: if self.direction =='BUY': shared.exchange['coinsInOrder'] += self.amount elif self.direction =='SELL': shared.exchange['marketInOrder'] += self.total self.output.info(str(time.ctime(date)) + " - Order "+str(self.orderNumber)+" filled") if self.stopLoss and self.filledOn: # TODO: implement live if self.direction == 'BUY' and candlestick.low < self.stopLoss: self.total -= self.total*self.fee shared.exchange['nbAsset'] -= self.amount shared.exchange['coinsInOrder'] -= self.amount self.output.warning(str(time.ctime(date)) + " - Order "+str(self.orderNumber)+": Stop Loss - Sell "+str(self.amount)+' '+shared.exchange['asset']+' at '+str(self.stopLoss)+' for '+str(self.total)+' '+shared.exchange['market']+' ('+str(self.fee*100)+'% fees)') self.close(self.stopLoss, date) return elif self.direction == 'SELL' and candlestick.high > self.stopLoss: self.amount -= self.amount*self.fee shared.exchange['nbMarket'] -= self.total shared.exchange['marketInOrder'] -= self.total self.output.warning(str(time.ctime(date)) + " - Order "+str(self.orderNumber)+": Stop Loss - Buy "+str(self.amount)+' '+shared.exchange['asset']+' ('+str(self.fee*100)+'% fees) at '+str(self.stopLoss)+' for '+str(self.total)+' '+shared.exchange['market']) self.close(self.stopLoss, date) return if self.takeProfit and self.filledOn: if self.direction == 'BUY' and candlestick.high > self.takeProfit: self.total -= self.total*self.fee shared.exchange['nbAsset'] -= self.amount shared.exchange['coinsInOrder'] -= self.amount self.output.success(str(time.ctime(date)) + " - Order "+str(self.orderNumber)+": Take Profit - Sell "+str(self.amount)+' '+shared.exchange['asset']+' at '+str(self.stopLoss)+' for '+str(self.total)+' '+shared.exchange['market']+' ('+str(self.fee*100)+'% fees)') self.close(self.takeProfit, date) return elif self.direction == 'SELL' and candlestick.low < self.takeProfit: self.amount -= self.amount*self.fee shared.exchange['nbMarket'] -= self.total shared.exchange['marketInOrder'] -= self.total self.output.success(str(time.ctime(date)) + " - Order "+str(self.orderNumber)+": Take Profit - Buy "+str(self.amount)+' '+shared.exchange['asset']+' ('+str(self.fee*100)+'% fees) at '+str(self.stopLoss)+' for '+str(self.total)+' '+shared.exchange['market']) self.close(self.takeProfit, date) return def close(self, currentPrice, date=0.0): if not self.backTest and self.live: date = float(time.time()) # TODO: implement not backtest pass self.status = "CLOSED" self.exitRate = currentPrice self.exitDate = date if self.direction == 'BUY': shared.exchange['nbMarket'] += self.total elif self.direction == 'SELL': shared.exchange['nbAsset'] += self.amount self.output.info(str(time.ctime(date)) + " - Order "+str(self.orderNumber)+" Closed") self.showTrade() def showTrade(self): tradeStatus = "Order #"+str(self.orderNumber)+" - Entry Price: "+str(self.rate)+" Status: "+str(self.status)+" Exit Price: "+str(self.exitRate) if (self.status == "CLOSED"): if (self.direction == 'BUY' and self.exitRate > self.rate) or (self.direction == 'SELL' and self.exitRate < self.rate): tradeStatus = tradeStatus + " Profit: \033[92m" else: tradeStatus = tradeStatus + " Loss: \033[91m" if self.direction == 'BUY': tradeStatus = tradeStatus+str((self.exitRate - self.rate)/self.total)+str(shared.exchange['market'])+"\033[0m" else: tradeStatus = tradeStatus+str((self.rate - self.exitRate)/self.exitRate*self.amount)+str(shared.exchange['asset'])+"\033[0m" self.output.log(tradeStatus) def updateStop(self, newStop): oldStop = self.stopLoss self.stopLoss = float(newStop); self.output.log("Trade stop moved from "+str(oldStop)+" to "+str(newStop))