Keep OO amplitude refreshed while holding a position.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -2011,9 +2011,19 @@ class Matcher:
|
||||
if index_px is None and snap.perp:
|
||||
index_px = snap.perp.mark_px
|
||||
qty = float(pos.get("option_qty_eth") or 0)
|
||||
qty2 = float(pos.get("option2_qty_eth") or qty)
|
||||
raw_q2 = pos.get("option2_qty_eth")
|
||||
try:
|
||||
qty2 = float(raw_q2) if raw_q2 is not None else 0.0
|
||||
except (TypeError, ValueError):
|
||||
qty2 = 0.0
|
||||
if qty2 <= 0:
|
||||
qty2 = qty
|
||||
contracts1 = float(pos.get("option_qty_contracts") or 0)
|
||||
contracts2 = float(pos.get("option2_qty_contracts") or 0)
|
||||
raw_c2 = pos.get("option2_qty_contracts")
|
||||
try:
|
||||
contracts2 = float(raw_c2) if raw_c2 is not None else 0.0
|
||||
except (TypeError, ValueError):
|
||||
contracts2 = 0.0
|
||||
prem1 = float(pos.get("initial_premium") or 0)
|
||||
prem2 = float(pos.get("initial_premium2") or 0)
|
||||
call_id = str(pos.get("option_inst_id") or "")
|
||||
@@ -2022,6 +2032,19 @@ class Matcher:
|
||||
entry2 = float(pos.get("option2_entry_px") or 0)
|
||||
oq1 = self._quote_held_option(call_id) if call_id else None
|
||||
oq2 = self._quote_held_option(put_id) if put_id else None
|
||||
# 盘口缓存未订阅到 Put 时,回退 session 监控腿(与「持仓虚值」同数据源)
|
||||
if (oq2 is None or (oq2.bid is None and oq2.mark_px is None)) and put_id:
|
||||
try:
|
||||
if snap.put and str(getattr(snap.put, "inst_id", "") or "") == put_id:
|
||||
oq2 = snap.put
|
||||
except Exception:
|
||||
pass
|
||||
if (oq1 is None or (oq1.bid is None and oq1.mark_px is None)) and call_id:
|
||||
try:
|
||||
if snap.call and str(getattr(snap.call, "inst_id", "") or "") == call_id:
|
||||
oq1 = snap.call
|
||||
except Exception:
|
||||
pass
|
||||
option_upl = 0.0
|
||||
option2_upl = 0.0
|
||||
fees = 0.0
|
||||
|
||||
@@ -332,6 +332,8 @@ class StrategySession:
|
||||
|
||||
def align_to_held_position(self) -> OptionPair | None:
|
||||
"""有活跃仓时:监控对锁定为持仓合约的到期/行权价。"""
|
||||
if _hedge_mode() == "option_option":
|
||||
self.refresh_oo_amplitude()
|
||||
call_id, put_id = _held_option_legs()
|
||||
held = call_id or _held_option_inst_id()
|
||||
if not held:
|
||||
@@ -441,11 +443,37 @@ class StrategySession:
|
||||
)
|
||||
return self._apply_pair(pair, mark=float(mark), idx=idx)
|
||||
|
||||
def refresh_oo_amplitude(self) -> dict[str, Any] | None:
|
||||
"""刷新振幅高低(有仓/无仓都要,否则持仓后 UI 指数/振幅会空)。"""
|
||||
if _hedge_mode() != "option_option":
|
||||
return None
|
||||
try:
|
||||
from ..exchange.candles import fetch_amplitude_hl_for_runtime
|
||||
|
||||
amp_pct, amp_hours, _, _ = _oo_settings()
|
||||
amp = fetch_amplitude_hl_for_runtime(amp_hours)
|
||||
if amp is None:
|
||||
return self._oo_amp
|
||||
self._oo_amp = {
|
||||
"high": float(amp.high),
|
||||
"low": float(amp.low),
|
||||
"mid": float(amp.mid),
|
||||
"range_pct": float(amp.range_pct),
|
||||
"hours": float(amp_hours),
|
||||
"max_pct": float(amp_pct),
|
||||
"ok": float(amp.range_pct) <= float(amp_pct) + 1e-12,
|
||||
}
|
||||
return self._oo_amp
|
||||
except Exception:
|
||||
logger.exception("refresh_oo_amplitude failed")
|
||||
return self._oo_amp
|
||||
|
||||
def align_oo_instruments(self) -> OptionPair | None:
|
||||
"""期期监控:按振幅高低点选虚值 Call/Put(展示用;振幅超限仍对齐候选)。"""
|
||||
from ..exchange.candles import fetch_amplitude_hl_for_runtime
|
||||
from .oo_selection import select_oo_pair
|
||||
|
||||
# 有仓也刷新振幅(钉仓不再走选约,否则 _oo_amp 一直空)
|
||||
self.refresh_oo_amplitude()
|
||||
if _has_open_position():
|
||||
return self.align_to_held_position()
|
||||
s = self.settings
|
||||
@@ -455,26 +483,17 @@ class StrategySession:
|
||||
if mark is None or mark <= 0:
|
||||
raise RuntimeError("无法获取标的标记/指数价格,无法选期期虚值")
|
||||
underlying = float(mark)
|
||||
amp = fetch_amplitude_hl_for_runtime(amp_hours)
|
||||
if amp is None:
|
||||
self._oo_amp = None
|
||||
if self._oo_amp is None:
|
||||
raise RuntimeError("无法获取振幅 K 线高低点")
|
||||
self._oo_amp = {
|
||||
"high": float(amp.high),
|
||||
"low": float(amp.low),
|
||||
"mid": float(amp.mid),
|
||||
"range_pct": float(amp.range_pct),
|
||||
"hours": float(amp_hours),
|
||||
"max_pct": float(amp_pct),
|
||||
"ok": float(amp.range_pct) <= float(amp_pct) + 1e-12,
|
||||
}
|
||||
amp_high = float(self._oo_amp["high"])
|
||||
amp_low = float(self._oo_amp["low"])
|
||||
contracts = self.ex.list_option_contracts(s.option_inst_family)
|
||||
skip = _skip_expiry_ymds_for_next()
|
||||
picked = select_oo_pair(
|
||||
contracts,
|
||||
spot=underlying,
|
||||
high=float(amp.high),
|
||||
low=float(amp.low),
|
||||
high=amp_high,
|
||||
low=amp_low,
|
||||
min_hours=float(min_hours),
|
||||
skip_expiry_ymds=skip,
|
||||
)
|
||||
@@ -518,6 +537,16 @@ class StrategySession:
|
||||
if amp is None:
|
||||
logger.info("oo: amplitude candles unavailable")
|
||||
return None
|
||||
# 无论是否超限都写入,供「指数/振幅」面板展示
|
||||
self._oo_amp = {
|
||||
"high": float(amp.high),
|
||||
"low": float(amp.low),
|
||||
"mid": float(amp.mid),
|
||||
"range_pct": float(amp.range_pct),
|
||||
"hours": float(amp_hours),
|
||||
"max_pct": float(amp_pct),
|
||||
"ok": float(amp.range_pct) <= float(amp_pct) + 1e-12,
|
||||
}
|
||||
if float(amp.range_pct) > float(amp_pct) + 1e-12:
|
||||
logger.info(
|
||||
"oo: amplitude %.3f%% > max %.3f%% (H=%.2f L=%.2f)",
|
||||
@@ -862,7 +891,9 @@ class StrategySession:
|
||||
|
||||
async def ensure_atm_async(self, *, force: bool = False) -> OptionPair | None:
|
||||
if _has_open_position():
|
||||
# 持仓期间:钉住持仓行权价(禁止漂到新 ATM/虚值)
|
||||
# 持仓期间:钉住持仓行权价(禁止漂到新 ATM/虚值);期期仍刷新振幅供 UI
|
||||
if _hedge_mode() == "option_option":
|
||||
await asyncio.to_thread(self.refresh_oo_amplitude)
|
||||
call_id, put_id = _held_option_legs()
|
||||
held = call_id or _held_option_inst_id()
|
||||
if held and (
|
||||
|
||||
@@ -766,14 +766,25 @@ export default function PlanPage() {
|
||||
: "—"}
|
||||
</span>
|
||||
<span className="pos-meta-item mono">
|
||||
{fmt(pos?.option2_qty_eth, 2)} ETH ·{" "}
|
||||
{fmt(pos?.option2_qty_contracts, 0)} 张
|
||||
{fmt(
|
||||
pos?.option2_qty_eth ??
|
||||
plan?.oo_put_qty_eth ??
|
||||
plan?.risk_sizing_preview?.put_qty_eth,
|
||||
2,
|
||||
)}{" "}
|
||||
ETH · {fmt(pos?.option2_qty_contracts, 0)} 张
|
||||
</span>
|
||||
<span
|
||||
className="pos-meta-item mono"
|
||||
title="开仓指数÷开仓均价"
|
||||
>
|
||||
杠杆 {fmt(pos?.option2_leverage, 0)}x
|
||||
杠杆{" "}
|
||||
{pos?.option2_leverage != null
|
||||
? `${fmt(pos.option2_leverage, 0)}x`
|
||||
: optionLevLabel(
|
||||
pos?.entry_index_px ?? snap?.index_px,
|
||||
pos?.option2_entry_px,
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="pos-grid">
|
||||
@@ -788,8 +799,8 @@ export default function PlanPage() {
|
||||
<span className="pos-value mono">
|
||||
{fmtBidLiqEx(
|
||||
"option",
|
||||
pos?.option2_mark_px,
|
||||
pos?.option2_bid_sz,
|
||||
pos?.option2_mark_px ?? snap?.put?.bid,
|
||||
pos?.option2_bid_sz ?? snap?.put?.bid_sz,
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user