Files
crypto_monitor/tests/test_hedge_plan_moneyness.py
T
dekun 3f6e67661b Lock hedge-plan option selection to ITM/ATM (perp) and ATM/OTM (OO).
Server validate on preview/start, UI filters and recommend templates, plus usability/security audit doc.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-05 21:50:35 +08:00

120 lines
4.3 KiB
Python

"""对冲计划虚实值选约与校验."""
import unittest
from lib.hedge_plan.hedge_plan_moneyness_lib import (
is_atm_or_otm,
is_itm_or_atm,
parse_strike_from_inst,
pick_itm_or_atm_contract,
recommend_oo_legs,
validate_oo_legs_moneyness,
validate_po_option_moneyness,
)
from lib.hedge_plan.hedge_plan_orders_lib import validate_start_body
class TestHedgeMoneyness(unittest.TestCase):
def test_parse_strike(self):
self.assertEqual(parse_strike_from_inst("ETH-USD-260731-1800-P"), 1800.0)
self.assertIsNone(parse_strike_from_inst("bad"))
def test_po_geometry(self):
# Call: K<=S 实/平
self.assertTrue(is_itm_or_atm(opt_type="C", strike=3500, index_px=3510))
self.assertFalse(is_itm_or_atm(opt_type="C", strike=3600, index_px=3510))
# Put: K>=S
self.assertTrue(is_itm_or_atm(opt_type="P", strike=3600, index_px=3510))
self.assertFalse(is_itm_or_atm(opt_type="P", strike=3400, index_px=3510))
def test_oo_geometry(self):
self.assertTrue(is_atm_or_otm(opt_type="C", strike=3600, index_px=3510))
self.assertFalse(is_atm_or_otm(opt_type="C", strike=3400, index_px=3510))
self.assertTrue(is_atm_or_otm(opt_type="P", strike=3400, index_px=3510))
self.assertFalse(is_atm_or_otm(opt_type="P", strike=3600, index_px=3510))
def test_validate_po_rejects_otm(self):
err = validate_po_option_moneyness(opt_type="P", strike=1700, index_px=1800)
self.assertIsNotNone(err)
self.assertIn("虚值", err)
self.assertIsNone(
validate_po_option_moneyness(opt_type="P", strike=1800, index_px=1800)
)
def test_validate_oo_rejects_itm(self):
err = validate_oo_legs_moneyness(
{"opt_type": "C", "strike": 1700},
{"opt_type": "P", "strike": 1900},
index_px=1800,
)
self.assertIsNotNone(err)
self.assertIn("实值", err)
self.assertIsNone(
validate_oo_legs_moneyness(
{"opt_type": "C", "strike": 1850},
{"opt_type": "P", "strike": 1750},
index_px=1800,
)
)
def test_pick_itm_or_atm(self):
contracts = [
{"inst_id": "E-C-3600", "opt_type": "C", "strike": 3600},
{"inst_id": "E-C-3500", "opt_type": "C", "strike": 3500},
{"inst_id": "E-C-3400", "opt_type": "C", "strike": 3400},
]
picked = pick_itm_or_atm_contract(
contracts, opt_type="C", index_px=3510, itm_max_dist=200
)
self.assertIsNotNone(picked)
self.assertEqual(picked["strike"], 3500)
def test_recommend_oo(self):
contracts = [
{"inst_id": "E-C-1800", "opt_type": "C", "strike": 1800},
{"inst_id": "E-P-1800", "opt_type": "P", "strike": 1800},
{"inst_id": "E-C-1900", "opt_type": "C", "strike": 1900},
{"inst_id": "E-P-1700", "opt_type": "P", "strike": 1700},
]
pair = recommend_oo_legs(contracts, index_px=1800, template="atm_straddle")
self.assertIsNotNone(pair)
self.assertEqual(pair[0]["opt_type"], "C")
self.assertEqual(pair[1]["opt_type"], "P")
def test_validate_start_body_po_otm(self):
err = validate_start_body(
"perp_options",
{
"direction": "long",
"entry": 1800,
"tp": 1900,
"sl": 1700,
"contracts": 1,
"opt_inst_id": "ETH-USD-260731-1700-P",
"opt_type": "P",
"strike": 1700,
"index_px": 1800,
"sheets": 1,
"exchange_symbol": "ETH/USDT:USDT",
},
)
self.assertIsNotNone(err)
self.assertIn("虚值", err)
def test_validate_start_body_oo_itm(self):
err = validate_start_body(
"options_options",
{
"target_price_up": 1900,
"target_price_down": 1700,
"index_px": 1800,
"leg_a": {"inst_id": "ETH-USD-260731-1700-C", "opt_type": "C", "strike": 1700},
"leg_b": {"inst_id": "ETH-USD-260731-1900-P", "opt_type": "P", "strike": 1900},
},
)
self.assertIsNotNone(err)
self.assertIn("实值", err)
if __name__ == "__main__":
unittest.main()