Extend semi-auto with ITM/ATM/OTM, offset, and 1:4 sizing.

Moneyness and perp:option units are plan-only; OTM enforces leverage >=180.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-08 11:42:57 +08:00
parent 4585dba3c3
commit 457838ef64
17 changed files with 428 additions and 41 deletions
+64 -7
View File
@@ -13,6 +13,8 @@ PHASE_WAIT_HUMAN = "wait_human"
REASON_POINTS = "semi_target_points"
REASON_PERP_NET = "semi_perp_exit"
TRADE_LOCK_KEY = "semi_trade_lock"
OTM_LEV_FLOOR = 180.0
MONEYNESS_CHOICES = ("itm", "atm", "otm")
def is_semi_auto(ledger: Ledger | None = None) -> bool:
@@ -60,12 +62,23 @@ def lock_trade_params(
view_side: str,
option_move_points: float,
perp_exit_unit: float,
moneyness: str = "otm",
otm_max_offset: float = 25.0,
perp_unit: float = 1.0,
option_unit: float = 4.0,
) -> None:
mny = str(moneyness or "otm").strip().lower()
if mny not in MONEYNESS_CHOICES:
mny = "otm"
payload = {
"group_id": str(group_id),
"view_side": "short" if view_side == "short" else "long",
"option_move_points": float(option_move_points),
"perp_exit_unit": float(perp_exit_unit),
"moneyness": mny,
"otm_max_offset": float(otm_max_offset),
"perp_unit": float(perp_unit),
"option_unit": float(option_unit),
}
db.set_setting(TRADE_LOCK_KEY, json.dumps(payload, ensure_ascii=False))
@@ -74,6 +87,19 @@ def clear_trade_lock(db: Any) -> None:
db.set_setting(TRADE_LOCK_KEY, "")
def _norm_moneyness(raw: str | None, default: str = "otm") -> str:
m = str(raw or default).strip().lower()
return m if m in MONEYNESS_CHOICES else default
def effective_min_leverage(moneyness: str, configured: float) -> float:
"""虚值强制不低于 180;实值/平值用配置值。"""
lev = max(1.0, float(configured))
if _norm_moneyness(moneyness) == "otm":
return max(lev, OTM_LEV_FLOOR)
return lev
def read_semi_params(
ledger: Ledger | None = None,
*,
@@ -96,7 +122,32 @@ def read_semi_params(
led.get_setting_float("semi_perp_exit_unit", s.semi_perp_exit_unit)
or s.semi_perp_exit_unit
)
# 持仓中优先用开仓时锁定的出场参数
moneyness = _norm_moneyness(
led.get_setting_str("semi_moneyness", s.semi_moneyness),
s.semi_moneyness,
)
otm_off = float(
led.get_setting_float("semi_otm_max_offset", s.semi_otm_max_offset)
or s.semi_otm_max_offset
)
perp_u = float(
led.get_setting_float("semi_perp_unit", s.semi_perp_unit) or s.semi_perp_unit
)
opt_u = float(
led.get_setting_float("semi_option_unit", s.semi_option_unit)
or s.semi_option_unit
)
if perp_u <= 0:
perp_u = float(s.semi_perp_unit)
if opt_u <= 0:
opt_u = float(s.semi_option_unit)
min_lev_cfg = float(
led.get_setting_float(
"semi_min_option_leverage", s.semi_min_option_leverage
)
or s.semi_min_option_leverage
)
# 持仓中优先用开仓时锁定的出场/选约参数
lock = read_trade_lock(led)
if lock and group_id and str(lock.get("group_id")) == str(group_id):
view = str(lock.get("view_side") or view).strip().lower()
@@ -105,8 +156,12 @@ def read_semi_params(
try:
move = float(lock.get("option_move_points", move))
exit_unit = float(lock.get("perp_exit_unit", exit_unit))
otm_off = float(lock.get("otm_max_offset", otm_off))
perp_u = float(lock.get("perp_unit", perp_u))
opt_u = float(lock.get("option_unit", opt_u))
except (TypeError, ValueError):
pass
moneyness = _norm_moneyness(str(lock.get("moneyness") or moneyness), moneyness)
return {
"enabled": is_semi_auto(led),
"armed": is_armed(led),
@@ -116,19 +171,21 @@ def read_semi_params(
"perp_side": "short" if view == "long" else "long",
"option_move_points": move,
"perp_exit_unit": exit_unit,
"moneyness": moneyness,
"otm_max_offset": max(0.0, otm_off),
"perp_unit": perp_u,
"option_unit": opt_u,
"min_option_hours": float(
led.get_setting_float(
"semi_min_option_hours", s.semi_min_option_hours
)
or s.semi_min_option_hours
),
"min_option_leverage": float(
led.get_setting_float(
"semi_min_option_leverage", s.semi_min_option_leverage
)
or s.semi_min_option_leverage
"min_option_leverage": effective_min_leverage(moneyness, min_lev_cfg),
"min_option_leverage_cfg": min_lev_cfg,
"trade_locked": bool(
lock and group_id and str(lock.get("group_id")) == str(group_id)
),
"trade_locked": bool(lock and group_id and str(lock.get("group_id")) == str(group_id)),
}