72 lines
2.1 KiB
Python
72 lines
2.1 KiB
Python
|
|
"""Rate-limit classification + downstream emission tests for PINK + DITAv2."""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from datetime import datetime, timezone
|
||
|
|
import unittest
|
||
|
|
|
||
|
|
from prod.clean_arch.dita_v2 import (
|
||
|
|
ControlUpdate,
|
||
|
|
ExecutionKernel,
|
||
|
|
InMemoryControlPlane,
|
||
|
|
InMemoryZincPlane,
|
||
|
|
KernelCommandType,
|
||
|
|
KernelDiagnosticCode,
|
||
|
|
KernelIntent,
|
||
|
|
KernelMode,
|
||
|
|
MockVenueAdapter,
|
||
|
|
MockVenueScenario,
|
||
|
|
TradeSide,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
class TestRateLimitContract(unittest.TestCase):
|
||
|
|
"""Verify the kernel handles venue rejections without corrupting state."""
|
||
|
|
|
||
|
|
def setUp(self):
|
||
|
|
self.control = InMemoryControlPlane()
|
||
|
|
self.control.update(ControlUpdate(
|
||
|
|
mode=KernelMode.DEBUG, trace_transitions=True,
|
||
|
|
))
|
||
|
|
self.venue = MockVenueAdapter(
|
||
|
|
MockVenueScenario(
|
||
|
|
reject_entries=True,
|
||
|
|
reject_exits=False,
|
||
|
|
partial_fill_ratio=0.0,
|
||
|
|
cancel_reject=False,
|
||
|
|
)
|
||
|
|
)
|
||
|
|
self.kernel = ExecutionKernel(
|
||
|
|
max_slots=1,
|
||
|
|
control_plane=self.control,
|
||
|
|
venue=self.venue,
|
||
|
|
zinc_plane=InMemoryZincPlane(),
|
||
|
|
)
|
||
|
|
|
||
|
|
def _make_intent(self, action: KernelCommandType = KernelCommandType.ENTER) -> KernelIntent:
|
||
|
|
return KernelIntent(
|
||
|
|
timestamp=datetime.now(timezone.utc),
|
||
|
|
intent_id="rate-test-001",
|
||
|
|
trade_id="rate-trade-001",
|
||
|
|
slot_id=0,
|
||
|
|
asset="BTCUSDT",
|
||
|
|
side=TradeSide.SHORT,
|
||
|
|
action=action,
|
||
|
|
reference_price=65000.0,
|
||
|
|
target_size=0.01,
|
||
|
|
leverage=2.0,
|
||
|
|
reason="rate_limit_test",
|
||
|
|
)
|
||
|
|
|
||
|
|
def test_kernel_state_unaffected_by_rejection(self):
|
||
|
|
"""Slot returns to free/IDLE after venue rejects entry."""
|
||
|
|
intent = self._make_intent(KernelCommandType.ENTER)
|
||
|
|
self.kernel.process_intent(intent)
|
||
|
|
slot = self.kernel.slot(0)
|
||
|
|
self.assertTrue(slot.is_free(),
|
||
|
|
f"Slot should be free after reject, got {slot.fsm_state}")
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
unittest.main()
|