Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions src/quant_platform_kit/longbridge/execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ def estimate_max_purchase_quantity(
*,
order_kind: str,
ref_price: float,
fractional_shares: bool = False,
) -> float:
from longport.openapi import OrderSide, OrderType

Expand All @@ -22,7 +21,6 @@ def estimate_max_purchase_quantity(
order_type=order_type,
side=OrderSide.Buy,
price=Decimal(str(ref_price)),
fractional_shares=bool(fractional_shares),
)
cash_max_qty = getattr(response, "cash_max_qty", 0)
return max(0.0, float(Decimal(str(cash_max_qty or "0"))))
Expand All @@ -42,15 +40,16 @@ 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"):
if submitted_quantity < Decimal("1") or submitted_quantity != submitted_quantity.to_integral_value():
return ExecutionReport(
symbol=symbol.split(".")[0],
side=side,
quantity=float(quantity),
status="rejected",
raw_payload={
"detail": (
"LongBridge submitted_quantity must be at least 1 share; "
"LongBridge submitted_quantity must be a whole-share quantity "
"of at least 1 share; "
f"got {submitted_quantity}."
),
"order_kind": order_kind,
Expand Down
47 changes: 25 additions & 22 deletions tests/test_longbridge_execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,28 +53,23 @@ def test_estimate_max_purchase_quantity(self) -> None:
quantity = estimate_max_purchase_quantity(ctx, "SOXL.US", order_kind="limit", ref_price=100.5)

self.assertEqual(quantity, 12)
self.assertIs(ctx.estimate_kwargs["fractional_shares"], False)
self.assertNotIn("fractional_shares", ctx.estimate_kwargs)

def test_estimate_max_purchase_quantity_can_request_fractional_buying_power(self) -> None:
def test_submit_order(self) -> None:
longport_module = types.ModuleType("longport")
openapi_module = types.ModuleType("longport.openapi")
openapi_module.OrderSide = types.SimpleNamespace(Buy="Buy")
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}):
quantity = estimate_max_purchase_quantity(
ctx,
"SOXX.US",
order_kind="limit",
ref_price=495.91,
fractional_shares=True,
)
report = submit_order(ctx, "SOXL.US", order_kind="limit", side="buy", quantity=5, submitted_price=100.25)

self.assertEqual(quantity, 12)
self.assertIs(ctx.estimate_kwargs["fractional_shares"], True)
self.assertEqual(report.status, "submitted")
self.assertEqual(report.broker_order_id, "OID-1")

def test_submit_order(self) -> None:
def test_submit_order_allows_whole_decimal_quantity(self) -> None:
longport_module = types.ModuleType("longport")
openapi_module = types.ModuleType("longport.openapi")
openapi_module.OrderSide = types.SimpleNamespace(Buy="Buy", Sell="Sell")
Expand All @@ -83,12 +78,12 @@ def test_submit_order(self) -> None:

ctx = FakeTradeContext()
with patch.dict(sys.modules, {"longport": longport_module, "longport.openapi": openapi_module}):
report = submit_order(ctx, "SOXL.US", order_kind="limit", side="buy", quantity=5, submitted_price=100.25)
report = submit_order(ctx, "SOXL.US", order_kind="limit", side="buy", quantity=1.0, submitted_price=100.25)

self.assertEqual(report.status, "submitted")
self.assertEqual(report.broker_order_id, "OID-1")
self.assertEqual(str(ctx.submit_args[3]), "1.0")

def test_submit_order_allows_decimal_quantity_at_or_above_one_share(self) -> None:
def test_submit_order_rejects_quantity_below_one_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")
Expand All @@ -97,12 +92,20 @@ def test_submit_order_allows_decimal_quantity_at_or_above_one_share(self) -> Non

ctx = FakeTradeContext()
with patch.dict(sys.modules, {"longport": longport_module, "longport.openapi": openapi_module}):
report = submit_order(ctx, "SOXL.US", order_kind="limit", side="buy", quantity=1.5, submitted_price=100.25)
report = submit_order(
ctx,
"SOXX.US",
order_kind="limit",
side="buy",
quantity=0.4326,
submitted_price=495.91,
)

self.assertEqual(report.status, "submitted")
self.assertEqual(str(ctx.submit_args[3]), "1.5")
self.assertEqual(report.status, "rejected")
self.assertIn("whole-share quantity of at least 1 share", report.raw_payload["detail"])
self.assertFalse(hasattr(ctx, "submit_args"))

def test_submit_order_rejects_quantity_below_one_before_api_call(self) -> None:
def test_submit_order_rejects_fractional_quantity_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")
Expand All @@ -116,12 +119,12 @@ def test_submit_order_rejects_quantity_below_one_before_api_call(self) -> None:
"SOXX.US",
order_kind="limit",
side="buy",
quantity=0.4326,
quantity=1.5,
submitted_price=495.91,
)

self.assertEqual(report.status, "rejected")
self.assertIn("at least 1 share", report.raw_payload["detail"])
self.assertIn("whole-share quantity of at least 1 share", report.raw_payload["detail"])
self.assertFalse(hasattr(ctx, "submit_args"))

def test_fetch_order_status(self) -> None:
Expand Down
82 changes: 0 additions & 82 deletions tests/test_longbridge_fractional_order_api_probe.py

This file was deleted.