import datetime import sys import unittest from pathlib import Path ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT)) sys.path.insert(0, str(ROOT / "src")) from platforms import joinquant_strategy, qmt_builtin_strategy from platforms.fake_joinquant_harness import FakeJoinQuantHarness, FakePosition from platforms.fake_qmt_harness import FakeQmtContext, FakeQmtHarness def price_histories(): return { "600000.XSHG": [15.0 - index * 0.02 for index in range(21)], "000001.XSHE": [10.0 + index * 0.10 for index in range(21)], "300750.XSHE": [100.0 + index * 1.5 for index in range(21)], "000333.XSHE": [20.0 + index * 0.05 for index in range(21)], } class JoinQuantContractTest(unittest.TestCase): def setUp(self): self.harness = FakeJoinQuantHarness(price_histories()) self.harness.initialize(joinquant_strategy) def test_initialize_enables_future_data_guard_and_weekly_open(self): self.assertIs(self.harness.options["avoid_future_data"], True) self.assertIs(self.harness.options["use_real_price"], True) self.assertEqual( self.harness.options["order_volume_ratio"], 0.1, ) self.assertEqual(self.harness.benchmark, "000905.XSHG") self.assertEqual(self.harness.order_cost["type"], "stock") self.assertAlmostEqual( self.harness.order_cost["cost"].open_commission, 0.00021, ) self.assertAlmostEqual( self.harness.order_cost["cost"].close_tax, 0.0005, ) self.assertEqual(self.harness.slippage["type"], "stock") self.assertAlmostEqual( self.harness.slippage["slippage"].value, 0.0004, ) self.assertEqual(len(self.harness.schedules), 1) _, run_time = self.harness.schedules[0] self.assertEqual(run_time, "open") def test_rebalance_submits_exact_portable_executable_deltas(self): self.assertIsNone(self.harness.run_scheduled()) self.harness.advance_session() plan = self.harness.run_scheduled() self.assertEqual(plan, self.harness.g.quant60_last_plan) self.assertTrue(self.harness.orders) ordered_symbols = {symbol for symbol, _ in self.harness.orders} expected = { symbol: int(delta) for symbol, delta in plan["orders"].items() if int(delta) != 0 } self.assertEqual(ordered_symbols, set(expected)) self.assertEqual( dict(self.harness.orders), expected, ) def test_missing_current_data_fails_closed_for_new_position(self): best = "300750.XSHE" del self.harness.current_data[best] self.assertIsNone(self.harness.run_scheduled()) self.harness.advance_session() self.harness.run_scheduled() ordered_symbols = {symbol for symbol, _ in self.harness.orders} self.assertNotIn(best, ordered_symbols) self.assertEqual( self.harness.g.quant60_skipped_orders[-1]["reason"], "current_data_unavailable", ) def test_star_market_buy_uses_daily_high_limit_protection(self): histories = price_histories() star = "688301.XSHG" histories[star] = [20.0 + index * 0.3 for index in range(21)] harness = FakeJoinQuantHarness(histories) harness.current_data[star].high_limit = 31.28 harness.initialize(joinquant_strategy) self.assertIsNone(harness.run_scheduled()) harness.advance_session() plan = harness.run_scheduled() self.assertGreater(plan["orders"][star], 0) styles = dict(harness.order_styles) self.assertEqual(styles[star].kind, "limit") self.assertAlmostEqual(styles[star].limit_price, 31.28) def test_star_market_sell_uses_daily_low_limit_protection(self): histories = price_histories() star = "688301.XSHG" histories[star] = [20.0 + index * 0.3 for index in range(21)] harness = FakeJoinQuantHarness( histories, total_value=1000.0, positions={star: FakePosition(1000)}, ) harness.current_data[star].low_limit = 17.06 harness.initialize(joinquant_strategy) self.assertIsNone(harness.run_scheduled()) harness.advance_session() plan = harness.run_scheduled() self.assertEqual(plan["orders"][star], -1000) styles = dict(harness.order_styles) self.assertEqual(styles[star].kind, "limit") self.assertAlmostEqual(styles[star].limit_price, 17.06) def test_invalid_star_protection_price_fails_closed(self): histories = price_histories() star = "688301.XSHG" histories[star] = [20.0 + index * 0.3 for index in range(21)] harness = FakeJoinQuantHarness(histories) harness.current_data[star].high_limit = float("nan") harness.initialize(joinquant_strategy) self.assertIsNone(harness.run_scheduled()) harness.advance_session() plan = harness.run_scheduled() self.assertGreater(plan["orders"][star], 0) self.assertNotIn(star, dict(harness.orders)) self.assertEqual( harness.g.quant60_skipped_orders[-1]["reason"], "star_protection_price_invalid", ) def test_incomplete_history_aborts_entire_rebalance_without_orders(self): histories = price_histories() histories["300750.XSHE"] = histories["300750.XSHE"][1:] harness = FakeJoinQuantHarness(histories) harness.initialize(joinquant_strategy) self.assertIsNone(harness.run_scheduled()) harness.advance_session() with self.assertRaises(joinquant_strategy.PriceHistoryUnavailable): harness.run_scheduled() self.assertEqual(harness.orders, []) def test_joinquant_unmanaged_position_aborts_without_orders(self): harness = FakeJoinQuantHarness( price_histories(), positions={"601318.XSHG": FakePosition(1000)}, ) harness.initialize(joinquant_strategy) harness.g.quant60_config["universe_mode"] = "fixed" self.assertIsNone(harness.run_scheduled()) harness.advance_session() with self.assertRaises(joinquant_strategy.UnmanagedPositionError): harness.run_scheduled() self.assertEqual(harness.orders, []) def test_executable_sell_delta_preserves_t1_cap_and_odd_lot(self): position = FakePosition(total_amount=1050, closeable_amount=200) harness = FakeJoinQuantHarness( price_histories(), total_value=1000.0, positions={"600000.XSHG": position}, ) harness.initialize(joinquant_strategy) self.assertIsNone(harness.run_scheduled()) harness.advance_session() plan = harness.run_scheduled() self.assertEqual(plan["orders"]["600000.XSHG"], -200) self.assertEqual(dict(harness.orders)["600000.XSHG"], 850) def test_one_session_week_executes_on_next_available_session(self): self.assertIsNone(self.harness.run_scheduled()) # Model a holiday week with no second session: the next available # session is the first session of the next ISO week. self.harness.advance_session(days=7) plan = self.harness.run_scheduled() self.assertIsNotNone(plan) self.assertTrue(self.harness.orders) self.assertEqual( self.harness.g.quant60_pending_first_session, self.harness.context.current_dt.date(), ) class CrossPlatformParityTest(unittest.TestCase): def test_same_completed_close_produces_same_portable_plan(self): jq = FakeJoinQuantHarness(price_histories()) jq.initialize(joinquant_strategy) jq.context.previous_date = datetime.date(2026, 7, 24) jq.context.current_dt = datetime.datetime(2026, 7, 27, 9, 30) jq_plan = joinquant_strategy.compute_plan(jq.context) qmt_histories = { symbol.replace(".XSHE", ".SZ").replace(".XSHG", ".SH"): values for symbol, values in price_histories().items() } context = FakeQmtContext( qmt_histories, bar_time=datetime.datetime(2026, 7, 24, 15, 0), ) qmt = FakeQmtHarness(context).install(qmt_builtin_strategy) qmt_builtin_strategy.init(context) self.assertEqual(context.commission["type"], 0) self.assertEqual( context.commission["values"], [0.0, 0.0005, 0.00021, 0.00021, 0.0, 5.0], ) self.assertEqual( context.slippage, {"type": 2, "value": 0.0002}, ) qmt_plan = qmt_builtin_strategy.compute_plan( context, datetime.datetime(2026, 7, 24, 15, 0) ) self.assertEqual(jq_plan, qmt_plan) self.assertEqual(joinquant_strategy.CONFIG["skip"], 0) self.assertEqual(qmt_builtin_strategy.CONFIG["skip"], 0) self.assertEqual(context.last_market_request["end_time"], "20260724") self.assertIs(context.last_market_request["fill_data"], False) self.assertEqual( jq.last_index_request, { "index_symbol": "000905.XSHG", "date": jq.context.previous_date, }, ) self.assertEqual( jq.last_extras_request["info"], "is_st", ) self.assertEqual( jq.last_extras_request["end_date"], jq.context.previous_date, ) self.assertEqual( context.last_sector_request, { "sector_name": "中证500", "timetag": context.get_bar_timetag(context.barpos), }, ) def test_pit_st_exclusion_is_identical_across_hosted_wrappers(self): st_canonical = "300750.XSHE" jq = FakeJoinQuantHarness( price_histories(), st_symbols={st_canonical}, ) jq.initialize(joinquant_strategy) jq.context.previous_date = datetime.date(2026, 7, 24) jq.context.current_dt = datetime.datetime(2026, 7, 27, 9, 30) jq_plan = joinquant_strategy.compute_plan(jq.context) qmt_histories = { symbol.replace(".XSHE", ".SZ").replace(".XSHG", ".SH"): values for symbol, values in price_histories().items() } qmt_symbol = "300750.SZ" context = FakeQmtContext( qmt_histories, bar_time=datetime.datetime(2026, 7, 24, 15, 0), st_periods={ qmt_symbol: { "ST": [["20260701", "20260731"]], } }, ) FakeQmtHarness(context).install(qmt_builtin_strategy) qmt_builtin_strategy.init(context) qmt_plan = qmt_builtin_strategy.compute_plan( context, datetime.datetime(2026, 7, 24, 15, 0), ) self.assertEqual(jq_plan, qmt_plan) self.assertNotIn(st_canonical, jq_plan["scores"]) self.assertNotIn(st_canonical, qmt_plan["scores"]) def test_qmt_live_orders_are_off_by_default(self): qmt_histories = { symbol.replace(".XSHE", ".SZ").replace(".XSHG", ".SH"): values for symbol, values in price_histories().items() } context = FakeQmtContext( qmt_histories, trade_mode="trading", bar_time=datetime.datetime(2026, 7, 24, 15, 0), ) harness = FakeQmtHarness(context) self.assertIsNone(harness.run(qmt_builtin_strategy)) context._bar_time += datetime.timedelta(days=7) plan = qmt_builtin_strategy.handlebar(context) self.assertIsNotNone(plan) self.assertEqual(harness.orders, []) def test_qmt_builtin_live_mutation_cannot_be_enabled_by_parameters(self): qmt_histories = { symbol.replace(".XSHE", ".SZ").replace(".XSHG", ".SH"): values for symbol, values in price_histories().items() } context = FakeQmtContext( qmt_histories, trade_mode="trading", bar_time=datetime.datetime(2026, 7, 24, 15, 0), ) harness = FakeQmtHarness(context).install(qmt_builtin_strategy) qmt_builtin_strategy.init(context) context.do_back_test = "False" self.assertFalse(qmt_builtin_strategy._is_backtest(context)) self.assertFalse(qmt_builtin_strategy._orders_allowed(context)) # Even an injected ContextInfo knob cannot enable broker mutation. context.q60_config = { "enable_live_orders": True, "live_confirmation": "Q60_LIVE_ACK", } self.assertFalse(qmt_builtin_strategy._orders_allowed(context)) def test_qmt_module_state_survives_contextinfo_rollback_between_bars(self): qmt_histories = { symbol.replace(".XSHE", ".SZ").replace(".XSHG", ".SH"): values for symbol, values in price_histories().items() } first_context = FakeQmtContext( qmt_histories, trade_mode="backtest", bar_time=datetime.datetime(2026, 7, 20, 15, 0), ) harness = FakeQmtHarness(first_context).install(qmt_builtin_strategy) qmt_builtin_strategy.init(first_context) self.assertFalse( any(name.startswith("q60_") for name in vars(first_context)) ) self.assertIsNone(qmt_builtin_strategy.handlebar(first_context)) # QMT documents that ContextInfo user attributes may roll back between # handlebar calls. Rebuild a clean platform context to model that # boundary; the module-global clock must still recognize a new week. rebuilt_context = FakeQmtContext( qmt_histories, trade_mode="backtest", bar_time=datetime.datetime(2026, 7, 27, 15, 0), ) self.assertFalse( any(name.startswith("q60_") for name in vars(rebuilt_context)) ) plan = qmt_builtin_strategy.handlebar(rebuilt_context) self.assertIsNotNone(plan) self.assertGreater(len(harness.orders), 0) self.assertIs(qmt_builtin_strategy.g.last_plan, plan) def test_qmt_backtest_submits_once_per_iso_week(self): qmt_histories = { symbol.replace(".XSHE", ".SZ").replace(".XSHG", ".SH"): values for symbol, values in price_histories().items() } context = FakeQmtContext(qmt_histories, trade_mode="backtest") harness = FakeQmtHarness(context) self.assertIsNone(harness.run(qmt_builtin_strategy)) context._bar_time += datetime.timedelta(days=7) first = qmt_builtin_strategy.handlebar(context) first_count = len(harness.orders) second = qmt_builtin_strategy.handlebar(context) self.assertIsNotNone(first) self.assertGreater(first_count, 0) self.assertIsNone(second) self.assertEqual(len(harness.orders), first_count) first_ids = {item["user_order_id"] for item in harness.orders} context._bar_time += datetime.timedelta(days=7) third = qmt_builtin_strategy.handlebar(context) second_ids = { item["user_order_id"] for item in harness.orders[first_count:] } self.assertIsNotNone(third) self.assertTrue(first_ids.isdisjoint(second_ids)) self.assertTrue(all(len(value.encode("ascii")) <= 24 for value in second_ids)) def test_qmt_incomplete_history_aborts_without_passorder(self): qmt_histories = { symbol.replace(".XSHE", ".SZ").replace(".XSHG", ".SH"): values for symbol, values in price_histories().items() } qmt_histories["300750.SZ"] = qmt_histories["300750.SZ"][1:] context = FakeQmtContext(qmt_histories, trade_mode="backtest") harness = FakeQmtHarness(context) self.assertIsNone(harness.run(qmt_builtin_strategy)) context._bar_time += datetime.timedelta(days=7) with self.assertRaises(qmt_builtin_strategy.PriceHistoryUnavailable): qmt_builtin_strategy.handlebar(context) self.assertEqual(harness.orders, []) def test_qmt_unmanaged_position_aborts_without_passorder(self): qmt_histories = { symbol.replace(".XSHE", ".SZ").replace(".XSHG", ".SH"): values for symbol, values in price_histories().items() } position = type( "Position", (), { "stock_code": "601318.SH", "volume": 1000, "can_use_volume": 1000, }, )() context = FakeQmtContext( qmt_histories, positions=[position], trade_mode="backtest", ) harness = FakeQmtHarness(context) self.assertIsNone(harness.run(qmt_builtin_strategy)) qmt_builtin_strategy.g.config["universe_mode"] = "fixed" context._bar_time += datetime.timedelta(days=7) with self.assertRaises(qmt_builtin_strategy.UnmanagedPositionError): qmt_builtin_strategy.handlebar(context) self.assertEqual(harness.orders, []) def test_qmt_minute_driver_is_rejected_before_orders(self): qmt_histories = { symbol.replace(".XSHE", ".SZ").replace(".XSHG", ".SH"): values for symbol, values in price_histories().items() } context = FakeQmtContext( qmt_histories, trade_mode="backtest", params={"period": "1m"}, ) harness = FakeQmtHarness(context) with self.assertRaises(qmt_builtin_strategy.UnsupportedStrategyPeriod): harness.run(qmt_builtin_strategy) self.assertEqual(harness.orders, []) def test_qmt_builtin_do_back_test_flag_enables_backtest_orders(self): qmt_histories = { symbol.replace(".XSHE", ".SZ").replace(".XSHG", ".SH"): values for symbol, values in price_histories().items() } context = FakeQmtContext(qmt_histories, trade_mode="backtest") del context.trade_mode context._param.pop("trade_mode") context.do_back_test = True harness = FakeQmtHarness(context) self.assertIsNone(harness.run(qmt_builtin_strategy)) context._bar_time += datetime.timedelta(days=7) plan = qmt_builtin_strategy.handlebar(context) self.assertIsNotNone(plan) self.assertGreater(len(harness.orders), 0) def test_qmt_source_is_ascii_despite_gbk_header(self): payload = (ROOT / "platforms/qmt_builtin_strategy.py").read_bytes() payload.decode("ascii") self.assertTrue(payload.startswith(b"# coding: gbk")) if __name__ == "__main__": unittest.main()