Compute option stats from exchange history instead of local DB.

Share history loading between history and stats APIs so average profit/loss matches the option history tab.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-11 09:34:49 +08:00
parent 68733eb4f9
commit baf94b4feb
5 changed files with 198 additions and 87 deletions
+18 -82
View File
@@ -197,17 +197,15 @@ def _enrich_position_row_display(
meta_cache: dict[str, dict[str, Any] | None] | None = None,
premium_override: float | None = None,
) -> dict[str, Any]:
from lib.exchange.okx_options_lib import format_position_row, tick_sz_and_ct_mult
from lib.options.options_history_lib import enrich_position_row_display
inst_id = str(raw_pos.get("instId") or "").strip()
tick_sz, ct_mult = tick_sz_and_ct_mult(ex, inst_id, meta_cache)
row = format_position_row(raw_pos, ct_mult=ct_mult, tick_sz=tick_sz)
if premium_override is not None:
row["premium_paid"] = premium_override
from lib.exchange.okx_options_lib import format_usdc_amount
row["premium_paid_fmt"] = format_usdc_amount(premium_override)
return row
return enrich_position_row_display(
cfg,
ex,
raw_pos,
meta_cache=meta_cache,
premium_override=premium_override,
)
def _attach_close_preview(
@@ -868,80 +866,13 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
ex, err = _require_options_ex(cfg)
if ex is None:
return jsonify({"ok": False, "msg": err})
from lib.exchange.okx_options_lib import (
fetch_all_option_positions_history,
format_live_option_history_row,
format_option_history_row,
tick_sz_and_ct_mult,
)
meta_cache: dict[str, dict[str, Any] | None] = {}
items: list[dict[str, Any]] = []
from lib.options.options_history_lib import load_options_history
raw_live = cfg["fetch_option_positions"](ex)
if raw_live is None:
return jsonify({"ok": False, "msg": "获取期权持仓失败"})
conn = cfg["get_db"]()
try:
for p in raw_live:
inst = str(p.get("instId") or "").strip()
premium_override = None
if inst:
rec = conn.execute(
"""
SELECT premium_paid FROM options_trades
WHERE inst_id = ? AND status = 'open'
ORDER BY id DESC LIMIT 1
""",
(inst,),
).fetchone()
if rec and rec["premium_paid"] is not None:
premium_override = float(rec["premium_paid"])
row = _enrich_position_row_display(
cfg,
ex,
p,
meta_cache=meta_cache,
premium_override=premium_override,
)
open_ms = None
ctime = p.get("cTime") or (row.get("raw") or {}).get("cTime")
try:
if ctime is not None and str(ctime).strip():
open_ms = int(float(ctime))
except (TypeError, ValueError):
open_ms = None
items.append(format_live_option_history_row(row, open_ms=open_ms))
finally:
conn.close()
hidden_keys: set[str] = set()
conn = cfg["get_db"]()
try:
init_options_tables(conn)
hidden_keys = {
str(r["history_key"])
for r in conn.execute("SELECT history_key FROM options_history_hidden").fetchall()
}
finally:
conn.close()
hist_raw = fetch_all_option_positions_history(ex, limit=200)
for raw in hist_raw:
inst_id = str(raw.get("instId") or "").strip()
tick_sz, ct_mult = tick_sz_and_ct_mult(ex, inst_id, meta_cache)
items.append(format_option_history_row(raw, tick_sz=tick_sz, ct_mult=ct_mult))
open_rows = [x for x in items if x.get("status") == "open"]
closed = [x for x in items if x.get("status") != "open"]
closed.sort(key=lambda x: int(x.get("close_ms") or 0), reverse=True)
open_rows.sort(key=lambda x: int(x.get("close_ms") or 0), reverse=True)
history = [
x
for x in (open_rows + closed)
if str(x.get("history_key") or "") not in hidden_keys
]
live_ids = {str(x.get("inst_id") or "") for x in open_rows}
history = load_options_history(ex, cfg)
live_ids = {str(x.get("inst_id") or "") for x in history if x.get("status") == "open"}
return jsonify({"ok": True, "history": history, "live_inst_ids": sorted(live_ids)})
@app.route("/api/options/stats")
@@ -950,9 +881,14 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
ex, err = _require_options_ex(cfg)
if ex is None:
return jsonify({"ok": False, "msg": err})
from lib.options.options_stats_lib import compute_options_stats
from lib.options.options_history_lib import load_options_history
from lib.options.options_stats_lib import compute_options_stats_from_history
return jsonify({"ok": True, **compute_options_stats(cfg["get_db"])})
raw_live = cfg["fetch_option_positions"](ex)
if raw_live is None:
return jsonify({"ok": False, "msg": "获取期权持仓失败"})
history = load_options_history(ex, cfg)
return jsonify({"ok": True, **compute_options_stats_from_history(history)})
@app.route("/api/options/history/<path:history_key>", methods=["DELETE"])
@lr