Files
Binance_Altcoin_Monitor/backend/app/stats.py
T
2026-05-22 14:05:18 +08:00

132 lines
4.2 KiB
Python

"""三日数据统计:连续三日 Top30 且 |涨跌|>=5%"""
from typing import Any
from .config import settings
from .db import get_latest_snapshot
def _items_by_symbol(items: list[dict]) -> dict[str, dict]:
return {x["symbol"]: x for x in items if x.get("symbol")}
def compute_three_day_stats() -> dict[str, Any]:
today_snap = get_latest_snapshot("today")
yesterday_snap = get_latest_snapshot("yesterday")
daybefore_snap = get_latest_snapshot("daybefore")
threshold = settings.change_threshold
top_n = settings.top_n
missing = []
if not today_snap:
missing.append("今日")
if not yesterday_snap:
missing.append("昨日")
if not daybefore_snap:
missing.append("前日")
if missing:
return {
"ok": False,
"missing_periods": missing,
"message": f"缺少快照:{', '.join(missing)},请等待刷新或手动触发",
"criteria": _criteria_text(threshold, top_n),
"count": 0,
"items": [],
"periods": _period_meta(today_snap, yesterday_snap, daybefore_snap),
}
today_map = _items_by_symbol(today_snap["items"])
yesterday_map = _items_by_symbol(yesterday_snap["items"])
daybefore_map = _items_by_symbol(daybefore_snap["items"])
symbols = set(today_map) & set(yesterday_map) & set(daybefore_map)
qualified: list[dict] = []
for sym in sorted(symbols):
t, y, b = today_map[sym], yesterday_map[sym], daybefore_map[sym]
if not (
abs(t.get("price_change_pct", 0)) >= threshold
and abs(y.get("price_change_pct", 0)) >= threshold
and abs(b.get("price_change_pct", 0)) >= threshold
):
continue
qualified.append(
{
"symbol": sym,
"today": _pick_fields(t),
"yesterday": _pick_fields(y),
"daybefore": _pick_fields(b),
"avg_change_pct": round(
(
t.get("price_change_pct", 0)
+ y.get("price_change_pct", 0)
+ b.get("price_change_pct", 0)
)
/ 3,
4,
),
"total_quote_volume": (
(t.get("quote_volume") or 0)
+ (y.get("quote_volume") or 0)
+ (b.get("quote_volume") or 0)
),
}
)
qualified.sort(key=lambda x: x["total_quote_volume"], reverse=True)
return {
"ok": True,
"criteria": _criteria_text(threshold, top_n),
"count": len(qualified),
"items": qualified,
"periods": _period_meta(today_snap, yesterday_snap, daybefore_snap),
"summary": {
"today_top30": len(today_map),
"yesterday_top30": len(yesterday_map),
"daybefore_top30": len(daybefore_map),
"intersection": len(symbols),
},
}
def _criteria_text(threshold: float, top_n: int) -> str:
return (
f"连续三日成交额 Top{top_n} 且每日 |涨跌幅| ≥ {threshold:g}%"
f"(今日/昨日/前日三个完整切日周期)"
)
def _pick_fields(row: dict) -> dict:
return {
"rank": row.get("rank"),
"quote_volume": row.get("quote_volume"),
"quote_volume_fmt": row.get("quote_volume_fmt"),
"price_change_pct": row.get("price_change_pct"),
"price_change_pct_fmt": row.get("price_change_pct_fmt"),
"funding_rate_fmt": row.get("funding_rate_fmt"),
"is_high_volume": row.get("is_high_volume"),
"is_high_change": row.get("is_high_change"),
}
def _period_meta(today, yesterday, daybefore) -> dict:
def one(snap, label):
if not snap:
return {"label": label, "ready": False}
return {
"label": label,
"ready": True,
"period_start": snap["period_start"],
"period_end": snap["period_end"],
"updated_at": snap.get("created_at"),
}
return {
"today": one(today, "今日"),
"yesterday": one(yesterday, "昨日"),
"daybefore": one(daybefore, "前日"),
}