def refresh_iexc_index(): try: stockSymbols = get_symbols(token=os.environ["IEXC_KEY"]) for index, stock in stockSymbols.iterrows(): if stock["exchange"] not in TickerParserServer.exchanges: TickerParserServer.exchanges[stock["exchange"]] = Exchange(stock["exchange"]) tickerId = stock["symbol"] if stock["iexId"] is None else stock["iexId"] TickerParserServer.iexcStocksIndex[stock["symbol"]] = {"id": tickerId, "name": stock["name"], "base": stock["symbol"], "quote": stock["currency"], "exchange": stock["exchange"]} TickerParserServer.exchanges[stock["exchange"]].properties.symbols.append(stock["symbol"]) forexSymbols = requests.get("https://cloud.iexapis.com/stable/ref-data/fx/symbols?token={}".format(os.environ["IEXC_KEY"])).json() derivedCurrencies = set() for pair in forexSymbols["pairs"]: derivedCurrencies.add(pair["fromCurrency"]) derivedCurrencies.add(pair["toCurrency"]) TickerParserServer.iexcForexIndex[pair["symbol"]] = {"id": pair["symbol"], "name": pair["symbol"], "base": pair["fromCurrency"], "quote": pair["toCurrency"], "reversed": False} TickerParserServer.iexcForexIndex[pair["toCurrency"] + pair["fromCurrency"]] = {"id": pair["symbol"], "name": pair["toCurrency"] + pair["fromCurrency"], "base": pair["toCurrency"], "quote": pair["fromCurrency"], "reversed": True} for fromCurrency in derivedCurrencies: for toCurrency in derivedCurrencies: symbol = fromCurrency + toCurrency if fromCurrency != toCurrency and symbol not in TickerParserServer.iexcForexIndex: TickerParserServer.iexcForexIndex[symbol] = {"id": symbol, "name": symbol, "base": fromCurrency, "quote": toCurrency, "reversed": False} otcSymbols = requests.get("https://cloud.iexapis.com/stable/ref-data/otc/symbols?token={}".format(os.environ["IEXC_KEY"])).json() for stock in otcSymbols: if stock["exchange"] not in TickerParserServer.exchanges: TickerParserServer.exchanges[stock["exchange"]] = Exchange(stock["exchange"]) tickerId = stock["symbol"] if stock["iexId"] is None else stock["iexId"] TickerParserServer.iexcOtcIndex[stock["symbol"]] = {"id": tickerId, "name": stock["name"], "base": stock["symbol"], "quote": stock["currency"], "exchange": stock["exchange"]} except Exception: print(traceback.format_exc())
def request_ccxt_depth(self, request): ticker = request.get_ticker() exchange = request.get_exchange() imageStyle = request.get_image_style() forceMode = "force" in imageStyle and request.authorId == 361916376069439490 uploadMode = "upload" in imageStyle and request.authorId == 361916376069439490 try: if exchange is None: return None, None exchange = Exchange(exchange.id, "crypto") try: depthData = exchange.properties.fetch_order_book(ticker.symbol) bestBid = depthData["bids"][0] bestAsk = depthData["asks"][0] lastPrice = (bestBid[0] + bestAsk[0]) / 2 except: return None, None imageData = self.generate_depth_image(depthData, bestBid, bestAsk, lastPrice) if uploadMode: bucket.blob("uploads/{}.png".format(int( time.time() * 1000))).upload_from_string( base64.decodebytes(imageData)) return imageData, None except Exception: print(traceback.format_exc()) if os.environ["PRODUCTION_MODE"]: self.logging.report_exception(user=ticker.id) return None, None
def _request_forex(cls, request, ticker): exchange = Exchange.from_dict(ticker.get("exchange")) try: if exchange is not None: return [{}, ""] rawData = get( "https://cloud.iexapis.com/stable/fx/latest?symbols={}&token={}" .format(ticker.get("id"), environ["IEXC_KEY"])).json() if rawData is None or type(rawData) is not list or len( rawData) == 0: return [{}, ""] except: return [{}, ""] price = rawData[0]["rate"] if price is None: return [{}, ""] if ticker.get("isReversed"): price = 1 / price payload = { "quotePrice": "{:,.5f} {}".format(price, ticker.get("quote")), "title": ticker.get("name"), "thumbnailUrl": static_storage.icon, "messageColor": "deep purple", "sourceText": "Data provided by IEX Cloud", "platform": "IEXC", "raw": { "quotePrice": [price], "timestamp": time() } } return [payload, ""]
def request_candles(cls, request): ticker = request.get("ticker") exchange = Exchange.from_dict(ticker.get("exchange")) if exchange is None: return [{}, ""] try: rawData = exchange.properties.fetch_ohlcv(ticker.get("symbol"), timeframe="1m", limit=3) if len(rawData ) == 0 or rawData[-1][4] is None or rawData[0][1] is None: return [{}, ""] except: return [{}, ""] payload = { "candles": [], "title": ticker.get("name"), "sourceText": "Data from {}".format(exchange.name), "platform": "CCXT" } for e in rawData: timestamp = e[0] / 1000 if ticker.get("isReversed"): payload["candles"].append( [timestamp, 1 / e[1], 1 / e[2], 1 / e[3], 1 / e[4]]) else: payload["candles"].append([timestamp, e[1], e[2], e[3], e[4]]) return [payload, ""]
def _request_depth(cls, request, ticker): exchange = Exchange.from_dict(ticker.get("exchange")) preferences = request.get("preferences") forceMode = {"id": "force", "value": "force"} in preferences uploadMode = {"id": "upload", "value": "upload"} in preferences if exchange is None: return [{}, ""] try: depthData = exchange.properties.fetch_order_book(ticker.get("symbol")) bestBid = depthData["bids"][0] bestAsk = depthData["asks"][0] lastPrice = (bestBid[0] + bestAsk[0]) / 2 except: return [{}, ""] imageBuffer = BytesIO() chartImage = Image.new("RGBA", (1600, 1200)) chartImage.paste(CCXT._generate_depth_image(depthData, bestBid, bestAsk, lastPrice)) chartImage = Image.alpha_composite(chartImage, CCXT.chartOverlay["normal"]) chartImage.save(imageBuffer, format="png") imageData = b64encode(imageBuffer.getvalue()) imageBuffer.close() # if uploadMode: # bucket.blob("uploads/{}.png".format(int(time() * 1000))).upload_from_string(decodebytes(imageData)) payload = { "data": imageData.decode(), "platform": "CCXT" } return [payload, ""]
def request_ccxt_candles(self, request): ticker = request.get_ticker() exchange = request.get_exchange() try: if exchange is None: return None, None exchange = Exchange(exchange.id, "crypto") try: rawData = exchange.properties.fetch_ohlcv(ticker.symbol, timeframe="1m", limit=3) if len(rawData) == 0 or rawData[-1][4] is None or rawData[0][1] is None: return None, None except: return None, None payload = { "candles": [], "title": ticker.name, "baseTicker": "USD" if ticker.base in CandleProcessor.stableCoinTickers else ticker.base, "quoteTicker": "USD" if ticker.quote in CandleProcessor.stableCoinTickers else ticker.quote, "sourceText": "on {}".format(exchange.name), "platform": "CCXT" } for e in rawData: timestamp = e[0] / 1000 if ticker.isReversed: payload["candles"].append([timestamp, 1 / e[1], 1 / e[2], 1 / e[3], 1 / e[4]]) else: payload["candles"].append([timestamp, e[1], e[2], e[3], e[4]]) return payload, None except Exception: print(traceback.format_exc()) if os.environ["PRODUCTION_MODE"]: self.logging.report_exception(user=ticker.id) return None, None
def refresh_ccxt_index(self): difference = set(ccxt.exchanges).symmetric_difference(supported.ccxtExchanges) newExchanges = [] newSupportedExchanges = [] unsupportedCryptoExchanges = [] for e in difference: try: ex = getattr(ccxt, e)() except: unsupportedCryptoExchanges.append(e) continue if e not in supported.ccxtExchanges: if ex.has['fetchOHLCV'] != False and ex.has['fetchOrderBook'] != False and ex.timeframes is not None and len(ex.timeframes) != 0: newSupportedExchanges.append(e) else: newExchanges.append(e) if len(newSupportedExchanges) != 0: print("New supported CCXT exchanges: {}".format(newSupportedExchanges)) if len(newExchanges) != 0: print("New partially unsupported CCXT exchanges: {}".format(newExchanges)) if len(unsupportedCryptoExchanges) != 0: print("New deprecated CCXT exchanges: {}".format(unsupportedCryptoExchanges)) completedTasks = set() sortedIndexReference = {} for platform in supported.cryptoExchanges: if platform not in sortedIndexReference: sortedIndexReference[platform] = {} for exchange in supported.cryptoExchanges[platform]: if exchange not in completedTasks: if exchange not in self.exchanges: self.exchanges[exchange] = Exchange(exchange, "crypto" if exchange in ccxt.exchanges else "traditional") try: self.exchanges[exchange].properties.load_markets() except: continue completedTasks.add(exchange) for symbol in self.exchanges[exchange].properties.symbols: if '.' not in symbol and (self.exchanges[exchange].properties.markets[symbol].get("active") is None or self.exchanges[exchange].properties.markets[symbol].get("active")): base = self.exchanges[exchange].properties.markets[symbol]["base"] quote = self.exchanges[exchange].properties.markets[symbol]["quote"] marketPair = symbol.split("/") if base != marketPair[0] or quote != marketPair[-1]: if marketPair[0] != marketPair[-1]: base, quote = marketPair[0], marketPair[-1] else: continue isIdentifiable = quote in self.coinGeckoIndex and self.coinGeckoIndex[quote]["market_cap_rank"] is not None if base not in sortedIndexReference[platform]: sortedIndexReference[platform][base] = {} if quote not in sortedIndexReference[platform][base]: if isIdentifiable: sortedIndexReference[platform][base][quote] = self.coinGeckoIndex[quote]["market_cap_rank"] else: sortedIndexReference[platform][base][quote] = MAXSIZE for platform in sortedIndexReference: self.ccxtIndex[platform] = {} for base in sortedIndexReference[platform]: if base not in self.ccxtIndex[platform]: self.ccxtIndex[platform][base] = [] self.ccxtIndex[platform][base] = sorted(sortedIndexReference[platform][base].keys(), key=lambda quote: sortedIndexReference[platform][base][quote]) try: self.ccxtIndex[platform][base].insert(0, self.ccxtIndex[platform][base].pop(self.ccxtIndex[platform][base].index("USDT"))) except: pass try: self.ccxtIndex[platform][base].insert(0, self.ccxtIndex[platform][base].pop(self.ccxtIndex[platform][base].index("USD"))) except: pass
def request_ccxt_quote(self, request): ticker = request.get_ticker() exchange = request.get_exchange() try: if exchange is None: return None, None exchange = Exchange(exchange.id) tf, limitTimestamp, candleOffset = Utils.get_highest_supported_timeframe(exchange.properties, datetime.datetime.now().astimezone(pytz.utc)) try: rawData = exchange.properties.fetch_ohlcv(ticker.symbol, timeframe=tf.lower(), since=limitTimestamp, limit=300) if len(rawData) == 0 or rawData[-1][4] is None or rawData[0][1] is None: return None, None except: print(traceback.format_exc()) return None, None price = [rawData[-1][4], rawData[0][1]] if len(rawData) < candleOffset else [rawData[-1][4], rawData[-candleOffset][1]] if ticker.isReversed: price = [1 / price[0], 1 / price[1]] volume = None if price[0] is None else sum([candle[5] for candle in rawData if int(candle[0] / 1000) >= int(exchange.properties.milliseconds() / 1000) - 86400]) / (price[0] if exchange.id in ["bitmex", "binancefutures"] else 1) priceChange = 0 if tf == "1m" or price[1] == 0 else (price[0] / price[1]) * 100 - 100 payload = { "quotePrice": "{:,.8f}".format(price[0]) if ticker.isReversed else Utils.format_price(exchange.properties, ticker.symbol, price[0]), "quoteVolume": volume, "quoteConvertedPrice": "≈ ${:,.6f}".format(price[0] * self.lastBitcoinQuote["quotePrice"][0]) if ticker.quote == "BTC" else None, "quoteConvertedVolume": "≈ ${:,.4f}".format(volume * self.lastBitcoinQuote["quotePrice"][0]) if ticker.quote == "BTC" else None, "title": ticker.name, "baseTicker": "USD" if ticker.base in QuoteProcessor.stableCoinTickers else ticker.base, "quoteTicker": "USD" if ticker.quote in QuoteProcessor.stableCoinTickers else ticker.quote, "change": priceChange, "thumbnailUrl": TickerParser.get_coingecko_image(ticker.base), "messageColor": "amber" if priceChange == 0 else ("green" if priceChange > 0 else "red"), "sourceText": "on {}".format(exchange.name), "platform": "CCXT", "raw": { "quotePrice": [price[0]] if tf == "1m" else price[:1], "quoteVolume": volume, "ticker": ticker, "exchange": exchange, "timestamp": time.time() } } return payload, None except Exception: print(traceback.format_exc()) if os.environ["PRODUCTION_MODE"]: self.logging.report_exception() return None, None
def _request_depth(cls, request, ticker): exchange = Exchange.from_dict(ticker.get("exchange")) preferences = request.get("preferences") forceMode = {"id": "force", "value": "force"} in preferences uploadMode = {"id": "upload", "value": "upload"} in preferences try: stock = Stock(ticker.get("symbol"), token=environ["IEXC_KEY"]) depthData = stock.get_book()[ticker.get("symbol")] rawData = stock.get_quote().loc[ticker.get("symbol")] if ticker.get("quote") is None and exchange is not None: return [ {}, "Orderbook visualization for `{}` is not available on {}.". format(ticker.get("name"), exchange.get("name")) ] lastPrice = (depthData["bids"][0]["price"] + depthData["asks"][0]["price"]) / 2 depthData = { "bids": [[e.get("price"), e.get("size")] for e in depthData["bids"] if e.get("price") >= lastPrice * 0.75], "asks": [[e.get("price"), e.get("size")] for e in depthData["asks"] if e.get("price") <= lastPrice * 1.25] } bestBid = depthData["bids"][0] bestAsk = depthData["asks"][0] except: return [{}, ""] imageBuffer = BytesIO() chartImage = Image.new("RGBA", (1600, 1200)) chartImage.paste( IEXC._generate_depth_image(depthData, bestBid, bestAsk, lastPrice)) chartImage = Image.alpha_composite(chartImage, IEXC.chartOverlay["normal"]) chartImage.save(imageBuffer, format="png") imageData = b64encode(imageBuffer.getvalue()) imageBuffer.close() # if uploadMode: # bucket.blob("uploads/{}.png".format(int(time() * 1000))).upload_from_string(decodebytes(imageData)) payload = {"data": imageData.decode(), "platform": "IEXC"} return [payload, ""]
def request_candles(cls, request): ticker = request.get("ticker") exchange = Exchange.from_dict(ticker.get("exchange")) try: stock = Stock(ticker.get("id"), token=environ["IEXC_KEY"]) rawData = stock.get_intraday_prices(chartLast=3) if len(rawData) == 0: return [{}, ""] if ticker.get("quote") is None and exchange is not None: return [{}, "Price for `{}` is not available on {}.".format(ticker.get("name"), exchange.get("name"))] except: return [{}, ""] payload = { "candles": [], "title": ticker.get("name"), "sourceText": "Data provided by IEX Cloud", "platform": "IEXC" } for index, e in rawData.iterrows(): parsedDatetime = None try: parsedDatetime = datetime.strptime(index, "%Y-%m-%d %I:%M %p") except: pass try: parsedDatetime = datetime.strptime(index, "%Y-%m-%d %I %p") except: pass try: parsedDatetime = datetime.strptime(index, "%Y-%m-%d None") except: pass if parsedDatetime is None: raise Exception("timestamp formatting mismatch: {}".format(index)) timestamp = timezone('US/Eastern').localize(parsedDatetime, is_dst=None).timestamp() if ticker.get("isReversed"): if "marketClose" in e: payload["candles"].append([timestamp, 1 / e.marketOpen, 1 / e.marketHigh, 1 / e.marketLow, 1 / e.marketClose]) else: payload["candles"].append([timestamp, 1 / e.open, 1 / e.high, 1 / e.low, 1 / e.close]) else: if "marketClose" in e: payload["candles"].append([timestamp, e.marketOpen, e.marketHigh, e.marketLow, e.marketClose]) else: payload["candles"].append([timestamp, e.open, e.high, e.low, e.close]) return [payload, ""]
def _request_quote(cls, request, ticker): exchange = Exchange.from_dict(ticker.get("exchange")) if exchange is None: return [{}, ""] tf, limitTimestamp, candleOffset = CCXT.get_highest_supported_timeframe(exchange.properties, datetime.now().astimezone(utc)) try: rawData = exchange.properties.fetch_ohlcv(ticker.get("symbol"), timeframe=tf.lower(), since=limitTimestamp, limit=300) if len(rawData) == 0 or rawData[-1][4] is None or rawData[0][1] is None: return [{}, ""] except: return [{}, ""] price = [rawData[-1][4], rawData[0][1]] if len(rawData) < candleOffset else [rawData[-1][4], rawData[-candleOffset][1]] if ticker.get("isReversed"): price = [1 / price[0], 1 / price[1]] volume = None if price[0] is None else sum([candle[5] for candle in rawData if int(candle[0] / 1000) >= int(exchange.properties.milliseconds() / 1000) - 86400]) / (price[0] if exchange.id == "bitmex" else 1) priceChange = 0 if tf == "1m" or price[1] == 0 else (price[0] / price[1]) * 100 - 100 coinThumbnail = static_storage.icon if ticker.get("image") is None else ticker.get("image") base = "USD" if ticker.get("base") in AbstractProvider.stableCoinTickers else ticker.get("base") quote = "USD" if ticker.get("quote") in AbstractProvider.stableCoinTickers else ticker.get("quote") payload = { "quotePrice": "{:,.10f}".format(price[0]).rstrip('0').rstrip('.') + " " + quote, "quoteVolume": "{:,.4f}".format(volume).rstrip('0').rstrip('.') + " " + base, "title": ticker.get("name"), "change": "{:+.2f} %".format(priceChange), "thumbnailUrl": coinThumbnail, "messageColor": "amber" if priceChange == 0 else ("green" if priceChange > 0 else "red"), "sourceText": "Data from {}".format(exchange.name), "platform": CCXT.name, "raw": { "quotePrice": [price[0]] if tf == "1m" else price[:1], "quoteVolume": [volume], "timestamp": time() } } if ticker.get("quote") == "BTC": payload["quoteConvertedPrice"] = "≈ ${:,.6f}".format(price[0] * CoinGecko.lastBitcoinQuote) payload["quoteConvertedVolume"] = "≈ ${:,.4f}".format(volume * CoinGecko.lastBitcoinQuote) return [payload, ""]
def request_ccxt_depth(self, request): ticker = request.get_ticker() exchange = request.get_exchange() try: if exchange is None: return None, None exchange = Exchange(exchange.id) try: depthData = exchange.properties.fetch_order_book(ticker.symbol) bestBid = depthData["bids"][0] bestAsk = depthData["asks"][0] lastPrice = (bestBid[0] + bestAsk[0]) / 2 except: return None, None imageData = self.generate_depth_image(depthData, bestBid, bestAsk, lastPrice) return imageData, None except Exception: print(traceback.format_exc()) if os.environ["PRODUCTION_MODE"]: self.logging.report_exception() return None, None
def request_lld(cls, request): ticker = request.get("ticker") exchange = Exchange.from_dict(ticker.get("exchange")) preferences = request.get("preferences") action = [e.get("value") for e in preferences if e.get("id") == "lld"] if len(action) == 0: return [{}, ""] action = action[0] if action == "funding": if exchange.id in ["bitmex"]: try: rawData = exchange.properties.public_get_instrument({"symbol": ticker.get("id")})[0] except: return [{}, "Requested funding data for `{}` is not available.".format(ticker.get("name"))] if rawData["fundingTimestamp"] is not None: fundingDate = datetime.strptime(rawData["fundingTimestamp"], "%Y-%m-%dT%H:%M:00.000Z").replace(tzinfo=utc) else: fundingDate = datetime.now().replace(tzinfo=utc) indicativeFundingTimestamp = datetime.timestamp(fundingDate) + 28800 indicativeFundingDate = datetime.utcfromtimestamp(indicativeFundingTimestamp).replace(tzinfo=utc) deltaFunding = fundingDate - datetime.now().astimezone(utc) deltaIndicative = indicativeFundingDate - datetime.now().astimezone(utc) hours1, seconds1 = divmod(deltaFunding.days * 86400 + deltaFunding.seconds, 3600) minutes1 = int(seconds1 / 60) hoursFunding = "{:d} {} ".format(hours1, "hours" if hours1 > 1 else "hour") if hours1 > 0 else "" minutesFunding = "{:d} {}".format(minutes1 if hours1 > 0 or minutes1 > 0 else seconds1, "{}".format("minute" if minutes1 == 1 else "minutes") if hours1 > 0 or minutes1 > 0 else ("second" if seconds1 == 1 else "seconds")) deltaFundingText = "{}{}".format(hoursFunding, minutesFunding) hours2, seconds2 = divmod(deltaIndicative.days * 86400 + deltaIndicative.seconds, 3600) minutes2 = int(seconds2 / 60) hoursIndicative = "{:d} {} ".format(hours2, "hours" if hours2 > 1 else "hour") if hours2 > 0 else "" minutesIndicative = "{:d} {}".format(minutes2 if hours2 > 0 or minutes2 > 0 else seconds2, "{}".format("minute" if minutes2 == 1 else "minutes") if hours2 > 0 or minutes2 > 0 else ("second" if seconds2 == 1 else "seconds")) deltaIndicativeText = "{}{}".format(hoursIndicative, minutesIndicative) fundingRate = float(rawData["fundingRate"]) * 100 predictedFundingRate = float(rawData["indicativeFundingRate"]) * 100 averageFundingRate = (fundingRate + predictedFundingRate) / 2 coinThumbnail = static_storage.icon if ticker.get("image") is None else ticker.get("image") payload = { "quotePrice": "Funding Rate: {:+.4f} %".format(fundingRate), "quoteConvertedPrice": "Predicted Rate: {:+.4f} % *(in {})*".format(predictedFundingRate, deltaIndicativeText), "title": ticker.get("name"), "change": "in {}".format(deltaFundingText), "thumbnailUrl": coinThumbnail, "messageColor": "yellow" if averageFundingRate == 0.01 else ("light green" if averageFundingRate < 0.01 else "deep orange"), "sourceText": "Funding on {}".format(exchange.name), "platform": CCXT.name, "raw": { "quotePrice": [fundingRate, predictedFundingRate], "timestamp": time() } } return [payload, ""] return [{}, "Funding data is only available on BitMEX."] elif action == "oi": if exchange.id in ["bitmex"]: try: rawData = exchange.properties.public_get_instrument({"symbol": ticker.get("id")})[0] except: return [{}, "Requested open interest data for `{}` is not available.".format(ticker.get("name"))] coinThumbnail = static_storage.icon if ticker.get("image") is None else ticker.get("image") payload = { "quotePrice": "Open interest: {:,.0f} contracts".format(float(rawData["openInterest"])), "quoteConvertedPrice": "Open value: {:,.4f} XBT".format(float(rawData["openValue"]) / 100000000), "title": ticker.get("name"), "thumbnailUrl": coinThumbnail, "messageColor": "deep purple", "sourceText": "Open interest on {}".format(exchange.name), "platform": CCXT.name, "raw": { "quotePrice": [float(rawData["openInterest"]), float(rawData["openValue"]) / 100000000], "timestamp": time() } } return [payload, ""] return [{}, "Open interest and open value data is only available on BitMEX."] elif action == "ls": if exchange.id in ["bitfinex2"]: try: longs = exchange.properties.publicGetStats1KeySizeSymbolLongLast({"key": "pos.size", "size": "1m", "symbol": "t{}".format(ticker.get("id")), "side": "long", "section": "last"}) shorts = exchange.properties.publicGetStats1KeySizeSymbolShortLast({"key": "pos.size", "size": "1m", "symbol": "t{}".format(ticker.get("id")), "side": "long", "section": "last"}) ratio = longs[1] / (longs[1] + shorts[1]) * 100 except: return [{}, ""] coinThumbnail = static_storage.icon if ticker.get("image") is None else ticker.get("image") payload = { "quotePrice": "{:.1f} % longs / {:.1f} % shorts".format(ratio, 100 - ratio), "title": ticker.get("name"), "change": "in {}".format(deltaFundingText), "thumbnailUrl": coinThumbnail, "messageColor": "deep purple", "sourceText": "Longs/shorts on {}".format(exchange.name), "platform": CCXT.name, "raw": { "quotePrice": [longs[1], shorts[1]], "timestamp": time() } } return [payload, ""] return [{}, "Longs and shorts data is only available on Bitfinex."] elif action == "dom": try: rawData = CoinGecko.connection.get_global() except: return [{}, "Requested dominance data for `{}` is not available.".format(ticker.get("name"))] if ticker.get("base").lower() not in rawData["market_cap_percentage"]: return [{}, "Dominance for {} does not exist.".format(ticker.get("base"))] coinDominance = rawData["market_cap_percentage"][ticker.get("base").lower()] coinThumbnail = static_storage.icon if ticker.get("image") is None else ticker.get("image") payload = { "quotePrice": "{} dominance: {:,.2f} %".format(ticker.get("base"), coinDominance), "title": "Market Dominance", "thumbnailUrl": coinThumbnail, "messageColor": "deep purple", "sourceText": "Market information from CoinGecko", "platform": CCXT.name, "raw": { "quotePrice": [coinDominance], "timestamp": time() } } return [payload, ""] else: return [{}, ""]
def _request_stocks(cls, request, ticker): exchange = Exchange.from_dict(ticker.get("exchange")) try: stock = Stock(ticker.get("symbol"), token=environ["IEXC_KEY"]) rawData = stock.get_quote().loc[ticker.get("symbol")] if ticker.get("quote") is None and exchange is not None: return [{}, "Price for `{}` is not available on {}.".format( ticker.get("name"), exchange.get("name"))] if rawData is None or (rawData["latestPrice"] is None and rawData["delayedPrice"] is None): return [{}, ""] except: return [{}, ""] try: coinThumbnail = stock.get_logo().loc[ticker.get("symbol")]["url"] except: coinThumbnail = static_storage.icon latestPrice = rawData["delayedPrice"] if rawData[ "latestPrice"] is None else rawData["latestPrice"] price = float( latestPrice if "isUSMarketOpen" not in rawData or rawData["isUSMarketOpen"] or "extendedPrice" not in rawData or rawData["extendedPrice"] is None else rawData["extendedPrice"]) if ticker.get("isReversed"): price = 1 / price priceChange = ( (1 / float(rawData["change"]) if ticker.get("isReversed") and float(rawData["change"]) != 0 else float(rawData["change"])) / price * 100 ) if "change" in rawData and rawData["change"] is not None else 0 payload = { "quotePrice": "{:,.10f}".format(price).rstrip('0').rstrip('.') + ("" if ticker.get("isReversed") else (" USD" if ticker.get("quote") is None else (" " + ticker.get("quote")))), "title": ticker.get("name"), "change": "{:+.2f} %".format(priceChange), "thumbnailUrl": coinThumbnail, "messageColor": "amber" if priceChange == 0 else ("green" if priceChange > 0 else "red"), "sourceText": "Data provided by IEX Cloud", "platform": "IEXC", "raw": { "quotePrice": [price], "timestamp": time() } } if "latestVolume" in rawData: volume = float(rawData["latestVolume"]) payload["quoteVolume"] = "{:,.4f}".format(volume).rstrip( '0').rstrip('.') + " " + ticker.get("base") payload["raw"]["quoteVolume"] = [volume] return [payload, ""]
def refresh_iexc_index(self): try: def get_url(url): while True: try: return get(url).json() except: print(format_exc()) sleep(10) iexcExchanges = set() exchanges = get_url("https://cloud.iexapis.com/stable/ref-data/market/us/exchanges?token={}".format(environ["IEXC_KEY"])) suffixMap = {} for exchange in exchanges: if exchange["refId"] == "": continue exchangeId = exchange["refId"] iexcExchanges.add(exchangeId.lower()) self.exchanges[exchangeId.lower()] = Exchange(exchangeId, "traditional", exchange["longName"], region="us") exchanges = get_url("https://cloud.iexapis.com/stable/ref-data/exchanges?token={}".format(environ["IEXC_KEY"])) for exchange in exchanges: exchangeId = exchange["exchange"].replace("Euronext Euronext", "Euronext") if exchangeId.lower() in iexcExchanges: continue iexcExchanges.add(exchangeId.lower()) self.exchanges[exchangeId.lower()] = Exchange(exchangeId, "traditional", exchange["description"], region=exchange["region"]) suffixMap[exchangeId.lower()] = exchange["exchangeSuffix"] difference = set(iexcExchanges).symmetric_difference(supported.iexcExchanges) newSupportedExchanges = [] unsupportedCryptoExchanges = [] for exchangeId in difference: if exchangeId not in supported.iexcExchanges: newSupportedExchanges.append(exchangeId) else: unsupportedCryptoExchanges.append(exchangeId) if len(newSupportedExchanges) != 0: print("New supported IEXC exchanges: {}".format(newSupportedExchanges)) if len(unsupportedCryptoExchanges) != 0: print("New deprecated IEXC exchanges: {}".format(unsupportedCryptoExchanges)) for exchangeId in supported.traditionalExchanges["IEXC"]: symbols = get_url("https://cloud.iexapis.com/stable/ref-data/exchange/{}/symbols?token={}".format(self.exchanges[exchangeId].id, environ["IEXC_KEY"])) if len(symbols) == 0: print("No symbols found on {}".format(exchangeId)) for symbol in symbols: suffix = suffixMap.get(exchangeId, "") tickerId = symbol["symbol"] if tickerId not in self.iexcStocksIndex: self.iexcStocksIndex[tickerId] = {"id": tickerId.removesuffix(suffix), "name": symbol["name"], "base": tickerId.removesuffix(suffix), "quote": symbol["currency"]} self.exchanges[exchangeId].properties.symbols.append(tickerId) forexSymbols = get_url("https://cloud.iexapis.com/stable/ref-data/fx/symbols?token={}".format(environ["IEXC_KEY"])) derivedCurrencies = set() for pair in forexSymbols["pairs"]: derivedCurrencies.add(pair["fromCurrency"]) derivedCurrencies.add(pair["toCurrency"]) self.iexcForexIndex[pair["symbol"]] = {"id": pair["symbol"], "name": pair["symbol"], "base": pair["fromCurrency"], "quote": pair["toCurrency"], "reversed": False} self.iexcForexIndex[pair["toCurrency"] + pair["fromCurrency"]] = {"id": pair["symbol"], "name": pair["toCurrency"] + pair["fromCurrency"], "base": pair["toCurrency"], "quote": pair["fromCurrency"], "reversed": True} for fromCurrency in derivedCurrencies: for toCurrency in derivedCurrencies: symbol = fromCurrency + toCurrency if fromCurrency != toCurrency and symbol not in self.iexcForexIndex: self.iexcForexIndex[symbol] = {"id": symbol, "name": symbol, "base": fromCurrency, "quote": toCurrency, "reversed": False} except Exception: print(format_exc())
def request_depth_visualization(self, request): ticker = request.get_ticker() exchange = request.get_exchange() try: if exchange is None: return None, "Data for {} isn't available.".format(ticker.name) exchange = Exchange(exchange.id) try: depthData = exchange.properties.fetch_order_book(ticker.symbol) bestBid = depthData["bids"][0] bestAsk = depthData["asks"][0] lastPrice = (bestBid[0] + bestAsk[0]) / 2 except: return None, None bidTotal = 0 xBids = [bestBid[0]] yBids = [0] for bid in depthData['bids']: if len(xBids) < 10 or bid[0] > lastPrice * 0.9: bidTotal += bid[1] xBids.append(bid[0]) yBids.append(bidTotal) askTotal = 0 xAsks = [bestAsk[0]] yAsks = [0] for ask in depthData['asks']: if len(xAsks) < 10 or ask[0] < lastPrice * 1.1: askTotal += ask[1] xAsks.append(ask[0]) yAsks.append(askTotal) fig = plt.figure(facecolor="#131722") ax = fig.add_subplot(1, 1, 1) ax.tick_params(color="#787878", labelcolor="#D9D9D9") ax.step(xBids, yBids, where="post", color="#27A59A") ax.step(xAsks, yAsks, where="post", color="#EF534F") ax.fill_between(xBids, yBids, 0, facecolor="#27A59A", interpolate=True, step="post", alpha=0.33, zorder=2) ax.fill_between(xAsks, yAsks, 0, facecolor="#EF534F", interpolate=True, step="post", alpha=0.33, zorder=2) plt.axvline(x=lastPrice, color="#758696", linestyle="--") ax.set_facecolor("#131722") for spine in ax.spines.values(): spine.set_edgecolor("#787878") ax.autoscale(enable=True, axis="both", tight=True) def on_draw(event): bboxes = [] for label in ax.get_yticklabels(): bbox = label.get_window_extent() bboxi = bbox.transformed(fig.transFigure.inverted()) bboxes.append(bboxi) bbox = mtransforms.Bbox.union(bboxes) if fig.subplotpars.left < bbox.width: fig.subplots_adjust(left=1.1 * bbox.width) fig.canvas.draw() return False ax.yaxis.set_major_formatter(tkr.FuncFormatter(lambda x, p: format(int(x), ','))) plt.setp(ax.get_xticklabels(), rotation=45, horizontalalignment='right') lastPriceLabel = bestAsk[0] if bestAsk[1] >= bestBid[1] else bestBid[0] xLabels = list(plt.xticks()[0][1:]) yLabels = list(plt.yticks()[0][1:]) for label in xLabels: plt.axvline(x=label, color="#363C4F", linewidth=1, zorder=1) for label in yLabels: plt.axhline(y=label, color="#363C4F", linewidth=1, zorder=1) diffLabels = 1 - xLabels[0] / xLabels[1] bottomBound, topBound = lastPriceLabel * (1 - diffLabels * (1/4)), lastPriceLabel * (1 + diffLabels * (1/4)) xLabels = [l for l in xLabels if not (bottomBound <= l <= topBound)] plt.xticks(xLabels + [lastPriceLabel]) plt.yticks(yLabels) ax.set_xlim([xBids[-1], xAsks[-1]]) ax.set_ylim([0, max(bidTotal, askTotal)]) fig.canvas.mpl_connect("draw_event", on_draw) plt.tight_layout() rawImageData = BytesIO() plt.savefig(rawImageData, format="png", edgecolor="none") rawImageData.seek(0) imageBuffer = BytesIO() chartImage = Image.new("RGBA", (1600, 1200)) chartImage.paste(Image.open(rawImageData)) chartImage = Image.alpha_composite(chartImage, self.imageOverlays["Alpha depth"]) chartImage.save(imageBuffer, format="png") imageData = base64.b64encode(imageBuffer.getvalue()) imageBuffer.close() return imageData, None except Exception: print(traceback.format_exc()) if os.environ["PRODUCTION_MODE"]: self.logging.report_exception() return None, None
def request_lld_quote(self, request): ticker = request.get_ticker() exchange = request.get_exchange() filters = request.get_filters() action = request.find_parameter_in_list("lld", filters) try: if exchange is not None: exchange = Exchange(exchange.id, "crypto") if action == "funding": if exchange.id in ["bitmex"]: try: rawData = exchange.properties.public_get_instrument( {"symbol": ticker.id})[0] except: return None, "Requested funding data for `{}` is not available.".format( ticker.name) if rawData["fundingTimestamp"] is not None: fundingDate = datetime.datetime.strptime( rawData["fundingTimestamp"], "%Y-%m-%dT%H:%M:00.000Z").replace(tzinfo=pytz.utc) else: fundingDate = datetime.datetime.now().replace( tzinfo=pytz.utc) indicativeFundingTimestamp = datetime.datetime.timestamp( fundingDate) + 28800 indicativeFundingDate = datetime.datetime.utcfromtimestamp( indicativeFundingTimestamp).replace(tzinfo=pytz.utc) deltaFunding = fundingDate - datetime.datetime.now( ).astimezone(pytz.utc) deltaIndicative = indicativeFundingDate - datetime.datetime.now( ).astimezone(pytz.utc) hours1, seconds1 = divmod( deltaFunding.days * 86400 + deltaFunding.seconds, 3600) minutes1 = int(seconds1 / 60) hoursFunding = "{:d} {} ".format( hours1, "hours" if hours1 > 1 else "hour") if hours1 > 0 else "" minutesFunding = "{:d} {}".format( minutes1 if hours1 > 0 or minutes1 > 0 else seconds1, "{}".format("minute" if minutes1 == 1 else "minutes") if hours1 > 0 or minutes1 > 0 else ("second" if seconds1 == 1 else "seconds")) deltaFundingText = "{}{}".format(hoursFunding, minutesFunding) hours2, seconds2 = divmod( deltaIndicative.days * 86400 + deltaIndicative.seconds, 3600) minutes2 = int(seconds2 / 60) hoursIndicative = "{:d} {} ".format( hours2, "hours" if hours2 > 1 else "hour") if hours2 > 0 else "" minutesIndicative = "{:d} {}".format( minutes2 if hours2 > 0 or minutes2 > 0 else seconds2, "{}".format("minute" if minutes2 == 1 else "minutes") if hours2 > 0 or minutes2 > 0 else ("second" if seconds2 == 1 else "seconds")) deltaIndicativeText = "{}{}".format( hoursIndicative, minutesIndicative) fundingRate = float(rawData["fundingRate"]) * 100 predictedFundingRate = float( rawData["indicativeFundingRate"]) * 100 averageFundingRate = (fundingRate + predictedFundingRate) / 2 payload = { "quotePrice": "Funding Rate: {:+.4f} % *(in {})*\nPredicted Rate: {:+.4f} % *(in {})*" .format(fundingRate, deltaFundingText, predictedFundingRate, deltaIndicativeText), "title": ticker.name, "baseTicker": ticker.base, "quoteTicker": ticker.quote, "thumbnailUrl": TickerParser.get_coingecko_image(ticker.base), "messageColor": "yellow" if averageFundingRate == 0.01 else ("light green" if averageFundingRate < 0.01 else "deep orange"), "sourceText": "Contract details on {}".format(exchange.name), "platform": "LLD", "raw": { "quotePrice": [fundingRate, predictedFundingRate], "ticker": ticker, "exchange": exchange, "timestamp": time.time() } } return payload, None return None, "Funding data is only available on BitMEX." elif action == "oi": if exchange.id in ["bitmex"]: try: rawData = exchange.properties.public_get_instrument( {"symbol": ticker.id})[0] except: return None, "Requested open interest data for `{}` is not available.".format( ticker.name) payload = { "quotePrice": "Open interest: {:,.0f} {}\nOpen value: {:,.4f} XBT". format(float(rawData["openInterest"]), "USD" if ticker.id == "XBTUSD" else "contracts", float(rawData["openValue"]) / 100000000), "title": ticker.name, "baseTicker": ticker.base, "quoteTicker": ticker.quote, "thumbnailUrl": TickerParser.get_coingecko_image(ticker.base), "messageColor": "deep purple", "sourceText": "Contract details on {}".format(exchange.name), "platform": "LLD", "raw": { "quotePrice": [ float(rawData["openInterest"]), float(rawData["openValue"]) / 100000000 ], "ticker": ticker, "exchange": exchange, "timestamp": time.time() } } return payload, None return None, "Open interest and open value data is only available on BitMEX." elif action == "ls": if exchange.id in ["bitfinex2"]: try: longs = exchange.properties.publicGetStats1KeySizeSymbolLongLast( { "key": "pos.size", "size": "1m", "symbol": "t{}".format(ticker.id), "side": "long", "section": "last" }) shorts = exchange.properties.publicGetStats1KeySizeSymbolShortLast( { "key": "pos.size", "size": "1m", "symbol": "t{}".format(ticker.id), "side": "long", "section": "last" }) ratio = longs[1] / (longs[1] + shorts[1]) * 100 except: return None, None payload = { "quotePrice": "{:.1f} % longs / {:.1f} % shorts".format( ratio, 100 - ratio), "title": "{} longs/shorts ratio".format(ticker.name), "baseTicker": ticker.base, "quoteTicker": ticker.quote, "thumbnailUrl": TickerParser.get_coingecko_image(ticker.base), "messageColor": "deep purple", "sourceText": "Data on {}".format(exchange.name), "platform": "LLD", "raw": { "quotePrice": [longs[1], shorts[1]], "ticker": ticker, "exchange": exchange, "timestamp": time.time() } } return payload, None return None, "Longs and shorts data is only available on Bitfinex." elif action == "sl": if exchange.id in ["bitfinex2"]: try: longs = exchange.properties.publicGetStats1KeySizeSymbolLongLast( { "key": "pos.size", "size": "1m", "symbol": "t{}".format(ticker.id), "side": "short", "section": "last" }) shorts = exchange.properties.publicGetStats1KeySizeSymbolShortLast( { "key": "pos.size", "size": "1m", "symbol": "t{}".format(ticker.id), "side": "short", "section": "last" }) ratio = shorts[1] / (longs[1] + shorts[1]) * 100 except: return None, None payload = { "quotePrice": "{:.1f} % shorts / {:.1f} % longs".format( ratio, 100 - ratio), "title": "{} shorts/longs ratio".format(ticker.name), "baseTicker": ticker.base, "quoteTicker": ticker.quote, "thumbnailUrl": TickerParser.get_coingecko_image(ticker.base), "messageColor": "deep purple", "sourceText": "Data on {}".format(exchange.name), "platform": "LLD", "raw": { "quotePrice": [longs[1], shorts[1]], "ticker": ticker, "exchange": exchange, "timestamp": time.time() } } return payload, None return None, "Longs and shorts data is only available on Bitfinex." elif action == "dom": try: rawData = self.coinGecko.get_global() except: return None, "Requested dominance data for `{}` is not available.".format( ticker.name) if ticker.base.lower() not in rawData["market_cap_percentage"]: return None, "Dominance for {} does not exist.".format( ticker.base) coinDominance = rawData["market_cap_percentage"][ ticker.base.lower()] payload = { "quotePrice": "{} dominance: {:,.2f} %".format(ticker.base, coinDominance), "title": "Market Dominance", "baseTicker": ticker.base, "quoteTicker": ticker.quote, "thumbnailUrl": TickerParser.get_coingecko_image(ticker.base), "messageColor": "deep purple", "sourceText": "Market information from CoinGecko", "platform": "LLD", "raw": { "quotePrice": coinDominance, "ticker": ticker, "timestamp": time.time() } } return payload, None else: return None, None except Exception: print(traceback.format_exc()) if os.environ["PRODUCTION_MODE"]: self.logging.report_exception(user=ticker.id) return None, None