diff --git a/lib/common/static/options_panel.js b/lib/common/static/options_panel.js index 35716c4..21bf756 100644 --- a/lib/common/static/options_panel.js +++ b/lib/common/static/options_panel.js @@ -444,6 +444,7 @@ '张数: ' + fmt(p.pos, 0) + " · 币量 " + fmt(p.eth_amount, 4) + "" + "" + '
' + + '
权利金' + fmt(p.premium_paid, 4) + " USDC
" + '
开仓均价' + fmt(p.avg_px, 4) + "
" + '
标记价' + fmt(p.mark_px, 4) + "
" + '
指数价' + fmt(p.idx_px, 0) + "
" + diff --git a/lib/exchange/okx_options_lib.py b/lib/exchange/okx_options_lib.py index 62df52d..8d002cb 100644 --- a/lib/exchange/okx_options_lib.py +++ b/lib/exchange/okx_options_lib.py @@ -216,6 +216,17 @@ def inst_family_from_inst_id(inst_id: str) -> str | None: return "-".join(parts[:-3]) +def option_fields_from_inst_id(inst_id: str) -> tuple[str | None, float | None]: + """从 instId 解析 optType 与 strike,如 ETH-USD_UM-260709-1700-P。""" + parts = (inst_id or "").strip().split("-") + if len(parts) < 2: + return None, None + tail = parts[-1].upper() + opt_type = tail if tail in ("C", "P") else None + strike = _safe_float(parts[-2]) if len(parts) >= 2 else None + return opt_type, strike + + def fetch_option_instrument_meta(ex: ccxt.okx, inst_id: str) -> dict[str, Any] | None: family = inst_family_from_inst_id(inst_id) if not family: @@ -778,6 +789,7 @@ def format_position_row(pos: dict[str, Any], ct_mult: float = 0.01) -> dict[str, close_breakeven_idx, expiry_breakeven_px, idx_distance_to_be, + total_premium, ) sheets = _safe_float(pos.get("pos")) or 0.0 @@ -786,8 +798,18 @@ def format_position_row(pos: dict[str, Any], ct_mult: float = 0.01) -> dict[str, upl = _safe_float(pos.get("upl")) upl_ratio = _safe_float(pos.get("uplRatio")) idx_px = _safe_float(pos.get("idxPx")) + inst_id = str(pos.get("instId") or "") opt_type = pos.get("optType") strike = _safe_float(pos.get("stk")) + parsed_type, parsed_strike = option_fields_from_inst_id(inst_id) + if not opt_type: + opt_type = parsed_type + if strike is None: + strike = parsed_strike + eth_amount = round(abs(sheets) * ct_mult, 8) + premium_paid = ( + round(total_premium(avg, eth_amount), 4) if avg is not None and eth_amount > 0 else None + ) delta_pa = _safe_float(pos.get("deltaPA")) expiry_be = expiry_breakeven_px( opt_type=str(opt_type or ""), @@ -805,12 +827,13 @@ def format_position_row(pos: dict[str, Any], ct_mult: float = 0.01) -> dict[str, ct_mult=ct_mult, ) return { - "inst_id": pos.get("instId"), + "inst_id": inst_id or pos.get("instId"), "pos": sheets, - "eth_amount": round(abs(sheets) * ct_mult, 8), + "eth_amount": eth_amount, "avg_px": avg, "mark_px": mark, "idx_px": idx_px, + "premium_paid": premium_paid, "upl": upl, "upl_ratio_pct": round(upl_ratio * 100, 2) if upl_ratio is not None else None, "exp_time": pos.get("expTime"), diff --git a/lib/options/options_register.py b/lib/options/options_register.py index 45903e3..d17e508 100644 --- a/lib/options/options_register.py +++ b/lib/options/options_register.py @@ -306,6 +306,24 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None: return jsonify({"ok": False, "msg": err}) raw = cfg["fetch_option_positions"](ex) rows = [cfg["format_position_row"](p) for p in raw] + 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) + finally: + conn.close() return jsonify({"ok": True, "positions": rows}) @app.route("/api/options/close", methods=["POST"]) diff --git a/lib/options/templates/options_panel.html b/lib/options/templates/options_panel.html index 2a1b8d3..40aa664 100644 --- a/lib/options/templates/options_panel.html +++ b/lib/options/templates/options_panel.html @@ -136,4 +136,4 @@
- + diff --git a/tests/test_options_pricing.py b/tests/test_options_pricing.py index 295bdb9..df94e80 100644 --- a/tests/test_options_pricing.py +++ b/tests/test_options_pricing.py @@ -202,6 +202,25 @@ def test_format_options_breakeven_line(): assert "指数3480" in s +def test_format_position_row_premium_and_inst_parse(): + from lib.exchange.okx_options_lib import format_position_row + + row = format_position_row( + { + "instId": "ETH-USD_UM-260709-1700-P", + "pos": "20", + "avgPx": "6.2", + "markPx": "6.3241", + "idxPx": "1746", + "upl": "0.0248", + "uplRatio": "0.02", + } + ) + assert row["opt_type"] == "P" + assert row["strike"] == 1700.0 + assert row["premium_paid"] == 1.24 + + def test_format_position_row_breakeven(): from lib.exchange.okx_options_lib import format_position_row