class BittrexExchange(object): def __init__(self, config={}): self.api = Bittrex() self.balance = None def buy(self, buyOrder): r = self.api.market_buylimit(buyOrder["market"], buyOrder["qty"], buyOrder["price"]).getData() if r["success"]: buyOrder["id"] = r["result"]["uuid"] else: buyOrder["status"] = "error" return buyOrder def sell(self, sellOrder): r = self.api.market_selllimit(sellOrder["market"], sellOrder["qty"], sellOrder["price"]).getData() if r["success"]: sellOrder["id"] = r["result"]["uuid"] else: sellOrder["status"] = "error" print(r) return sellOrder def cancel(self, orderId): r = self.api.market_cancel(orderId).getData() return r def getOrderStatusDirect(self, orderId): r = self.api.account_get_order(orderId).getData() return r def getOrderStatus(self, orderId): resp = self.getOrderStatusDirect(orderId) # print(resp) status = "pending" if resp["result"]["Quantity"] != resp["result"]["QuantityRemaining"]: status = "partial" if not resp["result"]["IsOpen"]: status = "completed" if resp["result"]["CancelInitiated"]: status = "cancelled" return { "status": status, "remaining": resp["result"]["QuantityRemaining"], "commissionPaid": resp["result"]["CommissionPaid"], "opened": resp["result"]["Opened"], "closed": resp["result"]["Closed"] } def processOrder(self, order): order.setExchange(self.getName()) self.log.info("bittrex exchange processing order") if order.rate != order.MARKET: if order.order_type == order.SELL: r = self.api.market_selllimit(order.market, order.qty, order.rate).getData() elif order.order_type == order.BUY: r = self.api.market_buylimit(order.market, order.qty, order.rate).getData() if r["success"]: order.ref_id = r["result"]["uuid"] order.status = order.OPEN else: order.status = order.ERROR order.meta["api"] = {"create": r} res = order.save() self.log.info("save results {}".format(res)) return Result(r["success"], r["message"], r["result"]) else: return Result.fail("Market orders not allowed on bittrex") def syncOrder(self, order): if order.status < order.TERMINATED_STATE: status = order.status results = self.api.account_get_order(order.ref_id) data = results.getData() if data["success"]: res = data["result"] if res["CancelInitiated"]: order.status = order.CANCELLED elif not res["IsOpen"] and res["Type"] == "LIMIT_SELL": order.status = order.COMPLETED elif not res["IsOpen"] and res["Type"] == "LIMIT_BUY": order.status = order.FILLED elif res["IsOpen"] and not res["Quantity"] > res[ "QuantityRemaining"] and order.status != order.PARTIAL_FILL: order.status = order.PARTIAL_FILL if status != order.status or "state" not in order.meta["api"]: self.log.info("found updates to order {}".format( order.ref_id)) order.meta["api"]["state"] = data order.save() if order.status == order.COMPLETED: # and order.order_type == Order.SELL: self.log.info( "looking for associated order: {}".format( order.assoc_id)) assocorder = Order.findById(order.assoc_id) if assocorder.isOk(): aorder = assocorder.data["results"][0] aorder.status = Order.COMPLETED self.log.info("found associated order {}".format( aorder.ref_id)) #instead get this rate from the api results... aorder.meta["sold_at"] = data["result"][ 'PricePerUnit'] aorder.assoc_id = order.pkey #data["result"]['PricePerUnit'] res = aorder.save() self.log.info( "saved associated order {}".format(res)) return True def getBalance(self, currency): if self.balance is None: self.getBalances() currency = currency.upper() if currency in self.balance: return self.balance[currency]["Available"] def getBalances(self): results = self.api.account_get_balances().getData() if results["success"]: self.balance = {} for c in results["result"]: self.balance[c["Currency"]] = c return self.balance
class CoinWatch(object): def __init__(self, config={}): self.bittrex = Bittrex() self.mongo = MongoWrapper.getInstance().getClient() self.history = None self.pendingorders = None self.bal = None def setupWatch(self): res = self.mongo.crypto.drop_collection("watch") #res = self.mongo.crypto.create_collection("watch") #res = self.mongo.crypto.watch.create_index([("name",pymongo.ASCENDING)],unique=True) def updateWatch(self, watch ): if "_id" in watch: searchDoc = {"_id": ObjectId(watch["_id"])} del watch["_id"] else: searchDoc = {"name": watch.get("name"), "exchange": watch.get("exchange")} return self.mongo.crypto.watchlist.replace_one(searchDoc, watch , upsert=True) def update(self, watch): if "name" in watch: return self.mongo.crypto.watchlist.replace_one({'name':watch['name']},watch,upsert=True) def removeWatch(self, market,exchange): if market is not None: return self.mongo.crypto.watchlist.delete_one({'name':market, 'exchange': exchange}) def loadWatchList(self,search = {}): res = self.mongo.crypto.watchlist.find(search) allrows = list(res) watchlist = [] headers = [] for row in allrows: for key in row: if key not in headers: headers.append(key) for row in allrows: r = {} for head in headers: if head in row: r[head] = row[head] else: r[head] = None watchlist.append(r) return watchlist def refresh(self): self.history = self.bittrex.account_get_orderhistory().data["result"] self.bal = self.bittrex.account_get_balances().data["result"] self.pendingorders = self.bittrex.market_get_open_orders().data["result"] def tableize(self, rows, headers = None, margin = 2): if len(rows) == 0: return headers = [] mincol = {} for row in rows: for c in row: col = str(c) if col not in headers: headers.append(col) mincol[col] = len(col) for row in rows: for head in headers: if head in row: hl = len(str(row[head])) if hl > mincol[head]: mincol[head] = hl for head in headers: print("{}".format(head.ljust(mincol[head]+margin)), end="") print("") for row in rows: for head in headers: if head in row and row[head] is not None: col = str(row[head]) else: col = "" print("{}".format(col.ljust(mincol[head]+margin)), end="") print("") def xtableize(self, rows): hid = 0 hl = 0 for idx, row in enumerate(rows): if len(row) > hl: hl = len(row) hid = idx mincol = [] if len(rows) == 0: return for head in rows[hid]: mincol.append(len(head)) for row in rows: for idx,head in enumerate(row): col = str(row[head]) l = mincol[idx] mincol[idx] = max(len(col),l) for idx,head in enumerate(rows[hid]): print("{}".format(head.ljust(mincol[idx]+2)),end="") print("") for row in rows: for idx,head in enumerate(row): col = str(row[head]) print("{}".format(col.ljust(mincol[idx]+2)),end="") print("") def getPricePercentDif(self, price1, price2): price1 = float(price1) price2 = float(price2) return ((price1 - price2) * 100) / price1 def getPriceFromPercent(self, price, percent ): return (price * percent) + price def order_summary(self, currency, details=False): orders = [] for idx, order in enumerate(reversed(self.history)): if order["Exchange"].endswith("-{}".format(currency)): if "OrderUuid" in order: del order['OrderUuid'] if "ConditionTarget" in order: del order['ConditionTarget'] if "Commission" in order: del order['Commission'] if "IsConditional" in order: del order['IsConditional'] if "TimeStamp" in order: del order['TimeStamp'] if "ImmediateOrCancel" in order: del order['ImmediateOrCancel'] if "Condition" in order: del order['Condition'] orders.append(order) if details: return orders qty = 0 olist = [] for order in orders: q = order["Quantity"] - order["QuantityRemaining"] if order["OrderType"] == "LIMIT_BUY": olist.append({'market': order["Exchange"], 'qty': q, "price": order["PricePerUnit"]}) qty += q elif order["OrderType"] == "LIMIT_SELL": qty -= q for buy in olist: if buy['qty'] > q: buy['qty'] -= q q = 0 else: q = q - buy['qty'] buy['qty'] = 0 markets = {} for order in olist: if order['qty'] > 0: market = order["market"] if market not in markets: markets[market] = self.buildWatcher(order={ "market": market, "price": order["price"], "total": order["price"] * order["qty"], "qty": order["qty"], "orders": 1 }) else: markets[market]["price"] += order["price"] markets[market]["total"] += order["price"] * order["qty"] markets[market]["qty"] += order["qty"] markets[market]["orders"] += 1 for market in markets: tick = self.bittrex.public_get_ticker(market).data["result"] avgPrice = markets[market]["total"] / markets[market]["qty"] markets[market]['price'] = avgPrice # markets[market]['price'] /= markets[market]['orders'] markets[market]['last'] = "{:.08f}".format(tick['Last']) markets[market]['bid'] = "{:.08f}".format(tick['Bid']) markets[market]['ask'] = "{:.08f}".format(tick['Ask']) avgPrice = markets[market]["total"] / markets[market]["qty"] # markets[market]['avg'] = avgPrice markets[market]['dif'] = "{:.02f}".format(self.getPricePercentDif( tick["Last"], avgPrice)) markets[market]['total'] = markets[market]['qty'] * tick["Last"] markets[market]['exchange'] = "bittrex" return markets def buildWatcher(self, order): obj = { "market": "", "price": 0, "qty": 0, "orders": 0, "last": 0, "bid": 0, "ask": 0, "dif": 0, "total": 0, } obj.update(order) return obj def cancelOrder(self,orderId): mc = self.bittrex.market_cancel(orderId) return mc.data["success"] def parsePending(self): if self.pendingorders is None: self.refresh() out = [] for order in self.pendingorders: out.append({ "oid": order["OrderUuid"], "exchange": order["Exchange"], "type": order["OrderType"], "qty": order["Quantity"], "remaining": order["QuantityRemaining"], "Limit": "{:.08f}".format(order["Limit"]), "Openend": order["Opened"], "Closed": order["Closed"], #"Cancelled": order["ImmediateOrCancelled"] }) return out def parse(self, market=None): if self.bal is None: self.refresh() acc = [] rows = [] for account in self.bal: if account['Balance'] > 0: if account["Currency"] not in ["USDT", "BTC"] and \ (market is None or market in account["Currency"].lower()): summary = self.order_summary(account["Currency"], market is not None) rows.append(summary) acc.append(account) if market is not None: return rows[0] out = [] for row in rows: for market in row: m = row[market] out.append(m) return out
class BittrexManager(ExchangeManager): def __init__(self, config={}): ExchangeManager.__init__(self, "BTRX", config) self.api = Bittrex() self.balance = None self.log = logging.getLogger('crypto') def processOrder(self, order): order.setExchange(self.getName()) self.log.info("bittrex exchange processing order") if order.rate != order.MARKET: if order.order_type == order.SELL: r = self.api.market_selllimit(order.market, order.qty, order.rate).getData() elif order.order_type == order.BUY: r = self.api.market_buylimit(order.market, order.qty, order.rate).getData() if r["success"]: order.ref_id = r["result"]["uuid"] order.status = order.OPEN else: order.status = order.ERROR order.meta["api"] = {"create": r} res = order.save() self.log.info("save results {}".format(res)) return Result(r["success"], r["message"], r["result"]) else: return Result.fail("Market orders not allowed on bittrex") def syncOrder(self, order): if order.status < order.TERMINATED_STATE: status = order.status results = self.api.account_get_order(order.ref_id) data = results.getData() if data["success"]: res = data["result"] if res["CancelInitiated"]: order.status = order.CANCELLED elif not res["IsOpen"] and res["Type"] == "LIMIT_SELL": order.status = order.COMPLETED elif not res["IsOpen"] and res["Type"] == "LIMIT_BUY": order.status = order.FILLED elif res["IsOpen"] and not res["Quantity"] > res[ "QuantityRemaining"] and order.status != order.PARTIAL_FILL: order.status = order.PARTIAL_FILL if status != order.status or "state" not in order.meta["api"]: self.log.info("found updates to order {}".format( order.ref_id)) order.meta["api"]["state"] = data order.save() if order.status == order.COMPLETED: # and order.order_type == Order.SELL: self.log.info( "looking for associated order: {}".format( order.assoc_id)) assocorder = Order.findById(order.assoc_id) if assocorder.isOk(): aorder = assocorder.data["results"][0] aorder.status = Order.COMPLETED self.log.info("found associated order {}".format( aorder.ref_id)) #instead get this rate from the api results... aorder.meta["sold_at"] = data["result"][ 'PricePerUnit'] aorder.assoc_id = order.pkey #data["result"]['PricePerUnit'] res = aorder.save() self.log.info( "saved associated order {}".format(res)) return True def getBalance(self, currency): if self.balance is None: self.getBalances() currency = currency.upper() if currency in self.balance: return self.balance[currency]["Available"] def getBalances(self): results = self.api.account_get_balances().getData() if results["success"]: self.balance = {} for c in results["result"]: self.balance[c["Currency"]] = c return self.balance
class HoldingMonitor(object): def __init__(self): self.log = logging.getLogger('crypto') #a list of all active investments self.holdings = [] #a record of all active trades self.trades = [] self.budget = 0 #a list of indicators used validate trade positions self.indicators = ["macd", "bbands"] #in memory data store for each market's analysis self.datastore = {} self.exchange = Bittrex() #candlestick chart data self.cs = None self.framesize = "5m" self.timeframe = "24h" self.settings = {"limit": 0.02, "profit": 0.03, "trailing_stop": 0.01} def setMarket(self, market): self.process(market) def setIndicators(self, indicators): self.indicators = indicators def setTimeframe(self, timeframe, framesize): self.timeframe = timeframe self.framesize = framesize def process(self, currency): if not currency in self.datastore: self.datastore[currency] = {} self.datastore[currency] = self.checkMarketStatus(currency) return self.datastore[currency] def checkMarkets(self, marketlist=None): if marketlist is None: marketlist = self.holdings for currency in marketlist: if not currency in self.datastore: self.datastore[currency] = {} self.datastore[currency] = self.checkMarketStatus(currency) return self.datastore def analyzeIndicators(self, currency, bot): analysis = self.datastore[currency] market = CoinCalc.getInstance().get_market(currency) idata = [] self.datastore[currency]["indicatordata"] = {} for indicator in analysis: if "analysis" in analysis[indicator]: self.log.info(analysis[indicator]) res = analysis[indicator]["analysis"] idata += [res] self.datastore[currency]["indicatordata"][ res["name"]] = analysis[indicator] botres = bot(self) action = botres["signal"] cur_price = botres["cur_price"] limitorder = botres["limitorder"] return { "market": market, "currency": currency, "cs_time": self.cs["time"][-1], "signal": action, "limitorder": limitorder, "indicators": idata } def checkMarketStatus(self, currency, data=None): if data is None: data = self.datastore[currency] market = CoinCalc.getInstance().get_market(currency) if market: traderTA = Trader(market) self.cs = traderTA.get_candlesticks(self.timeframe, self.framesize) ath = 0 for v in self.cs["high"]: if v > ath: ath = v atl = 9999999 for v in self.cs["low"]: if v < atl: atl = v ret = { "price": { "count": 0, "last": self.cs["closed"][-1], "open": self.cs["open"][-1], "HIGH": ath, "LOW": atl, "time": self.cs["time"][-1], } } for indicator in self.indicators: if indicator == "macd": macd = MACD(self.cs) ret["macd"] = macd.get_analysis(data) elif indicator == "bbands": bb = BBands(self.cs, timeperiod=20, nbdevup=2, nbdevdn=2) ret["bbands"] = bb.get_analysis(data) return ret def buyCurrency(self, indicatordata): #self.log.info(indicatordata) currency = indicatordata["currency"] indicator_time = indicatordata["cs_time"] bid_price = indicatordata["limitorder"] amount = 0.1 return self.xbuyCurrency(currency, indicator_time, bid_price, amount) def sellCurrency(self, indicatordata): return {} def xbuyCurrency(self, currency, indicator_time, bid_price, amount): for trade in self.trades: if trade["time"] == indicator_time and trade[ "currency"] == currency: return None market = CoinCalc.getInstance().get_market(currency) #TODO: grab this value dynamically trade_percent = 0.0025 trade_cost = amount * bid_price * trade_percent break_even_price = trade_cost * 2 + bid_price # for testing purposes... buy_price = bid_price limit = buy_price - (buy_price * self.settings["limit"]) exit = buy_price + (buy_price * self.settings["profit"]) support = 0 resistance = 0 trailing_limit = 0 buyMap = { "time": indicator_time, "currency": currency, "market": market, "desired_entry": bid_price, "real_entry": buy_price, "amount": amount, "trade_cost": trade_cost, "break_even_price": break_even_price, "desired_exit": exit, "real_exit": 0, "limit": limit, "trailing_limit": trailing_limit, "total_profit": 0 } self.trades += [buyMap] return self.trades def analyzeTrade(self, trade): #buy_price = bid_price #limit = buy_price - (buy_price * self.settings["limit"] ) #exit = buy_price + ( buy_price * self.settings["profit"] ) cur_price = self.cs["closed"][-1] growth = ((cur_price - trade["real_entry"]) / cur_price) * 100 trade["action"] = "hodl" trade["growth"] = round(growth, 2) trade["profit"] = (cur_price * trade["amount"]) - ( trade["real_entry"] * trade["amount"]) #trade[""] = 0 return trade # depends on a call to self.getMarkets() to have been made to get last price data def analyzeTrades(self): tradedata = [] for trade in self.trades: tradedata += [self.analyzeTrade(trade)] return tradedata def getHoldings(self): holdings = {} calc = CoinCalc.getInstance() bal = self.exchange.account_get_balances().getData() if bal and bal["success"] == True: for currency in bal["result"]: if currency["Available"] > 0: holdings[currency["Currency"]] = { "Market": calc.get_market(currency["Currency"]), "Balance": currency["Balance"], "Available": currency["Available"], "Pending": currency["Pending"] } self.holdings = holdings return self.holdings