Commit 2dfbf29
Changed files (4)
src/price/binance.py
@@ -51,6 +51,7 @@ async def get_binance_price(coin: str, interval: str = "30m") -> dict:
klines = response.json()
if not klines:
return {"texts": f"Binance price failed: {coin}"}
+ klines = sorted(klines, key=lambda x: x[0])
high_price = max(float(number(x[2])) for x in klines)
low_price = min(float(number(x[3])) for x in klines)
open_price = float(number(klines[0][1]))
@@ -62,7 +63,7 @@ async def get_binance_price(coin: str, interval: str = "30m") -> dict:
title = f"{symbol}•{interval}•Binance"
subtitle = f"开: {open_price} 高: {high_price} 低: {low_price} 收: {close_price} 涨: {change_pct:+.2%} 振: {abs(amplitude):.2%}"
text = f"{title}\n"
- text += f"时间段: {ts_to_dt(klines[0][0]):%m-%d %H:%M} - {ts_to_dt(klines[-1][0]):%m-%d %H:%M}\n"
+ text += f"时间段: {ts_to_dt(klines[0][0]):%m-%d %H:%M} → {ts_to_dt(klines[-1][0]):%m-%d %H:%M}\n"
text += f"开盘价: {open_price}\n"
text += f"最高价: {high_price}\n"
text += f"最低价: {low_price}\n"
src/price/entrypoint.py
@@ -12,6 +12,7 @@ from messages.sender import send2tg
from messages.utils import equal_prefix, startswith_prefix
from price.binance import get_binance_price
from price.coinmarketcap import cmc_convert_price, get_cmc_price
+from price.okx import get_okx_price
HELP = f"""
💵**查询价格**
@@ -61,8 +62,8 @@ async def get_asset_price(client: Client, message: Message, **kwargs) -> None:
return
# match "BTC"
- if res := await get_binance_price(text):
- await send2tg(client, message, **res, **kwargs)
+ if (res := await get_binance_price(text)) or (res := await get_okx_price(text)):
+ await send2tg(client, message, **res, **kwargs) # with klines chart
elif res := await get_cmc_price(text):
await send2tg(client, message, texts=res, **kwargs)
else:
src/price/okx.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+from config import API, PROXY, cache
+from networking import hx_req
+from price.chart import generate_chart
+from utils import number, ts_to_dt
+
+
+@cache.memoize(ttl=7200)
+async def get_okx_symbols() -> dict[str, str]:
+ """Get all symbols from OKX."""
+ res = {}
+ url = f"{API.OKX}/api/v5/public/instruments"
+ for instType in ["SWAP", "SPOT"]:
+ params = {"instType": instType}
+ response = await hx_req(url, params=params, proxy=PROXY.CRYPTO, check_has_kv=["data"])
+ data = response.json()["data"]
+ res |= {coin["instId"].replace("-", ""): coin["instId"] for coin in data if coin["state"] == "live"}
+ return res
+
+
+@cache.memoize(ttl=60)
+async def get_okx_price(coin: str, interval: str = "30m") -> dict:
+ """Get the price of a crypto asset from OKX."""
+ symbols = await get_okx_symbols()
+ coin = coin.upper().replace("-", "")
+ symbol = coin
+ suffixes = ["USDT", "USDC", "USDTSWAP", "USDCSWAP", "USDSWAP"]
+ while symbol not in symbols and suffixes:
+ symbol = f"{coin}{suffixes.pop(0)}".upper()
+ if symbol not in symbols:
+ return {}
+ inst_id = symbols[symbol]
+ url = f"{API.OKX}/api/v5/market/candles?instId={inst_id}&bar={interval}&limit=49"
+ response = await hx_req(url, proxy=PROXY.CRYPTO, check_kv={"code": "0"})
+ klines = response.json()["data"] # ts, o, h, l, c
+ if not klines:
+ return {"texts": f"OKX price failed: {coin}"}
+ klines = sorted(klines, key=lambda x: x[0])
+ high_price = max(float(number(x[2])) for x in klines)
+ low_price = min(float(number(x[3])) for x in klines)
+ open_price = float(number(klines[0][1]))
+ close_price = float(number(klines[-1][4]))
+ change_pct = (close_price - open_price) / open_price
+ amplitude = (high_price - low_price) / low_price
+ if close_price < open_price:
+ amplitude *= -1
+ title = f"{symbol}•{interval}•OKX"
+ subtitle = f"开: {open_price} 高: {high_price} 低: {low_price} 收: {close_price} 涨: {change_pct:+.2%} 振: {abs(amplitude):.2%}"
+ text = f"{title}\n"
+ text += f"时间段: {ts_to_dt(klines[0][0]):%m-%d %H:%M} → {ts_to_dt(klines[-1][0]):%m-%d %H:%M}\n"
+ text += f"开盘价: {open_price}\n"
+ text += f"最高价: {high_price}\n"
+ text += f"最低价: {low_price}\n"
+ text += f"收盘价: {close_price} \n"
+ text += f"涨跌幅: {change_pct:+.2%}\n"
+ text += f"振幅: {amplitude:.2%}"
+ chart = await generate_chart(klines, interval, title, subtitle)
+ return {"texts": text, "media": [{"photo": chart}]}
src/config.py
@@ -81,6 +81,7 @@ class API:
TIKHUB_WEIBO_VIDEO = os.getenv("TIKHUB_WEIBO_VIDEO_API", "https://api.tikhub.io/api/v1/weibo/web/fetch_short_video_data?share_text=")
BINANCE_SPOT = os.getenv("BINANCE_SPOT_API", "https://data-api.binance.vision")
BINANCE_UM = os.getenv("BINANCE_UM_API", "https://fapi.binance.com")
+ OKX = os.getenv("OKX_API", "https://www.okx.com")
class PROVIDER: # default API provider