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:
+104
-45
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user