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_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_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_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, ""]