Block option auto-close on stub bids far below mark.

Reject target and depth closes when bid is a residual tick, and show invalid-bid UI instead of recycling at junk prices.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-15 21:05:54 +08:00
parent e15eca76f3
commit c025c06fac
7 changed files with 387 additions and 21 deletions
+81 -1
View File
@@ -13,8 +13,12 @@ from lib.options.options_db import init_options_tables
from lib.options.options_monitor_lib import options_monitor_loop
from lib.options.options_pricing_lib import (
calc_order_size,
close_ref_prices,
ct_mult_from_meta,
estimate_close_by_bids,
fetch_option_mark_px,
filter_bids_for_close,
is_stub_bid_px,
min_sz_from_meta,
premium_per_sheet,
total_premium,
@@ -707,6 +711,41 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
return jsonify(order)
elif depth_split:
ct_mult = float(q.get("ct_mult") or 0.01)
from lib.exchange.okx_options_lib import option_fields_from_inst_id
mark_px = _safe_float(pos.get("markPx")) or _safe_float(q.get("mark_px") or q.get("mark"))
if mark_px is None:
mark_px = fetch_option_mark_px(ex, inst_id)
opt_type = pos.get("optType") or q.get("opt_type")
strike = _safe_float(pos.get("stk")) or _safe_float(q.get("strike"))
if not opt_type or strike is None:
pt, ps = option_fields_from_inst_id(inst_id)
opt_type = opt_type or pt
if strike is None:
strike = ps
idx_px = _safe_float(pos.get("idxPx")) or _safe_float(q.get("index_px"))
mark_px, intrinsic_px = close_ref_prices(
mark_px=mark_px, opt_type=str(opt_type or ""), strike=strike, index_px=idx_px
)
book0 = cfg["fetch_option_book_depth"](ex, inst_id, 5)
usable0, stub_only0, stub_reason0 = filter_bids_for_close(
book0.get("bids") or [], mark_px=mark_px, intrinsic_px=intrinsic_px
)
if stub_only0 or not usable0:
bid_chk = None
if book0.get("bids"):
bid_chk = _safe_float((book0.get("bids") or [{}])[0].get("px"))
bid_chk = bid_chk or _safe_float(bid)
stub, stub_reason = is_stub_bid_px(bid_chk, mark_px=mark_px, intrinsic_px=intrinsic_px)
if stub or stub_only0:
return jsonify(
{
"ok": False,
"msg": stub_reason0 or stub_reason or "暂无有效买盘,禁止按买盘自动平仓",
"stopped_reason": "stub_bid",
"auto_close_blocked": True,
}
)
remaining = close_sheets
submitted_sheets = 0
filled_or_reduced_sheets = 0
@@ -726,7 +765,22 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
break
remaining = min(remaining, current_avail)
book = cfg["fetch_option_book_depth"](ex, inst_id, 5)
preview = estimate_close_by_bids(book.get("bids") or [], remaining, ct_mult=ct_mult)
preview = estimate_close_by_bids(
book.get("bids") or [],
remaining,
ct_mult=ct_mult,
mark_px=mark_px,
intrinsic_px=intrinsic_px,
)
if preview.get("auto_close_blocked") or preview.get("bid_invalid"):
return jsonify(
{
"ok": False,
"msg": preview.get("bid_invalid_reason") or "暂无有效买盘,禁止按买盘自动平仓",
"stopped_reason": "stub_bid",
"auto_close_blocked": True,
}
)
levels = preview.get("levels") or []
if not levels:
stopped_reason = "no_bid_depth"
@@ -829,7 +883,33 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
}
)
else:
from lib.exchange.okx_options_lib import option_fields_from_inst_id
mark_px = _safe_float(pos.get("markPx")) or _safe_float(q.get("mark_px") or q.get("mark"))
if mark_px is None:
mark_px = fetch_option_mark_px(ex, inst_id)
opt_type = pos.get("optType") or q.get("opt_type")
strike = _safe_float(pos.get("stk")) or _safe_float(q.get("strike"))
if not opt_type or strike is None:
pt, ps = option_fields_from_inst_id(inst_id)
opt_type = opt_type or pt
if strike is None:
strike = ps
idx_px = _safe_float(pos.get("idxPx")) or _safe_float(q.get("index_px"))
mark_px, intrinsic_px = close_ref_prices(
mark_px=mark_px, opt_type=str(opt_type or ""), strike=strike, index_px=idx_px
)
close_px = float(bid)
stub, stub_reason = is_stub_bid_px(close_px, mark_px=mark_px, intrinsic_px=intrinsic_px)
if stub:
return jsonify(
{
"ok": False,
"msg": stub_reason or "暂无有效买盘,禁止按买盘自动平仓",
"stopped_reason": "stub_bid",
"auto_close_blocked": True,
}
)
order = cfg["place_option_limit_order"](
ex,
inst_id=inst_id,