fix: OKX options buy/close orders with tick alignment and reduceOnly

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-07 08:52:18 +08:00
parent ae0c44e20d
commit 05586242f0
6 changed files with 269 additions and 65 deletions
+31 -20
View File
@@ -59,6 +59,8 @@ def install_options_trading(app: Flask, repo_root: str, app_module: Any) -> None
def _build_cfg(app_module: Any) -> dict[str, Any]:
from lib.exchange.okx_options_lib import (
_pos_side_from_position,
_safe_float,
build_option_chain,
estimate_usdt_to_usdc,
execute_convert,
@@ -67,6 +69,7 @@ def _build_cfg(app_module: Any) -> dict[str, Any]:
format_position_row,
options_api_ready,
place_option_limit_order,
place_option_market_order,
quote_option_contract,
transfer_ccy,
)
@@ -91,6 +94,7 @@ def _build_cfg(app_module: Any) -> dict[str, Any]:
"build_option_chain": build_option_chain,
"quote_option_contract": quote_option_contract,
"place_option_limit_order": place_option_limit_order,
"place_option_market_order": place_option_market_order,
"fetch_option_positions": fetch_option_positions,
"fetch_options_balances": fetch_options_balances,
"format_position_row": format_position_row,
@@ -220,6 +224,7 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
if not sizing.get("ok"):
return jsonify({"ok": False, "msg": sizing.get("msg") or "张数计算失败", "sizing": sizing})
sheets = int(sizing["sheets"])
tick_sz = q.get("tick_sz")
order = cfg["place_option_limit_order"](
ex,
inst_id=inst_id,
@@ -227,6 +232,7 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
sheets=sheets,
price=float(ask),
td_mode=cfg["td_mode"],
tick_sz=tick_sz,
)
if not order.get("ok"):
return jsonify(order)
@@ -291,38 +297,43 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
pos = next((p for p in raw_positions if str(p.get("instId")) == inst_id), None)
if not pos:
return jsonify({"ok": False, "msg": "未找到持仓"})
avail = float(pos.get("availPos") or pos.get("pos") or 0)
close_sheets = int(sheets) if sheets else int(abs(avail))
avail = _safe_float(pos.get("availPos"))
if avail is None or avail <= 0:
avail = abs(_safe_float(pos.get("pos")) or 0)
close_sheets = int(sheets) if sheets else int(avail)
if close_sheets < 1:
return jsonify({"ok": False, "msg": "可平张数不足"})
td_mode = str(pos.get("mgnMode") or cfg["td_mode"])
pos_side = _pos_side_from_position(pos) or "net"
tick_sz = q.get("tick_sz")
if use_market:
try:
resp = ex.private_post_trade_order(
{
"instId": inst_id,
"tdMode": cfg["td_mode"],
"side": "sell",
"ordType": "market",
"sz": str(close_sheets),
}
)
data_rows = (resp or {}).get("data") or []
if not data_rows or str(data_rows[0].get("sCode")) != "0":
return jsonify({"ok": False, "msg": data_rows[0].get("sMsg") if data_rows else "市价平仓失败"})
order = {"ok": True, "data": data_rows[0]}
except Exception as e:
return jsonify({"ok": False, "msg": str(e)})
order = cfg["place_option_market_order"](
ex,
inst_id=inst_id,
side="sell",
sheets=close_sheets,
td_mode=td_mode,
reduce_only=True,
pos_side=pos_side,
)
if not order.get("ok"):
return jsonify(order)
else:
close_px = float(bid)
order = cfg["place_option_limit_order"](
ex,
inst_id=inst_id,
side="sell",
sheets=close_sheets,
price=float(bid),
td_mode=cfg["td_mode"],
price=close_px,
td_mode=td_mode,
tick_sz=tick_sz,
reduce_only=True,
pos_side=pos_side,
)
if not order.get("ok"):
return jsonify(order)
bid = order.get("px", close_px)
prem_recv = total_premium(float(bid or 0), close_sheets * float(q.get("ct_mult") or 0.01))
conn = cfg["get_db"]()
try: