Snapshot PINK DITAv2 system + Sprint 0 flaw-fix verification
First commit of the previously-untracked PINK-on-DITAv2 migration system (execution moves to the Rust kernel; policy stays on legacy DITA, so Alpha Engine algorithmic integrity is preserved). BLUE is untouched. Sprint 0 (safety snapshot + flaw-fix verification, MARKET single-leg scope): - Verified Rust FSM fixes (flaws 2,4,10,11,13) by source read of lib.rs. - Hardened 5 vacuous/guarded assertions in test_flaws.py so each flaw test genuinely exercises its fix. Most important: Flaw 5 now asserts capital moves by EXACTLY realized PnL (was entering/exiting at the same price). - Offline suites: 533 passed, 0 failed (35 flaws + 402 kernel/accounting/ bridge + 96 runtime/persistence/multi-exit/restart/seams). - GATE PASS: MARKET-path-critical flaws 1,2,5 confirmed fixed + green. - Added SPRINT0_FLAW_VERIFICATION.md report and _rust_kernel/.gitignore (excludes Rust target/ build artifacts). LIMIT/partial-fill remain explicitly out of scope (MARKET-only bring-up). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
196
prod/tests/test_dita_v2_hazelcast.py
Normal file
196
prod/tests/test_dita_v2_hazelcast.py
Normal file
@@ -0,0 +1,196 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone
|
||||
import unittest
|
||||
|
||||
from prod.clean_arch.dita_v2 import (
|
||||
ControlUpdate,
|
||||
ExecutionKernel,
|
||||
HazelcastProjection,
|
||||
KernelCommandType,
|
||||
KernelControlSnapshot,
|
||||
KernelIntent,
|
||||
KernelMode,
|
||||
KernelVerbosity,
|
||||
MockVenueAdapter,
|
||||
MockVenueScenario,
|
||||
TradeSide,
|
||||
TradeStage,
|
||||
TradeSlot,
|
||||
build_projection,
|
||||
build_position_state_row,
|
||||
)
|
||||
from prod.clean_arch.dita_v2.hazelcast_projection import HazelcastProjector, HazelcastRowWriter
|
||||
|
||||
|
||||
class CaptureSink:
|
||||
def __init__(self) -> None:
|
||||
self.rows: list[tuple[str, dict[str, object]]] = []
|
||||
|
||||
def __call__(self, name: str, row: dict[str, object]) -> None:
|
||||
self.rows.append((name, dict(row)))
|
||||
|
||||
|
||||
class FakeMap:
|
||||
def __init__(self) -> None:
|
||||
self.rows: dict[str, object] = {}
|
||||
|
||||
def put(self, key: str, value: object) -> None:
|
||||
self.rows[key] = value
|
||||
|
||||
|
||||
class FakeTopic:
|
||||
def __init__(self) -> None:
|
||||
self.messages: list[str] = []
|
||||
|
||||
def publish(self, message: str) -> None:
|
||||
self.messages.append(message)
|
||||
|
||||
|
||||
class FakeHazelcastClient:
|
||||
def __init__(self) -> None:
|
||||
self.maps: dict[str, FakeMap] = {}
|
||||
self.topics: dict[str, FakeTopic] = {}
|
||||
|
||||
def get_map(self, name: str) -> FakeMap:
|
||||
return self.maps.setdefault(name, FakeMap())
|
||||
|
||||
def get_topic(self, name: str) -> FakeTopic:
|
||||
return self.topics.setdefault(name, FakeTopic())
|
||||
|
||||
|
||||
class TestDITAv2Hazelcast(unittest.TestCase):
|
||||
def test_build_position_state_row_has_compatibility_fields(self) -> None:
|
||||
slot = TradeSlot(
|
||||
slot_id=0,
|
||||
trade_id="trade-1",
|
||||
asset="BTCUSDT",
|
||||
side=TradeSide.SHORT,
|
||||
entry_price=100.0,
|
||||
size=1.0,
|
||||
initial_size=1.0,
|
||||
leverage=2.0,
|
||||
fsm_state=TradeStage.POSITION_OPEN,
|
||||
)
|
||||
row = build_position_state_row(
|
||||
slot,
|
||||
KernelControlSnapshot(
|
||||
mode=KernelMode.DEBUG,
|
||||
verbosity=KernelVerbosity.TRACE,
|
||||
runtime_namespace="dita_v2",
|
||||
strategy_namespace="dita_v2",
|
||||
event_namespace="dita_v2",
|
||||
actor_name="ExecutionKernel",
|
||||
exec_venue="bingx",
|
||||
data_venue="binance",
|
||||
ledger_authority="exchange",
|
||||
),
|
||||
)
|
||||
for key in (
|
||||
"runtime_namespace",
|
||||
"strategy_namespace",
|
||||
"event_namespace",
|
||||
"actor_name",
|
||||
"exec_venue",
|
||||
"data_venue",
|
||||
"ledger_authority",
|
||||
"trade_id",
|
||||
"asset",
|
||||
"slot_id",
|
||||
"fsm_state",
|
||||
):
|
||||
self.assertIn(key, row)
|
||||
self.assertEqual(row["trade_id"], "trade-1")
|
||||
self.assertEqual(row["fsm_state"], TradeStage.POSITION_OPEN.value)
|
||||
|
||||
def test_projection_sink_writes_blue_pink_compatible_rows(self) -> None:
|
||||
sink = CaptureSink()
|
||||
projection = HazelcastProjection(writer=sink)
|
||||
control = KernelControlSnapshot(mode=KernelMode.DEBUG, verbosity=KernelVerbosity.TRACE)
|
||||
projection.write_control(control)
|
||||
slot = TradeSlot(
|
||||
slot_id=1,
|
||||
trade_id="trade-2",
|
||||
asset="ETHUSDT",
|
||||
side=TradeSide.LONG,
|
||||
entry_price=50.0,
|
||||
size=2.0,
|
||||
initial_size=2.0,
|
||||
leverage=3.0,
|
||||
fsm_state=TradeStage.POSITION_OPEN,
|
||||
)
|
||||
projection.write_slot(slot)
|
||||
self.assertGreaterEqual(len(sink.rows), 2)
|
||||
control_name, control_row = sink.rows[0]
|
||||
slot_name, slot_row = sink.rows[1]
|
||||
self.assertEqual(control_name, "hz:dita_control")
|
||||
self.assertEqual(slot_name, "hz:dita_active_slots")
|
||||
self.assertEqual(control_row["mode"], KernelMode.DEBUG.value)
|
||||
self.assertEqual(slot_row["trade_id"], "trade-2")
|
||||
self.assertEqual(slot_row["runtime_namespace"], "dita_v2")
|
||||
self.assertEqual(slot_row["ledger_authority"], "exchange")
|
||||
|
||||
def test_hazelcast_row_writer_routes_maps_and_topics(self) -> None:
|
||||
client = FakeHazelcastClient()
|
||||
writer = HazelcastRowWriter(client)
|
||||
writer("hz:dita_active_slots", {"trade_id": "trade-3", "slot_id": 0})
|
||||
writer("hz:dita_control", {"mode": "DEBUG"})
|
||||
writer("hz:dita_trade_events", {"event_id": "evt-1", "trade_id": "trade-3"})
|
||||
self.assertIn("trade-3", client.get_map("hz:dita_active_slots").rows)
|
||||
self.assertIn("control", client.get_map("hz:dita_control").rows)
|
||||
self.assertEqual(len(client.get_topic("hz:dita_trade_events").messages), 1)
|
||||
|
||||
def test_build_projection_uses_client_when_requested(self) -> None:
|
||||
client = FakeHazelcastClient()
|
||||
projection = build_projection(client=client, prefer_real_hazelcast=True)
|
||||
projection.write_control(KernelControlSnapshot(mode=KernelMode.DEBUG, verbosity=KernelVerbosity.TRACE))
|
||||
projection.write_slot(
|
||||
TradeSlot(
|
||||
slot_id=0,
|
||||
trade_id="trade-4",
|
||||
asset="BTCUSDT",
|
||||
side=TradeSide.SHORT,
|
||||
entry_price=100.0,
|
||||
size=1.0,
|
||||
initial_size=1.0,
|
||||
leverage=2.0,
|
||||
fsm_state=TradeStage.POSITION_OPEN,
|
||||
)
|
||||
)
|
||||
self.assertIn("control", client.get_map("hz:dita_control").rows)
|
||||
self.assertIn("trade-4", client.get_map("hz:dita_active_slots").rows)
|
||||
|
||||
def test_kernel_emits_projection_rows(self) -> None:
|
||||
sink = CaptureSink()
|
||||
kernel = ExecutionKernel(
|
||||
control_plane=None,
|
||||
venue=MockVenueAdapter(MockVenueScenario(emit_fill_on_submit=True, partial_fill_ratio=1.0)),
|
||||
projection=HazelcastProjection(writer=sink),
|
||||
)
|
||||
kernel.update_control(ControlUpdate(mode=KernelMode.DEBUG, verbosity=KernelVerbosity.TRACE))
|
||||
kernel.process_intent(
|
||||
KernelIntent(
|
||||
timestamp=datetime.now(timezone.utc),
|
||||
intent_id="intent-1",
|
||||
trade_id="trade-1",
|
||||
slot_id=0,
|
||||
asset="BTCUSDT",
|
||||
side=TradeSide.SHORT,
|
||||
action=KernelCommandType.ENTER,
|
||||
reference_price=100.0,
|
||||
target_size=1.0,
|
||||
leverage=2.0,
|
||||
exit_leg_ratios=(1.0,),
|
||||
reason="TEST",
|
||||
)
|
||||
)
|
||||
names = [name for name, _ in sink.rows]
|
||||
self.assertIn("hz:dita_control", names)
|
||||
self.assertIn("hz:dita_active_slots", names)
|
||||
slot_rows = [row for name, row in sink.rows if name == "hz:dita_active_slots"]
|
||||
self.assertTrue(any(row["trade_id"] == "trade-1" for row in slot_rows))
|
||||
self.assertTrue(any(row["runtime_namespace"] == "dita_v2" for row in slot_rows))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user