Sync exchange PnL when hub loads trade records API.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-21 09:40:42 +08:00
parent 6def61fae3
commit 1bc12a32c6
4 changed files with 82 additions and 2 deletions
+11 -1
View File
@@ -2440,6 +2440,11 @@ def insert_trade_record(
opened_at_ms=open_ts_ms,
closed_at_ms=close_ts_ms,
)
# 中控只拉 /api/trade_records,平仓当下也尝试回填交易所盈亏(内部 25s 节流)
try:
sync_trade_records_from_exchange(conn, force=False)
except Exception:
pass
return tid
@@ -6942,7 +6947,11 @@ def sync_trade_records_from_exchange(conn, force=False):
matched += 1
stats["matched"] = matched
stats["ok"] = True
_LAST_EXCHANGE_PNL_SYNC_AT = now
# 仍有未匹配且历史非空:缩短节流,避免平仓后历史稍晚入库时卡在「估」
if matched < stats["pending"] and hist:
_LAST_EXCHANGE_PNL_SYNC_AT = now - 15.0
else:
_LAST_EXCHANGE_PNL_SYNC_AT = now
try:
conn.commit()
except Exception:
@@ -9378,6 +9387,7 @@ register_trade_records_api(
filter_trade_records_excluding_miss=filter_trade_records_excluding_miss,
app_tz=APP_TZ,
format_price_fn=format_price_for_symbol,
sync_exchange_pnl_fn=lambda conn: sync_trade_records_from_exchange(conn, force=False),
)
def _dashboard_enrich_orders(items):
+11 -1
View File
@@ -2359,6 +2359,11 @@ def insert_trade_record(
opened_at_ms=open_ts_ms,
closed_at_ms=close_ts_ms,
)
# 中控只拉 /api/trade_records,平仓当下也尝试回填交易所盈亏(内部 25s 节流)
try:
sync_trade_records_from_exchange(conn, force=False)
except Exception:
pass
return tid
@@ -4090,7 +4095,11 @@ def sync_trade_records_from_exchange(conn, force=False):
matched += 1
stats["matched"] = matched
stats["ok"] = True
_LAST_EXCHANGE_PNL_SYNC_AT = now
# 仍有未匹配且历史非空:缩短节流,避免平仓后历史稍晚入库时卡在「估」
if matched < stats["pending"] and hist:
_LAST_EXCHANGE_PNL_SYNC_AT = now - 15.0
else:
_LAST_EXCHANGE_PNL_SYNC_AT = now
try:
conn.commit()
except Exception:
@@ -9066,6 +9075,7 @@ register_trade_records_api(
filter_trade_records_excluding_miss=filter_trade_records_excluding_miss,
app_tz=APP_TZ,
format_price_fn=format_price_for_symbol,
sync_exchange_pnl_fn=lambda conn: sync_trade_records_from_exchange(conn, force=False),
)
+10
View File
@@ -19,7 +19,12 @@ def register_trade_records_api(
filter_trade_records_excluding_miss: Callable[[list], list],
app_tz: Any,
format_price_fn: Callable[[Any, Any], str] | None = None,
sync_exchange_pnl_fn: Callable[[Any], Any] | None = None,
) -> None:
"""
sync_exchange_pnl_fn(conn): 可选,列表前节流回填交易所已实现盈亏.
中控只走本 API,不经实例整页渲染,必须在此触发,否则盈亏U会一直显示「估」.
"""
from lib.instance.records_list_lib import list_trade_records_page
@app.route("/api/trade_records")
@@ -40,6 +45,11 @@ def register_trade_records_api(
offset = 0
conn = get_db()
try:
if sync_exchange_pnl_fn is not None:
try:
sync_exchange_pnl_fn(conn)
except Exception:
pass
payload = list_trade_records_page(
conn,
start_bj,
+50
View File
@@ -0,0 +1,50 @@
"""records_api_register: list API can trigger exchange pnl sync."""
from __future__ import annotations
import unittest
from unittest.mock import MagicMock
from flask import Flask
from lib.instance.records_api_register import register_trade_records_api
class RecordsApiSyncHookTest(unittest.TestCase):
def test_list_calls_sync_hook(self):
app = Flask(__name__)
sync_calls = []
def _login(fn):
return fn
def _get_db():
conn = MagicMock()
conn.execute.return_value.fetchall.return_value = []
return conn
def _win():
return {"start_utc": None, "end_utc": None}
register_trade_records_api(
app,
login_required=_login,
get_db=_get_db,
list_window_from_request=_win,
utc_window_to_bj_sql_strings=lambda *a, **k: ("2026-01-01", "2026-12-31"),
sql_list_time_field=lambda *a, **k: "COALESCE(closed_at, created_at, opened_at)",
to_effective_trade_dict=lambda r: dict(r) if hasattr(r, "keys") else r,
filter_trade_records_excluding_miss=lambda xs: xs,
app_tz=None,
sync_exchange_pnl_fn=lambda conn: sync_calls.append(conn),
)
client = app.test_client()
resp = client.get("/api/trade_records")
self.assertEqual(resp.status_code, 200)
self.assertEqual(len(sync_calls), 1)
body = resp.get_json()
self.assertTrue(body.get("ok"))
if __name__ == "__main__":
unittest.main()