e261df0009
Co-authored-by: Cursor <cursoragent@cursor.com>
150 lines
5.8 KiB
Python
150 lines
5.8 KiB
Python
"""期权历史列表(交易所 positions-history + 当前持仓)."""
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from lib.exchange.okx_options_lib import format_premium_amount
|
|
from lib.options.options_db import init_options_tables, sum_open_premium_paid
|
|
from lib.options.options_margin_mode_lib import margin_mode_from_inst_id, premium_ccy_for_mode
|
|
|
|
|
|
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)
|
|
row_mode = margin_mode_from_inst_id(inst_id) if inst_id else "usdc"
|
|
underly = str(row.get("underlying") or (inst_id.split("-")[0] if inst_id else "ETH") or "ETH")
|
|
premium_ccy = premium_ccy_for_mode(row_mode, underly)
|
|
row["margin_mode"] = row_mode
|
|
row["premium_ccy"] = premium_ccy
|
|
row["margin_mode_label"] = "币本位" if row_mode == "coin" else "USDC"
|
|
if premium_override is not None:
|
|
row["premium_paid"] = premium_override
|
|
row["premium_paid_fmt"] = format_premium_amount(row.get("premium_paid"), ccy=premium_ccy)
|
|
return row
|
|
|
|
|
|
def _safe_float(v: Any) -> float | None:
|
|
if v is None or v == "":
|
|
return None
|
|
try:
|
|
return float(v)
|
|
except (TypeError, ValueError):
|
|
return None
|
|
|
|
|
|
def _load_local_closed_by_inst(conn: Any) -> dict[str, dict[str, Any]]:
|
|
"""同合约取最新已平本地单,用于补交易所历史权利金/盈亏."""
|
|
out: dict[str, dict[str, Any]] = {}
|
|
try:
|
|
rows = conn.execute(
|
|
"""
|
|
SELECT inst_id, premium_paid, realized_pnl, premium_ccy, margin_mode,
|
|
open_quote, close_quote, sheets, closed_at
|
|
FROM options_trades
|
|
WHERE status = 'closed'
|
|
ORDER BY id DESC
|
|
"""
|
|
).fetchall()
|
|
except Exception:
|
|
return out
|
|
for r in rows:
|
|
inst = str(r["inst_id"] or "").strip()
|
|
if not inst or inst in out:
|
|
continue
|
|
out[inst] = dict(r)
|
|
return out
|
|
|
|
|
|
def _overlay_local_closed(row: dict[str, Any], local: dict[str, Any] | None) -> dict[str, Any]:
|
|
if not local:
|
|
return row
|
|
prem = _safe_float(row.get("premium_paid"))
|
|
pnl = _safe_float(row.get("realized_pnl"))
|
|
local_prem = _safe_float(local.get("premium_paid"))
|
|
local_pnl = _safe_float(local.get("realized_pnl"))
|
|
# 交易所缺数或被两位小数抹成 0 时,用本地币本位落库值
|
|
if (prem is None or abs(prem) < 1e-10) and local_prem is not None and abs(local_prem) > 0:
|
|
row["premium_paid"] = local_prem
|
|
if (pnl is None or abs(pnl) < 1e-10) and local_pnl is not None and abs(local_pnl) > 0:
|
|
row["realized_pnl"] = local_pnl
|
|
if not row.get("premium_ccy") and local.get("premium_ccy"):
|
|
row["premium_ccy"] = local.get("premium_ccy")
|
|
if not row.get("margin_mode") and local.get("margin_mode"):
|
|
row["margin_mode"] = local.get("margin_mode")
|
|
ccy = str(row.get("premium_ccy") or "USDC").strip().upper() or "USDC"
|
|
row["premium_paid_fmt"] = format_premium_amount(row.get("premium_paid"), ccy=ccy)
|
|
return row
|
|
|
|
|
|
def load_options_history(ex: Any, cfg: dict[str, Any]) -> list[dict[str, Any]]:
|
|
"""与期权历史页相同的数据源:交易所全平记录 + 当前持仓,排除本地隐藏项."""
|
|
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 []
|
|
|
|
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()
|
|
}
|
|
local_closed = _load_local_closed_by_inst(conn)
|
|
for p in raw_live:
|
|
inst = str(p.get("instId") or "").strip()
|
|
premium_override = sum_open_premium_paid(conn, inst) if inst else None
|
|
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))
|
|
|
|
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)
|
|
row = format_option_history_row(raw, tick_sz=tick_sz, ct_mult=ct_mult)
|
|
items.append(_overlay_local_closed(row, local_closed.get(inst_id)))
|
|
finally:
|
|
conn.close()
|
|
|
|
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)
|
|
return [
|
|
x
|
|
for x in (open_rows + closed)
|
|
if str(x.get("history_key") or "") not in hidden_keys
|
|
]
|