0ee1d8be5d
When enabled, lock perp long→buy Put or short→buy Call with ITM/ATM only; off keeps ATM/ask rules. Co-authored-by: Cursor <cursoragent@cursor.com>
104 lines
2.5 KiB
Python
104 lines
2.5 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class Signal:
|
|
bias: str # strike_below_spot | strike_above_spot | call_ask_gt_put | put_ask_gt_call
|
|
option_side: str # call | put
|
|
perp_side: str # long | short
|
|
call_ask: float
|
|
put_ask: float
|
|
|
|
|
|
def decide(
|
|
call_ask: float | None,
|
|
put_ask: float | None,
|
|
*,
|
|
strike: float | None = None,
|
|
mark_px: float | None = None,
|
|
) -> Signal | None:
|
|
"""
|
|
开仓方向:
|
|
- 行权价 < 标的 → 买 Call + 永续空(ATM 偏下)
|
|
- 行权价 > 标的 → 买 Put + 永续多(ATM 偏上)
|
|
- 行权价 ≈ 标的 → 回退 Call/Put 卖一比价
|
|
"""
|
|
if call_ask is None or put_ask is None:
|
|
return None
|
|
ca = float(call_ask)
|
|
pa = float(put_ask)
|
|
|
|
if strike is not None and mark_px is not None and float(mark_px) > 0:
|
|
diff = float(strike) - float(mark_px)
|
|
if diff < -1e-9:
|
|
return Signal(
|
|
bias="strike_below_spot",
|
|
option_side="call",
|
|
perp_side="short",
|
|
call_ask=ca,
|
|
put_ask=pa,
|
|
)
|
|
if diff > 1e-9:
|
|
return Signal(
|
|
bias="strike_above_spot",
|
|
option_side="put",
|
|
perp_side="long",
|
|
call_ask=ca,
|
|
put_ask=pa,
|
|
)
|
|
|
|
if ca > pa:
|
|
return Signal(
|
|
bias="call_ask_gt_put",
|
|
option_side="call",
|
|
perp_side="short",
|
|
call_ask=ca,
|
|
put_ask=pa,
|
|
)
|
|
if pa > ca:
|
|
return Signal(
|
|
bias="put_ask_gt_call",
|
|
option_side="put",
|
|
perp_side="long",
|
|
call_ask=ca,
|
|
put_ask=pa,
|
|
)
|
|
return None
|
|
|
|
|
|
def decide_fixed(
|
|
call_ask: float | None,
|
|
put_ask: float | None,
|
|
*,
|
|
perp_side: str,
|
|
) -> Signal | None:
|
|
"""
|
|
固定方向:
|
|
- 永续多 → 买 Put
|
|
- 永续空 → 买 Call
|
|
"""
|
|
if call_ask is None or put_ask is None:
|
|
return None
|
|
side = (perp_side or "").strip().lower()
|
|
ca = float(call_ask)
|
|
pa = float(put_ask)
|
|
if side == "long":
|
|
return Signal(
|
|
bias="fixed_long_put",
|
|
option_side="put",
|
|
perp_side="long",
|
|
call_ask=ca,
|
|
put_ask=pa,
|
|
)
|
|
if side == "short":
|
|
return Signal(
|
|
bias="fixed_short_call",
|
|
option_side="call",
|
|
perp_side="short",
|
|
call_ask=ca,
|
|
put_ask=pa,
|
|
)
|
|
return None
|