fix(hub): merge strategy snapshots into archive for gate_bot
Include strategy_trade_snapshots when trade_records is empty, harden SQL for older schemas, and show per-exchange sync errors in the archive UI. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -95,6 +95,7 @@ DIR = Path(__file__).resolve().parent
|
||||
HUB_BUILD = "20260607-hub-archive"
|
||||
_archive_sync_stop: asyncio.Event | None = None
|
||||
_archive_sync_task: asyncio.Task | None = None
|
||||
_last_archive_sync: dict | None = None
|
||||
HUB_AGENT_TIMEOUT = float(os.getenv("HUB_AGENT_TIMEOUT", "8"))
|
||||
HUB_FLASK_TIMEOUT = float(os.getenv("HUB_FLASK_TIMEOUT", "10"))
|
||||
HUB_BOARD_TIMEOUT = float(os.getenv("HUB_BOARD_TIMEOUT", "45"))
|
||||
@@ -239,6 +240,7 @@ def _schedule_board_refresh() -> None:
|
||||
|
||||
|
||||
async def _run_archive_sync_once() -> dict:
|
||||
global _last_archive_sync
|
||||
init_archive_db()
|
||||
settings = load_settings()
|
||||
targets = enabled_exchanges(settings)
|
||||
@@ -254,11 +256,25 @@ async def _run_archive_sync_once() -> dict:
|
||||
limit=ARCHIVE_TRADE_LIMIT,
|
||||
)
|
||||
if not trades_resp.get("ok"):
|
||||
st = trades_resp.get("status")
|
||||
msg = (
|
||||
trades_resp.get("msg")
|
||||
or trades_resp.get("error")
|
||||
or trades_resp.get("detail")
|
||||
or "拉取交易失败"
|
||||
)
|
||||
if st == 404:
|
||||
msg = (
|
||||
"HTTP 404:该 Flask 未注册 /api/hub/trades/archive。"
|
||||
"请在仓库根目录 git pull 后 pm2 restart crypto_gate crypto_gate_bot"
|
||||
)
|
||||
results.append(
|
||||
{
|
||||
"exchange_key": ex_key,
|
||||
"name": ex.get("name"),
|
||||
"ok": False,
|
||||
"msg": trades_resp.get("msg") or trades_resp.get("error") or "拉取交易失败",
|
||||
"status": st,
|
||||
"msg": msg,
|
||||
}
|
||||
)
|
||||
continue
|
||||
@@ -282,8 +298,17 @@ async def _run_archive_sync_once() -> dict:
|
||||
trades,
|
||||
remote_fetch,
|
||||
)
|
||||
r["name"] = ex.get("name")
|
||||
r["trade_count"] = len(trades)
|
||||
results.append(r)
|
||||
return {"ok": True, "exchanges": len(targets), "results": results}
|
||||
out = {
|
||||
"ok": True,
|
||||
"exchanges": len(targets),
|
||||
"results": results,
|
||||
"updated_at": __import__("datetime").datetime.now().isoformat(timespec="seconds"),
|
||||
}
|
||||
_last_archive_sync = out
|
||||
return out
|
||||
|
||||
|
||||
async def _archive_sync_loop() -> None:
|
||||
@@ -549,9 +574,13 @@ def _fetch_instance_trades_archive_sync(
|
||||
if r.status_code >= 400:
|
||||
parsed = _parse_http_json_body(r)
|
||||
parsed.setdefault("ok", False)
|
||||
parsed.setdefault("status", r.status_code)
|
||||
return parsed
|
||||
data = r.json() if r.content else {}
|
||||
return data if isinstance(data, dict) else {"ok": False, "msg": "无效 JSON"}
|
||||
if isinstance(data, dict):
|
||||
data.setdefault("ok", True)
|
||||
return data
|
||||
return {"ok": False, "msg": "无效 JSON"}
|
||||
except Exception as e:
|
||||
return {"ok": False, "msg": str(e)}
|
||||
|
||||
@@ -1662,6 +1691,7 @@ def api_archive_meta():
|
||||
"sync_interval_sec": ARCHIVE_SYNC_INTERVAL_SEC,
|
||||
"visible_bars_default": ARCHIVE_VISIBLE_BARS_DEFAULT,
|
||||
"exchanges": exchanges,
|
||||
"last_sync": _last_archive_sync,
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user