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))
class BotApi(object): def __init__(self): self.exchange = shared.exchange['name'] self.key = shared.api['key'] self.secret = shared.api['secret'] self.output = BotLog() if shared.exchange['name'] == 'poloniex': self.api = poloniex.Poloniex(self.key, self.secret) elif shared.exchange['name'] == 'kraken': self.api = krakenex.API(self.key, self.secret) def returnChartData(self, pair, period, start, end): data = [] if self.exchange == 'poloniex': if (period not in [300, 900, 1800, 7200, 14400, 86400]): self.output.fail( 'Poloniex requires periods in seconds: 300,900,1800,7200,14400, or 86400' ) sys.exit(2) poloData = self.api.returnChartData(pair, period=int(period), start=int(start), end=int(end)) #TODO: check for error for datum in poloData: if (datum['open'] and datum['close'] and datum['high'] and datum['low'] and datum['date'] and datum['volume'] and datum['weightedAverage']): data.append( BotCandlestick(period, float(datum['open']), float(datum['close']), float(datum['high']), float(datum['low']), float(datum['weightedAverage']), float(datum['volume']), float(datum['date']))) elif self.exchange == 'kraken': if (period not in [1, 5, 15, 30, 60, 240, 1440, 10080, 21600]): self.output.fail( 'Kraken requires periods in minutes: 1, 5, 15, 30, 60, 240, 1440, 10080, 21600' ) sys.exit(2) krakenData = self.api.query_public('OHLC', data={ 'pair': pair, 'since': start, 'interval': period }) # check for error if len(krakenData['error']) > 0: for e in krakenData['error']: self.output.fail(e) sys.exit() for datum in krakenData['result'][pair]: data.append( BotCandlestick(period, float(datum[1]), float(datum[4]), float(datum[2]), float(datum[3]), float(datum[5]), float(datum[0]))) else: self.output.fail("Not a valid exchange") sys.exit(2) return data def returnTicker(self, pair): ticker = {} if self.exchange == 'poloniex': ticker = self.api.returnTicker()[pair] #TODO: check for error elif self.exchange == 'kraken': data = self.api.query_public('Ticker', data={'pair': pair}) #TODO: check for error data = data['result'][pair] ticker = { 'last': float(data['c'][0]), 'lowestAsk': float(data['a'][0]), 'highestBid': float(data['b'][0]), 'high24hr': float(data['h'][1]), 'low24hr': float(data['l'][1]) } else: self.output.fail("Not a valid exchange") sys.exit(2) print(ticker) return ticker def returnOpenOrders(self, pair): orders = [] if self.exchange == 'poloniex': data = self.api.returnOpenOrders(pair) #TODO: check for error orders = data elif self.exchange == 'kraken': data = self.api.query_private('OpenOrders') #TODO: check for error orders = data['result'] #TODO: This is a bit tricky for now, need to figure out how to deal with pair names (XXDGXXBT is "the same" as XDGXBT for example) else: self.output.fail("Not a valid exchange") sys.exit(2) return orders def returnBalances(self): balances = {} if self.exchange == 'poloniex': try: data = self.api.returnBalances() balances = data except Exception as e: print(e) return False elif self.exchange == 'kraken': data = self.api.query_private('Balance') #TODO: check for error balances = data['result'] else: self.output.fail("Not a valid exchange") sys.exit(2) return balances def marketBuy(self, pair, rate, amout): order = {} if self.exchange == 'poloniex': try: #no market buy on poloniex, buying at 110% of the price "hacks" this data = self.api.buy(shared.exchange['pair'], rate='{0:.10f}'.format(rate * 1.1), amount=amount, orderType="fillOrKill") order = data except Exception as e: self.output.fail(e) return False elif self.exchange == 'kraken': data = self.api.query_private('AddOrder', data={ 'ordertype': 'market', 'pair': pair, 'type': 'buy', 'volume': str(amount) }) if len(data['error']) > 0: for e in data['error']: self.output.fail(e) return False order = {'orderNumber': data['result']['txid'][0]} else: self.output.fail("Not a valid exchange") sys.exit(2) return order def limitBuy(self, pair, rate, amount): order = {} if self.exchange == 'poloniex': try: data = self.api.buy(shared.exchange['pair'], rate='{0:.10f}'.format(rate), amount=amount) order = data except Exception as e: print(e) return False elif self.exchange == 'kraken': data = self.api.query_private('AddOrder', data={ 'ordertype': 'limit', 'pair': pair, 'type': 'buy', 'price': '{0:.10f}'.format(rate), 'volume': str(amount) }) print(data) if len(data['error']) > 0: for e in data['error']: self.output.fail(e) return False order = {'orderNumber': data['result']['txid'][0]} else: self.output.fail("Not a valid exchange") sys.exit(2) return order def marketSell(self, pair, rate, amount): order = {} if self.exchange == 'poloniex': try: #no market sell on poloniex, selling at 90% of the price "hacks" this data = self.api.sell(shared.exchange['pair'], rate='{0:.10f}'.format(rate * 0.9), amount=amount, orderType="fillOrKill") order = data except Exception as e: print(e) return False elif self.exchange == 'kraken': data = self.api.query_private('AddOrder', data={ 'ordertype': 'market', 'pair': pair, 'type': 'sell', 'volume': str(amount) }) if len(data['error']) > 0: for e in data['error']: self.output.fail(e) return False order = {'orderNumber': data['result']['txid'][0]} else: self.output.fail("Not a valid exchange") sys.exit(2) return order def limitSell(self, pair, rate, amount): order = {} if self.exchange == 'poloniex': try: data = self.api.sell(shared.exchange['pair'], rate='{0:.10f}'.format(rate), amount=amount) order = data except Exception as e: print(e) return False elif self.exchange == 'kraken': data = self.api.query_private('AddOrder', data={ 'ordertype': 'limit', 'pair': pair, 'type': 'sell', 'price': '{0:.10f}'.format(rate), 'volume': str(amount) }) if len(data['error']) > 0: for e in data['error']: self.output.fail(e) return False order = {'orderNumber': data['result']['txid'][0]} else: self.output.fail("Not a valid exchange") sys.exit(2) return order def cancelOrder(self, orderID): orderCanceled = True if self.exchange == 'poloniex': try: data = self.api.cancelOrder(orderID) if data['success'] == 0: return False except Exception as e: print(e) return False elif self.exchange == 'kraken': data = self.api.query_private('CancelOrder', data={'txid': orderID}) if len(data['error']) > 0: for e in data['error']: self.output.fail(e) return False else: self.output.fail("Not a valid exchange") sys.exit(2) return orderCanceled