Let semi-auto reopen nearest expiry; ignore one-expiry-per-day.
Far quarterly skips were masking usable near OTM quotes like 260810. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1186,7 +1186,12 @@ class StrategyEngine:
|
|||||||
one_expiry_per_day = self.ledger.get_setting_bool(
|
one_expiry_per_day = self.ledger.get_setting_bool(
|
||||||
"one_expiry_per_day", s.one_expiry_per_day
|
"one_expiry_per_day", s.one_expiry_per_day
|
||||||
)
|
)
|
||||||
if pick is not None and one_expiry_per_day:
|
# 半自动人工授权:允许同到期再开;全自动仍受一日一到期约束
|
||||||
|
if (
|
||||||
|
pick is not None
|
||||||
|
and one_expiry_per_day
|
||||||
|
and not is_semi_auto(self.ledger)
|
||||||
|
):
|
||||||
from .clock import (
|
from .clock import (
|
||||||
expiry_blocked_by_one_per_day,
|
expiry_blocked_by_one_per_day,
|
||||||
used_expiry_ymds,
|
used_expiry_ymds,
|
||||||
|
|||||||
@@ -89,10 +89,10 @@ def _as_bool_setting(raw: str | None, default: bool) -> bool:
|
|||||||
return str(raw).strip().lower() in ("1", "true", "yes", "on")
|
return str(raw).strip().lower() in ("1", "true", "yes", "on")
|
||||||
|
|
||||||
|
|
||||||
def _skip_expiry_ymds_for_next() -> set[str]:
|
def _skip_expiry_ymds_for_next(*, include_used: bool = True) -> set[str]:
|
||||||
"""
|
"""
|
||||||
空仓选约/监控应跳过的到期日:
|
空仓选约/监控应跳过的到期日:
|
||||||
- 历史上已开过该到期(one_expiry_per_day,跨日)
|
- 历史上已开过该到期(one_expiry_per_day,跨日);半自动可关
|
||||||
- 仍有待结算残留期权的到期档(该档已「完成」开平,盯下一档)
|
- 仍有待结算残留期权的到期档(该档已「完成」开平,盯下一档)
|
||||||
"""
|
"""
|
||||||
skip: set[str] = set()
|
skip: set[str] = set()
|
||||||
@@ -102,6 +102,7 @@ def _skip_expiry_ymds_for_next() -> set[str]:
|
|||||||
|
|
||||||
s = get_settings()
|
s = get_settings()
|
||||||
db = get_db()
|
db = get_db()
|
||||||
|
if include_used:
|
||||||
one_exp_day = _as_bool_setting(
|
one_exp_day = _as_bool_setting(
|
||||||
db.get_setting("one_expiry_per_day", str(s.one_expiry_per_day)),
|
db.get_setting("one_expiry_per_day", str(s.one_expiry_per_day)),
|
||||||
s.one_expiry_per_day,
|
s.one_expiry_per_day,
|
||||||
@@ -765,14 +766,25 @@ class StrategySession:
|
|||||||
self._last_pick_fail = f"无剩余≥{min_hours:g}h 的到期"
|
self._last_pick_fail = f"无剩余≥{min_hours:g}h 的到期"
|
||||||
return None
|
return None
|
||||||
|
|
||||||
skip_expiries = _skip_expiry_ymds_for_next()
|
# 半自动:人工授权可重复开同到期,不受 one_expiry_per_day 挡住;
|
||||||
|
# 仍跳过有残余腿的到期。全自动保持原「一日一到期」。
|
||||||
|
skip_expiries = _skip_expiry_ymds_for_next(include_used=not semi_on)
|
||||||
|
first_skip = ""
|
||||||
last_skip = ""
|
last_skip = ""
|
||||||
|
|
||||||
|
def _note_skip(msg: str) -> None:
|
||||||
|
nonlocal first_skip, last_skip
|
||||||
|
last_skip = msg
|
||||||
|
if not first_skip:
|
||||||
|
first_skip = msg
|
||||||
|
|
||||||
for ymd in eligible:
|
for ymd in eligible:
|
||||||
if ymd in skip_expiries:
|
if ymd in skip_expiries:
|
||||||
|
_note_skip(f"{ymd} 有残余期权(跳过该到期)")
|
||||||
logger.info(
|
logger.info(
|
||||||
"skip expiry=%s: used today and/or residual pending",
|
"skip expiry=%s: residual pending (semi=%s)",
|
||||||
ymd,
|
ymd,
|
||||||
|
semi_on,
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
pair = select_option_pair(
|
pair = select_option_pair(
|
||||||
@@ -785,7 +797,7 @@ class StrategySession:
|
|||||||
)
|
)
|
||||||
if pair is None:
|
if pair is None:
|
||||||
if semi_on and semi_mny == "otm":
|
if semi_on and semi_mny == "otm":
|
||||||
last_skip = (
|
_note_skip(
|
||||||
f"{ymd} 无{opt_side_hint or '?'}虚值"
|
f"{ymd} 无{opt_side_hint or '?'}虚值"
|
||||||
f"(偏离≤{float(semi_otm_off or 0):g})"
|
f"(偏离≤{float(semi_otm_off or 0):g})"
|
||||||
)
|
)
|
||||||
@@ -797,7 +809,7 @@ class StrategySession:
|
|||||||
underlying,
|
underlying,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
last_skip = f"{ymd} 无合格行权价"
|
_note_skip(f"{ymd} 无合格行权价")
|
||||||
continue
|
continue
|
||||||
if fixed_on:
|
if fixed_on:
|
||||||
from .selection import is_otm
|
from .selection import is_otm
|
||||||
@@ -809,13 +821,13 @@ class StrategySession:
|
|||||||
strike=pair.strike,
|
strike=pair.strike,
|
||||||
mark_px=underlying,
|
mark_px=underlying,
|
||||||
):
|
):
|
||||||
last_skip = f"{ymd} K{pair.strike:g} 非虚值"
|
_note_skip(f"{ymd} K{pair.strike:g} 非虚值")
|
||||||
continue
|
continue
|
||||||
if (
|
if (
|
||||||
atm_open_offset(pair.strike, underlying)
|
atm_open_offset(pair.strike, underlying)
|
||||||
> float(semi_otm_off or 0) + 1e-9
|
> float(semi_otm_off or 0) + 1e-9
|
||||||
):
|
):
|
||||||
last_skip = (
|
_note_skip(
|
||||||
f"{ymd} K{pair.strike:g} 偏离>"
|
f"{ymd} K{pair.strike:g} 偏离>"
|
||||||
f"{float(semi_otm_off or 0):g}"
|
f"{float(semi_otm_off or 0):g}"
|
||||||
)
|
)
|
||||||
@@ -828,7 +840,7 @@ class StrategySession:
|
|||||||
strike=pair.strike,
|
strike=pair.strike,
|
||||||
mark_px=underlying,
|
mark_px=underlying,
|
||||||
):
|
):
|
||||||
last_skip = f"{ymd} K{pair.strike:g} 非实值/平值"
|
_note_skip(f"{ymd} K{pair.strike:g} 非实值/平值")
|
||||||
logger.info(
|
logger.info(
|
||||||
"skip expiry=%s strike=%.0f not ITM/ATM for %s mark=%.2f",
|
"skip expiry=%s strike=%.0f not ITM/ATM for %s mark=%.2f",
|
||||||
ymd,
|
ymd,
|
||||||
@@ -845,7 +857,7 @@ class StrategySession:
|
|||||||
max_offset=max_atm_off,
|
max_offset=max_atm_off,
|
||||||
enabled=atm_off_on,
|
enabled=atm_off_on,
|
||||||
):
|
):
|
||||||
last_skip = f"{ymd} ATM偏离{offset:.1f}>{max_atm_off:g}"
|
_note_skip(f"{ymd} ATM偏离{offset:.1f}>{max_atm_off:g}")
|
||||||
logger.info(
|
logger.info(
|
||||||
"skip expiry=%s strike=%.0f atm_offset=%.1f > max=%.1f",
|
"skip expiry=%s strike=%.0f atm_offset=%.1f > max=%.1f",
|
||||||
ymd,
|
ymd,
|
||||||
@@ -878,13 +890,13 @@ class StrategySession:
|
|||||||
need = "Call" if (opt_side_hint == "call") else (
|
need = "Call" if (opt_side_hint == "call") else (
|
||||||
"Put" if opt_side_hint == "put" else "Call/Put"
|
"Put" if opt_side_hint == "put" else "Call/Put"
|
||||||
)
|
)
|
||||||
last_skip = f"{ymd} K{pair.strike:g} 缺{need}卖一"
|
_note_skip(f"{ymd} K{pair.strike:g} 缺{need}卖一")
|
||||||
continue
|
continue
|
||||||
opt_ask = sig.call_ask if sig.option_side == "call" else sig.put_ask
|
opt_ask = sig.call_ask if sig.option_side == "call" else sig.put_ask
|
||||||
lev = option_leverage(underlying, opt_ask)
|
lev = option_leverage(underlying, opt_ask)
|
||||||
hours_left = hours_until_expiry(ymd, expiry_ms=pair.expiry_ms)
|
hours_left = hours_until_expiry(ymd, expiry_ms=pair.expiry_ms)
|
||||||
if lev is None or lev + 1e-9 < min_lev:
|
if lev is None or lev + 1e-9 < min_lev:
|
||||||
last_skip = (
|
_note_skip(
|
||||||
f"{ymd} {sig.option_side.upper()}@{pair.strike:g} "
|
f"{ymd} {sig.option_side.upper()}@{pair.strike:g} "
|
||||||
f"杠杆{(f'{lev:.0f}x' if lev else 'n/a')}<{min_lev:g}x"
|
f"杠杆{(f'{lev:.0f}x' if lev else 'n/a')}<{min_lev:g}x"
|
||||||
)
|
)
|
||||||
@@ -921,14 +933,16 @@ class StrategySession:
|
|||||||
underlying_px=underlying,
|
underlying_px=underlying,
|
||||||
hedge_mode="perp_option",
|
hedge_mode="perp_option",
|
||||||
)
|
)
|
||||||
if last_skip:
|
# 报最近到期(列表最前)的原因,避免只显示远月 261225 造成误会
|
||||||
|
why = first_skip or last_skip
|
||||||
|
if why:
|
||||||
hint = ""
|
hint = ""
|
||||||
if semi_on:
|
if semi_on:
|
||||||
hint = (
|
hint = (
|
||||||
f"(半自动{opt_side_hint or '?'}·"
|
f"(半自动{opt_side_hint or '?'}·"
|
||||||
f"{semi_mny or '?'}·≥{min_lev:g}x·≥{min_hours:g}h)"
|
f"{semi_mny or '?'}·≥{min_lev:g}x·≥{min_hours:g}h)"
|
||||||
)
|
)
|
||||||
self._last_pick_fail = f"最近跳过: {last_skip}{hint}"
|
self._last_pick_fail = f"{why}{hint}"
|
||||||
else:
|
else:
|
||||||
self._last_pick_fail = "合格到期均被跳过(一日一到期/残余等)"
|
self._last_pick_fail = "合格到期均被跳过(一日一到期/残余等)"
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -49,6 +49,8 @@
|
|||||||
**以损定仓(半自动)**:系统设置 `sizing_mode=risk_based` 时,开仓前用**盘口卖一**估算单位成本 → 预算反推 k →
|
**以损定仓(半自动)**:系统设置 `sizing_mode=risk_based` 时,开仓前用**盘口卖一**估算单位成本 → 预算反推 k →
|
||||||
`期权量 = 期权单位 × k`,`永续量 = 永续单位 × k`(配比不变)。首页「半自动 · 本单」下方按当前报价档卖一实时预览开仓量。
|
`期权量 = 期权单位 × k`,`永续量 = 永续单位 × k`(配比不变)。首页「半自动 · 本单」下方按当前报价档卖一实时预览开仓量。
|
||||||
|
|
||||||
|
选约到期与报价列表一致:取剩余 ≥ 最短 h 的**最近**到期。半自动**不受**全自动「一日一到期」限制(仍可跳过有残余腿的到期)。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. 出场
|
## 3. 出场
|
||||||
|
|||||||
Reference in New Issue
Block a user