Files
siloqy/prod/docs/VIOLET_BLUE_PARITY_STRUCTURAL_DIVERGENCE.md
Codex fac287d678 VIOLET V3.4c: make boost/beta, signal-gen, OB bit-identical to BLUE (+ exhaustive tests)
Operator directive: VIOLET must do IDENTICALLY what BLUE does for the three parity
flags — approximation cannot guarantee bit-for-bit functioning. Reworked
live_blue_source.py to call BLUE's OWN code paths, not reconstruct/substitute them.

boost/beta — was reading the published DOLPHIN_FEATURES.acb_boost scalar
(acb_processor_service's daily value). BLUE's trader does NOT use that for sizing; it
recomputes live via acb.get_dynamic_boost_from_hz(exf_latest, w750_velocity, direction)
with a bare AdaptiveCircuitBreaker() and NO ob_engine (nautilus_event_trader.py
on_exf_update:4769 / rollover prewarm:2710). New _source_boost_beta replicates that call
exactly (reads exf_latest + latest_eigen_scan.w750_velocity; 0.0→None like BLUE; on stale
exf ValueError → neutral, mirroring BLUE's "ACB Stale Data Fallback"). The published
acb_boost is never read. Test pins bit-identity against the real ACB.

signal-gen (dc_status) — was AlphaSignalGenerator() bare defaults; coincidentally equal to
BLUE today, but BLUE builds it from ENGINE_KWARGS (trader:128-133, threaded at
esf_alpha_orchestrator.py:180-191), so a champion retune would silently diverge. Now
constructed with BLUE_SIGNAL_GEN_KWARGS (vel_div_* imported from the kernel constants).
Test parses ENGINE_KWARGS from the trader source and asserts each param matches — drift
becomes a red test, not a silent miss.

OB — was a reinvented HazelcastOBProvider reading asset_*_ob with custom parsing. Now uses
BLUE's OWN HZOBProvider + OBFeatureEngine, wired exactly as _wire_obf
(nautilus_event_trader.py:4967-4980): step_live(assets, bar_idx) then get_market. Engine
is injectable + persistent so OB accumulation matches BLUE across scans (caller owns it).

Deleted: HazelcastOBProvider, _extract_acb, the status-label mc path. mc_scale fix
(begin_day cat/env thresholds) retained. Module docstring + structural-divergence doc
updated: all three flags FIXED; only _derive_mc_scale remains hand-replicated (pinned by
formula test). OPEN follow-up: launcher shadow_decision_step should pass a persistent
ob_engine + bar_idx for cross-scan OB history.

34 tests (33 + live-HZ smoke deselected): boost/beta-vs-ACB bit-identity (incl. w750=0→None,
no-exf, stale ValueError, ignores acb_boost), signal-gen ENGINE_KWARGS pin (parametrized),
OB wiring (step_live call, HZ coords, neutral paths), mc_scale formula, sequence dc/selector
parity, anomaly handling. violet-only; no shared-file edits.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 17:33:01 +02:00

6.5 KiB

VIOLET ↔ BLUE parity: structural divergence & re-derivation debt

Date: 2026-06-16 Status: ACKNOWLEDGED TRADEOFF / open architectural debt Raised by: operator, during the V3.4c review.

The problem, stated plainly

VIOLET reproduces BLUE's sizing behaviour bit-for-bit by intent, but it does so in a different module / file / scope structure than BLUE. BLUE's logic lives in one place — the monolithic NDAlphaEngine (nautilus_dolphin/nautilus_dolphin/nautilus/esf_alpha_orchestrator.py), which holds day-state (_day_base_boost, _day_beta, _day_mc_scale, _day_posture), constructs its own signal_gen/bet_sizer, and runs begin_day / _try_entry inline.

VIOLET re-expresses that same logic spread across: sizing.py, live_blue_source.py, live_factors.py, live_factor_source.py, decision_engine.py, alpha_wrappers.py.

Consequence (the operator's concern, verbatim intent): because VIOLET imitates the computations while ending up with a different structure, any orderly, systemic, verifiable BLUE↔VIOLET algo parity comparison — and any future refactor of either side — is much harder. The surfaces do not line up 1:1, so a diff between the two engines is not mechanical; it requires a human to know which VIOLET fragment mirrors which BLUE line.

Two kinds of reuse — and only one is safe

  1. WRAPPED kernels (safe — single source of truth). VIOLET imports and calls BLUE's actual kernel objects. A BLUE change propagates automatically.

    • esof_size_mult_from_score, parse_esof_payload, esof_score_from_payload (esof_size_gate.py) — wrapped by sizing.py / live_factor_source.py.
    • OBFeatureEngine.get_market (ob_features.py) — wrapped by live_blue_source.py.
    • AlphaSignalGenerator.generate (alpha_signal_generator.py) — wrapped by live_blue_source.py.
    • AlphaAssetSelector / AlphaBetSizer — wrapped by alpha_wrappers.py.
    • map_internal_conviction_to_exchange_leverage (bingx/leverage.py) — wrapped by exchange_leverage.py.
  2. HAND-REPLICATED arithmetic (the debt — duplicated formulas, drift-prone). VIOLET transcribes BLUE's pure float arithmetic into its own functions. A BLUE change here is SILENT in VIOLET until someone notices.

Re-derivation inventory (the drift liabilities)

Computation BLUE authority (file:line) VIOLET replica Parity safety-net today Drift risk
5-factor compose + caps esf_alpha_orchestrator.py:600-619 sizing.VioletSizer.compose @gate Monte-Carlo vs REAL orchestrator (bit-identity) LOW (gated)
regime_size_mult = boost·(1+β·s³)·mc esf_alpha_orchestrator.py:898-909 sizing.VioletSizer.regime_size_mult same gate LOW-MED
strength_cubic esf_alpha_orchestrator.py:872-885 sizing.VioletSizer.strength_cubic same gate LOW-MED
market_ob_mult consensus esf_alpha_orchestrator.py:587-595 sizing.VioletSizer.market_ob_mult same gate MED
dc_lev_mult esf_alpha_orchestrator.py:575-577 sizing.VioletSizer.dc_lev_mult unit only MED (but ≡1.0 while dc_leverage_boost=1.0)
mc_scale esf_alpha_orchestrator.py:956-962 (begin_day) live_blue_source._derive_mc_scale parametrized formula test (8 cases inc. divergences) LOW-MED (only remaining hand-replica)
boost/beta source trader recompute acb.get_dynamic_boost_from_hz(exf_latest) FIXED 2026-06-16: _source_boost_beta calls the SAME get_dynamic_boost_from_hz over exf_latest+w750 (bare AdaptiveCircuitBreaker(), no ob_engine) bit-identity test vs the real ACB LOW
dc_status config signal_gen built with ENGINE_KWARGS :180-191 FIXED: AlphaSignalGenerator(**BLUE_SIGNAL_GEN_KWARGS) param test parses ENGINE_KWARGS from trader source LOW
OB feed live OB accumulation via HZOBProvider FIXED: BLUE's own HZOBProvider + OBFeatureEngine + step_live/get_market (persistent engine + bar_idx) wiring test (HZ coords, step_live call) LOW (caller must pass persistent engine)

Update 2026-06-16: the three MED-risk flags above were brought to bit-identity per operator directive ("VIOLET should do identically what BLUE does"). boost/beta now call the SAME get_dynamic_boost_from_hz BLUE's trader calls (the published acb_boost is NOT used); dc_status uses AlphaSignalGenerator pinned to BLUE's ENGINE_KWARGS; OB uses BLUE's HZOBProvider. The reinvented HazelcastOBProvider and the _extract_acb/status-label paths were deleted. The ONLY remaining hand-replicated arithmetic is _derive_mc_scale (begin_day computes it inline inside an un-callable method), pinned by a formula test.

OPEN follow-up: the launcher (shadow_decision_step) must pass a PERSISTENT ob_engine + per-scan-incrementing bar_idx into source_live_blue_sizing_factors so OB accumulation matches BLUE across scans; today it single-shots, which is wired-correctly but historyless.

Why we accept it (for now)

  • The kernels that carry the heavy alpha are WRAPPED, not copied.
  • The composition arithmetic IS gated bit-for-bit against the real orchestrator.
  • VIOLET must stay a read-only, DARK mirror of a running BLUE; it cannot import BLUE's live in-process day-state, so some reconstruction from published HZ surfaces is unavoidable.
  1. Parity-pin every hand-replicated formula. For each row above, add a test that imports BLUE's authoritative function/constant and asserts VIOLET's replica equals it over a sampled grid — converting silent drift into a red test. Where BLUE's logic is trapped inside begin_day (mc_scale), refactor a pure mc_scale_from(cat, env) helper on the BLUE side (BLUE-domain change, operator-gated) that BOTH engines call.
  2. Single ambiguity owner. Surfaces like MC (status label vs begin_day thresholds) and ACB (published acb_boost vs trader recompute) have two disagreeing sources; pick ONE canonical per factor and document it (see _derive_mc_scale docstring).
  3. Backplane convergence (the real fix). When the DITAv2 Rust middleware becomes the shared backplane, BOTH BLUE and VIOLET should consume factors from it rather than each computing/replicating — collapsing this divergence at the source. Until then, every new hand-replication MUST be logged in this table.

Maintenance rule

Any change to a BLUE formula in the left column REQUIRES a matching change + test update in the VIOLET replica in the same PR. Any NEW hand-replication MUST add a row here.