67 production .py modules that the running PINK service imports but which were never committed: prod/bingx/ (HTTP client, market/user streams, journal, config), prod/clean_arch/ adapters/persistence/runtime/dita/dita_v2 production modules and their co-located tests. Rule going forward: every module imported by launch_dolphin_pink.py / pink_direct.py must appear in git ls-files. Excludes _backup dirs, __pycache__, and non-code files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
98 lines
3.6 KiB
Python
98 lines
3.6 KiB
Python
"""Hazelcast-compatible projection helpers for DITAv2."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from datetime import datetime
|
|
import os
|
|
from typing import Any, Callable, Dict, Iterable, List, Optional
|
|
|
|
from .account import AccountProjection
|
|
from .contracts import KernelTransition, TradeSlot, TradeStage, VenueEvent
|
|
from .control import KernelControlSnapshot
|
|
from .journal import _transition_row
|
|
from .utils import json_safe
|
|
|
|
Writer = Callable[[str, Dict[str, Any]], None]
|
|
|
|
|
|
@dataclass
|
|
class HazelcastProjection:
|
|
"""Projection helper for BLUE/PINK-compatible durable writes."""
|
|
|
|
active_slots_map: str = "hz:dita_active_slots"
|
|
trade_events_topic: str = "hz:dita_trade_events"
|
|
control_map: str = "hz:dita_control"
|
|
writer: Optional[Writer] = None
|
|
control_snapshot: Optional[KernelControlSnapshot] = None
|
|
|
|
def write_slot(self, slot: TradeSlot) -> Dict[str, Any]:
|
|
row = build_position_state_row(slot, self.control_snapshot)
|
|
if self.writer is not None:
|
|
self.writer(self.active_slots_map, row)
|
|
return row
|
|
|
|
def write_transition(
|
|
self,
|
|
*,
|
|
transition: KernelTransition,
|
|
slot: TradeSlot,
|
|
event: Optional[VenueEvent] = None,
|
|
control: Optional[KernelControlSnapshot] = None,
|
|
) -> Dict[str, Any]:
|
|
row = _transition_row(transition=transition, slot=slot, event=event, control=control)
|
|
if self.writer is not None:
|
|
self.writer(self.trade_events_topic, row)
|
|
return row
|
|
|
|
def write_control(self, control: KernelControlSnapshot) -> Dict[str, Any]:
|
|
self.control_snapshot = control
|
|
row = control.as_dict()
|
|
if self.writer is not None:
|
|
self.writer(self.control_map, row)
|
|
return row
|
|
|
|
|
|
def build_projection(
|
|
*,
|
|
writer: Optional[Writer] = None,
|
|
client: Optional[Any] = None,
|
|
prefer_real_hazelcast: Optional[bool] = None,
|
|
control_snapshot: Optional[KernelControlSnapshot] = None,
|
|
) -> HazelcastProjection:
|
|
"""Build the active projection helper with an operator-visible switch.
|
|
|
|
The default remains the callback-based projection helper. If a Hazelcast
|
|
client is supplied and the caller opts in via ``prefer_real_hazelcast`` or
|
|
``DITA_V2_HAZELCAST=REAL``, the helper routes directly through the
|
|
client-backed map/topic writer path.
|
|
"""
|
|
|
|
env_choice = os.environ.get("DITA_V2_HAZELCAST", "").strip().upper()
|
|
real_requested = prefer_real_hazelcast if prefer_real_hazelcast is not None else env_choice in {"REAL", "REAL_HZ", "HAZELCAST"}
|
|
if real_requested and client is not None:
|
|
try:
|
|
from .hazelcast_projection import HazelcastRowWriter
|
|
|
|
writer = HazelcastRowWriter(client)
|
|
except Exception:
|
|
pass
|
|
return HazelcastProjection(writer=writer, control_snapshot=control_snapshot)
|
|
|
|
|
|
def build_position_state_row(slot: TradeSlot, control: Optional[KernelControlSnapshot] = None) -> Dict[str, Any]:
|
|
"""Build a state row shaped for durable compatibility."""
|
|
row = slot.to_dict()
|
|
row.update(
|
|
{
|
|
"runtime_namespace": control.runtime_namespace if control else "dita_v2",
|
|
"strategy_namespace": control.strategy_namespace if control else "dita_v2",
|
|
"event_namespace": control.event_namespace if control else "dita_v2",
|
|
"actor_name": control.actor_name if control else "ExecutionKernel",
|
|
"exec_venue": control.exec_venue if control else "bingx",
|
|
"data_venue": control.data_venue if control else "binance",
|
|
"ledger_authority": control.ledger_authority if control else "exchange",
|
|
}
|
|
)
|
|
return row
|