3520fc0214
Co-authored-by: Cursor <cursoragent@cursor.com>
133 lines
3.9 KiB
Python
133 lines
3.9 KiB
Python
"""期期到期结算指数:虚值 Call fill=0 不得反推成行权价。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from app.api.trades import _infer_settle_index, _overlay_expiry_zero_fills
|
|
|
|
|
|
def test_otm_call_zero_fill_does_not_become_strike(monkeypatch) -> None:
|
|
g = {
|
|
"hedge_mode": "option_option",
|
|
"option_side": "call",
|
|
"option2_side": "put",
|
|
"option_inst_id": "ETH-USD-260811-1920-C", # OKX 样式:不走公开回退
|
|
"strike": 1920.0,
|
|
"strike2": 1890.0,
|
|
"settle_index_px": None,
|
|
"close_reason": "expiry",
|
|
"close_at_ms": 1,
|
|
}
|
|
fills = [
|
|
{"leg": "option", "action": "close", "fill_px": 0.0, "slip": 0},
|
|
{"leg": "option2", "action": "close", "fill_px": 0.0, "slip": 0},
|
|
]
|
|
monkeypatch.setattr(
|
|
"app.api.public_index.maybe_public_settle_index", lambda _g: None
|
|
)
|
|
assert _infer_settle_index(g, fills) is None
|
|
|
|
|
|
def test_itm_put_fill_infers_settle_near_1875() -> None:
|
|
g = {
|
|
"hedge_mode": "option_option",
|
|
"option_side": "call",
|
|
"option2_side": "put",
|
|
"strike": 1920.0,
|
|
"strike2": 1920.0,
|
|
"settle_index_px": None,
|
|
}
|
|
fills = [
|
|
{"leg": "option", "action": "close", "fill_px": 0.0, "slip": 0},
|
|
{"leg": "option2", "action": "close", "fill_px": 45.0, "slip": 0},
|
|
]
|
|
assert _infer_settle_index(g, fills) == 1875.0
|
|
|
|
|
|
def test_stored_settle_wins() -> None:
|
|
g = {
|
|
"option_side": "call",
|
|
"strike": 1920.0,
|
|
"settle_index_px": 1875.2,
|
|
}
|
|
fills = [{"leg": "option", "action": "close", "fill_px": 0.0, "slip": 0}]
|
|
assert _infer_settle_index(g, fills) == 1875.2
|
|
|
|
|
|
def test_public_fallback_and_overlay(monkeypatch) -> None:
|
|
g = {
|
|
"hedge_mode": "option_option",
|
|
"option_side": "call",
|
|
"option2_side": "put",
|
|
"option_inst_id": "ETH-USD_UM-260811-1940-C",
|
|
"strike": 1940.0,
|
|
"strike2": 1920.0,
|
|
"settle_index_px": None,
|
|
"close_reason": "expiry",
|
|
"close_at_ms": 1786435200000,
|
|
}
|
|
fills = [
|
|
{"leg": "option", "action": "close", "fill_px": 0.0, "qty_eth": 7, "slip": 0},
|
|
{"leg": "option2", "action": "close", "fill_px": 0.0, "qty_eth": 7, "slip": 0},
|
|
]
|
|
monkeypatch.setattr(
|
|
"app.api.public_index.maybe_public_settle_index", lambda _g: 1877.8
|
|
)
|
|
assert _infer_settle_index(g, fills) == 1877.8
|
|
view = _overlay_expiry_zero_fills(g, fills, 1877.8)
|
|
assert view[0]["fill_px"] == 0.0 # call OTM
|
|
assert abs(view[1]["fill_px"] - (1920 - 1877.8)) < 1e-9
|
|
|
|
|
|
def test_overlay_wrong_near_zero_put_fill() -> None:
|
|
"""账单误写成 0.2 时,应按内在价值 42.2 覆盖,Put 显示盈利。"""
|
|
g = {
|
|
"hedge_mode": "option_option",
|
|
"option_side": "call",
|
|
"option2_side": "put",
|
|
"strike": 1940.0,
|
|
"strike2": 1920.0,
|
|
"close_reason": "expiry",
|
|
}
|
|
fills = [
|
|
{
|
|
"leg": "option",
|
|
"action": "open",
|
|
"fill_px": 12.0,
|
|
"qty_eth": 7.5,
|
|
"fee": 0.045,
|
|
"slip": 0,
|
|
},
|
|
{
|
|
"leg": "option2",
|
|
"action": "open",
|
|
"fill_px": 12.8,
|
|
"qty_eth": 7.0,
|
|
"fee": 0.045,
|
|
"slip": 0,
|
|
},
|
|
{
|
|
"leg": "option",
|
|
"action": "close",
|
|
"fill_px": 0.0,
|
|
"qty_eth": 7.5,
|
|
"fee": 0,
|
|
"slip": 0,
|
|
},
|
|
{
|
|
"leg": "option2",
|
|
"action": "close",
|
|
"fill_px": 0.2,
|
|
"qty_eth": 7.0,
|
|
"fee": 0.0007,
|
|
"slip": 0,
|
|
},
|
|
]
|
|
from app.sim.pnl import summarize_fills_pnl
|
|
|
|
view = _overlay_expiry_zero_fills(g, fills, 1877.8)
|
|
assert abs(view[3]["fill_px"] - 42.2) < 1e-9
|
|
s = summarize_fills_pnl(view)
|
|
# Put: (42.2 - 12.8) * 7 = 205.8
|
|
assert abs(float(s["option2_pnl"] or 0) - 205.8) < 1e-6
|
|
assert float(s["option2_pnl"] or 0) > 0
|