Align funds bar and win-rate with OO expiry intrinsic repair; wire Binance balances.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-11 17:18:16 +08:00
parent 3520fc0214
commit fc74b8e913
5 changed files with 254 additions and 20 deletions
+130 -1
View File
@@ -330,9 +330,122 @@ def _enrich_group(g: dict, fills: list) -> dict:
if is_oo:
g["option2_leverage"] = _option_leverage_for_leg(g, fills, leg="option2")
g["_view_fills"] = view_fills
g["_overlay_settle"] = float(settle) if settle is not None else None
g["_overlaid"] = overlaid
return g
def persist_expiry_overlay_if_needed(db: Any, g: dict, raw_fills: list) -> None:
"""把内在价值覆盖写回库:fills / realized_pnl / settle_index,并补本地账本差额。"""
if not g.get("_overlaid"):
return
view_fills = g.get("_view_fills") or []
net = g.get("net_pnl")
if net is None:
return
settle = g.get("_overlay_settle")
group_id = str(g.get("group_id") or "")
if not group_id:
return
old_net = float(g.get("realized_pnl") or 0)
# 已对齐则跳过(避免每次列表刷库)
if abs(old_net - float(net)) < 0.02:
stored = g.get("settle_index_px")
try:
if settle is None or (
stored is not None and abs(float(stored) - float(settle)) < 0.05
):
# 仍可能 fills 未写回;检查是否还有 overlay 标记需要落库
pass
else:
pass
except (TypeError, ValueError):
pass
raw_close = {
str(dict(f).get("leg")): dict(f)
for f in raw_fills
if str(dict(f).get("action") or "") == "close"
and str(dict(f).get("leg") or "") in ("option", "option2")
}
cash_delta = 0.0
updates: list[tuple] = []
for vf in view_fills:
if not isinstance(vf, dict) or not vf.get("_overlay_intrinsic"):
continue
leg = str(vf.get("leg") or "")
old = raw_close.get(leg)
if not old or old.get("id") is None:
continue
try:
old_px = float(old.get("fill_px") or 0)
new_px = float(vf.get("fill_px") or 0)
qty = float(vf.get("qty_eth") or old.get("qty_eth") or 0)
except (TypeError, ValueError):
continue
if abs(old_px - new_px) <= 1e-9:
continue
cash_delta += (new_px - old_px) * qty
updates.append(
(
new_px,
new_px,
new_px * qty,
int(old["id"]),
)
)
if not updates and abs(old_net - float(net)) < 0.02:
# 只缺 settle
if settle is None:
return
try:
if g.get("settle_index_px") is not None and abs(
float(g["settle_index_px"]) - float(settle)
) < 0.05:
return
except (TypeError, ValueError):
pass
with db._lock:
for base, fill, notional, fid in updates:
db._conn.execute(
"UPDATE fills SET base_px=?, fill_px=?, notional=?, slip=0 WHERE id=?",
(base, fill, notional, fid),
)
db._conn.execute(
"""UPDATE groups SET realized_pnl=?,
settle_index_px=COALESCE(?, settle_index_px),
note=CASE
WHEN instr(COALESCE(note,''), 'expiry_intrinsic_repair')>0 THEN note
ELSE trim(COALESCE(note,'') || ' | expiry_intrinsic_repair')
END
WHERE group_id=? AND status='closed'""",
(
float(net),
float(settle) if settle is not None else None,
group_id,
),
)
db._conn.commit()
if abs(cash_delta) > 1e-9:
try:
from ..sim.ledger import Ledger
Ledger(db).apply_cash(
cash_delta,
kind="repair_option_intrinsic",
group_id=group_id,
note=f"expiry intrinsic overlay cash_delta={cash_delta:.4f}",
allow_negative=True,
)
except Exception:
pass
# 刷新内存中的 realized,供同请求后续使用
g["realized_pnl"] = float(net)
@router.get("/groups")
async def list_groups(_user: Annotated[str, Depends(require_user)]) -> dict:
db = get_db()
@@ -345,7 +458,13 @@ async def list_groups(_user: Annotated[str, Depends(require_user)]) -> dict:
(g["group_id"],),
)
gr = _enrich_group(g, fills)
try:
persist_expiry_overlay_if_needed(db, gr, list(fills))
except Exception:
pass
gr.pop("_view_fills", None)
gr.pop("_overlay_settle", None)
gr.pop("_overlaid", None)
groups.append(gr)
return {"groups": groups}
@@ -362,11 +481,21 @@ async def group_detail(
"SELECT * FROM fills WHERE group_id=? ORDER BY id ASC", (group_id,)
)
gr = _enrich_group(_row(g), fills)
try:
persist_expiry_overlay_if_needed(db, gr, list(fills))
except Exception:
pass
view_fills = gr.pop("_view_fills", None) or fills
gr.pop("_overlay_settle", None)
gr.pop("_overlaid", None)
return {
"group": gr,
"fills": [
{k: v for k, v in (dict(x) if not isinstance(x, dict) else x).items() if k != "_overlay_intrinsic"}
{
k: v
for k, v in (dict(x) if not isinstance(x, dict) else x).items()
if k != "_overlay_intrinsic"
}
for x in view_fills
],
"pnl_summary": gr.get("pnl_summary"),