VIOLET V3.4b: validate live-factor field paths + HZ sourcing adapter
#1 (validation) — VIOLET_V34B_LIVE_FACTOR_FIELD_VALIDATION.md documents the ground truth from BLUE's own code (esf_alpha_orchestrator / adaptive_circuit_breaker / nautilus_event_trader): of the 8 sizing inputs, ONLY `posture` is a flat HZ key. `esof_score` is in HZ but as a payload to parse. The other five — boost, beta, mc_scale, ob_median_imbalance, ob_agreement_pct, dc_status — are BLUE-organ outputs (ACB over DOLPHIN_FEATURES.exf_latest, MC flag derivation, OBFeatureEngine, the per-asset signal generator) and are NOT present as scalars in any HZ map. This inverts live_factors.py's flat-snapshot premise; its speculative alternate paths (acb_boost / s_acb_boost / ("acb","boost") / …) match nothing real. The full live sourcing of the five is a multi-organ sprint (V3.4c), not a HZ scrape. #2 (sourcing adapter) — live_factor_source.py sources what BLUE actually publishes, read-only and pure (callers pass already-fetched HZ blobs; no client, no I/O): - posture ← engine_snapshot['posture'] (DOLPHIN_STATE_BLUE), default APEX - esof_score ← DOLPHIN_FEATURES['esof_latest'] via BLUE's OWN parse_esof_payload + esof_score_from_payload (wrap, don't reimplement; staleness gate honored when max_age_s supplied) - boost/beta/mc_scale/ob_*/dc_status ← BLUE's neutral sentinels (1.0/0.0/1.0/None/ None/"NONE") until V3.4c — explicit, never silently faked. Flows through the validated extract_live_sizing_factors normalizer. ORGAN_DERIVED_ FACTORS names the six deferred to V3.4c so the journal can mark them NEUTRAL. 9 new tests green (posture default/upper, esof dict+raw-JSON+staleness, neutral integration, all-neutral-when-empty). violet-only; no shared-file edits; no soak. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
123
prod/clean_arch/violet/live_factor_source.py
Normal file
123
prod/clean_arch/violet/live_factor_source.py
Normal file
@@ -0,0 +1,123 @@
|
||||
"""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)
|
||||
85
prod/clean_arch/violet/test_violet_live_factor_source.py
Normal file
85
prod/clean_arch/violet/test_violet_live_factor_source.py
Normal file
@@ -0,0 +1,85 @@
|
||||
"""V3.4b: live_factor_source — SizingFactors from BLUE's published HZ blobs.
|
||||
|
||||
Validates the field-path findings in
|
||||
prod/docs/VIOLET_V34B_LIVE_FACTOR_FIELD_VALIDATION.md: posture + esof_score are
|
||||
sourced LIVE from engine_snapshot / the esof_latest payload, the six organ-derived
|
||||
factors fall to BLUE's neutral sentinels.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, "/mnt/dolphinng5_predict")
|
||||
|
||||
from prod.clean_arch.violet.decision_engine import SizingFactors
|
||||
from prod.clean_arch.violet.live_factor_source import (
|
||||
ORGAN_DERIVED_FACTORS,
|
||||
esof_score_from_features,
|
||||
posture_from_engine_snapshot,
|
||||
source_live_sizing_factors,
|
||||
)
|
||||
|
||||
|
||||
def test_posture_sourced_and_upper_cased():
|
||||
assert posture_from_engine_snapshot({"posture": "STALKER"}) == "STALKER"
|
||||
assert posture_from_engine_snapshot({"posture": "restored"}) == "RESTORED"
|
||||
|
||||
|
||||
def test_posture_defaults_to_apex_like_blue():
|
||||
assert posture_from_engine_snapshot(None) == "APEX"
|
||||
assert posture_from_engine_snapshot({}) == "APEX"
|
||||
assert posture_from_engine_snapshot({"posture": ""}) == "APEX"
|
||||
assert posture_from_engine_snapshot({"posture": None}) == "APEX"
|
||||
|
||||
|
||||
def test_esof_score_parsed_from_dict_payload():
|
||||
# max_age_s=None skips staleness; advisory_score preferred over score.
|
||||
assert esof_score_from_features({"advisory_score": 0.5}) == 0.5
|
||||
assert esof_score_from_features({"score": -0.1}) == pytest.approx(-0.1)
|
||||
|
||||
|
||||
def test_esof_score_parsed_from_raw_json_string():
|
||||
# The HZ value is a raw JSON blob — BLUE's parse_esof_payload handles it.
|
||||
assert esof_score_from_features('{"advisory_score": 0.25}') == 0.25
|
||||
|
||||
|
||||
def test_esof_score_none_when_missing_or_unparseable():
|
||||
assert esof_score_from_features(None) is None
|
||||
assert esof_score_from_features("not json") is None
|
||||
assert esof_score_from_features({}) is None # no advisory_score/score key
|
||||
|
||||
|
||||
def test_esof_staleness_gate_honored_when_max_age_supplied():
|
||||
# A payload with an ancient timestamp is stale → None when max_age_s is set.
|
||||
stale = {"advisory_score": 0.5, "unix": 0.0} # 1970 → very old
|
||||
assert esof_score_from_features(stale, max_age_s=30.0) is None
|
||||
# …but with no freshness gate (None) the score still comes through.
|
||||
assert esof_score_from_features(stale, max_age_s=None) == 0.5
|
||||
|
||||
|
||||
def test_source_live_factors_posture_and_esof_live_rest_neutral():
|
||||
factors = source_live_sizing_factors(
|
||||
engine_snapshot={"posture": "RESTORED", "capital": 71591.1},
|
||||
esof_payload={"advisory_score": 0.42},
|
||||
)
|
||||
assert isinstance(factors, SizingFactors)
|
||||
assert factors.posture == "RESTORED"
|
||||
assert factors.esof_score == 0.42
|
||||
# the six organ-derived factors at BLUE's own neutral sentinels (V3.4c will source)
|
||||
assert factors.boost == 1.0 and factors.beta == 0.0 and factors.mc_scale == 1.0
|
||||
assert factors.ob_median_imbalance is None and factors.ob_agreement_pct is None
|
||||
assert factors.dc_status == "NONE"
|
||||
|
||||
|
||||
def test_source_live_factors_all_neutral_when_no_blobs():
|
||||
assert source_live_sizing_factors() == SizingFactors()
|
||||
|
||||
|
||||
def test_organ_derived_factor_set_is_the_documented_six():
|
||||
assert set(ORGAN_DERIVED_FACTORS) == {
|
||||
"boost", "beta", "mc_scale",
|
||||
"ob_median_imbalance", "ob_agreement_pct", "dc_status",
|
||||
}
|
||||
Reference in New Issue
Block a user