"""hub_perp_options_calc_lib 永期对冲测算.""" import unittest from lib.hub.hub_perp_options_calc_lib import ( calc_perp_options, calc_perp_options_hedge, calc_perp_options_points, ) class HubPerpOptionsCalcTests(unittest.TestCase): def test_example_1800_50_15_100(self): data, err = calc_perp_options_hedge( base="ETH", spot=1800, capital_usdt=3000, target_profit_u=15, move_mode="points", move_value=50, perp_leverage=10, option_leverage=100, ct_mult=0.01, ) self.assertIsNone(err) assert data is not None self.assertEqual(data["prem_per_coin"], 18.0) self.assertEqual(data["perp_gross_u"], 50.0) # fee = (1800+1850)*0.0005 = 1.825 self.assertAlmostEqual(data["perp_fee_u"], 1.825, places=6) self.assertAlmostEqual(data["premium_budget_u"], 33.175, places=6) self.assertAlmostEqual(data["opt_coins"], 33.175 / 18.0, places=6) self.assertAlmostEqual(data["opt_sheets"], data["opt_coins"] / 0.01, places=4) self.assertAlmostEqual(data["case_a"]["net_u"], 15.0, places=6) # B: opt_net = coins*50 - coins*18 = coins*32; portfolio = opt_net - 50 coins = data["opt_coins"] self.assertAlmostEqual(data["case_b"]["opt_net_u"], coins * 32.0, places=6) self.assertAlmostEqual(data["case_b"]["portfolio_net_u"], coins * 32.0 - 50.0, places=6) self.assertAlmostEqual(data["perp_margin_u"], 180.0, places=6) self.assertTrue(data["capital_ok"]) sw = data["case_sideways"] self.assertAlmostEqual(sw["premium_u"], data["premium_total_u"], places=6) self.assertAlmostEqual(sw["fee_u"], 1.8, places=6) self.assertAlmostEqual(sw["max_loss_u"], data["premium_total_u"] + 1.8, places=6) self.assertAlmostEqual(sw["net_u"], -sw["max_loss_u"], places=6) def test_pct_mode(self): data, err = calc_perp_options_hedge( base="BTC", spot=100000, capital_usdt=5000, target_profit_u=100, move_mode="pct", move_value=1, perp_leverage=5, option_leverage=50, ct_mult=0.01, ) self.assertIsNone(err) assert data is not None self.assertAlmostEqual(data["move_points"], 1000.0, places=6) self.assertAlmostEqual(data["perp_gross_u"], 1000.0, places=6) self.assertAlmostEqual(data["prem_per_coin"], 2000.0, places=6) def test_budget_too_small(self): data, err = calc_perp_options_hedge( base="ETH", spot=1800, capital_usdt=3000, target_profit_u=60, move_mode="points", move_value=50, perp_leverage=10, option_leverage=100, ) self.assertIsNone(data) self.assertIn("无法开期权", err or "") def test_bad_base(self): data, err = calc_perp_options_hedge( base="SOL", spot=100, capital_usdt=1000, target_profit_u=10, move_mode="points", move_value=5, perp_leverage=5, option_leverage=20, ) self.assertIsNone(data) self.assertIsNotNone(err) def test_points_ratio_1_to_2(self): # spot=1800, optLev=100 → prem/coin=18; ratio 1:2 → opt=2, premium=36 # A: move = (15+36+2*1800*0.0005)/(1-0.0005) = (51+1.8)/0.9995 data, err = calc_perp_options_points( base="ETH", spot=1800, capital_usdt=3000, target_profit_u=15, perp_leverage=10, option_leverage=100, ratio_perp=1, ratio_opt=2, ct_mult=0.01, ) self.assertIsNone(err) assert data is not None self.assertEqual(data["calc_mode"], "points") self.assertEqual(data["opt_coins"], 2.0) self.assertEqual(data["premium_total_u"], 36.0) expect_a = (15 + 36 + 2 * 1800 * 0.0005) / 0.9995 self.assertAlmostEqual(data["case_a"]["move_points"], expect_a, places=6) self.assertAlmostEqual(data["case_a"]["net_u"], 15.0, places=5) # 期权净利=目标: (15+36)/2 = 25.5 self.assertAlmostEqual(data["case_b"]["move_points_opt_net"], 25.5, places=6) self.assertAlmostEqual(data["case_b"]["opt_net_u"], 15.0, places=6) # 组合净利=目标: (15+36)/(2-1) = 51 self.assertAlmostEqual(data["case_b"]["move_points_portfolio"], 51.0, places=6) self.assertAlmostEqual(data["case_b"]["portfolio_net_u"], 15.0, places=6) # 横盘:权利金36 + 同价开平费 2*1800*0.0005=1.8 → 37.8 sw = data["case_sideways"] self.assertAlmostEqual(sw["premium_u"], 36.0, places=6) self.assertAlmostEqual(sw["fee_u"], 1.8, places=6) self.assertAlmostEqual(sw["max_loss_u"], 37.8, places=6) self.assertAlmostEqual(sw["net_u"], -37.8, places=6) def test_points_absolute_2_to_4(self): # 2:4 按绝对币数,不再归一成 1:2 data, err = calc_perp_options_points( base="ETH", spot=1800, capital_usdt=3000, target_profit_u=15, perp_leverage=10, option_leverage=100, ratio_perp=2, ratio_opt=4, ct_mult=0.01, ) self.assertIsNone(err) assert data is not None self.assertEqual(data["perp_coins"], 2.0) self.assertEqual(data["opt_coins"], 4.0) self.assertEqual(data["premium_total_u"], 72.0) self.assertAlmostEqual(data["perp_margin_u"], 360.0, places=6) expect_a = (15 + 72 + 2 * 1800 * 2 * 0.0005) / (2 * 0.9995) self.assertAlmostEqual(data["case_a"]["move_points"], expect_a, places=6) self.assertAlmostEqual(data["case_a"]["net_u"], 15.0, places=5) self.assertAlmostEqual(data["case_b"]["move_points_portfolio"], 43.5, places=6) self.assertAlmostEqual(data["case_b"]["portfolio_net_u"], 15.0, places=6) sw = data["case_sideways"] self.assertAlmostEqual(sw["fee_u"], 3.6, places=6) self.assertAlmostEqual(sw["max_loss_u"], 75.6, places=6) def test_points_ratio_1_to_1_no_portfolio(self): data, err = calc_perp_options( calc_mode="points", base="ETH", spot=1800, capital_usdt=3000, target_profit_u=15, perp_leverage=10, option_leverage=100, ratio_perp=1, ratio_opt=1, ) self.assertIsNone(err) assert data is not None self.assertIsNone(data["case_b"]["move_points_portfolio"]) self.assertIsNotNone(data["case_b"]["portfolio_error"]) if __name__ == "__main__": unittest.main()