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>
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
"""对冲计划虚实值选约与校验."""
|
||||
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()
|
||||
Reference in New Issue
Block a user