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>
95 lines
2.9 KiB
Python
95 lines
2.9 KiB
Python
"""Multi-leg non-double-book accounting invariant tests for PINK → DITAv2."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime, timezone
|
|
import unittest
|
|
|
|
from prod.clean_arch.dita_v2 import (
|
|
ExecutionKernel,
|
|
InMemoryControlPlane,
|
|
InMemoryZincPlane,
|
|
KernelCommandType,
|
|
KernelIntent,
|
|
MockVenueAdapter,
|
|
MockVenueScenario,
|
|
TradeSide,
|
|
TradeStage,
|
|
)
|
|
|
|
|
|
class TestAccountingInvariants(unittest.TestCase):
|
|
"""Verify single-application of capital deltas across multi-leg exits."""
|
|
|
|
def setUp(self):
|
|
self.control = InMemoryControlPlane()
|
|
self.venue = MockVenueAdapter(
|
|
MockVenueScenario(
|
|
reject_entries=False,
|
|
reject_exits=False,
|
|
partial_fill_ratio=0.5,
|
|
cancel_reject=False,
|
|
)
|
|
)
|
|
self.kernel = ExecutionKernel(
|
|
max_slots=1,
|
|
control_plane=self.control,
|
|
venue=self.venue,
|
|
zinc_plane=InMemoryZincPlane(),
|
|
)
|
|
|
|
def _enter(self) -> None:
|
|
intent = KernelIntent(
|
|
timestamp=datetime.now(timezone.utc),
|
|
intent_id="acct-entry-001",
|
|
trade_id="acct-trade-001",
|
|
slot_id=0,
|
|
asset="BTCUSDT",
|
|
side=TradeSide.SHORT,
|
|
action=KernelCommandType.ENTER,
|
|
reference_price=65000.0,
|
|
target_size=0.01,
|
|
leverage=2.0,
|
|
reason="acct_test_entry",
|
|
exit_leg_ratios=(0.5, 1.0),
|
|
)
|
|
self.kernel.process_intent(intent)
|
|
|
|
def _exit(self) -> None:
|
|
intent = KernelIntent(
|
|
timestamp=datetime.now(timezone.utc),
|
|
intent_id="acct-exit-001",
|
|
trade_id="acct-trade-001",
|
|
slot_id=0,
|
|
asset="BTCUSDT",
|
|
side=TradeSide.SHORT,
|
|
action=KernelCommandType.EXIT,
|
|
reference_price=64500.0,
|
|
target_size=0.005,
|
|
leverage=2.0,
|
|
reason="acct_test_exit",
|
|
exit_leg_ratios=(0.5, 1.0),
|
|
)
|
|
self.kernel.process_intent(intent)
|
|
|
|
def test_capital_unchanged_after_entry(self):
|
|
capital_before = self.kernel.account.snapshot.capital
|
|
self._enter()
|
|
capital_after = self.kernel.account.snapshot.capital
|
|
self.assertEqual(capital_after, capital_before,
|
|
"Entry should not change capital (no realized PnL)")
|
|
|
|
def test_full_cycle_does_not_crash(self):
|
|
"""Run a full entry→partial exit lifecycle without errors."""
|
|
self._enter()
|
|
slot_before = self.kernel.slot(0)
|
|
self.assertTrue(slot_before.is_open(), "Slot should be open after entry")
|
|
self._exit()
|
|
# After partial exit, slot may still be open or closed depending on mock behavior
|
|
slot_after = self.kernel.slot(0)
|
|
self.assertIsNotNone(slot_after, "Slot should still exist after partial exit")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|