feat: 监控区 2x2 布局与左上今日统计卡
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -630,6 +630,7 @@ def register_hub_routes(app):
|
||||
fetch_trades_for_trading_day,
|
||||
summarize_trades,
|
||||
)
|
||||
from lib.trade.daily_open_limit_lib import count_opens_for_trading_day
|
||||
|
||||
c = _ctx()
|
||||
get_db = c.get("get_db")
|
||||
@@ -651,6 +652,7 @@ def register_hub_routes(app):
|
||||
row_to_dict_fn=c.get("row_to_dict"),
|
||||
reset_hour=reset_hour,
|
||||
)
|
||||
opens_today = count_opens_for_trading_day(conn, trading_day)
|
||||
finally:
|
||||
conn.close()
|
||||
stats = summarize_trades(trades)
|
||||
@@ -659,6 +661,7 @@ def register_hub_routes(app):
|
||||
"ok": True,
|
||||
"trading_day": trading_day,
|
||||
"trading_day_reset_hour": reset_hour,
|
||||
"opens_today": opens_today,
|
||||
"trades": trades,
|
||||
"stats": stats,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
"""监控区看板:三所当日统计聚合。"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
|
||||
def _coerce_float(value: Any) -> float | None:
|
||||
if value is None or value == "":
|
||||
return None
|
||||
try:
|
||||
return float(value)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
def position_unrealized_pnl(pos: dict[str, Any]) -> float:
|
||||
for key in ("unrealized_pnl", "unrealizedPnl", "upnl"):
|
||||
v = _coerce_float(pos.get(key))
|
||||
if v is not None:
|
||||
return v
|
||||
return 0.0
|
||||
|
||||
|
||||
def _open_positions(agent: dict[str, Any] | None) -> list[dict[str, Any]]:
|
||||
if not isinstance(agent, dict):
|
||||
return []
|
||||
positions = agent.get("positions")
|
||||
if not isinstance(positions, list):
|
||||
return []
|
||||
out: list[dict[str, Any]] = []
|
||||
for p in positions:
|
||||
if not isinstance(p, dict):
|
||||
continue
|
||||
try:
|
||||
c = abs(float(p.get("contracts") or 0))
|
||||
except (TypeError, ValueError):
|
||||
c = 0.0
|
||||
if c > 1e-12:
|
||||
out.append(p)
|
||||
return out
|
||||
|
||||
|
||||
def aggregate_monitor_board_totals(
|
||||
rows: list[dict[str, Any]],
|
||||
*,
|
||||
trading_day: str,
|
||||
reset_hour: int = 8,
|
||||
) -> dict[str, Any]:
|
||||
"""汇总监控 board 各行 → 左上统计卡数据。"""
|
||||
open_count = 0
|
||||
closed_count = 0
|
||||
win_count = 0
|
||||
loss_count = 0
|
||||
win_pnl_u = 0.0
|
||||
loss_pnl_u = 0.0
|
||||
open_position_count = 0
|
||||
float_pnl_u = 0.0
|
||||
|
||||
for row in rows or []:
|
||||
if not isinstance(row, dict):
|
||||
continue
|
||||
day_stats = row.get("day_stats") if isinstance(row.get("day_stats"), dict) else {}
|
||||
if day_stats.get("ok"):
|
||||
open_count += int(day_stats.get("opens_today") or 0)
|
||||
st = day_stats.get("trade_stats") if isinstance(day_stats.get("trade_stats"), dict) else {}
|
||||
closed_count += int(st.get("closed_count") or 0)
|
||||
win_count += int(st.get("win_count") or 0)
|
||||
loss_count += int(st.get("loss_count") or 0)
|
||||
win_pnl_u += float(st.get("win_pnl_u") or 0)
|
||||
loss_pnl_u += float(st.get("loss_pnl_u") or 0)
|
||||
|
||||
ag = row.get("agent") if isinstance(row.get("agent"), dict) else {}
|
||||
open_pos = _open_positions(ag)
|
||||
open_position_count += len(open_pos)
|
||||
agent_upnl = _coerce_float(ag.get("total_unrealized_pnl"))
|
||||
if agent_upnl is not None:
|
||||
float_pnl_u += agent_upnl
|
||||
else:
|
||||
float_pnl_u += sum(position_unrealized_pnl(p) for p in open_pos)
|
||||
|
||||
return {
|
||||
"trading_day": trading_day,
|
||||
"reset_hour": int(reset_hour),
|
||||
"open_count": open_count,
|
||||
"closed_count": closed_count,
|
||||
"win_count": win_count,
|
||||
"loss_count": loss_count,
|
||||
"win_pnl_u": round(win_pnl_u, 4),
|
||||
"loss_pnl_u": round(loss_pnl_u, 4),
|
||||
"realized_pnl_u": round(win_pnl_u + loss_pnl_u, 4),
|
||||
"open_position_count": open_position_count,
|
||||
"float_pnl_u": round(float_pnl_u, 4),
|
||||
}
|
||||
@@ -616,6 +616,8 @@ def fetch_trades_for_archive(
|
||||
def summarize_trades(trades: list[dict]) -> dict[str, Any]:
|
||||
"""单笔列表 → 笔数 / 盈亏 / 胜败统计。"""
|
||||
total_pnl = 0.0
|
||||
win_pnl = 0.0
|
||||
loss_pnl = 0.0
|
||||
win = loss = flat = 0
|
||||
for t in trades or []:
|
||||
try:
|
||||
@@ -625,8 +627,10 @@ def summarize_trades(trades: list[dict]) -> dict[str, Any]:
|
||||
total_pnl += pnl
|
||||
if pnl > 1e-9:
|
||||
win += 1
|
||||
win_pnl += pnl
|
||||
elif pnl < -1e-9:
|
||||
loss += 1
|
||||
loss_pnl += pnl
|
||||
else:
|
||||
flat += 1
|
||||
return {
|
||||
@@ -635,4 +639,6 @@ def summarize_trades(trades: list[dict]) -> dict[str, Any]:
|
||||
"loss_count": loss,
|
||||
"flat_count": flat,
|
||||
"total_pnl_u": round(total_pnl, 4),
|
||||
"win_pnl_u": round(win_pnl, 4),
|
||||
"loss_pnl_u": round(loss_pnl, 4),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user