feat: 监控区 2x2 布局与左上今日统计卡
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -14,6 +14,8 @@ _REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
if str(_REPO_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(_REPO_ROOT))
|
||||
|
||||
from lib.hub.hub_monitor_totals_lib import aggregate_monitor_board_totals
|
||||
from lib.hub.hub_trades_lib import current_trading_day
|
||||
from lib.hub.hub_order_sync_lib import (
|
||||
cond_order_role,
|
||||
dedupe_conditional_orders_by_role,
|
||||
@@ -1493,13 +1495,19 @@ async def _fetch_flask_json(
|
||||
method: str = "GET",
|
||||
data=None,
|
||||
json_body: dict | None = None,
|
||||
params: dict | None = None,
|
||||
) -> dict | None:
|
||||
base = (ex.get("flask_url") or "").rstrip("/")
|
||||
if not base:
|
||||
return None
|
||||
try:
|
||||
if method == "GET":
|
||||
r = await client.get(f"{base}{path}", headers=_hub_headers(), timeout=HUB_FLASK_TIMEOUT)
|
||||
r = await client.get(
|
||||
f"{base}{path}",
|
||||
headers=_hub_headers(),
|
||||
timeout=HUB_FLASK_TIMEOUT,
|
||||
params=params or None,
|
||||
)
|
||||
else:
|
||||
headers = {**_hub_headers(), "Content-Type": "application/json"}
|
||||
if json_body is not None:
|
||||
@@ -2036,15 +2044,16 @@ def _merge_flask_exchange_tpsl(agent_row: dict, snap: dict | None, hub_mon: dict
|
||||
|
||||
|
||||
async def _fetch_exchange_flask_bundle(
|
||||
client: httpx.AsyncClient, ex: dict
|
||||
) -> tuple[dict | None, dict | None, list | None, dict | None, dict | None]:
|
||||
"""单所 Flask:monitor / meta / price_snapshot / account(有 flask_url 时)并行拉取。"""
|
||||
client: httpx.AsyncClient, ex: dict, *, trading_day: str | None = None
|
||||
) -> tuple[dict | None, dict | None, list | None, dict | None, dict | None, dict | None]:
|
||||
"""单所 Flask:monitor / meta / price_snapshot / account / trades/today(有 flask_url 时)并行拉取。"""
|
||||
caps = ex.get("capabilities") or []
|
||||
tasks = [
|
||||
_fetch_flask_json(client, ex, "/api/hub/monitor"),
|
||||
_fetch_flask_json(client, ex, "/api/hub/meta"),
|
||||
]
|
||||
has_flask = bool((ex.get("flask_url") or "").strip())
|
||||
day = (trading_day or "").strip()
|
||||
if has_flask:
|
||||
tasks.extend(
|
||||
[
|
||||
@@ -2052,11 +2061,21 @@ async def _fetch_exchange_flask_bundle(
|
||||
_fetch_flask_json(client, ex, "/api/hub/account"),
|
||||
]
|
||||
)
|
||||
if day:
|
||||
tasks.append(
|
||||
_fetch_flask_json(
|
||||
client,
|
||||
ex,
|
||||
"/api/hub/trades/today",
|
||||
params={"trading_day": day},
|
||||
)
|
||||
)
|
||||
results = await asyncio.gather(*tasks)
|
||||
hub_mon = results[0]
|
||||
meta = results[1]
|
||||
snap = results[2] if has_flask and len(results) > 2 else None
|
||||
account = results[3] if has_flask and len(results) > 3 else None
|
||||
trades_today = results[4] if has_flask and day and len(results) > 4 else None
|
||||
key_prices = None
|
||||
want_prices = HUB_BOARD_KEY_PRICES and "key" in caps
|
||||
if want_prices and isinstance(snap, dict):
|
||||
@@ -2067,14 +2086,34 @@ async def _fetch_exchange_flask_bundle(
|
||||
key_prices,
|
||||
snap if isinstance(snap, dict) else None,
|
||||
account if isinstance(account, dict) else None,
|
||||
trades_today if isinstance(trades_today, dict) else None,
|
||||
)
|
||||
|
||||
|
||||
def _trading_day_reset_hour() -> int:
|
||||
try:
|
||||
return int(os.getenv("TRADING_DAY_RESET_HOUR", "8") or "8")
|
||||
except ValueError:
|
||||
return 8
|
||||
|
||||
|
||||
def _day_stats_from_trades_body(body: dict | None) -> dict:
|
||||
if not isinstance(body, dict) or not body.get("ok"):
|
||||
return {"ok": False}
|
||||
stats = body.get("stats") if isinstance(body.get("stats"), dict) else {}
|
||||
return {
|
||||
"ok": True,
|
||||
"trading_day": body.get("trading_day"),
|
||||
"opens_today": int(body.get("opens_today") or 0),
|
||||
"trade_stats": stats,
|
||||
}
|
||||
|
||||
|
||||
async def _assemble_board_row(
|
||||
client: httpx.AsyncClient, ex: dict, agent_row: dict
|
||||
client: httpx.AsyncClient, ex: dict, agent_row: dict, *, trading_day: str
|
||||
) -> dict:
|
||||
hub_mon, meta, key_prices, snap, account = await _fetch_exchange_flask_bundle(
|
||||
client, ex
|
||||
hub_mon, meta, key_prices, snap, account, trades_today = await _fetch_exchange_flask_bundle(
|
||||
client, ex, trading_day=trading_day
|
||||
)
|
||||
if isinstance(hub_mon, dict):
|
||||
_merge_flask_order_price_fields(hub_mon, snap)
|
||||
@@ -2101,23 +2140,31 @@ async def _assemble_board_row(
|
||||
"trading_usdt": account.get("trading_usdt") if acct_ok else None,
|
||||
"available_trading_usdt": account.get("available_trading_usdt") if acct_ok else None,
|
||||
"account_ok": acct_ok,
|
||||
"day_stats": _day_stats_from_trades_body(trades_today),
|
||||
}
|
||||
|
||||
|
||||
async def _build_monitor_board_payload() -> dict:
|
||||
exchanges = enabled_exchanges()
|
||||
reset_hour = _trading_day_reset_hour()
|
||||
trading_day = current_trading_day(reset_hour=reset_hour)
|
||||
async with httpx.AsyncClient() as client:
|
||||
agent_rows = await asyncio.gather(
|
||||
*[_fetch_agent_status(client, ex) for ex in exchanges]
|
||||
)
|
||||
out = await asyncio.gather(
|
||||
*[
|
||||
_assemble_board_row(client, ex, agent_row)
|
||||
_assemble_board_row(client, ex, agent_row, trading_day=trading_day)
|
||||
for ex, agent_row in zip(exchanges, agent_rows)
|
||||
]
|
||||
)
|
||||
rows = list(out)
|
||||
totals = aggregate_monitor_board_totals(
|
||||
rows, trading_day=trading_day, reset_hour=reset_hour
|
||||
)
|
||||
return {
|
||||
"rows": list(out),
|
||||
"rows": rows,
|
||||
"totals": totals,
|
||||
"updated_at": __import__("datetime").datetime.now().isoformat(timespec="seconds"),
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user