from __future__ import annotations from datetime import datetime, timezone import random from typing import Any import pytest from prod.clean_arch.dita_v2 import ( AccountProjection, BingxVenueAdapter, BackendMode, ControlUpdate, ExecutionKernel, InMemoryControlPlane, InMemoryZincPlane, KernelCommandType, KernelControlSnapshot, KernelDiagnosticCode, KernelEventKind, KernelIntent, KernelMode, KernelOutcome, KernelSeverity, KernelVerbosity, MemoryKernelJournal, MockVenueAdapter, MockVenueScenario, TradeSide, TradeSlot, TradeStage, VenueEvent, VenueEventStatus, VenueOrder, VenueOrderStatus, ) def mk_intent( *, action: KernelCommandType = KernelCommandType.ENTER, slot_id: int = 0, trade_id: str = "trade-1", asset: str = "BTCUSDT", side: TradeSide = TradeSide.SHORT, target_size: float = 1.0, leverage: float = 2.0, reference_price: float = 100.0, exit_leg_ratios=(1.0,), reason: str = "TEST", ) -> KernelIntent: return KernelIntent( timestamp=datetime.now(timezone.utc), intent_id=f"intent-{trade_id}-{action.value}", trade_id=trade_id, slot_id=slot_id, asset=asset, side=side, action=action, reference_price=reference_price, target_size=target_size, leverage=leverage, exit_leg_ratios=tuple(exit_leg_ratios), reason=reason, ) def mk_event( *, kind: KernelEventKind, status: VenueEventStatus, trade_id: str = "trade-1", slot_id: int = 0, venue_order_id: str = "V-00000001", venue_client_id: str = "trade-1:intent-1", side: TradeSide = TradeSide.SHORT, asset: str = "BTCUSDT", price: float = 100.0, size: float = 1.0, filled_size: float = 1.0, remaining_size: float = 0.0, reason: str = "", ) -> VenueEvent: return VenueEvent( timestamp=datetime.now(timezone.utc), event_id=f"evt-{kind.value.lower()}", trade_id=trade_id, slot_id=slot_id, kind=kind, status=status, venue_order_id=venue_order_id, venue_client_id=venue_client_id, side=side, asset=asset, price=price, size=size, filled_size=filled_size, remaining_size=remaining_size, reason=reason, raw_payload={"status": status.value}, ) def mk_kernel( *, max_slots: int = 3, venue: Any | None = None, control_mode: KernelMode = KernelMode.DEBUG, verbosity: KernelVerbosity = KernelVerbosity.TRACE, ) -> ExecutionKernel: return ExecutionKernel( max_slots=max_slots, control_plane=InMemoryControlPlane( KernelControlSnapshot(mode=control_mode, verbosity=verbosity, backend_mode=BackendMode.MOCK) ), venue=venue or MockVenueAdapter(), journal=MemoryKernelJournal(), zinc_plane=InMemoryZincPlane(), account=AccountProjection(), ) def _seed_open_slot(slot: TradeSlot, *, trade_id: str = "trade-1", asset: str = "BTCUSDT") -> None: slot.trade_id = trade_id slot.asset = asset slot.side = TradeSide.SHORT slot.entry_price = 100.0 slot.size = 1.0 slot.initial_size = 1.0 slot.leverage = 2.0 slot.fsm_state = TradeStage.POSITION_OPEN slot.active_entry_order = VenueOrder( internal_trade_id=trade_id, venue_order_id="V-00000001", venue_client_id=f"{trade_id}:entry", side=TradeSide.SHORT, intended_size=1.0, status=VenueOrderStatus.FILLED, metadata={"slot_id": slot.slot_id, "asset": asset}, ) def _seed_entry_order(slot: TradeSlot, *, trade_id: str = "trade-1", asset: str = "BTCUSDT", status: VenueOrderStatus = VenueOrderStatus.NEW) -> None: slot.active_entry_order = VenueOrder( internal_trade_id=trade_id, venue_order_id="V-00000001", venue_client_id=f"{trade_id}:entry", side=TradeSide.SHORT, intended_size=1.0, status=status, metadata={"slot_id": slot.slot_id, "asset": asset}, ) def _seed_exit_order(slot: TradeSlot, *, trade_id: str = "trade-1", asset: str = "BTCUSDT", intended_size: float = 0.5) -> None: slot.active_exit_order = VenueOrder( internal_trade_id=trade_id, venue_order_id="V-00000002", venue_client_id=f"{trade_id}:exit", side=TradeSide.SHORT, intended_size=intended_size, status=VenueOrderStatus.NEW, metadata={"slot_id": slot.slot_id, "asset": asset}, ) def _configure_slot_state(slot: TradeSlot, state: TradeStage, *, trade_id: str = "trade-1", asset: str = "BTCUSDT") -> None: slot.trade_id = trade_id if state not in {TradeStage.IDLE, TradeStage.CLOSED} else "" slot.asset = asset if state not in {TradeStage.IDLE, TradeStage.CLOSED} else "" slot.side = TradeSide.SHORT if state not in {TradeStage.IDLE, TradeStage.CLOSED} else TradeSide.FLAT slot.entry_price = 100.0 if state not in {TradeStage.IDLE, TradeStage.CLOSED} else 0.0 slot.size = 1.0 if state in {TradeStage.POSITION_OPEN, TradeStage.EXIT_WORKING, TradeStage.EXIT_REQUESTED, TradeStage.EXIT_SENT, TradeStage.ENTRY_WORKING, TradeStage.ORDER_REQUESTED, TradeStage.ORDER_SENT} else 0.0 slot.initial_size = slot.size slot.leverage = 2.0 if state not in {TradeStage.IDLE, TradeStage.CLOSED} else 0.0 slot.fsm_state = state slot.closed = state == TradeStage.CLOSED slot.active_entry_order = None slot.active_exit_order = None if state in {TradeStage.ORDER_REQUESTED, TradeStage.ORDER_SENT, TradeStage.ENTRY_WORKING, TradeStage.POSITION_OPEN, TradeStage.POSITION_OPENED}: slot.active_entry_order = VenueOrder( internal_trade_id=trade_id, venue_order_id="V-00000001", venue_client_id=f"{trade_id}:entry", side=TradeSide.SHORT, intended_size=1.0, status=VenueOrderStatus.NEW if state in {TradeStage.ORDER_REQUESTED, TradeStage.ORDER_SENT, TradeStage.ENTRY_WORKING} else VenueOrderStatus.FILLED, metadata={"slot_id": slot.slot_id, "asset": asset}, ) if state in {TradeStage.EXIT_REQUESTED, TradeStage.EXIT_SENT, TradeStage.EXIT_WORKING}: slot.active_exit_order = VenueOrder( internal_trade_id=trade_id, venue_order_id="V-00000002", venue_client_id=f"{trade_id}:exit", side=TradeSide.SHORT, intended_size=0.5, status=VenueOrderStatus.NEW, metadata={"slot_id": slot.slot_id, "asset": asset}, ) # 18 invalid-intent slot tests @pytest.mark.parametrize( "slot_id,action,expected", [ (-1, KernelCommandType.ENTER, KernelDiagnosticCode.INVALID_SLOT_ID), (-1, KernelCommandType.EXIT, KernelDiagnosticCode.INVALID_SLOT_ID), (-1, KernelCommandType.MARK_PRICE, KernelDiagnosticCode.INVALID_SLOT_ID), (-1, KernelCommandType.RECONCILE, KernelDiagnosticCode.INVALID_SLOT_ID), (-1, KernelCommandType.CANCEL, KernelDiagnosticCode.INVALID_SLOT_ID), (3, KernelCommandType.ENTER, KernelDiagnosticCode.INVALID_SLOT_ID), (3, KernelCommandType.EXIT, KernelDiagnosticCode.INVALID_SLOT_ID), (3, KernelCommandType.MARK_PRICE, KernelDiagnosticCode.INVALID_SLOT_ID), (3, KernelCommandType.RECONCILE, KernelDiagnosticCode.INVALID_SLOT_ID), (3, KernelCommandType.CANCEL, KernelDiagnosticCode.INVALID_SLOT_ID), (99, KernelCommandType.ENTER, KernelDiagnosticCode.INVALID_SLOT_ID), (99, KernelCommandType.EXIT, KernelDiagnosticCode.INVALID_SLOT_ID), (99, KernelCommandType.MARK_PRICE, KernelDiagnosticCode.INVALID_SLOT_ID), (99, KernelCommandType.RECONCILE, KernelDiagnosticCode.INVALID_SLOT_ID), (99, KernelCommandType.CANCEL, KernelDiagnosticCode.INVALID_SLOT_ID), (7, KernelCommandType.ENTER, KernelDiagnosticCode.INVALID_SLOT_ID), (7, KernelCommandType.EXIT, KernelDiagnosticCode.INVALID_SLOT_ID), (7, KernelCommandType.MARK_PRICE, KernelDiagnosticCode.INVALID_SLOT_ID), ], ) def test_kernel_rejects_invalid_slot_ids_with_codes(slot_id: int, action: KernelCommandType, expected: KernelDiagnosticCode) -> None: kernel = mk_kernel(max_slots=3) outcome = kernel.process_intent(mk_intent(slot_id=slot_id, action=action)) assert outcome.accepted is False assert outcome.diagnostic_code == expected assert outcome.details["reason"] == "INVALID_SLOT_ID" # 20 entry-path tests @pytest.mark.parametrize( "scenario,expected_state,expected_code,expected_size", [ (MockVenueScenario(emit_fill_on_submit=True, partial_fill_ratio=1.0), TradeStage.POSITION_OPEN, KernelDiagnosticCode.OK, 1.0), (MockVenueScenario(emit_fill_on_submit=True, partial_fill_ratio=0.5), TradeStage.ENTRY_WORKING, KernelDiagnosticCode.OK, 0.5), (MockVenueScenario(emit_fill_on_submit=False, partial_fill_ratio=0.5), TradeStage.ENTRY_WORKING, KernelDiagnosticCode.OK, 0.5), (MockVenueScenario(reject_entries=True), TradeStage.IDLE, KernelDiagnosticCode.OK, 0.0), (MockVenueScenario(emit_fill_on_submit=True, partial_fill_ratio=0.25), TradeStage.ENTRY_WORKING, KernelDiagnosticCode.OK, 0.25), (MockVenueScenario(emit_fill_on_submit=True, partial_fill_ratio=0.75), TradeStage.ENTRY_WORKING, KernelDiagnosticCode.OK, 0.75), (MockVenueScenario(emit_ack_before_fill=True, emit_fill_on_submit=False, partial_fill_ratio=0.0), TradeStage.ENTRY_WORKING, KernelDiagnosticCode.OK, 0.0), (MockVenueScenario(emit_ack_before_fill=True, emit_fill_on_submit=True, partial_fill_ratio=1.0), TradeStage.POSITION_OPEN, KernelDiagnosticCode.OK, 1.0), (MockVenueScenario(emit_ack_before_fill=False, emit_fill_on_submit=True, partial_fill_ratio=1.0), TradeStage.POSITION_OPEN, KernelDiagnosticCode.OK, 1.0), (MockVenueScenario(emit_ack_before_fill=False, emit_fill_on_submit=True, partial_fill_ratio=0.5), TradeStage.ENTRY_WORKING, KernelDiagnosticCode.OK, 0.5), (MockVenueScenario(emit_ack_before_fill=True, emit_fill_on_submit=True, partial_fill_ratio=0.9), TradeStage.ENTRY_WORKING, KernelDiagnosticCode.OK, 0.9), (MockVenueScenario(emit_ack_before_fill=True, emit_fill_on_submit=True, partial_fill_ratio=0.1), TradeStage.ENTRY_WORKING, KernelDiagnosticCode.OK, 0.1), (MockVenueScenario(emit_ack_before_fill=True, emit_fill_on_submit=True, partial_fill_ratio=1.0, reject_entries=False), TradeStage.POSITION_OPEN, KernelDiagnosticCode.OK, 1.0), (MockVenueScenario(emit_ack_before_fill=True, emit_fill_on_submit=False, partial_fill_ratio=1.0), TradeStage.POSITION_OPEN, KernelDiagnosticCode.OK, 1.0), (MockVenueScenario(emit_ack_before_fill=True, emit_fill_on_submit=False, partial_fill_ratio=0.2), TradeStage.ENTRY_WORKING, KernelDiagnosticCode.OK, 0.2), (MockVenueScenario(emit_ack_before_fill=False, emit_fill_on_submit=False, partial_fill_ratio=0.3), TradeStage.ENTRY_WORKING, KernelDiagnosticCode.OK, 0.3), (MockVenueScenario(emit_ack_before_fill=False, emit_fill_on_submit=False, partial_fill_ratio=1.0), TradeStage.POSITION_OPEN, KernelDiagnosticCode.OK, 1.0), (MockVenueScenario(emit_ack_before_fill=False, emit_fill_on_submit=False, partial_fill_ratio=0.0), TradeStage.ENTRY_WORKING, KernelDiagnosticCode.OK, 0.0), (MockVenueScenario(emit_ack_before_fill=True, emit_fill_on_submit=True, partial_fill_ratio=0.6), TradeStage.ENTRY_WORKING, KernelDiagnosticCode.OK, 0.6), (MockVenueScenario(emit_ack_before_fill=True, emit_fill_on_submit=True, partial_fill_ratio=0.4), TradeStage.ENTRY_WORKING, KernelDiagnosticCode.OK, 0.4), ], ) def test_kernel_entry_path_matrix( scenario: MockVenueScenario, expected_state: TradeStage, expected_code: KernelDiagnosticCode, expected_size: float, ) -> None: kernel = mk_kernel(venue=MockVenueAdapter(scenario)) outcome = kernel.process_intent(mk_intent()) assert outcome.accepted is True assert outcome.diagnostic_code == expected_code assert kernel.slot(0).fsm_state == expected_state assert kernel.slot(0).size == pytest.approx(expected_size, abs=1e-6) # 20 exit-path tests @pytest.mark.parametrize( "initial_state,event_kind,event_status,expected_state,expected_code", [ (TradeStage.POSITION_OPEN, KernelEventKind.PARTIAL_FILL, VenueEventStatus.PARTIALLY_FILLED, TradeStage.EXIT_WORKING, KernelDiagnosticCode.OK), (TradeStage.POSITION_OPEN, KernelEventKind.FULL_FILL, VenueEventStatus.FILLED, TradeStage.CLOSED, KernelDiagnosticCode.OK), (TradeStage.EXIT_REQUESTED, KernelEventKind.PARTIAL_FILL, VenueEventStatus.PARTIALLY_FILLED, TradeStage.EXIT_WORKING, KernelDiagnosticCode.OK), (TradeStage.EXIT_REQUESTED, KernelEventKind.FULL_FILL, VenueEventStatus.FILLED, TradeStage.CLOSED, KernelDiagnosticCode.OK), (TradeStage.EXIT_SENT, KernelEventKind.PARTIAL_FILL, VenueEventStatus.PARTIALLY_FILLED, TradeStage.EXIT_WORKING, KernelDiagnosticCode.OK), (TradeStage.EXIT_SENT, KernelEventKind.FULL_FILL, VenueEventStatus.FILLED, TradeStage.CLOSED, KernelDiagnosticCode.OK), (TradeStage.EXIT_WORKING, KernelEventKind.PARTIAL_FILL, VenueEventStatus.PARTIALLY_FILLED, TradeStage.EXIT_WORKING, KernelDiagnosticCode.OK), (TradeStage.EXIT_WORKING, KernelEventKind.FULL_FILL, VenueEventStatus.FILLED, TradeStage.CLOSED, KernelDiagnosticCode.OK), (TradeStage.EXIT_WORKING, KernelEventKind.CANCEL_ACK, VenueEventStatus.CANCELED, TradeStage.POSITION_OPEN, KernelDiagnosticCode.OK), (TradeStage.EXIT_WORKING, KernelEventKind.CANCEL_REJECT, VenueEventStatus.CANCELED_REJECTED, TradeStage.EXIT_WORKING, KernelDiagnosticCode.CANCEL_REJECTED), (TradeStage.POSITION_OPEN, KernelEventKind.CANCEL_ACK, VenueEventStatus.CANCELED, TradeStage.POSITION_OPEN, KernelDiagnosticCode.OK), (TradeStage.POSITION_OPEN, KernelEventKind.CANCEL_REJECT, VenueEventStatus.CANCELED_REJECTED, TradeStage.POSITION_OPEN, KernelDiagnosticCode.CANCEL_REJECTED), (TradeStage.EXIT_REQUESTED, KernelEventKind.CANCEL_ACK, VenueEventStatus.CANCELED, TradeStage.POSITION_OPEN, KernelDiagnosticCode.OK), (TradeStage.EXIT_SENT, KernelEventKind.CANCEL_ACK, VenueEventStatus.CANCELED, TradeStage.POSITION_OPEN, KernelDiagnosticCode.OK), (TradeStage.EXIT_REQUESTED, KernelEventKind.CANCEL_REJECT, VenueEventStatus.CANCELED_REJECTED, TradeStage.EXIT_REQUESTED, KernelDiagnosticCode.CANCEL_REJECTED), (TradeStage.EXIT_SENT, KernelEventKind.CANCEL_REJECT, VenueEventStatus.CANCELED_REJECTED, TradeStage.EXIT_SENT, KernelDiagnosticCode.CANCEL_REJECTED), (TradeStage.POSITION_OPEN, KernelEventKind.ORDER_REJECT, VenueEventStatus.REJECTED, TradeStage.POSITION_OPEN, KernelDiagnosticCode.EXIT_ORDER_REJECTED), (TradeStage.EXIT_WORKING, KernelEventKind.ORDER_REJECT, VenueEventStatus.REJECTED, TradeStage.POSITION_OPEN, KernelDiagnosticCode.EXIT_ORDER_REJECTED), (TradeStage.EXIT_REQUESTED, KernelEventKind.ORDER_REJECT, VenueEventStatus.REJECTED, TradeStage.POSITION_OPEN, KernelDiagnosticCode.EXIT_ORDER_REJECTED), (TradeStage.EXIT_SENT, KernelEventKind.ORDER_REJECT, VenueEventStatus.REJECTED, TradeStage.POSITION_OPEN, KernelDiagnosticCode.EXIT_ORDER_REJECTED), ], ) def test_kernel_exit_path_matrix( initial_state: TradeStage, event_kind: KernelEventKind, event_status: VenueEventStatus, expected_state: TradeStage, expected_code: KernelDiagnosticCode, ) -> None: kernel = mk_kernel() slot = kernel.slot(0) _configure_slot_state(slot, initial_state) if event_kind in {KernelEventKind.ORDER_REJECT, KernelEventKind.PARTIAL_FILL, KernelEventKind.FULL_FILL}: _seed_exit_order(slot, trade_id=slot.trade_id or "trade-1", asset="BTCUSDT", intended_size=slot.size or 0.5) if initial_state in {TradeStage.EXIT_REQUESTED, TradeStage.EXIT_SENT, TradeStage.EXIT_WORKING} and event_kind in { KernelEventKind.CANCEL_ACK, KernelEventKind.CANCEL_REJECT, KernelEventKind.ORDER_ACK, }: _seed_exit_order(slot, trade_id=slot.trade_id or "trade-1", asset="BTCUSDT", intended_size=slot.size or 0.5) outcome = kernel.on_venue_event( mk_event( kind=event_kind, status=event_status, trade_id=slot.trade_id or "trade-1", venue_order_id=slot.active_exit_order.venue_order_id if slot.active_exit_order else "V-00000002", venue_client_id=slot.active_exit_order.venue_client_id if slot.active_exit_order else "trade-1:exit", side=TradeSide.SHORT, asset="BTCUSDT", size=float(slot.size or 0.5), filled_size=float(slot.size or 0.5) if event_kind == KernelEventKind.FULL_FILL else float((slot.size or 0.5) / 2.0), remaining_size=0.0, ) ) assert outcome.diagnostic_code == expected_code assert kernel.slot(0).fsm_state == expected_state # 18 event-resolution tests @pytest.mark.parametrize( "event,initial_state,expected_state,expected_code", [ (mk_event(kind=KernelEventKind.ORDER_ACK, status=VenueEventStatus.ACKED), TradeStage.IDLE, TradeStage.ENTRY_WORKING, KernelDiagnosticCode.OK), (mk_event(kind=KernelEventKind.ORDER_ACK, status=VenueEventStatus.ACKED), TradeStage.EXIT_REQUESTED, TradeStage.EXIT_WORKING, KernelDiagnosticCode.OK), (mk_event(kind=KernelEventKind.ORDER_REJECT, status=VenueEventStatus.REJECTED), TradeStage.ENTRY_WORKING, TradeStage.IDLE, KernelDiagnosticCode.ENTRY_ORDER_REJECTED), (mk_event(kind=KernelEventKind.ORDER_REJECT, status=VenueEventStatus.REJECTED), TradeStage.EXIT_WORKING, TradeStage.POSITION_OPEN, KernelDiagnosticCode.EXIT_ORDER_REJECTED), (mk_event(kind=KernelEventKind.ORDER_REJECT, status=VenueEventStatus.REJECTED), TradeStage.IDLE, TradeStage.IDLE, KernelDiagnosticCode.ORDER_REJECTED), (mk_event(kind=KernelEventKind.PARTIAL_FILL, status=VenueEventStatus.PARTIALLY_FILLED), TradeStage.ENTRY_WORKING, TradeStage.ENTRY_WORKING, KernelDiagnosticCode.OK), (mk_event(kind=KernelEventKind.FULL_FILL, status=VenueEventStatus.FILLED), TradeStage.ENTRY_WORKING, TradeStage.POSITION_OPEN, KernelDiagnosticCode.OK), (mk_event(kind=KernelEventKind.PARTIAL_FILL, status=VenueEventStatus.PARTIALLY_FILLED), TradeStage.EXIT_WORKING, TradeStage.EXIT_WORKING, KernelDiagnosticCode.OK), (mk_event(kind=KernelEventKind.FULL_FILL, status=VenueEventStatus.FILLED), TradeStage.EXIT_WORKING, TradeStage.CLOSED, KernelDiagnosticCode.OK), (mk_event(kind=KernelEventKind.CANCEL_ACK, status=VenueEventStatus.CANCELED), TradeStage.EXIT_WORKING, TradeStage.POSITION_OPEN, KernelDiagnosticCode.OK), (mk_event(kind=KernelEventKind.CANCEL_REJECT, status=VenueEventStatus.CANCELED_REJECTED), TradeStage.EXIT_WORKING, TradeStage.EXIT_WORKING, KernelDiagnosticCode.CANCEL_REJECTED), (mk_event(kind=KernelEventKind.MARK_PRICE, status=VenueEventStatus.ACKED), TradeStage.POSITION_OPEN, TradeStage.POSITION_OPEN, KernelDiagnosticCode.OK), (mk_event(kind=KernelEventKind.RECONCILE, status=VenueEventStatus.ACKED), TradeStage.POSITION_OPEN, TradeStage.STALE_STATE_RECONCILING, KernelDiagnosticCode.OK), (mk_event(kind=KernelEventKind.ORDER_ACK, status=VenueEventStatus.ACKED, venue_order_id="V-2"), TradeStage.POSITION_OPEN, TradeStage.POSITION_OPEN, KernelDiagnosticCode.OK), (mk_event(kind=KernelEventKind.ORDER_ACK, status=VenueEventStatus.ACKED, venue_order_id="V-3"), TradeStage.ENTRY_WORKING, TradeStage.ENTRY_WORKING, KernelDiagnosticCode.OK), (mk_event(kind=KernelEventKind.FULL_FILL, status=VenueEventStatus.FILLED, venue_order_id="V-4"), TradeStage.EXIT_WORKING, TradeStage.CLOSED, KernelDiagnosticCode.OK), (mk_event(kind=KernelEventKind.CANCEL_ACK, status=VenueEventStatus.CANCELED, venue_order_id="V-5"), TradeStage.POSITION_OPEN, TradeStage.POSITION_OPEN, KernelDiagnosticCode.OK), (mk_event(kind=KernelEventKind.CANCEL_REJECT, status=VenueEventStatus.CANCELED_REJECTED, venue_order_id="V-6"), TradeStage.POSITION_OPEN, TradeStage.POSITION_OPEN, KernelDiagnosticCode.CANCEL_REJECTED), ], ) def test_kernel_event_matrix(event: VenueEvent, initial_state: TradeStage, expected_state: TradeStage, expected_code: KernelDiagnosticCode) -> None: kernel = mk_kernel() slot = kernel.slot(0) _configure_slot_state(slot, initial_state) entry_states = {TradeStage.IDLE, TradeStage.ORDER_REQUESTED, TradeStage.ORDER_SENT, TradeStage.ENTRY_WORKING} exit_states = {TradeStage.POSITION_OPEN, TradeStage.EXIT_REQUESTED, TradeStage.EXIT_SENT, TradeStage.EXIT_WORKING} if initial_state in entry_states and event.kind in {KernelEventKind.ORDER_ACK, KernelEventKind.PARTIAL_FILL, KernelEventKind.FULL_FILL}: _seed_entry_order(slot, trade_id="trade-1", asset="BTCUSDT") elif initial_state == TradeStage.ENTRY_WORKING and event.kind == KernelEventKind.ORDER_REJECT: _seed_entry_order(slot, trade_id="trade-1", asset="BTCUSDT") if initial_state in exit_states: if event.kind == KernelEventKind.ORDER_REJECT: _seed_exit_order(slot, trade_id="trade-1", asset="BTCUSDT", intended_size=1.0) elif event.kind in {KernelEventKind.PARTIAL_FILL, KernelEventKind.FULL_FILL}: _seed_exit_order(slot, trade_id="trade-1", asset="BTCUSDT", intended_size=1.0) elif initial_state in {TradeStage.EXIT_REQUESTED, TradeStage.EXIT_SENT, TradeStage.EXIT_WORKING} and event.kind in { KernelEventKind.ORDER_ACK, KernelEventKind.CANCEL_ACK, KernelEventKind.CANCEL_REJECT, }: _seed_exit_order(slot, trade_id="trade-1", asset="BTCUSDT", intended_size=1.0) if initial_state == TradeStage.POSITION_OPEN and event.kind == KernelEventKind.ORDER_ACK: slot.active_entry_order = None fill_size = 1.0 if event.kind == KernelEventKind.FULL_FILL else 0.5 if event.kind == KernelEventKind.PARTIAL_FILL else 0.0 resolved_event = mk_event( kind=event.kind, status=event.status, trade_id=event.trade_id, slot_id=event.slot_id, venue_order_id=slot.active_entry_order.venue_order_id if slot.active_entry_order else slot.active_exit_order.venue_order_id if slot.active_exit_order else event.venue_order_id, venue_client_id=slot.active_entry_order.venue_client_id if slot.active_entry_order else slot.active_exit_order.venue_client_id if slot.active_exit_order else event.venue_client_id, side=event.side, asset=event.asset, price=event.price, size=1.0, filled_size=fill_size, remaining_size=max(0.0, 1.0 - fill_size), reason=event.reason, ) outcome = kernel.on_venue_event(resolved_event) assert outcome.state == expected_state assert outcome.diagnostic_code == expected_code def test_kernel_rate_limited_event_is_characterized_without_state_drift() -> None: kernel = mk_kernel() slot = kernel.slot(0) _configure_slot_state(slot, TradeStage.ENTRY_WORKING) _seed_entry_order(slot, trade_id="trade-rate-limit", asset="BTCUSDT") before = slot.to_dict() outcome = kernel.on_venue_event( mk_event( kind=KernelEventKind.RATE_LIMITED, status=VenueEventStatus.RATE_LIMITED, trade_id="trade-rate-limit", venue_order_id="V-RATE-LIMITED", venue_client_id="trade-rate-limit:entry", reason="code:100410 endpoint is in disabled/frequency-limited period", size=1.0, filled_size=0.0, remaining_size=1.0, ) ) after = kernel.slot(0).to_dict() assert outcome.accepted is False assert outcome.diagnostic_code == KernelDiagnosticCode.RATE_LIMITED assert outcome.severity == KernelSeverity.WARNING assert outcome.details["venue_event_kind"] == KernelEventKind.RATE_LIMITED.value assert outcome.details["severity"] == KernelSeverity.WARNING.value assert outcome.details["release_eta"] == "few minutes" assert outcome.details["retryable"] is True assert after["fsm_state"] == before["fsm_state"] assert after["trade_id"] == before["trade_id"] assert after["size"] == before["size"] # 24 fuzz cases @pytest.mark.parametrize("seed", list(range(24))) def test_kernel_fuzz_event_sequences(seed: int) -> None: rng = random.Random(seed) kernel = mk_kernel(max_slots=4) current_trade_id = f"trade-{seed}" # Seed one slot open for exit/reconcile fuzzing. seed_slot = kernel.slot(0) _seed_open_slot(seed_slot, trade_id=current_trade_id) seed_slot.exit_leg_ratios = (0.25, 0.25, 0.5) kinds = [ KernelEventKind.ORDER_ACK, KernelEventKind.ORDER_REJECT, KernelEventKind.PARTIAL_FILL, KernelEventKind.FULL_FILL, KernelEventKind.CANCEL_ACK, KernelEventKind.CANCEL_REJECT, KernelEventKind.MARK_PRICE, KernelEventKind.RECONCILE, ] for idx in range(12): kind = rng.choice(kinds) if kind in {KernelEventKind.ORDER_ACK, KernelEventKind.ORDER_REJECT}: seed_slot.active_entry_order = VenueOrder( internal_trade_id=current_trade_id, venue_order_id=f"V-{seed:04d}-{idx:02d}", venue_client_id=f"{current_trade_id}:entry-{idx}", side=TradeSide.SHORT, intended_size=1.0, status=VenueOrderStatus.NEW, metadata={"slot_id": 0, "asset": "BTCUSDT"}, ) if kind in {KernelEventKind.CANCEL_ACK, KernelEventKind.CANCEL_REJECT, KernelEventKind.PARTIAL_FILL, KernelEventKind.FULL_FILL}: seed_slot.active_exit_order = VenueOrder( internal_trade_id=current_trade_id, venue_order_id=f"V-{seed:04d}-{idx:02d}", venue_client_id=f"{current_trade_id}:exit-{idx}", side=TradeSide.SHORT, intended_size=0.5, filled_size=0.0, status=VenueOrderStatus.NEW, metadata={"slot_id": 0, "asset": "BTCUSDT"}, ) event = mk_event(kind=kind, status=_status_for_kind(kind), trade_id=current_trade_id, venue_order_id=f"V-{seed:04d}-{idx:02d}", venue_client_id=f"{current_trade_id}:{idx}") outcome = kernel.on_venue_event(event) assert isinstance(outcome, KernelOutcome) assert outcome.diagnostic_code in set(KernelDiagnosticCode) assert kernel.slot(0).fsm_state in set(TradeStage) def _status_for_kind(kind: KernelEventKind) -> VenueEventStatus: return { KernelEventKind.ORDER_ACK: VenueEventStatus.ACKED, KernelEventKind.ORDER_REJECT: VenueEventStatus.REJECTED, KernelEventKind.PARTIAL_FILL: VenueEventStatus.PARTIALLY_FILLED, KernelEventKind.FULL_FILL: VenueEventStatus.FILLED, KernelEventKind.CANCEL_ACK: VenueEventStatus.CANCELED, KernelEventKind.CANCEL_REJECT: VenueEventStatus.CANCELED_REJECTED, KernelEventKind.MARK_PRICE: VenueEventStatus.ACKED, KernelEventKind.RECONCILE: VenueEventStatus.ACKED, }[kind] # 22 explicit edge-condition tests @pytest.mark.parametrize( "slot_state,action,expected_code", [ (TradeStage.IDLE, KernelCommandType.EXIT, KernelDiagnosticCode.NO_OPEN_POSITION), (TradeStage.CLOSED, KernelCommandType.EXIT, KernelDiagnosticCode.NO_OPEN_POSITION), (TradeStage.POSITION_OPEN, KernelCommandType.CANCEL, KernelDiagnosticCode.NO_ACTIVE_EXIT_ORDER), (TradeStage.IDLE, KernelCommandType.CANCEL, KernelDiagnosticCode.NO_ACTIVE_EXIT_ORDER), (TradeStage.IDLE, KernelCommandType.RECONCILE, KernelDiagnosticCode.STALE_STATE_RECONCILE), (TradeStage.POSITION_OPEN, KernelCommandType.RECONCILE, KernelDiagnosticCode.STALE_STATE_RECONCILE), (TradeStage.POSITION_OPEN, KernelCommandType.MARK_PRICE, KernelDiagnosticCode.OK), (TradeStage.EXIT_WORKING, KernelCommandType.MARK_PRICE, KernelDiagnosticCode.OK), (TradeStage.ENTRY_WORKING, KernelCommandType.MARK_PRICE, KernelDiagnosticCode.OK), (TradeStage.ORDER_REQUESTED, KernelCommandType.MARK_PRICE, KernelDiagnosticCode.OK), (TradeStage.ORDER_SENT, KernelCommandType.MARK_PRICE, KernelDiagnosticCode.OK), (TradeStage.EXIT_REQUESTED, KernelCommandType.MARK_PRICE, KernelDiagnosticCode.OK), (TradeStage.EXIT_SENT, KernelCommandType.MARK_PRICE, KernelDiagnosticCode.OK), (TradeStage.STALE_STATE_RECONCILING, KernelCommandType.MARK_PRICE, KernelDiagnosticCode.OK), (TradeStage.POSITION_OPEN, KernelCommandType.ENTER, KernelDiagnosticCode.SLOT_BUSY), (TradeStage.EXIT_WORKING, KernelCommandType.ENTER, KernelDiagnosticCode.SLOT_BUSY), (TradeStage.ORDER_REQUESTED, KernelCommandType.ENTER, KernelDiagnosticCode.SLOT_BUSY), (TradeStage.ORDER_SENT, KernelCommandType.ENTER, KernelDiagnosticCode.SLOT_BUSY), (TradeStage.POSITION_OPEN, KernelCommandType.EXIT, KernelDiagnosticCode.OK), (TradeStage.EXIT_WORKING, KernelCommandType.EXIT, KernelDiagnosticCode.OK), (TradeStage.POSITION_OPEN, KernelCommandType.CANCEL, KernelDiagnosticCode.OK), (TradeStage.EXIT_WORKING, KernelCommandType.CANCEL, KernelDiagnosticCode.OK), ], ) def test_kernel_action_edge_conditions(slot_state: TradeStage, action: KernelCommandType, expected_code: KernelDiagnosticCode) -> None: kernel = mk_kernel(venue=MockVenueAdapter(MockVenueScenario(emit_fill_on_submit=True, partial_fill_ratio=1.0))) slot = kernel.slot(0) _configure_slot_state(slot, slot_state) if action == KernelCommandType.ENTER and expected_code == KernelDiagnosticCode.SLOT_BUSY: slot.trade_id = f"occupied-{slot_state.value.lower()}" if action == KernelCommandType.CANCEL and expected_code == KernelDiagnosticCode.OK: _seed_exit_order(slot, trade_id=slot.trade_id or "trade-1", asset=slot.asset or "BTCUSDT", intended_size=0.5) outcome = kernel.process_intent(mk_intent(action=action, target_size=0.5, exit_leg_ratios=(0.25, 0.25, 0.5))) assert outcome.diagnostic_code == expected_code # 20 transition-detail tests @pytest.mark.parametrize("mode", [KernelMode.NORMAL, KernelMode.DEBUG]) @pytest.mark.parametrize("verbosity", [KernelVerbosity.QUIET, KernelVerbosity.TRACE]) @pytest.mark.parametrize("control_enabled", [True, False]) @pytest.mark.parametrize("closed", [True, False]) @pytest.mark.parametrize("state", [TradeStage.IDLE, TradeStage.POSITION_OPEN]) def test_transition_details_and_control_modes_are_captured( mode: KernelMode, verbosity: KernelVerbosity, control_enabled: bool, closed: bool, state: TradeStage, ) -> None: kernel = mk_kernel() if control_enabled: kernel.update_control( ControlUpdate( mode=mode, verbosity=verbosity, trace_transitions=True, ) ) slot = kernel.slot(0) _seed_open_slot(slot) slot.fsm_state = state slot.closed = closed event = mk_event(kind=KernelEventKind.MARK_PRICE, status=VenueEventStatus.ACKED) outcome = kernel.on_venue_event(event) assert outcome.transitions transition = outcome.transitions[0] assert transition.control_mode in {KernelMode.NORMAL.value, KernelMode.DEBUG.value} assert transition.control_verbosity in {KernelVerbosity.QUIET.value, KernelVerbosity.TRACE.value} assert "asset" in transition.details assert "side" in transition.details