Enrich sim option positions with public index and mark prices.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-14 21:04:00 +08:00
parent 7b1fd91ab1
commit b9544e53ee
2 changed files with 81 additions and 16 deletions
+80 -15
View File
@@ -552,20 +552,85 @@ class SimBroker:
)
return result
def option_positions_okx_rows(self) -> list[dict[str, Any]]:
"""对齐 OKX positions 行字段, 供 format_position_row 使用."""
rows = []
def option_positions_okx_rows(self, exchange: Any = None) -> list[dict[str, Any]]:
"""对齐 OKX positions 行字段, 供 format_position_row 使用.
模拟盘补充公开行情的 idxPx / markPx, 否则指数价与平掉回本均为空.
"""
from lib.exchange.okx_options_lib import (
expiry_ms_from_inst_id,
fetch_index_price,
inst_family_from_inst_id,
option_fields_from_inst_id,
quote_option_contract,
)
rows: list[dict[str, Any]] = []
idx_cache: dict[str, float | None] = {}
for p in self.list_option_positions():
rows.append(
{
"instId": p["inst_id"],
"pos": str(p["sheets"]),
"avgPx": str(p["entry_px"]),
"markPx": str(p["entry_px"]),
"upl": "0",
"uplRatio": "0",
"posSide": "long",
"mgnMode": "isolated",
}
)
inst_id = str(p["inst_id"] or "")
sheets = float(p["sheets"])
entry = float(p["entry_px"])
ct_mult = float(p.get("ct_mult") or 0.01)
opt_type, strike = option_fields_from_inst_id(inst_id)
mark = entry
idx = None
exp_ms = expiry_ms_from_inst_id(inst_id)
if exchange is not None and inst_id:
try:
q = quote_option_contract(exchange, inst_id)
if q.get("ok"):
raw_mark = q.get("mark")
if raw_mark is None:
raw_mark = q.get("bid")
if raw_mark is not None and float(raw_mark) > 0:
mark = float(raw_mark)
if q.get("index_px") is not None:
idx = float(q["index_px"])
if q.get("opt_type"):
opt_type = str(q["opt_type"])
if q.get("strike") is not None:
strike = float(q["strike"])
if q.get("exp_time") is not None:
try:
exp_ms = int(float(q["exp_time"]))
except (TypeError, ValueError):
pass
except Exception:
pass
if idx is None:
family = inst_family_from_inst_id(inst_id) or ""
uly = family.replace("_UM", "") if family else ""
if uly and uly not in idx_cache:
try:
idx_cache[uly] = fetch_index_price(exchange, uly)
except Exception:
idx_cache[uly] = None
idx = idx_cache.get(uly)
eth = abs(sheets) * ct_mult
upl = (mark - entry) * eth
prem = float(p.get("premium_paid_usdc") or 0) or (entry * eth)
upl_ratio = (upl / prem) if prem > 1e-12 else 0.0
row: dict[str, Any] = {
"instId": inst_id,
"pos": str(sheets),
"availPos": str(sheets),
"avgPx": str(entry),
"markPx": str(mark),
"upl": str(round(upl, 4)),
"uplRatio": str(round(upl_ratio, 6)),
"posSide": "long",
"mgnMode": "isolated",
}
if idx is not None:
row["idxPx"] = str(idx)
if opt_type:
row["optType"] = opt_type
if strike is not None:
row["stk"] = str(strike)
if exp_ms:
row["expTime"] = str(exp_ms)
rows.append(row)
return rows
+1 -1
View File
@@ -340,7 +340,7 @@ def _patch_okx_options_lib(app_module: Any) -> None:
def fetch_option_positions(ex):
try:
if _GET_DB is not None and is_sim_mode(_GET_DB):
return broker().option_positions_okx_rows()
return broker().option_positions_okx_rows(ex)
except Exception:
pass
return _orig_fetch_pos(ex)