diff --git a/src/quant_platform_kit/longbridge/execution.py b/src/quant_platform_kit/longbridge/execution.py index 136939d..b4bf38b 100644 --- a/src/quant_platform_kit/longbridge/execution.py +++ b/src/quant_platform_kit/longbridge/execution.py @@ -40,7 +40,7 @@ def submit_order( order_type = OrderType.LO if order_kind == "limit" else OrderType.MO order_side = OrderSide.Buy if side == "buy" else OrderSide.Sell submitted_quantity = Decimal(str(quantity)) - if submitted_quantity < Decimal("1") or submitted_quantity != submitted_quantity.to_integral_value(): + if submitted_quantity < Decimal("1"): return ExecutionReport( symbol=symbol.split(".")[0], side=side, @@ -48,8 +48,21 @@ def submit_order( status="rejected", raw_payload={ "detail": ( - "LongBridge submitted_quantity must be a whole-share quantity " - "of at least 1 share; " + "LongBridge submitted_quantity must be at least 1 share; " + f"got {submitted_quantity}." + ), + "order_kind": order_kind, + }, + ) + if side == "buy" and submitted_quantity != submitted_quantity.to_integral_value(): + return ExecutionReport( + symbol=symbol.split(".")[0], + side=side, + quantity=float(quantity), + status="rejected", + raw_payload={ + "detail": ( + "LongBridge fractional buy orders are not supported by this adapter; " f"got {submitted_quantity}." ), "order_kind": order_kind, diff --git a/tests/test_longbridge_execution.py b/tests/test_longbridge_execution.py index 65f017b..cc71b92 100644 --- a/tests/test_longbridge_execution.py +++ b/tests/test_longbridge_execution.py @@ -102,10 +102,10 @@ def test_submit_order_rejects_quantity_below_one_before_api_call(self) -> None: ) self.assertEqual(report.status, "rejected") - self.assertIn("whole-share quantity of at least 1 share", report.raw_payload["detail"]) + self.assertIn("at least 1 share", report.raw_payload["detail"]) self.assertFalse(hasattr(ctx, "submit_args")) - def test_submit_order_rejects_fractional_quantity_before_api_call(self) -> None: + def test_submit_order_rejects_fractional_buy_before_api_call(self) -> None: longport_module = types.ModuleType("longport") openapi_module = types.ModuleType("longport.openapi") openapi_module.OrderSide = types.SimpleNamespace(Buy="Buy", Sell="Sell") @@ -124,9 +124,23 @@ def test_submit_order_rejects_fractional_quantity_before_api_call(self) -> None: ) self.assertEqual(report.status, "rejected") - self.assertIn("whole-share quantity of at least 1 share", report.raw_payload["detail"]) + self.assertIn("fractional buy orders are not supported", report.raw_payload["detail"]) self.assertFalse(hasattr(ctx, "submit_args")) + def test_submit_order_allows_fractional_sell_at_or_above_one_share(self) -> None: + longport_module = types.ModuleType("longport") + openapi_module = types.ModuleType("longport.openapi") + openapi_module.OrderSide = types.SimpleNamespace(Buy="Buy", Sell="Sell") + openapi_module.OrderType = types.SimpleNamespace(LO="LO", MO="MO") + openapi_module.TimeInForceType = types.SimpleNamespace(Day="Day") + + ctx = FakeTradeContext() + with patch.dict(sys.modules, {"longport": longport_module, "longport.openapi": openapi_module}): + report = submit_order(ctx, "BOXX.US", order_kind="market", side="sell", quantity=4.6177) + + self.assertEqual(report.status, "submitted") + self.assertEqual(str(ctx.submit_args[3]), "4.6177") + def test_fetch_order_status(self) -> None: status = fetch_order_status(FakeTradeContext(), "OID-1")