Require real ask depth for options and hedge opens.
Block mark-as-ask opens, show reference mark when no depth, cap sheets to ask size; leave close paths unchanged. Document in docs/更新文档.md. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -779,6 +779,38 @@ def build_option_chain(
|
||||
return out
|
||||
|
||||
|
||||
def option_buy_liquidity_ok(ask: Any, ask_sz: Any) -> tuple[bool, str]:
|
||||
"""开仓仅认真实卖一价+卖一深度;不接受标记价/内在价值顶包."""
|
||||
a = _safe_float(ask)
|
||||
s = _safe_float(ask_sz)
|
||||
if a is None or a <= 0:
|
||||
return False, "暂无卖一价,无法买入"
|
||||
if s is None or s <= 0:
|
||||
return False, "暂无卖一深度,无法买入"
|
||||
return True, ""
|
||||
|
||||
|
||||
def cap_option_buy_sheets_to_ask_depth(
|
||||
sheets: int,
|
||||
ask_sz: Any,
|
||||
*,
|
||||
min_sz: int = 1,
|
||||
) -> tuple[int | None, str]:
|
||||
"""将买入张数限制在卖一深度内(向下取整)."""
|
||||
depth = _safe_float(ask_sz)
|
||||
if depth is None or depth <= 0:
|
||||
return None, "暂无卖一深度,无法买入"
|
||||
max_sheets = int(math.floor(depth + 1e-12))
|
||||
need = max(1, int(min_sz or 1))
|
||||
if max_sheets < need:
|
||||
return None, f"卖一深度不足 {need} 张(当前 {depth:g})"
|
||||
want = max(0, int(sheets))
|
||||
capped = min(want, max_sheets)
|
||||
if capped < need:
|
||||
return None, f"卖一深度不足 {need} 张(当前 {depth:g})"
|
||||
return capped, ""
|
||||
|
||||
|
||||
def quote_option_contract(ex: ccxt.okx, inst_id: str) -> dict[str, Any]:
|
||||
inst_id = (inst_id or "").strip()
|
||||
if not inst_id:
|
||||
@@ -805,6 +837,7 @@ def quote_option_contract(ex: ccxt.okx, inst_id: str) -> dict[str, Any]:
|
||||
return {"ok": False, "msg": "行情限频,请稍后重试"}
|
||||
return {"ok": False, "msg": "合约不存在"}
|
||||
t = t_rows[0] if t_rows else {}
|
||||
# 开仓用真实盘口卖一;绝不把标记价写入 ask
|
||||
ask = _safe_float(t.get("askPx"))
|
||||
bid = _safe_float(t.get("bidPx"))
|
||||
ask_sz = _safe_float(t.get("askSz"))
|
||||
@@ -821,10 +854,15 @@ def quote_option_contract(ex: ccxt.okx, inst_id: str) -> dict[str, Any]:
|
||||
bid_sz = book_bid_sz
|
||||
mark = _safe_float(t.get("markPx"))
|
||||
tick_sz = meta.get("tickSz")
|
||||
if ask is None and mark is not None:
|
||||
ask = round_option_px(mark, tick_sz, "buy")
|
||||
# 买一缺失时仍可用标记价补展示(平仓路径读 bid);开仓 ask 不顶包
|
||||
if bid is None and mark is not None:
|
||||
bid = round_option_px(mark, tick_sz, "sell")
|
||||
ref_ask = None
|
||||
if ask is None and mark is not None and mark > 0:
|
||||
ref_ask = round_option_px(mark, tick_sz, "buy")
|
||||
can_open, open_block_msg = option_buy_liquidity_ok(ask, ask_sz)
|
||||
book_ask = ask
|
||||
book_ask_sz = ask_sz
|
||||
uly = str(meta.get("uly") or "")
|
||||
idx = fetch_index_price(ex, uly)
|
||||
opt_type = meta.get("optType")
|
||||
@@ -832,18 +870,24 @@ def quote_option_contract(ex: ccxt.okx, inst_id: str) -> dict[str, Any]:
|
||||
expiry_be = expiry_breakeven_from_ask(
|
||||
opt_type=str(opt_type or ""),
|
||||
strike=strike,
|
||||
ask_px=ask,
|
||||
ask_px=book_ask if can_open else None,
|
||||
mark_px=mark,
|
||||
)
|
||||
return {
|
||||
"ok": True,
|
||||
"inst_id": inst_id,
|
||||
"meta": meta,
|
||||
"ask": ask,
|
||||
"ask": book_ask if can_open else None,
|
||||
"bid": bid,
|
||||
"ask_sz": ask_sz,
|
||||
"ask_sz": book_ask_sz if can_open else None,
|
||||
"bid_sz": bid_sz,
|
||||
"mark": mark,
|
||||
"ref_ask": ref_ask,
|
||||
"book_ask": book_ask,
|
||||
"book_ask_sz": book_ask_sz,
|
||||
"can_open": can_open,
|
||||
"ask_source": "book" if can_open else "none",
|
||||
"open_block_msg": "" if can_open else open_block_msg,
|
||||
"index_px": idx,
|
||||
"expiry_be_px": expiry_be,
|
||||
"dist_expiry_be": idx_distance_to_be(idx, expiry_be),
|
||||
|
||||
Reference in New Issue
Block a user