124 lines
5.3 KiB
Python
124 lines
5.3 KiB
Python
|
|
"""VIOLET V3.4b: source live ``SizingFactors`` from BLUE's published HZ planes.
|
||
|
|
|
||
|
|
The field-path validation (``prod/docs/VIOLET_V34B_LIVE_FACTOR_FIELD_VALIDATION.md``)
|
||
|
|
established that of the eight sizing inputs, only ``posture`` and ``esof_score`` are
|
||
|
|
present in maps live BLUE publishes to Hazelcast:
|
||
|
|
|
||
|
|
- ``posture`` ← ``DOLPHIN_STATE_BLUE`` ``engine_snapshot['posture']``
|
||
|
|
- ``esof_score`` ← ``DOLPHIN_FEATURES['esof_latest'|'esof_advisor_latest']``,
|
||
|
|
parsed by BLUE's OWN ``parse_esof_payload`` /
|
||
|
|
``esof_score_from_payload`` (wrap, don't reimplement).
|
||
|
|
|
||
|
|
The remaining five (``boost``, ``beta``, ``mc_scale``, ``ob_median_imbalance``,
|
||
|
|
``ob_agreement_pct``, ``dc_status``) are BLUE-organ outputs — the ACB over
|
||
|
|
``DOLPHIN_FEATURES['exf_latest']``, the MC flag→scale derivation, ``OBFeatureEngine``,
|
||
|
|
and the per-asset signal generator — and are NOT present as scalars in any HZ map.
|
||
|
|
Sourcing them live is the V3.4c organ-wiring sprint.
|
||
|
|
|
||
|
|
Until then this adapter sources the two HZ-available factors faithfully and supplies
|
||
|
|
BLUE's OWN neutral sentinels for the organ-derived five (``boost=1.0``, ``beta=0.0``,
|
||
|
|
``mc_scale=1.0``, ``ob_*=None``, ``dc_status="NONE"``). The result flows through the
|
||
|
|
validated ``extract_live_sizing_factors`` normalizer, so the V3.4 shadow breakdown
|
||
|
|
records posture+esof LIVE and the rest NEUTRAL — explicit, never silently faked.
|
||
|
|
|
||
|
|
Pure boundary: callers pass already-fetched HZ blobs (the DARK service reads the maps
|
||
|
|
and hands the dicts in). No Hazelcast client here, no I/O, no launcher coupling —
|
||
|
|
mirrors ``live_factors.py``'s philosophy and keeps the adapter fully unit-testable.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import sys
|
||
|
|
from collections.abc import Mapping
|
||
|
|
from pathlib import Path
|
||
|
|
from typing import Any, Optional
|
||
|
|
|
||
|
|
from .decision_engine import SizingFactors
|
||
|
|
from .domain import typed
|
||
|
|
from .live_factors import extract_live_sizing_factors
|
||
|
|
|
||
|
|
_PROJECT_ROOT = Path(__file__).resolve().parents[3]
|
||
|
|
|
||
|
|
# The organ-derived factors this adapter cannot source from HZ yet (V3.4c). Listed so
|
||
|
|
# the journal/diagnostics can mark them NEUTRAL rather than mistaking them for live.
|
||
|
|
ORGAN_DERIVED_FACTORS = (
|
||
|
|
"boost", "beta", "mc_scale",
|
||
|
|
"ob_median_imbalance", "ob_agreement_pct", "dc_status",
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def _import_esof_gate() -> Any:
|
||
|
|
"""Import BLUE's ``esof_size_gate`` (same root-injection as ``sizing.py``)."""
|
||
|
|
try:
|
||
|
|
from nautilus_dolphin.nautilus import esof_size_gate # type: ignore
|
||
|
|
except ImportError:
|
||
|
|
for p in (str(_PROJECT_ROOT / "nautilus_dolphin"), str(_PROJECT_ROOT)):
|
||
|
|
if p not in sys.path:
|
||
|
|
sys.path.insert(0, p)
|
||
|
|
sys.modules.pop("nautilus_dolphin", None)
|
||
|
|
from nautilus_dolphin.nautilus import esof_size_gate # type: ignore
|
||
|
|
return esof_size_gate
|
||
|
|
|
||
|
|
|
||
|
|
def posture_from_engine_snapshot(snapshot: Optional[Mapping[str, Any]]) -> str:
|
||
|
|
"""BLUE's ``engine_snapshot['posture']`` (DOLPHIN_STATE_BLUE), defaulting to APEX.
|
||
|
|
|
||
|
|
Mirrors BLUE's own default (``getattr(self, '_day_posture', 'APEX')``,
|
||
|
|
esf_alpha_orchestrator.py:365/613). Upper-cased for the SizingFactors contract.
|
||
|
|
"""
|
||
|
|
if not isinstance(snapshot, Mapping):
|
||
|
|
return "APEX"
|
||
|
|
raw = snapshot.get("posture")
|
||
|
|
text = str(raw).strip() if raw is not None else ""
|
||
|
|
return text.upper() if text else "APEX"
|
||
|
|
|
||
|
|
|
||
|
|
def esof_score_from_features(
|
||
|
|
esof_raw: Any,
|
||
|
|
*,
|
||
|
|
max_age_s: Optional[float] = None,
|
||
|
|
) -> Optional[float]:
|
||
|
|
"""Extract the EsoF advisory score from a raw HZ ``esof_latest`` value.
|
||
|
|
|
||
|
|
Mirrors BLUE's ``_read_esof_payload`` two-step exactly: ``parse_esof_payload(raw)``
|
||
|
|
(the HZ value is a raw JSON blob) then ``esof_score_from_payload`` — both BLUE's
|
||
|
|
OWN functions (nautilus_event_trader.py:716,729), no reimplementation. ``max_age_s``
|
||
|
|
mirrors BLUE's freshness gate (``ESOF_FRESHNESS_S``); ``None`` skips the staleness
|
||
|
|
check. Returns ``None`` when the value is missing/unparseable/stale — SizingFactors
|
||
|
|
then leaves ``esof_score`` unset (BLUE's ``esof_size_mult_from_score(None)`` neutral
|
||
|
|
path).
|
||
|
|
"""
|
||
|
|
if esof_raw is None:
|
||
|
|
return None
|
||
|
|
gate = _import_esof_gate()
|
||
|
|
payload = gate.parse_esof_payload(esof_raw)
|
||
|
|
if not payload:
|
||
|
|
return None
|
||
|
|
score = gate.esof_score_from_payload(payload, max_age_s=max_age_s)
|
||
|
|
return None if score is None else float(score)
|
||
|
|
|
||
|
|
|
||
|
|
@typed
|
||
|
|
def source_live_sizing_factors(
|
||
|
|
*,
|
||
|
|
engine_snapshot: Optional[Mapping[str, Any]] = None,
|
||
|
|
esof_payload: Any = None,
|
||
|
|
esof_max_age_s: Optional[float] = None,
|
||
|
|
) -> SizingFactors:
|
||
|
|
"""Build live ``SizingFactors`` from BLUE's published HZ blobs.
|
||
|
|
|
||
|
|
``posture`` and ``esof_score`` are sourced LIVE from ``engine_snapshot`` and the
|
||
|
|
``esof_latest`` payload; the six organ-derived factors fall to BLUE's neutral
|
||
|
|
sentinels via the ``extract_live_sizing_factors`` defaults. The DARK service is
|
||
|
|
expected to fetch ``DOLPHIN_STATE_BLUE['engine_snapshot']`` and
|
||
|
|
``DOLPHIN_FEATURES['esof_latest']`` and pass them here.
|
||
|
|
"""
|
||
|
|
posture = posture_from_engine_snapshot(engine_snapshot)
|
||
|
|
esof_score = esof_score_from_features(esof_payload, max_age_s=esof_max_age_s)
|
||
|
|
|
||
|
|
hz_snapshot: dict[str, Any] = {"posture": posture}
|
||
|
|
if esof_score is not None:
|
||
|
|
hz_snapshot["esof_score"] = esof_score
|
||
|
|
|
||
|
|
return extract_live_sizing_factors(hz_snapshot=hz_snapshot)
|