Align option display precision with OKX and use exchange position history.

Format prices by tickSz, move bid depth/recovery to card end with plain styling, and load option history from OKX positions-history instead of local DB.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-11 09:14:43 +08:00
parent 1b31c61aac
commit fdea5bb610
6 changed files with 396 additions and 156 deletions
+104 -45
View File
@@ -189,6 +189,27 @@ def _refresh_position_avail(cfg: dict[str, Any], ex: Any, inst_id: str) -> int |
return _position_avail_sheets(pos)
def _enrich_position_row_display(
cfg: dict[str, Any],
ex: Any,
raw_pos: dict[str, Any],
*,
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
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
def _attach_close_preview(
cfg: dict[str, Any],
ex: Any,
@@ -469,24 +490,33 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
if raw is None:
return jsonify({"ok": False, "msg": "获取期权持仓失败"})
_sync_options_trades(cfg, raw_positions=raw)
rows = [cfg["format_position_row"](p) for p in raw]
meta_cache: dict[str, dict[str, Any] | None] = {}
conn = cfg["get_db"]()
try:
for row in rows:
inst = row.get("inst_id")
if not inst:
continue
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:
row["premium_paid"] = round(float(rec["premium_paid"]), 4)
rows = []
for p in raw:
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,
)
_attach_close_preview(cfg, ex, row, premium_paid=_safe_float(row.get("premium_paid")))
rows.append(row)
finally:
conn.close()
return jsonify({"ok": True, "positions": rows})
@@ -838,24 +868,66 @@ 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})
_sync_options_trades(cfg)
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]] = []
raw_live = cfg["fetch_option_positions"](ex)
if raw_live is None:
return jsonify({"ok": False, "msg": "获取期权持仓失败"})
conn = cfg["get_db"]()
try:
init_options_tables(conn)
rows = conn.execute(
"""
SELECT id, inst_id, underlying, opt_type, strike, sheets, eth_amount,
open_quote, premium_paid, close_quote, premium_received,
realized_pnl, status, signal_note, created_at, closed_at
FROM options_trades
ORDER BY id DESC
LIMIT 200
"""
).fetchall()
items = [dict(r) for r in rows]
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()
return jsonify({"ok": True, "history": items})
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 = open_rows + closed
live_ids = {str(x.get("inst_id") or "") for x in open_rows}
return jsonify({"ok": True, "history": history, "live_inst_ids": sorted(live_ids)})
@app.route("/api/options/stats")
@lr
@@ -867,26 +939,13 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
return jsonify({"ok": True, **compute_options_stats(cfg["get_db"])})
@app.route("/api/options/history/<int:trade_id>", methods=["DELETE"])
@app.route("/api/options/history/<path:trade_id>", methods=["DELETE"])
@lr
def api_options_history_delete(trade_id: int):
def api_options_history_delete(trade_id: str):
ex, err = _require_options_ex(cfg)
if ex is None:
return jsonify({"ok": False, "msg": err})
conn = cfg["get_db"]()
try:
init_options_tables(conn)
row = conn.execute(
"SELECT id, status FROM options_trades WHERE id = ?",
(trade_id,),
).fetchone()
if not row:
return jsonify({"ok": False, "msg": "记录不存在"})
conn.execute("DELETE FROM options_trades WHERE id = ?", (trade_id,))
conn.commit()
finally:
conn.close()
return jsonify({"ok": True})
return jsonify({"ok": False, "msg": "历史仓位来自交易所,不支持本地删除"})
def _start_monitor_thread(app: Flask, cfg: dict[str, Any]) -> None:
+2 -3
View File
@@ -198,11 +198,10 @@
<th>状态</th>
<th>盈亏</th>
<th>时间</th>
<th>操作</th>
</tr>
</thead>
<tbody id="opt-history-tbody">
<tr><td colspan="7" class="muted">加载中…</td></tr>
<tr><td colspan="6" class="muted">加载中…</td></tr>
</tbody>
</table>
</div>
@@ -212,4 +211,4 @@
</div>
</div>
<script src="/static/options_expiry_countdown.js?v=1"></script>
<script src="/static/options_panel.js?v=23"></script>
<script src="/static/options_panel.js?v=24"></script>