feat: resolve and persist actual order venue
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
"""Venue propagation from the strategy wire contract to broker execution."""
|
||||
from __future__ import annotations
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
from trex_core.adapters.persistence.in_memory_intent_store import InMemoryIntentStore
|
||||
from trex_core.application.broker_port import BalanceItem, FillStatus, OrderAck, OrderRequest
|
||||
from trex_core.application.execution import ExecutionService
|
||||
from trex_core.application.intents import IntentService
|
||||
from trex_core.application.intents import BuyIntent, IntentService
|
||||
from trex_core.application.messaging import RpcRequest
|
||||
from trex_core.application.messaging_handlers import make_intent_rpc_handler
|
||||
from trex_core.domain.slot import SlotService, SlotState
|
||||
@@ -12,13 +14,18 @@ from trex_core.domain.slot.in_memory_repository import InMemorySlotRepository
|
||||
|
||||
|
||||
class RecordingBroker:
|
||||
def __init__(self) -> None:
|
||||
def __init__(self, *, actual_venue: str | None = None) -> None:
|
||||
self.orders: list[OrderRequest] = []
|
||||
self.cancel_venues: list[str] = []
|
||||
self.actual_venue = actual_venue
|
||||
|
||||
def submit_order(self, req: OrderRequest) -> OrderAck:
|
||||
self.orders.append(req)
|
||||
return OrderAck(accepted=True, broker_order_id=f"order-{len(self.orders)}")
|
||||
return OrderAck(
|
||||
accepted=True,
|
||||
broker_order_id=f"order-{len(self.orders)}",
|
||||
venue=self.actual_venue or req.venue,
|
||||
)
|
||||
|
||||
def poll_fill(self, broker_order_id: str, *, side: str, symbol: str) -> FillStatus:
|
||||
return FillStatus(state="accepted")
|
||||
@@ -104,6 +111,41 @@ def test_invalid_venue_is_rejected_before_slot_transition() -> None:
|
||||
assert repo.list_slots("default")[0].status is SlotState.INACTIVE
|
||||
|
||||
|
||||
def test_omitted_wire_venue_is_auto_then_actual_venue_is_persisted() -> None:
|
||||
repo = InMemorySlotRepository()
|
||||
service = SlotService(repo)
|
||||
intents = IntentService(
|
||||
service,
|
||||
repo,
|
||||
InMemoryIntentStore(),
|
||||
resolve_symbol=lambda portfolio_id: "005930",
|
||||
)
|
||||
repo.reconcile_universe("default", {"PF-1": "005930"})
|
||||
broker = RecordingBroker(actual_venue="NXT")
|
||||
execution = ExecutionService(service, repo, broker)
|
||||
handler = make_intent_rpc_handler(intents)
|
||||
|
||||
response = handler(
|
||||
RpcRequest(
|
||||
payload={
|
||||
"intent_type": "buy",
|
||||
"intent_id": "auto-buy",
|
||||
"portfolio_id": "PF-1",
|
||||
"qty": 1,
|
||||
},
|
||||
correlation_id="auto-buy",
|
||||
)
|
||||
)
|
||||
|
||||
slot_id = UUID(response.payload["slot_id"])
|
||||
pending = repo.fetch_slot(slot_id)
|
||||
assert pending is not None and pending.venue == "AUTO"
|
||||
execution.run_once()
|
||||
dispatched = repo.fetch_slot(slot_id)
|
||||
assert broker.orders[0].venue == "AUTO"
|
||||
assert dispatched is not None and dispatched.venue == "NXT"
|
||||
|
||||
|
||||
def test_inactive_transition_resets_venue_to_krx() -> None:
|
||||
repo, intents, _, _ = _wire()
|
||||
handler = make_intent_rpc_handler(intents)
|
||||
@@ -131,6 +173,30 @@ def test_inactive_transition_resets_venue_to_krx() -> None:
|
||||
assert reset.venue == "KRX"
|
||||
|
||||
|
||||
def test_holding_transition_resets_next_order_venue_to_auto() -> None:
|
||||
repo, intents, _, _ = _wire()
|
||||
decision = intents.submit_buy(
|
||||
BuyIntent(
|
||||
intent_id="nxt-buy-holding",
|
||||
portfolio_id="PF-1",
|
||||
qty=1,
|
||||
venue="NXT",
|
||||
)
|
||||
)
|
||||
slot = repo.fetch_slot(decision.slot_id)
|
||||
service = SlotService(repo)
|
||||
holding = service.transit_slot(
|
||||
slot.slot_id,
|
||||
SlotState.PENDING_BUY,
|
||||
SlotState.HOLDING,
|
||||
reason="fill",
|
||||
actor="test",
|
||||
qty=1,
|
||||
avg_price=1000,
|
||||
)
|
||||
assert holding.venue == "AUTO"
|
||||
|
||||
|
||||
def test_operator_cancel_uses_original_nxt_venue() -> None:
|
||||
repo, intents, execution, broker = _wire()
|
||||
handler = make_intent_rpc_handler(intents)
|
||||
|
||||
Reference in New Issue
Block a user