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:
Codex
2026-05-30 18:26:43 +02:00
parent 34d01fe6a4
commit 3d7b00e28d
89 changed files with 32782 additions and 0 deletions

View File

@@ -0,0 +1,612 @@
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime, timezone
import math
import pytest
from prod.clean_arch.dita_v2 import (
BackendMode,
ControlUpdate,
ExecutionKernel,
InMemoryControlPlane,
InMemoryZincPlane,
KernelCommandType,
KernelControlSnapshot,
KernelDiagnosticCode,
KernelEventKind,
KernelIntent,
KernelMode,
KernelVerbosity,
MemoryKernelJournal,
TradeSide,
TradeSlot,
TradeStage,
VenueEvent,
VenueEventStatus,
VenueOrder,
VenueOrderStatus,
)
class NoopVenueAdapter:
def submit(self, intent): # type: ignore[override]
return []
def cancel(self, order, *, reason: str = ""): # type: ignore[override]
return []
def open_orders(self): # type: ignore[override]
return []
def open_positions(self): # type: ignore[override]
return []
def reconcile(self): # type: ignore[override]
return []
@dataclass(frozen=True)
class IntentGuardCase:
name: str
slot_id: int
seed_state: str
action: KernelCommandType
trade_id: str
intent_trade_id: str
expected_state: TradeStage
expected_code: KernelDiagnosticCode
expected_accepted: bool
@dataclass(frozen=True)
class DuplicateCase:
name: str
seed_state: str
first_kind: KernelEventKind
second_kind: KernelEventKind
expected_state: TradeStage
event_factory_name: str
@dataclass(frozen=True)
class StaleCase:
name: str
second_kind: KernelEventKind
same_event_id_as_initial: bool
expected_accepted: bool
@dataclass(frozen=True)
class ZincMirrorCase:
name: str
op: str
@dataclass(frozen=True)
class SlotRigorCase:
name: str
op: str
def _build_kernel(slot_count: int = 3) -> tuple[ExecutionKernel, MemoryKernelJournal, InMemoryZincPlane]:
journal = MemoryKernelJournal()
zinc = InMemoryZincPlane()
kernel = ExecutionKernel(
max_slots=slot_count,
control_plane=InMemoryControlPlane(
KernelControlSnapshot(
mode=KernelMode.DEBUG,
verbosity=KernelVerbosity.TRACE,
backend_mode=BackendMode.MOCK,
debug_clickhouse_enabled=True,
trace_transitions=True,
mirror_to_hazelcast=True,
)
),
venue=NoopVenueAdapter(),
journal=journal,
zinc_plane=zinc,
)
return kernel, journal, zinc
def _make_entry_order(trade_id: str, slot_id: int, *, size: float = 1.0, status: VenueOrderStatus = VenueOrderStatus.NEW) -> VenueOrder:
return VenueOrder(
internal_trade_id=trade_id,
venue_order_id=f"V-ENTRY-{slot_id}-{trade_id}",
venue_client_id=f"{trade_id}:entry:{slot_id}",
side=TradeSide.SHORT,
intended_size=size,
filled_size=size if status == VenueOrderStatus.FILLED else 0.0,
average_fill_price=100.0,
status=status,
metadata={"slot_id": slot_id},
)
def _make_exit_order(trade_id: str, slot_id: int, *, size: float, status: VenueOrderStatus = VenueOrderStatus.NEW) -> VenueOrder:
return VenueOrder(
internal_trade_id=trade_id,
venue_order_id=f"V-EXIT-{slot_id}-{trade_id}",
venue_client_id=f"{trade_id}:exit:{slot_id}",
side=TradeSide.SHORT,
intended_size=size,
filled_size=size if status == VenueOrderStatus.FILLED else 0.0,
average_fill_price=99.0,
status=status,
metadata={"slot_id": slot_id},
)
def _seed_free_slot(slot_id: int) -> TradeSlot:
return TradeSlot(slot_id=slot_id)
def _seed_entry_working_slot(trade_id: str, slot_id: int) -> TradeSlot:
return TradeSlot(
slot_id=slot_id,
trade_id=trade_id,
asset="BTCUSDT",
side=TradeSide.SHORT,
entry_price=0.0,
size=0.0,
initial_size=0.0,
leverage=2.0,
entry_time=datetime.now(timezone.utc),
exit_leg_ratios=(1.0,),
active_leg_index=0,
active_entry_order=_make_entry_order(trade_id, slot_id, status=VenueOrderStatus.NEW),
fsm_state=TradeStage.ENTRY_WORKING,
)
def _seed_position_open_slot(trade_id: str, slot_id: int, *, size: float = 1.0, side: TradeSide = TradeSide.SHORT) -> TradeSlot:
return TradeSlot(
slot_id=slot_id,
trade_id=trade_id,
asset="BTCUSDT",
side=side,
entry_price=100.0,
size=size,
initial_size=size,
leverage=2.0,
entry_time=datetime.now(timezone.utc),
exit_leg_ratios=(0.5, 0.5),
active_leg_index=0,
active_entry_order=_make_entry_order(trade_id, slot_id, size=size, status=VenueOrderStatus.FILLED),
fsm_state=TradeStage.POSITION_OPEN,
)
def _seed_exit_working_slot(trade_id: str, slot_id: int, *, size: float = 1.0) -> TradeSlot:
slot = _seed_position_open_slot(trade_id, slot_id, size=size)
slot.active_exit_order = _make_exit_order(trade_id, slot_id, size=slot.next_exit_ratio() * size, status=VenueOrderStatus.NEW)
slot.fsm_state = TradeStage.EXIT_WORKING
return slot
def _seed_closed_slot(trade_id: str, slot_id: int) -> TradeSlot:
return TradeSlot(
slot_id=slot_id,
trade_id=trade_id,
asset="BTCUSDT",
side=TradeSide.SHORT,
entry_price=100.0,
size=0.0,
initial_size=1.0,
leverage=2.0,
entry_time=datetime.now(timezone.utc),
closed=True,
fsm_state=TradeStage.CLOSED,
)
def _make_intent(
*,
trade_id: str,
slot_id: int,
action: KernelCommandType,
leverage: float = 2.0,
size: float = 1.0,
side: TradeSide = TradeSide.SHORT,
reason: str = "HARNESS",
) -> KernelIntent:
return KernelIntent(
timestamp=datetime.now(timezone.utc),
intent_id=f"intent-{trade_id}-{action.value}-{slot_id}",
trade_id=trade_id,
slot_id=slot_id,
asset="BTCUSDT",
side=side,
action=action,
reference_price=100.0,
target_size=size,
leverage=leverage,
exit_leg_ratios=(0.5, 0.5) if action == KernelCommandType.EXIT else (1.0,),
reason=reason,
)
def _make_event(
slot: TradeSlot,
*,
kind: KernelEventKind,
event_id: str,
filled_size: float = 0.0,
reason: str = "",
slot_id: int | None = None,
) -> VenueEvent:
order = slot.active_exit_order or slot.active_entry_order
venue_order_id = order.venue_order_id if order else f"V-{kind.value}-{slot.slot_id}"
venue_client_id = order.venue_client_id if order else f"{slot.trade_id}:client:{slot.slot_id}"
status = {
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,
KernelEventKind.CONTROL: VenueEventStatus.ACKED,
}[kind]
return VenueEvent(
timestamp=datetime.now(timezone.utc),
event_id=event_id,
trade_id=slot.trade_id,
slot_id=slot.slot_id if slot_id is None else slot_id,
kind=kind,
status=status,
venue_order_id=venue_order_id,
venue_client_id=venue_client_id,
side=slot.side if slot.side != TradeSide.FLAT else TradeSide.SHORT,
asset=slot.asset or "BTCUSDT",
price=99.0 if kind == KernelEventKind.MARK_PRICE else 100.0,
size=max(slot.size, 1.0),
filled_size=filled_size,
remaining_size=max(0.0, max(slot.size, 1.0) - filled_size),
reason=reason,
)
INTENT_GUARD_CASES = [
IntentGuardCase("invalid_negative_enter", -1, "free", KernelCommandType.ENTER, "trade-neg", "trade-neg", TradeStage.IDLE, KernelDiagnosticCode.INVALID_SLOT_ID, False),
IntentGuardCase("invalid_high_exit", 99, "free", KernelCommandType.EXIT, "trade-high", "trade-high", TradeStage.IDLE, KernelDiagnosticCode.INVALID_SLOT_ID, False),
IntentGuardCase("unsupported_control", 0, "free", KernelCommandType.CONTROL, "trade-control", "trade-control", TradeStage.IDLE, KernelDiagnosticCode.UNSUPPORTED_INTENT, False),
IntentGuardCase("free_exit", 0, "free", KernelCommandType.EXIT, "trade-free-exit", "trade-free-exit", TradeStage.IDLE, KernelDiagnosticCode.NO_OPEN_POSITION, False),
IntentGuardCase("free_cancel", 0, "free", KernelCommandType.CANCEL, "trade-free-cancel", "trade-free-cancel", TradeStage.IDLE, KernelDiagnosticCode.NO_ACTIVE_EXIT_ORDER, False),
IntentGuardCase("busy_enter_different_trade", 0, "position_open", KernelCommandType.ENTER, "trade-open", "trade-new", TradeStage.POSITION_OPEN, KernelDiagnosticCode.SLOT_BUSY, False),
IntentGuardCase("same_trade_enter_allowed", 0, "position_open", KernelCommandType.ENTER, "trade-open", "trade-open", TradeStage.ORDER_REQUESTED, KernelDiagnosticCode.OK, True),
IntentGuardCase("closed_exit", 0, "closed", KernelCommandType.EXIT, "trade-closed", "trade-closed", TradeStage.CLOSED, KernelDiagnosticCode.NO_OPEN_POSITION, False),
IntentGuardCase("open_reconcile", 0, "position_open", KernelCommandType.RECONCILE, "trade-reconcile", "trade-reconcile", TradeStage.STALE_STATE_RECONCILING, KernelDiagnosticCode.STALE_STATE_RECONCILE, True),
IntentGuardCase("free_mark_price", 0, "free", KernelCommandType.MARK_PRICE, "trade-mark", "trade-mark", TradeStage.IDLE, KernelDiagnosticCode.OK, True),
]
DUPLICATE_CASES = [
DuplicateCase("entry_ack_duplicate", "entry_working", KernelEventKind.ORDER_ACK, KernelEventKind.ORDER_ACK, TradeStage.ENTRY_WORKING, "ack"),
DuplicateCase("entry_partial_duplicate", "entry_working", KernelEventKind.PARTIAL_FILL, KernelEventKind.PARTIAL_FILL, TradeStage.ENTRY_WORKING, "partial-entry"),
DuplicateCase("entry_full_duplicate", "entry_working", KernelEventKind.FULL_FILL, KernelEventKind.FULL_FILL, TradeStage.POSITION_OPEN, "full-entry"),
DuplicateCase("exit_ack_duplicate", "exit_working", KernelEventKind.CANCEL_ACK, KernelEventKind.CANCEL_ACK, TradeStage.POSITION_OPEN, "ack-exit"),
DuplicateCase("exit_partial_duplicate", "exit_working", KernelEventKind.PARTIAL_FILL, KernelEventKind.PARTIAL_FILL, TradeStage.EXIT_WORKING, "partial-exit"),
DuplicateCase("exit_full_duplicate", "exit_working", KernelEventKind.FULL_FILL, KernelEventKind.FULL_FILL, TradeStage.CLOSED, "full-exit"),
DuplicateCase("cancel_reject_duplicate", "exit_working", KernelEventKind.CANCEL_REJECT, KernelEventKind.CANCEL_REJECT, TradeStage.EXIT_WORKING, "reject-exit"),
DuplicateCase("mark_price_duplicate", "position_open", KernelEventKind.MARK_PRICE, KernelEventKind.MARK_PRICE, TradeStage.POSITION_OPEN, "mark"),
DuplicateCase("reconcile_duplicate", "position_open", KernelEventKind.RECONCILE, KernelEventKind.RECONCILE, TradeStage.STALE_STATE_RECONCILING, "reconcile"),
DuplicateCase("entry_reject_duplicate", "entry_working", KernelEventKind.ORDER_REJECT, KernelEventKind.ORDER_REJECT, TradeStage.IDLE, "reject-entry"),
]
STALE_CASES = [
StaleCase("stale_ack", KernelEventKind.ORDER_ACK, False, False),
StaleCase("stale_reject", KernelEventKind.ORDER_REJECT, False, False),
StaleCase("stale_partial", KernelEventKind.PARTIAL_FILL, False, False),
StaleCase("stale_full", KernelEventKind.FULL_FILL, False, False),
StaleCase("stale_cancel_ack", KernelEventKind.CANCEL_ACK, False, False),
StaleCase("stale_cancel_reject", KernelEventKind.CANCEL_REJECT, False, False),
StaleCase("stale_mark_price", KernelEventKind.MARK_PRICE, False, False),
StaleCase("stale_control", KernelEventKind.CONTROL, False, False),
StaleCase("stale_reconcile", KernelEventKind.RECONCILE, False, True),
StaleCase("stale_duplicate_precedence", KernelEventKind.ORDER_ACK, True, False),
]
ZINC_MIRROR_CASES = [
ZincMirrorCase("intent_published_on_enter", "intent"),
ZincMirrorCase("invalid_slot_intent_still_publishes", "invalid_intent"),
ZincMirrorCase("slot_write_updates_state_region", "direct_write"),
ZincMirrorCase("venue_event_updates_state_region", "venue_event"),
ZincMirrorCase("control_update_writes_region", "control_update"),
ZincMirrorCase("snapshot_reflects_control", "snapshot"),
ZincMirrorCase("reconcile_from_slots_writes_all", "reconcile"),
ZincMirrorCase("free_slot_selects_first_free", "free_slot"),
ZincMirrorCase("read_slots_sorted", "sorted_read"),
ZincMirrorCase("slot_overwrite_replaces_previous_state", "overwrite"),
]
SLOT_RIGOR_CASES = [
SlotRigorCase("idle_slot_is_free", "idle_free"),
SlotRigorCase("closed_slot_is_free", "closed_free"),
SlotRigorCase("entry_working_is_not_free", "entry_not_free"),
SlotRigorCase("open_slot_is_not_free", "open_not_free"),
SlotRigorCase("mark_price_zero_is_noop", "mark_zero"),
SlotRigorCase("mark_price_negative_is_noop", "mark_negative"),
SlotRigorCase("mark_price_nan_is_noop", "mark_nan"),
SlotRigorCase("short_price_rise_negative_pnl", "short_rise"),
SlotRigorCase("short_price_drop_positive_pnl", "short_drop"),
SlotRigorCase("exit_leg_consume_and_clamp", "exit_leg"),
]
def _seed_for_intent_case(kernel: ExecutionKernel, case: IntentGuardCase) -> None:
if case.seed_state == "free":
return
if case.seed_state == "entry_working":
kernel._set_slot(_seed_entry_working_slot(case.trade_id, case.slot_id))
return
if case.seed_state == "position_open":
kernel._set_slot(_seed_position_open_slot(case.trade_id, case.slot_id))
return
if case.seed_state == "exit_working":
kernel._set_slot(_seed_exit_working_slot(case.trade_id, case.slot_id))
return
if case.seed_state == "closed":
kernel._set_slot(_seed_closed_slot(case.trade_id, case.slot_id))
return
raise AssertionError(case.seed_state)
def _seed_for_duplicate_case(kernel: ExecutionKernel, case: DuplicateCase) -> TradeSlot:
if case.seed_state == "entry_working":
slot = _seed_entry_working_slot(f"trade-{case.name}", 0)
elif case.seed_state == "exit_working":
slot = _seed_exit_working_slot(f"trade-{case.name}", 0)
elif case.seed_state == "position_open":
slot = _seed_position_open_slot(f"trade-{case.name}", 0)
elif case.seed_state == "stale":
slot = _seed_position_open_slot(f"trade-{case.name}", 0)
else:
raise AssertionError(case.seed_state)
kernel._set_slot(slot)
return kernel._get_slot(0)
def _seed_for_stale_case(kernel: ExecutionKernel) -> TradeSlot:
slot = _seed_position_open_slot("trade-stale", 0)
kernel._set_slot(slot)
return kernel._get_slot(0)
def _seed_for_zinc_case(kernel: ExecutionKernel, case: ZincMirrorCase) -> None:
if case.op == "intent":
return
if case.op == "invalid_intent":
return
if case.op == "direct_write":
kernel._set_slot(_seed_position_open_slot("trade-write", 0))
return
if case.op == "venue_event":
kernel._set_slot(_seed_entry_working_slot("trade-event", 0))
return
if case.op == "control_update":
return
if case.op == "snapshot":
return
if case.op == "reconcile":
return
if case.op == "free_slot":
kernel._set_slot(_seed_position_open_slot("trade-free", 0))
kernel._set_slot(_seed_free_slot(1))
return
if case.op == "sorted_read":
return
if case.op == "overwrite":
return
raise AssertionError(case.op)
@pytest.mark.parametrize("case", INTENT_GUARD_CASES, ids=lambda case: case.name)
def test_kernel_intent_guard_matrix(case: IntentGuardCase) -> None:
kernel, _, zinc = _build_kernel()
_seed_for_intent_case(kernel, case)
intent = _make_intent(
trade_id=case.intent_trade_id,
slot_id=case.slot_id,
action=case.action,
leverage=-3.0 if case.name == "same_trade_enter_allowed" else 2.5,
size=1.0,
reason=case.name,
)
outcome = kernel.process_intent(intent)
assert outcome.accepted is case.expected_accepted
assert outcome.diagnostic_code == case.expected_code
assert outcome.state == case.expected_state
if case.slot_id >= 0 and case.slot_id < kernel.max_slots:
assert zinc.intent_region
assert zinc.intent_region[-1].intent_id == intent.intent_id
if case.name == "same_trade_enter_allowed":
current = kernel.slot(case.slot_id).to_dict()
assert current["fsm_state"] == TradeStage.ORDER_REQUESTED.value
assert current["leverage"] == 1.0
@pytest.mark.parametrize("case", DUPLICATE_CASES, ids=lambda case: case.name)
def test_kernel_duplicate_event_matrix(case: DuplicateCase) -> None:
kernel, _, _ = _build_kernel()
slot = _seed_for_duplicate_case(kernel, case)
fill_size = slot.size or 1.0
if case.seed_state == "exit_working" and case.first_kind == KernelEventKind.PARTIAL_FILL:
fill_size = max(0.1, fill_size * 0.4)
first_event = _make_event(slot, kind=case.first_kind, event_id=f"dup-{case.name}", filled_size=fill_size)
first = kernel.on_venue_event(first_event)
second = kernel.on_venue_event(first_event)
assert first.diagnostic_code in {
KernelDiagnosticCode.OK,
KernelDiagnosticCode.STALE_STATE_RECONCILE,
KernelDiagnosticCode.ENTRY_ORDER_REJECTED,
KernelDiagnosticCode.EXIT_ORDER_REJECTED,
KernelDiagnosticCode.ORDER_REJECTED,
KernelDiagnosticCode.CANCEL_REJECTED,
}
assert second.diagnostic_code == KernelDiagnosticCode.DUPLICATE_EVENT
assert second.state == case.expected_state
assert second.accepted is True
assert kernel.slot(0).to_dict()["seen_event_ids"].count(first_event.event_id) == 1
@pytest.mark.parametrize("case", STALE_CASES, ids=lambda case: case.name)
def test_kernel_stale_state_matrix(case: StaleCase) -> None:
kernel, _, _ = _build_kernel()
slot = _seed_for_stale_case(kernel)
initial = _make_event(slot, kind=KernelEventKind.RECONCILE, event_id="stale-entry", filled_size=slot.size or 1.0)
initial_outcome = kernel.on_venue_event(initial)
assert initial_outcome.diagnostic_code == KernelDiagnosticCode.OK
assert kernel.slot(0).fsm_state == TradeStage.STALE_STATE_RECONCILING
if case.same_event_id_as_initial:
event = _make_event(kernel._get_slot(0), kind=case.second_kind, event_id="stale-entry", filled_size=slot.size or 1.0, reason=case.name)
else:
event = _make_event(kernel._get_slot(0), kind=case.second_kind, event_id=f"stale-{case.name}", filled_size=slot.size or 1.0, reason=case.name)
outcome = kernel.on_venue_event(event)
if case.same_event_id_as_initial:
assert outcome.diagnostic_code == KernelDiagnosticCode.DUPLICATE_EVENT
assert outcome.accepted is True
else:
assert outcome.diagnostic_code == KernelDiagnosticCode.STALE_STATE_RECONCILE
assert outcome.accepted is case.expected_accepted
assert outcome.state == TradeStage.STALE_STATE_RECONCILING
assert kernel.slot(0).fsm_state == TradeStage.STALE_STATE_RECONCILING
@pytest.mark.parametrize("case", ZINC_MIRROR_CASES, ids=lambda case: case.name)
def test_kernel_zinc_mirror_matrix(case: ZincMirrorCase) -> None:
kernel, _, zinc = _build_kernel()
_seed_for_zinc_case(kernel, case)
if case.op == "intent":
intent = _make_intent(trade_id="trade-intent", slot_id=0, action=KernelCommandType.ENTER, size=1.25)
outcome = kernel.process_intent(intent)
assert outcome.accepted is True
assert zinc.intent_region
assert zinc.intent_region[-1].intent_id == intent.intent_id
assert zinc.read_slots()[0].trade_id == "trade-intent"
elif case.op == "invalid_intent":
intent = _make_intent(trade_id="trade-invalid", slot_id=-1, action=KernelCommandType.EXIT, size=1.0)
outcome = kernel.process_intent(intent)
assert outcome.diagnostic_code == KernelDiagnosticCode.INVALID_SLOT_ID
assert len(zinc.intent_region) == 1
assert zinc.intent_region[-1].intent_id == intent.intent_id
elif case.op == "direct_write":
slot = _seed_position_open_slot("trade-write", 0, size=1.5)
kernel._set_slot(slot)
mirrored = zinc.read_slots()[0]
assert mirrored.trade_id == "trade-write"
assert mirrored.size == 1.5
assert mirrored.fsm_state == TradeStage.POSITION_OPEN
elif case.op == "venue_event":
slot = kernel._get_slot(0)
event = _make_event(slot, kind=KernelEventKind.FULL_FILL, event_id="zinc-fill", filled_size=slot.size or 1.0)
outcome = kernel.on_venue_event(event)
assert outcome.diagnostic_code == KernelDiagnosticCode.OK
mirrored = zinc.read_slots()[0]
assert mirrored.fsm_state == TradeStage.POSITION_OPEN
assert mirrored.seen_event_ids == ("zinc-fill",)
elif case.op == "control_update":
snapshot = kernel.update_control(
ControlUpdate(
mode=KernelMode.DEBUG,
verbosity=KernelVerbosity.TRACE,
trace_transitions=True,
mirror_to_hazelcast=False,
)
)
assert snapshot.mode == KernelMode.DEBUG
assert zinc.read_control().mode == KernelMode.DEBUG
assert zinc.read_control().trace_transitions is True
elif case.op == "snapshot":
kernel.update_control(ControlUpdate(mode=KernelMode.DEBUG, verbosity=KernelVerbosity.VERBOSE))
payload = kernel.snapshot()
assert payload["control"]["mode"] == KernelMode.DEBUG.value
assert payload["control"]["verbosity"] == KernelVerbosity.VERBOSE.value
elif case.op == "reconcile":
slots = [
_seed_position_open_slot("trade-a", 2),
_seed_closed_slot("trade-b", 0),
_seed_free_slot(1),
]
outcome = kernel.reconcile_from_slots(slots)
assert outcome.diagnostic_code == KernelDiagnosticCode.RECONCILED
mirrored_ids = [slot.slot_id for slot in zinc.read_slots()]
assert mirrored_ids == [0, 1, 2]
elif case.op == "free_slot":
assert kernel.free_slot().slot_id == 1
elif case.op == "sorted_read":
kernel._set_slot(_seed_position_open_slot("trade-c", 2))
kernel._set_slot(_seed_position_open_slot("trade-a", 0))
kernel._set_slot(_seed_position_open_slot("trade-b", 1))
ids = [slot.slot_id for slot in zinc.read_slots()]
assert ids == [0, 1, 2]
elif case.op == "overwrite":
kernel._set_slot(_seed_position_open_slot("trade-old", 0, size=1.0))
kernel._set_slot(_seed_position_open_slot("trade-new", 0, size=2.0))
mirrored = zinc.read_slots()[0]
assert mirrored.trade_id == "trade-new"
assert mirrored.size == 2.0
assert mirrored.initial_size == 2.0
else: # pragma: no cover - exhaustive
raise AssertionError(case.op)
@pytest.mark.parametrize("case", SLOT_RIGOR_CASES, ids=lambda case: case.name)
def test_trade_slot_state_machine_rigor_matrix(case: SlotRigorCase) -> None:
if case.op == "idle_free":
slot = TradeSlot(slot_id=0)
assert slot.is_free() is True
assert slot.is_open() is False
elif case.op == "closed_free":
slot = _seed_closed_slot("trade-closed", 0)
assert slot.is_free() is True
assert slot.is_open() is False
elif case.op == "entry_not_free":
slot = _seed_entry_working_slot("trade-entry", 0)
assert slot.is_free() is False
assert slot.is_open() is True
elif case.op == "open_not_free":
slot = _seed_position_open_slot("trade-open", 0)
assert slot.is_free() is False
assert slot.is_open() is True
elif case.op == "mark_zero":
slot = _seed_position_open_slot("trade-mark", 0, size=1.0)
slot.mark_price(0.0)
assert slot.unrealized_pnl == 0.0
elif case.op == "mark_negative":
slot = _seed_position_open_slot("trade-mark", 0, size=1.0)
slot.mark_price(-10.0)
assert slot.unrealized_pnl == 0.0
elif case.op == "mark_nan":
slot = _seed_position_open_slot("trade-mark", 0, size=1.0)
slot.mark_price(float("nan"))
assert slot.unrealized_pnl == 0.0
elif case.op == "short_rise":
slot = _seed_position_open_slot("trade-short-rise", 0, size=1.0, side=TradeSide.SHORT)
slot.mark_price(110.0)
assert slot.unrealized_pnl < 0.0
elif case.op == "short_drop":
slot = _seed_position_open_slot("trade-short-drop", 0, size=1.0, side=TradeSide.SHORT)
slot.mark_price(90.0)
assert slot.unrealized_pnl > 0.0
elif case.op == "exit_leg":
slot = _seed_position_open_slot("trade-leg", 0, size=1.0)
slot.exit_leg_ratios = (0.25, 0.75)
first = slot.consume_exit_leg()
second = slot.consume_exit_leg()
third = slot.consume_exit_leg()
assert first == 0.25
assert second == 0.75
assert third == 1.0
assert slot.active_leg_index == 2
assert slot.next_exit_ratio() == 1.0
else: # pragma: no cover - exhaustive
raise AssertionError(case.op)