80 lines
2.4 KiB
Python
80 lines
2.4 KiB
Python
"""VIOLET launcher shadow helpers for live BLUE factor sourcing.
|
|
|
|
These helpers stay separate from the launcher module so they can be unit-tested
|
|
without importing the full launcher import chain.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import os
|
|
|
|
LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
def build_shadow_live_source(
|
|
*,
|
|
client_factory=None,
|
|
selector_factory=None,
|
|
source_factory=None,
|
|
scan_history_factory=None,
|
|
):
|
|
"""Create the read-only BLUE live-factor mirror for the shadow path."""
|
|
if client_factory is None or selector_factory is None or source_factory is None or scan_history_factory is None:
|
|
import hazelcast
|
|
|
|
from .alpha_wrappers import VioletAssetSelector
|
|
from .live_blue_source import LiveBlueScanHistory, source_live_blue_sizing_factors
|
|
|
|
client_factory = client_factory or (lambda: hazelcast.HazelcastClient(
|
|
cluster_name=os.environ.get("HZ_CLUSTER", "dolphin"),
|
|
cluster_members=[os.environ.get("HZ_HOST", "localhost:5701")],
|
|
))
|
|
selector_factory = selector_factory or VioletAssetSelector
|
|
source_factory = source_factory or source_live_blue_sizing_factors
|
|
scan_history_factory = scan_history_factory or LiveBlueScanHistory
|
|
|
|
client = client_factory()
|
|
return {
|
|
"client": client,
|
|
"scan_history": scan_history_factory(),
|
|
"selector": selector_factory(),
|
|
"live_source": source_factory,
|
|
}
|
|
|
|
|
|
def shadow_decision_step(
|
|
shadow: dict,
|
|
payload: dict,
|
|
*,
|
|
scan_number: int,
|
|
now_ns: int,
|
|
vel_div: float,
|
|
vol_ok: bool,
|
|
) -> bool:
|
|
"""Run one shadow decision against the live BLUE factor plane."""
|
|
shadow["engine"].observe(payload, scan_number)
|
|
live_source = shadow.get("live_source")
|
|
factors = None
|
|
if live_source is not None:
|
|
live_result = live_source(
|
|
shadow["client"],
|
|
scan_history=shadow["scan_history"],
|
|
selector=shadow["selector"],
|
|
)
|
|
shadow["last_live_source"] = live_result
|
|
factors = live_result.factors
|
|
if factors is None:
|
|
return False
|
|
decision = shadow["engine"].decide(
|
|
now_ns=now_ns,
|
|
scan_number=scan_number,
|
|
capital=shadow["capital"],
|
|
vel_div=vel_div,
|
|
vol_ok=vol_ok,
|
|
factors=factors,
|
|
)
|
|
if decision is None:
|
|
return False
|
|
return shadow["journal"].journal(decision, mono_ns=now_ns)
|