import unittest from lib.trade.entry_model_lib import ( ENTRY_MODEL_BIG_DIV_A, ENTRY_MODEL_BIG_DIV_B, ENTRY_MODEL_LAUNCH_A, ENTRY_MODEL_LAUNCH_B, ENTRY_MODEL_SMALL_DIV, ENTRY_CATEGORY_REVERSAL, ENTRY_CATEGORY_TREND, build_intraday_entry_reason_options, build_trend_div_entry_reason_options, entry_model_categories, entry_model_category, entry_model_display_label, entry_model_label, format_entry_type_display, hub_meta_entry_context, intraday_entry_model_options, is_intraday_trading_profile, open_position_button_label, parse_manual_order_style_fields, resolve_trade_record_entry_reason, trade_style_for_entry_model, trend_manual_entry_reason_count, ) from lib.trade.trade_policy_lib import TradePolicy, load_trade_policy class TestEntryModelLib(unittest.TestCase): def test_intraday_profile_btc_eth_whitelist(self): policy = load_trade_policy( { "TRADE_SYMBOL_RESTRICT_ENABLED": "true", "TRADE_SYMBOL_WHITELIST": "BTC,ETH", } ) self.assertTrue(is_intraday_trading_profile(policy)) self.assertEqual(trend_manual_entry_reason_count(policy), 2) def test_trend_div_profile_alt(self): policy = load_trade_policy( { "TRADE_SYMBOL_RESTRICT_ENABLED": "false", "TRADE_SYMBOL_WHITELIST": "BTC,ETH", } ) self.assertFalse(is_intraday_trading_profile(policy)) self.assertEqual(trend_manual_entry_reason_count(policy), 5) def test_entry_model_maps_trade_style(self): self.assertEqual(trade_style_for_entry_model(ENTRY_MODEL_LAUNCH_A), "trend") self.assertEqual(trade_style_for_entry_model(ENTRY_MODEL_BIG_DIV_A), "trend") self.assertEqual(trade_style_for_entry_model(ENTRY_MODEL_SMALL_DIV), "swing") self.assertEqual(entry_model_label(ENTRY_MODEL_LAUNCH_B), "启动B") self.assertEqual(entry_model_category(ENTRY_MODEL_LAUNCH_A), ENTRY_CATEGORY_REVERSAL) self.assertEqual(entry_model_category(ENTRY_MODEL_BIG_DIV_B), ENTRY_CATEGORY_TREND) def test_entry_model_categories_two_level(self): cats = entry_model_categories() keys = [c["key"] for c in cats] self.assertEqual(keys, ["reversal", "trend", "swing"]) reversal = cats[0]["options"] self.assertEqual([o["code"] for o in reversal], ["launch_a", "launch_b"]) self.assertEqual(len(cats[2]["options"]), 1) def test_parse_trend_div_requires_entry_model(self): policy = TradePolicy(False, "both", False, ()) style, code, err = parse_manual_order_style_fields(policy, {}) self.assertTrue(err) self.assertEqual(code, None) style, code, err = parse_manual_order_style_fields( policy, {"entry_model": ENTRY_MODEL_SMALL_DIV} ) self.assertIsNone(err) self.assertEqual(code, ENTRY_MODEL_SMALL_DIV) self.assertEqual(style, "swing") style, code, err = parse_manual_order_style_fields( policy, {"entry_model": ENTRY_MODEL_LAUNCH_A} ) self.assertIsNone(err) self.assertEqual(code, ENTRY_MODEL_LAUNCH_A) self.assertEqual(style, "trend") def test_hub_meta_intraday(self): policy = load_trade_policy( { "TRADE_SYMBOL_RESTRICT_ENABLED": "true", "TRADE_SYMBOL_WHITELIST": "BTC,ETH", } ) ctx = hub_meta_entry_context(policy) self.assertTrue(ctx["intraday_discipline"]) self.assertEqual(ctx["order_entry_profile"], "intraday") policy = TradePolicy(False, "both", True, ("BTC", "ETH")) style, code, err = parse_manual_order_style_fields(policy, {"trade_style": "swing"}) self.assertIsNone(err) self.assertIsNone(code) self.assertEqual(style, "swing") def test_intraday_entry_model_options(self): opts = intraday_entry_model_options() codes = [o.code for o in opts] self.assertEqual(codes, ["liquidity_false_break", "structure_breakout"]) self.assertEqual(entry_model_label("liquidity_false_break"), "假破") def test_parse_intraday_requires_entry_model(self): policy = TradePolicy(True, "both", True, ("BTC", "ETH")) style, code, err = parse_manual_order_style_fields(policy, {}) self.assertTrue(err) style, code, err = parse_manual_order_style_fields( policy, {"entry_model": "structure_breakout"} ) self.assertIsNone(err) self.assertEqual(code, "structure_breakout") self.assertEqual(style, "trend") def test_open_position_button_intraday(self): policy = TradePolicy(True, "both", True, ("BTC", "ETH")) self.assertEqual( open_position_button_label(policy, "full_margin"), "开仓(日内·全仓杠杆)", ) def test_resolve_entry_reason_from_model(self): er = resolve_trade_record_entry_reason(entry_model=ENTRY_MODEL_BIG_DIV_B) self.assertEqual(er, "顺势/大分歧B") er2 = resolve_trade_record_entry_reason(entry_model=ENTRY_MODEL_LAUNCH_A) self.assertEqual(er2, "反转/启动A") def test_entry_model_display_label(self): self.assertEqual(entry_model_display_label(ENTRY_MODEL_LAUNCH_A), "反转/启动A") self.assertEqual(entry_model_display_label(ENTRY_MODEL_SMALL_DIV), "波段单/小分歧") self.assertEqual(entry_model_display_label("liquidity_false_break"), "波段单/假破") self.assertEqual(format_entry_type_display("启动A"), "反转/启动A") self.assertEqual(entry_model_label(ENTRY_MODEL_LAUNCH_B), "启动B") def test_resolve_entry_reason_trade_style_fallback(self): er = resolve_trade_record_entry_reason(trade_style="swing") self.assertEqual(er, "波段单") er2 = resolve_trade_record_entry_reason(trade_style="trend") self.assertEqual(er2, "趋势单") def test_build_trend_div_journal_options(self): opts = build_trend_div_entry_reason_options(("趋势回调",)) self.assertEqual(opts[:5], ("反转/启动A", "反转/启动B", "顺势/大分歧A", "顺势/大分歧B", "波段单/小分歧")) self.assertIn("趋势单", opts) self.assertIn("波段单", opts) self.assertIn("趋势回调", opts) def test_build_intraday_journal_options_only_four(self): opts = build_intraday_entry_reason_options( ( "关键位箱体突破", "关键位回调触价开仓", "关键位突破触价开仓", ), ("趋势回调", "顺势加仓"), ) self.assertEqual( opts, ( "波段单/假破", "波段单/结构突破", "关键位回调触价开仓", "关键位突破触价开仓", ), ) def test_normalize_review_entry_reason(self): from lib.trade.entry_model_lib import normalize_review_entry_reason allowed = build_trend_div_entry_reason_options(()) self.assertEqual(normalize_review_entry_reason("反转/启动A", allowed), "反转/启动A") self.assertEqual(normalize_review_entry_reason("启动A", allowed), "反转/启动A") self.assertEqual(normalize_review_entry_reason("趋势单", allowed), "趋势单") if __name__ == "__main__": unittest.main()