修复读取
This commit is contained in:
+78
-35
@@ -4742,59 +4742,83 @@ def fetch_gate_positions_close_history():
|
||||
return []
|
||||
ensure_markets_loaded()
|
||||
since_ms = exchange_position_sync_since_ms()
|
||||
try:
|
||||
rows = exchange.fetch_positions_history(
|
||||
None,
|
||||
since=int(since_ms),
|
||||
limit=int(EXCHANGE_POSITION_HISTORY_LIMIT),
|
||||
params={"settle": "usdt"},
|
||||
)
|
||||
except Exception:
|
||||
try:
|
||||
rows = exchange.fetch_positions_history(
|
||||
None,
|
||||
since=int(since_ms),
|
||||
limit=int(EXCHANGE_POSITION_HISTORY_LIMIT),
|
||||
params={},
|
||||
)
|
||||
except Exception:
|
||||
return []
|
||||
until_ms = int(time.time() * 1000)
|
||||
out = []
|
||||
for p in rows or []:
|
||||
h = _normalize_gate_position_history_entry(p)
|
||||
if h and h["close_ms"] and h["side"] in ("long", "short") and h["symbol_u"]:
|
||||
out.append(h)
|
||||
return out
|
||||
offset = 0
|
||||
page_limit = min(100, int(EXCHANGE_POSITION_HISTORY_LIMIT))
|
||||
max_total = int(EXCHANGE_POSITION_HISTORY_LIMIT)
|
||||
|
||||
def _pull(params_extra):
|
||||
nonlocal offset
|
||||
offset = 0
|
||||
while len(out) < max_total:
|
||||
params = dict(params_extra)
|
||||
params["offset"] = offset
|
||||
params["until"] = until_ms
|
||||
try:
|
||||
rows = exchange.fetch_positions_history(
|
||||
None,
|
||||
since=int(since_ms),
|
||||
limit=page_limit,
|
||||
params=params,
|
||||
)
|
||||
except Exception:
|
||||
return False
|
||||
if not rows:
|
||||
break
|
||||
for p in rows:
|
||||
h = _normalize_gate_position_history_entry(p)
|
||||
if h and h["close_ms"] and h["side"] in ("long", "short") and h["symbol_u"]:
|
||||
out.append(h)
|
||||
offset += len(rows)
|
||||
if len(rows) < page_limit:
|
||||
break
|
||||
return True
|
||||
|
||||
if not _pull({"settle": "usdt"}):
|
||||
_pull({})
|
||||
return out[:max_total]
|
||||
|
||||
|
||||
def sync_trade_records_from_exchange(conn):
|
||||
"""为未同步的 trade_records 回填 Gate 平仓历史中的已实现盈亏。"""
|
||||
def sync_trade_records_from_exchange(conn, force=False):
|
||||
"""为未同步的 trade_records 回填 Gate 平仓历史中的已实现盈亏。返回统计 dict。"""
|
||||
global _LAST_EXCHANGE_PNL_SYNC_AT
|
||||
stats = {"ok": False, "hist_count": 0, "matched": 0, "pending": 0, "skipped": False}
|
||||
if not exchange_private_api_configured():
|
||||
return
|
||||
stats["reason"] = "未配置 GATE_API_KEY / GATE_API_SECRET"
|
||||
return stats
|
||||
now = time.time()
|
||||
if now - _LAST_EXCHANGE_PNL_SYNC_AT < 25.0:
|
||||
return
|
||||
if not force and now - _LAST_EXCHANGE_PNL_SYNC_AT < 25.0:
|
||||
stats["ok"] = True
|
||||
stats["skipped"] = True
|
||||
return stats
|
||||
try:
|
||||
hist = fetch_gate_positions_close_history()
|
||||
except Exception:
|
||||
return
|
||||
except Exception as e:
|
||||
stats["reason"] = str(e)
|
||||
return stats
|
||||
stats["hist_count"] = len(hist)
|
||||
if not hist:
|
||||
_LAST_EXCHANGE_PNL_SYNC_AT = now
|
||||
return
|
||||
stats["ok"] = True
|
||||
stats["reason"] = "交易所平仓历史为空(请检查 API 权限或 EXCHANGE_POSITION_SYNC_FROM_BJ)"
|
||||
return stats
|
||||
candidates = conn.execute(
|
||||
"""
|
||||
SELECT id, symbol, direction, closed_at, closed_at_ms, opened_at, opened_at_ms
|
||||
FROM trade_records
|
||||
WHERE (exchange_sync_key IS NULL OR TRIM(exchange_sync_key) = '')
|
||||
OR exchange_realized_pnl IS NULL
|
||||
ORDER BY id DESC
|
||||
LIMIT 200
|
||||
"""
|
||||
).fetchall()
|
||||
stats["pending"] = len(candidates)
|
||||
if not candidates:
|
||||
stats["ok"] = True
|
||||
_LAST_EXCHANGE_PNL_SYNC_AT = now
|
||||
return
|
||||
return stats
|
||||
used = set()
|
||||
matched = 0
|
||||
for tr in candidates:
|
||||
close_ms_trade = _to_ms_with_fallback(
|
||||
tr["closed_at_ms"] if "closed_at_ms" in tr.keys() else None, tr["closed_at"]
|
||||
@@ -4848,11 +4872,15 @@ def sync_trade_records_from_exchange(conn):
|
||||
(float(pnl_val), eo, ec, sk, int(tr["id"])),
|
||||
)
|
||||
used.add(sk)
|
||||
matched += 1
|
||||
stats["matched"] = matched
|
||||
stats["ok"] = True
|
||||
_LAST_EXCHANGE_PNL_SYNC_AT = now
|
||||
try:
|
||||
conn.commit()
|
||||
except Exception:
|
||||
pass
|
||||
return stats
|
||||
|
||||
|
||||
# ====================== 主页面 ======================
|
||||
@@ -4874,11 +4902,12 @@ def render_main_page(page="trade"):
|
||||
order_list = []
|
||||
for o in raw_order_list:
|
||||
order_list.append(enrich_order_item(row_to_dict(o), current_capital))
|
||||
exchange_pnl_sync = {}
|
||||
if exchange_private_api_configured():
|
||||
try:
|
||||
sync_trade_records_from_exchange(conn)
|
||||
except Exception:
|
||||
pass
|
||||
exchange_pnl_sync = sync_trade_records_from_exchange(conn) or {}
|
||||
except Exception as e:
|
||||
exchange_pnl_sync = {"ok": False, "reason": str(e)}
|
||||
raw_records = conn.execute("SELECT * FROM trade_records ORDER BY id DESC").fetchall()
|
||||
records = [to_effective_trade_dict(r) for r in raw_records]
|
||||
total = len(records)
|
||||
@@ -4947,9 +4976,23 @@ def render_main_page(page="trade"):
|
||||
key_auto_min_planned_rr=KEY_AUTO_MIN_PLANNED_RR,
|
||||
key_gate_rule_text=key_gate_rule_text,
|
||||
kline_timeframe=KLINE_TIMEFRAME,
|
||||
exchange_pnl_sync=exchange_pnl_sync,
|
||||
)
|
||||
|
||||
|
||||
@app.route("/api/sync_exchange_pnl")
|
||||
@login_required
|
||||
def api_sync_exchange_pnl():
|
||||
conn = get_db()
|
||||
stats = sync_trade_records_from_exchange(conn, force=True)
|
||||
try:
|
||||
conn.commit()
|
||||
except Exception:
|
||||
pass
|
||||
conn.close()
|
||||
return jsonify(stats)
|
||||
|
||||
|
||||
@app.route("/")
|
||||
@login_required
|
||||
def index():
|
||||
|
||||
Reference in New Issue
Block a user