增加k线图
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
"""日 K 线:优先 SQLite 本地库,不足或过期再请求币安。"""
|
||||
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
from .binance import binance_client
|
||||
from .config import settings
|
||||
from .db import get_daily_klines_from_db, get_kline_meta, save_daily_klines
|
||||
from .exceptions import BinanceRateLimitedError
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _is_db_fresh(symbol: str, min_bars: int) -> bool:
|
||||
meta = get_kline_meta(symbol)
|
||||
if not meta or meta.get("bar_count", 0) < min_bars:
|
||||
return False
|
||||
try:
|
||||
last_fetch = datetime.fromisoformat(meta["last_fetch_at"])
|
||||
except ValueError:
|
||||
return False
|
||||
age = (datetime.now() - last_fetch).total_seconds()
|
||||
return age < settings.chart_cache_minutes * 60
|
||||
|
||||
|
||||
async def sync_daily_klines(symbol: str, limit: int | None = None) -> list[dict]:
|
||||
"""从币安拉取并写入本地库。"""
|
||||
sym = symbol.upper()
|
||||
n = min(limit or settings.chart_kline_limit, 1500)
|
||||
candles = await binance_client.get_daily_klines(sym, n)
|
||||
save_daily_klines(sym, candles)
|
||||
logger.info("Saved %d daily klines for %s to DB", len(candles), sym)
|
||||
return candles
|
||||
|
||||
|
||||
async def get_daily_candles(
|
||||
symbol: str,
|
||||
limit: int | None = None,
|
||||
force_refresh: bool = False,
|
||||
) -> tuple[list[dict], str]:
|
||||
"""
|
||||
返回 (candles, source)。
|
||||
source: db | db_stale | binance
|
||||
"""
|
||||
sym = symbol.upper().strip()
|
||||
n = min(limit or settings.chart_kline_limit, 1500)
|
||||
min_bars = min(n, 50)
|
||||
|
||||
if not force_refresh and _is_db_fresh(sym, min_bars):
|
||||
candles = get_daily_klines_from_db(sym, n)
|
||||
if len(candles) >= min_bars:
|
||||
return candles, "db"
|
||||
|
||||
stored = get_daily_klines_from_db(sym, n)
|
||||
if binance_client.is_rate_limited():
|
||||
if stored:
|
||||
logger.warning("Rate limited, serve stale DB klines for %s", sym)
|
||||
return stored, "db_stale"
|
||||
raise BinanceRateLimitedError(binance_client.rate_limit_remaining_sec(), sym)
|
||||
|
||||
try:
|
||||
candles = await sync_daily_klines(sym, n)
|
||||
return candles, "binance"
|
||||
except BinanceRateLimitedError:
|
||||
if stored:
|
||||
return stored, "db_stale"
|
||||
raise
|
||||
except Exception:
|
||||
if stored:
|
||||
return stored, "db_stale"
|
||||
raise
|
||||
|
||||
|
||||
async def prefetch_symbols(symbols: list[str]) -> None:
|
||||
"""后台预拉 Top 币种日 K 入库(串行,避免 418)。"""
|
||||
seen: set[str] = set()
|
||||
for raw in symbols:
|
||||
sym = raw.upper().strip()
|
||||
if not sym or sym in seen or not sym.endswith("USDT"):
|
||||
continue
|
||||
seen.add(sym)
|
||||
if _is_db_fresh(sym, min(50, settings.chart_kline_limit)):
|
||||
continue
|
||||
if binance_client.is_rate_limited():
|
||||
logger.warning("Prefetch stopped — rate limited")
|
||||
break
|
||||
try:
|
||||
await sync_daily_klines(sym)
|
||||
except BinanceRateLimitedError:
|
||||
logger.warning("Prefetch rate limited at %s", sym)
|
||||
break
|
||||
except Exception as e:
|
||||
logger.warning("Prefetch %s failed: %s", sym, e)
|
||||
Reference in New Issue
Block a user