Realign ATM to current mark before open; skip while in position.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-25 07:52:37 +08:00
parent 1155cf082d
commit 7db4c9c8a3
4 changed files with 70 additions and 6 deletions
+55 -5
View File
@@ -15,6 +15,19 @@ from .types import MarketSnapshot, OptionPair
logger = logging.getLogger(__name__)
# 现价偏离当前行权价超过该点数则重选 ATM(ETH 期权常见步进 5)
_ATM_DRIFT_POINTS = 5.0
def _has_open_position() -> bool:
try:
from ..models.db import get_db
row = get_db().fetchone("SELECT status FROM positions WHERE id=1")
return bool(row and row["status"] == "open")
except Exception:
return False
class MarketGateway:
def __init__(self, settings: Settings | None = None) -> None:
@@ -109,6 +122,41 @@ class MarketGateway:
)
return pair
def _mark_for_atm(self) -> float | None:
snap = self.snapshot()
if snap.perp and snap.perp.mark_px:
return float(snap.perp.mark_px)
if snap.index_px:
return float(snap.index_px)
if snap.perp and snap.perp.bid and snap.perp.ask:
return (float(snap.perp.bid) + float(snap.perp.ask)) / 2
return None
def atm_needs_realign(self, mark_px: float | None = None) -> bool:
"""到期日变了,或现价已偏离当前行权价超过阈值。"""
if self._pair is None:
return True
want = next_session_expiry_ymd()
if self._pair.expiry_ymd != want:
return True
mark = mark_px if mark_px is not None else self._mark_for_atm()
if mark is None or mark <= 0:
return False
return abs(float(self._pair.strike) - float(mark)) >= _ATM_DRIFT_POINTS
async def ensure_atm_async(self, *, force: bool = False) -> OptionPair | None:
"""空仓时按现价对齐 ATM。有持仓时不切换,避免盯市合约被换掉。"""
if _has_open_position():
return self._pair
if force or self.atm_needs_realign():
logger.info(
"ATM realign force=%s old_strike=%s",
force,
self._pair.strike if self._pair else None,
)
return await self.realign_async()
return self._pair
def snapshot(self) -> MarketSnapshot:
return self.cache.snapshot(self.settings.perp_inst_id)
@@ -116,7 +164,7 @@ class MarketGateway:
return self.snapshot().to_dict()
async def _refresh_loop(self) -> None:
"""周期性刷新指数价;跨日到期切换时重对齐。"""
"""周期性刷新指数价;空仓时按到期/ATM 偏离重对齐。"""
while True:
await asyncio.sleep(30)
try:
@@ -124,10 +172,12 @@ class MarketGateway:
self.rest.fetch_index_ticker, self.settings.index_inst_id
)
self.cache.set_index_px(idx)
want = next_session_expiry_ymd()
if self._pair and self._pair.expiry_ymd != want:
logger.info("expiry rollover %s -> %s", self._pair.expiry_ymd, want)
await self.realign_async()
mark = await asyncio.to_thread(
self.rest.fetch_mark_price, self.settings.perp_inst_id
)
if mark:
self.cache.set_mark_px(self.settings.perp_inst_id, mark)
await self.ensure_atm_async(force=False)
except asyncio.CancelledError:
raise
except Exception as e: