05661f3f30
Co-authored-by: Cursor <cursoragent@cursor.com>
171 lines
6.1 KiB
Python
171 lines
6.1 KiB
Python
"""中控统计汇总:KPI 对比 + 余额曲线."""
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from lib.bridge_client_lib import pull_all_enabled, pull_all_stats
|
|
from lib.cash_sample_lib import query_cash_series, record_cash_samples_from_overview
|
|
|
|
|
|
def _safe_float(v: Any) -> float | None:
|
|
if v is None or v == "":
|
|
return None
|
|
try:
|
|
return float(v)
|
|
except (TypeError, ValueError):
|
|
return None
|
|
|
|
|
|
def _safe_int(v: Any) -> int:
|
|
try:
|
|
return int(float(v))
|
|
except (TypeError, ValueError):
|
|
return 0
|
|
|
|
|
|
def normalize_window(raw: Any) -> str:
|
|
w = str(raw or "7d").strip().lower()
|
|
if w in ("1d", "day", "今日"):
|
|
return "today"
|
|
if w in ("7", "week"):
|
|
return "7d"
|
|
if w in ("30", "month"):
|
|
return "30d"
|
|
if w in ("0", "全部", "total"):
|
|
return "all"
|
|
return w if w in ("today", "7d", "30d", "all") else "7d"
|
|
|
|
|
|
def build_stats_summary(
|
|
window: str = "7d",
|
|
*,
|
|
snaps: list[dict[str, Any]] | None = None,
|
|
) -> dict[str, Any]:
|
|
w = normalize_window(window)
|
|
# 实时状态(余额/权利金/浮盈)来自快照;窗口 KPI 来自各机 stats
|
|
# snaps 可由热缓存传入,避免统计页再现场重复拉全机
|
|
if snaps is None:
|
|
snaps = pull_all_enabled()
|
|
try:
|
|
record_cash_samples_from_overview(snaps)
|
|
except Exception:
|
|
pass
|
|
stats_rows = pull_all_stats(w)
|
|
stats_by_id = {r.get("id"): r for r in stats_rows}
|
|
|
|
machines: list[dict[str, Any]] = []
|
|
sum_cash = 0.0
|
|
sum_premium = 0.0
|
|
sum_unreal = 0.0
|
|
sum_net = 0.0
|
|
sum_profit = 0.0
|
|
sum_loss = 0.0
|
|
sum_closed = 0
|
|
sum_wins = 0
|
|
sum_losses = 0
|
|
online_n = 0
|
|
cash_known = False
|
|
premium_known = False
|
|
unreal_known = False
|
|
|
|
for snap in snaps:
|
|
iid = snap.get("id")
|
|
st = stats_by_id.get(iid) or {}
|
|
stats = st.get("stats") if isinstance(st.get("stats"), dict) else {}
|
|
body = snap.get("snapshot") if isinstance(snap.get("snapshot"), dict) else {}
|
|
premium = body.get("premium") if isinstance(body.get("premium"), dict) else {}
|
|
pnl = body.get("pnl") if isinstance(body.get("pnl"), dict) else {}
|
|
brief = body.get("positions_brief") if isinstance(body.get("positions_brief"), list) else []
|
|
cash = _safe_float(premium.get("cash_usdt"))
|
|
paid = _safe_float(premium.get("paid_usdt"))
|
|
ratio = _safe_float(premium.get("ratio_pct"))
|
|
unreal = _safe_float(pnl.get("unrealized_usdt"))
|
|
net = _safe_float(stats.get("net_realized_pnl"))
|
|
profit = _safe_float(stats.get("total_profit"))
|
|
loss = _safe_float(stats.get("total_loss"))
|
|
closed = _safe_int(stats.get("total_closed"))
|
|
wins = _safe_int(stats.get("win_count"))
|
|
losses = _safe_int(stats.get("loss_count"))
|
|
win_rate = _safe_float(stats.get("win_rate"))
|
|
avg_hold = _safe_float(stats.get("avg_hold_sec"))
|
|
online = bool(snap.get("online"))
|
|
mode = ((body.get("instance") or {}) if isinstance(body.get("instance"), dict) else {}).get(
|
|
"mode"
|
|
)
|
|
return_pct = None
|
|
if online and stats.get("ok") and net is not None and cash is not None and cash > 0:
|
|
return_pct = round(float(net) / float(cash) * 100.0, 2)
|
|
if online:
|
|
online_n += 1
|
|
if cash is not None:
|
|
sum_cash += cash
|
|
cash_known = True
|
|
if paid is not None:
|
|
sum_premium += paid
|
|
premium_known = True
|
|
if unreal is not None:
|
|
sum_unreal += unreal
|
|
unreal_known = True
|
|
if online and stats.get("ok"):
|
|
if net is not None:
|
|
sum_net += net
|
|
if profit is not None:
|
|
sum_profit += profit
|
|
if loss is not None:
|
|
sum_loss += loss
|
|
sum_closed += closed
|
|
sum_wins += wins
|
|
sum_losses += losses
|
|
machines.append(
|
|
{
|
|
"id": iid,
|
|
"name": snap.get("name"),
|
|
"online": online,
|
|
"error": snap.get("error") or st.get("error") or "",
|
|
"mode": mode,
|
|
"cash_usdt": cash,
|
|
"premium_usdt": paid,
|
|
"ratio_pct": ratio,
|
|
"unrealized_usdt": unreal,
|
|
"pos_count": len(brief),
|
|
"total_closed": closed if online and stats.get("ok") else None,
|
|
"win_count": wins if online and stats.get("ok") else None,
|
|
"loss_count": losses if online and stats.get("ok") else None,
|
|
"win_rate": win_rate if online and stats.get("ok") else None,
|
|
"net_realized_pnl": net if online and stats.get("ok") else None,
|
|
"return_rate_pct": return_pct,
|
|
"total_profit": profit if online and stats.get("ok") else None,
|
|
"total_loss": loss if online and stats.get("ok") else None,
|
|
"avg_hold_sec": avg_hold if online and stats.get("ok") else None,
|
|
"stats_ok": bool(online and stats.get("ok")),
|
|
}
|
|
)
|
|
|
|
agg_win_rate = round(sum_wins / sum_closed * 100, 2) if sum_closed else 0.0
|
|
return {
|
|
"ok": True,
|
|
"window": w,
|
|
"totals": {
|
|
"online": online_n,
|
|
"machines": len(machines),
|
|
"cash_usdt": round(sum_cash, 4) if cash_known else None,
|
|
"premium_usdt": round(sum_premium, 4) if premium_known else None,
|
|
"unrealized_usdt": round(sum_unreal, 4) if unreal_known else None,
|
|
"net_realized_pnl": round(sum_net, 4),
|
|
"total_profit": round(sum_profit, 4),
|
|
"total_loss": round(sum_loss, 4),
|
|
"total_closed": sum_closed,
|
|
"win_count": sum_wins,
|
|
"loss_count": sum_losses,
|
|
"win_rate": agg_win_rate,
|
|
},
|
|
"machines": machines,
|
|
}
|
|
|
|
|
|
def build_stats_curve(range_key: str = "7d") -> dict[str, Any]:
|
|
# 曲线只读本地采样库;余额采样由总览常驻 pull 负责
|
|
data = query_cash_series(range_key=range_key)
|
|
data["ok"] = True
|
|
return data
|