Add control residual options table with liquidity-only manual close.
Fleet status exposes enriched residuals; manual close skips the premium recovery gate while auto mid-close remains unchanged. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -189,6 +189,57 @@ def test_residual_book_pending_guard(tmp_path, monkeypatch) -> None:
|
||||
db.close()
|
||||
|
||||
|
||||
def test_manual_close_skips_premium_ratio(tmp_path, monkeypatch) -> None:
|
||||
monkeypatch.setenv("MODE", "SIM")
|
||||
db = Database(tmp_path / "manual.db")
|
||||
db.set_setting("residual_min_premium_pct", "20")
|
||||
_seed_residual(db, initial_premium=100.0, qty=2.0, entry_px=50.0)
|
||||
m = Matcher(db)
|
||||
|
||||
# bid=5 → premium=10 < 20%,自动路径应跳过,手动可平
|
||||
oq = SimpleNamespace(bid=5.0, ask=5.5, bid_sz=10_000.0, mark_px=5.0)
|
||||
monkeypatch.setattr(m, "_quote_held_option", lambda _id: oq)
|
||||
monkeypatch.setattr(m, "_close_spot_px", lambda _snap: 1900.0)
|
||||
monkeypatch.setattr(m, "_ct_mult", lambda _id: 0.01)
|
||||
|
||||
assert m.try_close_one_residual(m.list_residual_options()[0]) is None
|
||||
enriched = m.list_residual_options_enriched()
|
||||
assert len(enriched) == 1
|
||||
assert enriched[0]["liquidity_ok"] is True
|
||||
assert enriched[0]["recovery_pct"] is not None
|
||||
assert float(enriched[0]["recovery_pct"]) < 20.0
|
||||
|
||||
r = m.close_residual_manual("G-res")
|
||||
assert r.ok is True
|
||||
row = db.fetchone(
|
||||
"SELECT status FROM residual_options WHERE group_id=?", ("G-res",)
|
||||
)
|
||||
assert row is not None and row["status"] == "settled"
|
||||
db.close()
|
||||
|
||||
|
||||
def test_manual_close_liquidity_still_required(tmp_path, monkeypatch) -> None:
|
||||
monkeypatch.setenv("MODE", "SIM")
|
||||
db = Database(tmp_path / "manual_liq.db")
|
||||
db.set_setting("residual_min_premium_pct", "20")
|
||||
_seed_residual(db, initial_premium=100.0, qty=2.0)
|
||||
m = Matcher(db)
|
||||
|
||||
oq = SimpleNamespace(bid=5.0, ask=5.5, bid_sz=1.0, mark_px=5.0)
|
||||
monkeypatch.setattr(m, "_quote_held_option", lambda _id: oq)
|
||||
monkeypatch.setattr(m, "_close_spot_px", lambda _snap: 1900.0)
|
||||
monkeypatch.setattr(m, "_ct_mult", lambda _id: 0.01)
|
||||
|
||||
r = m.close_residual_manual("G-res")
|
||||
assert r.ok is False
|
||||
assert "liquidity" in (r.detail or "")
|
||||
row = db.fetchone(
|
||||
"SELECT status FROM residual_options WHERE group_id=?", ("G-res",)
|
||||
)
|
||||
assert row is not None and row["status"] == "pending"
|
||||
db.close()
|
||||
|
||||
|
||||
def test_settings_exposes_residual_min_premium_pct(tmp_path, monkeypatch) -> None:
|
||||
monkeypatch.setenv("MODE", "SIM")
|
||||
from app.api import settings as settings_api
|
||||
|
||||
Reference in New Issue
Block a user