Files
siloqy/prod/tests/test_pink_ditav2_rate_limit_contract.py

72 lines
2.1 KiB
Python
Raw Normal View History

"""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()