86 lines
5.7 KiB
Markdown
86 lines
5.7 KiB
Markdown
|
|
# 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` | **unit only — NO pin to BLUE's fn** | **HIGH** |
|
||
|
|
| `boost`/`beta` source | trader recompute `acb.get_dynamic_boost_from_hz(exf_latest)` | reads published `DOLPHIN_FEATURES.acb_boost` (acb_processor_service) | none | MED (two surfaces may differ on dynamic-β / OB Sub-4) |
|
||
|
|
| `dc_status` config | `signal_gen` built with threaded params `:180-191` | `AlphaSignalGenerator()` **bare defaults** | none | MED (cosmetic while dc_lev_mult≡1.0) |
|
||
|
|
| OB feed shape | live OB accumulation | single-snapshot `HazelcastOBProvider` + `bar_idx=0` | none | MED |
|
||
|
|
|
||
|
|
The worst link is **`mc_scale`**: pure duplication of `begin_day`'s thresholds with no
|
||
|
|
test that pins it to BLUE's actual function (BLUE computes it inline inside `begin_day`,
|
||
|
|
which is not callable in isolation). The V3.4c bug fixed on 2026-06-16 (the adapter keyed
|
||
|
|
off the MC service's `status` label instead of BLUE's `cat`/`env` thresholds) is exactly
|
||
|
|
the failure mode this structure invites.
|
||
|
|
|
||
|
|
## 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.
|
||
|
|
|
||
|
|
## Mitigations (recommended, not yet done)
|
||
|
|
|
||
|
|
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.
|