Files
DOLPHIN/prod/docs/GREEN_TO_BLUE_LATEST_CHANGES.md
hjnormey 01c19662cb initial: import DOLPHIN baseline 2026-04-21 from dolphinng5_predict working tree
Includes core prod + GREEN/BLUE subsystems:
- prod/ (BLUE harness, configs, scripts, docs)
- nautilus_dolphin/ (GREEN Nautilus-native impl + dvae/ preserved)
- adaptive_exit/ (AEM engine + models/bucket_assignments.pkl)
- Observability/ (EsoF advisor, TUI, dashboards)
- external_factors/ (EsoF producer)
- mc_forewarning_qlabs_fork/ (MC regime/envelope)

Excludes runtime caches, logs, backups, and reproducible artifacts per .gitignore.
2026-04-21 16:58:38 +02:00

20 KiB
Executable File
Raw Permalink Blame History

GREEN→BLUE Algorithmic Parity — Change Log & Current State

Date: 2026-04-19 Author: Crush (AI Agent) Scope: GREEN DolphinActor (nautilus_dolphin/nautilus_dolphin/nautilus/dolphin_actor.py) — only GREEN code was modified BLUE reference: prod/nautilus_event_trader.pyuntouched Doctrinal reference: prod/docs/SYSTEM_BIBLE_v7.md


0. Executive Summary

GREEN's DolphinActor (the Nautilus Strategy subclass) had 6 algorithmic divergences from BLUE's live production system (nautilus_event_trader.py). These divergences meant GREEN was running a materially different strategy — different risk gates, different hibernate behavior, different MC-Forewarner config.

All 6 gaps have been closed. A 104-test parity suite (test_green_blue_parity.py) now gates future changes.

Result: GREEN now runs the identical NDAlphaEngine algorithm as BLUE, with the same parameters, same signal formula, same risk gates, and same hibernate protection — differing only in (a) Nautilus execution layer (b) V7 RT exit engine (c) output channel isolation.


1. Parity Gap Inventory

Gap 1 — _MC_BASE_CFG (MC-Forewarner config vector)

File: dolphin_actor.py, line ~50 (frozen constant)

The MC-Forewarner assesses risk against a config vector to set _day_mc_scale. If GREEN feeds it different parameters than BLUE, the MC gate opens/closes at different thresholds — silently changing trade sizing and halt behavior.

Parameter Before (GREEN) After (GREEN) BLUE Gold Spec Impact
max_leverage 5.00 8.00 8.00 MC assessed at 5x — would flag GREEN as LOWER risk than it actually runs. Trades BLUE would gate as ORANGE/RED, GREEN would let through.
max_hold_bars 120 250 250 MC model trained on 250-bar holds. Feeding 120 means it underestimates exposure duration → underestimates catastrophic probability.
min_irp_alignment 0.45 0.0 0.0 MC config assumed IRP filter at 0.45 — trades with alignment 0.00.44 would be "unexpected" by the model.

Change applied:

_MC_BASE_CFG = {
    ...
    'max_leverage': 8.00,        # was 5.00
    ...
    'max_hold_bars': 250,        # was 120
    ...
    'min_irp_alignment': 0.0,    # was 0.45
    ...
}

Verification: 30 parameterized tests in TestMCBaseCfgParity assert every key matches BLUE gold values. Three targeted tests (test_max_leverage_is_8x, test_max_hold_bars_is_250, test_min_irp_alignment_is_zero) provide named assertions.


Gap 2 — vol_ok (BTC Volatility Gate)

File: dolphin_actor.py, _on_scan_timer method, line ~654

BLUE uses a rolling 50-bar BTC dvol computation to gate entries during low-volatility periods:

# BLUE (nautilus_event_trader.py:438-453)
btc_prices.append(float(btc_price))
arr = np.array(btc_prices)
dvol = float(np.std(np.diff(arr) / arr[:-1]))
return dvol > VOL_P60_THRESHOLD    # 0.00009868

GREEN previously used a simple warmup counter:

# GREEN before (dolphin_actor.py:654)
vol_regime_ok = (self._bar_idx_today >= 100)

Impact: GREEN would trade in flat, dead markets where BLUE would correctly suppress entries. Conversely, during the first 100 bars of a volatile day, GREEN would suppress entries while BLUE would allow them.

Change applied:

  1. New module-level constants (lines 7073):

    BTC_VOL_WINDOW = 50
    VOL_P60_THRESHOLD = 0.00009868
    
  2. New __init__ field (line 146):

    self.btc_prices: deque = deque(maxlen=BTC_VOL_WINDOW + 2)
    
  3. New method _compute_vol_ok(self, scan) (line 918):

    def _compute_vol_ok(self, scan: dict) -> bool:
        assets = scan.get('assets', [])
        prices = scan.get('asset_prices', [])
        if not assets or not prices:
            return True
        prices_dict = dict(zip(assets, prices))
        btc_price = prices_dict.get('BTCUSDT')
        if btc_price is None:
            return True
        self.btc_prices.append(float(btc_price))
        if len(self.btc_prices) < BTC_VOL_WINDOW:
            return True
        arr = np.array(self.btc_prices)
        dvol = float(np.std(np.diff(arr) / arr[:-1]))
        return dvol > VOL_P60_THRESHOLD
    
  4. Call site changed (line 667):

    # Before:
    vol_regime_ok = (self._bar_idx_today >= 100)
    # After:
    vol_regime_ok = self._compute_vol_ok(scan)
    

Formula parity: np.std(np.diff(arr) / arr[:-1]) computes the standard deviation of BTC bar-to-bar returns over the last 50 bars. This is identical to BLUE's _compute_vol_ok in nautilus_event_trader.py:438-453.

Edge cases preserved:

  • < 50 prices collected → returns True (insufficient data, don't block)
  • No BTCUSDT in scan → returns True
  • Empty scan → returns True

Verification: 8 tests in TestVolOkParity.


Gap 3 — ALGO_VERSION (Lineage Tracking)

File: dolphin_actor.py, line 70

BLUE tags every ENTRY and EXIT log with [v2_gold_fix_v50-v750] for post-hoc analysis and data-science queries. GREEN had no versioning at all.

Change applied:

  1. New module-level constant:

    ALGO_VERSION = "v2_gold_fix_v50-v750"
    
  2. ENTRY log (line 711):

    self.log.info(f"ENTRY: {_entry} [{ALGO_VERSION}]")
    
  3. EXIT log (line 727):

    self.log.info(f"EXIT: {_exit} [{ALGO_VERSION}]")
    

Verification: 3 tests in TestAlgoVersion.


Gap 4 — Hibernate Protection (Per-Bucket SL)

File: dolphin_actor.py, _on_scan_timer posture sync block (lines 609639)

BLUE arms a per-bucket TP+SL when HIBERNATE is declared while a position is open, instead of force-closing via HIBERNATE_HALT:

# BLUE (nautilus_event_trader.py:333-363)
if posture_now == 'HIBERNATE' and position is not None:
    bucket = bucket_assignments.get(pos.asset, 'default')
    sl_pct = _BUCKET_SL_PCT[bucket]
    em_state['stop_pct_override'] = sl_pct
    _hibernate_protect_active = pos.trade_id
    # _day_posture stays at prev value — no HIBERNATE_HALT fires

GREEN previously just set regime_dd_halt = True and let the engine force-close with HIBERNATE_HALT on the next bar — losing the per-bucket precision.

Change applied:

  1. New module-level constant (lines 7582):

    _BUCKET_SL_PCT: dict = {
        0: 0.015,   # Low-vol high-corr nano-cap
        1: 0.012,   # Med-vol low-corr mid-price (XRP/XLM class)
        2: 0.015,   # Mega-cap BTC/ETH — default
        3: 0.025,   # High-vol mid-corr STAR bucket (ENJ/ADA/DOGE)
        4: 0.008,   # Worst bucket (BNB/LTC/LINK) — cut fast
        5: 0.018,   # High-vol low-corr micro-price (ATOM/TRX class)
        6: 0.030,   # Extreme-vol mid-corr (FET/ZRX)
        'default': 0.015,
    }
    
  2. New __init__ fields (lines 147148):

    self._bucket_assignments: dict = {}
    self._hibernate_protect_active: str | None = None
    
  3. New method _load_bucket_assignments() (line 941): loads KMeans bucket map from adaptive_exit/models/bucket_assignments.pkl.

  4. New method _hibernate_protect_position() (line 956): arms per-bucket stop_pct_override on the exit_manager, sets _hibernate_protect_active.

  5. Posture sync block rewritten (lines 609632) — mirrors BLUE's exact logic:

    • HIBERNATE + open position + no protect active → _hibernate_protect_position() (arms TP+SL)
    • HIBERNATE + no position → _day_posture = 'HIBERNATE' (HALT fires normally)
    • Non-HIBERNATE + protect was active → clear protect mode
    • Non-HIBERNATE + no protect → just lift halt
  6. Exit re-labeling (lines 713727): when a hibernate-protected trade exits:

    • FIXED_TP → HIBERNATE_TP
    • STOP_LOSS → HIBERNATE_SL
    • MAX_HOLD → HIBERNATE_MAXHOLD
    • Then finalize posture to HIBERNATE (or note recovery)

Behavioral difference from before:

Scenario Before (GREEN) After (GREEN) BLUE
HIBERNATE with open B3 position HIBERNATE_HALT (force-close at market) FIXED_TP=0.95% or SL=2.5% FIXED_TP=0.95% or SL=2.5%
HIBERNATE with open B4 position HIBERNATE_HALT (force-close) SL=0.8% (cut fast) SL=0.8% (cut fast)
HIBERNATE, no position regime_dd_halt=True regime_dd_halt=True regime_dd_halt=True

Verification: 7 tests in TestHibernateProtectionParity + 10 tests in TestBucketSlPctParity.


Gap 5 — _load_bucket_assignments() (Bucket Map Loading)

File: dolphin_actor.py, line 941

GREEN had no bucket loading. BLUE loads from adaptive_exit/models/bucket_assignments.pkl to route per-bucket SL levels during hibernate protection.

Change applied: New method + call in on_start() (line 412).

Graceful degradation: if .pkl is absent or corrupted, logs a warning and falls back to _BUCKET_SL_PCT['default'] (1.5%).


Gap 6 — from collections import deque (Missing Import)

File: dolphin_actor.py, line 6

The btc_prices deque requires deque from collections. The original import line only had namedtuple.

Change applied: from collections import namedtuplefrom collections import deque, namedtuple


2. Complete Diff Summary (per-file)

nautilus_dolphin/nautilus_dolphin/nautilus/dolphin_actor.py

Total lines: 1763 (was ~1649 before changes; +114 net lines)

Location Change Type Description
Line 6 Import fix Added deque to collections import
Lines 6783 New constants ALGO_VERSION, BTC_VOL_WINDOW, VOL_P60_THRESHOLD, _BUCKET_SL_PCT
Line 70 New ALGO_VERSION = "v2_gold_fix_v50-v750"
Line 72 New BTC_VOL_WINDOW = 50
Line 73 New VOL_P60_THRESHOLD = 0.00009868
Lines 7582 New _BUCKET_SL_PCT dict (7 buckets + default)
Line 146 New field self.btc_prices: deque
Line 147 New field self._bucket_assignments: dict
Line 148 New field self._hibernate_protect_active: str | None
Line 412 New call self._load_bucket_assignments() in on_start()
Line 55 MC cfg fix max_leverage: 5.00 → 8.00
Line 58 MC cfg fix max_hold_bars: 120 → 250
Line 63 MC cfg fix min_irp_alignment: 0.45 → 0.0
Lines 609632 Rewritten Posture sync block — BLUE-parity hibernate protection
Line 620 New call self._hibernate_protect_position()
Line 667 Changed vol_regime_ok = self._compute_vol_ok(scan) (was >= 100)
Line 711 Changed ENTRY log now includes [{ALGO_VERSION}]
Lines 713727 New block Hibernate-protected exit re-labeling
Lines 918939 New method _compute_vol_ok() — rolling 50-bar BTC dvol
Lines 941955 New method _load_bucket_assignments() — pkl loader
Lines 956984 New method _hibernate_protect_position() — per-bucket SL arming

3. Files NOT Modified

File Reason
prod/nautilus_event_trader.py BLUE — do not touch
prod/configs/blue.yml BLUE — do not touch
prod/configs/green.yml Already had correct values (max_leverage=8.0, max_hold_bars=250, min_irp_alignment=0.0, vol_p60=0.00009868, boost_mode=d_liq). No changes needed.
nautilus_dolphin/nautilus_dolphin/nautilus/esf_alpha_orchestrator.py Engine core — shared by both BLUE and GREEN
nautilus_dolphin/nautilus_dolphin/nautilus/proxy_boost_engine.py Engine factory — shared, correct
nautilus_dolphin/nautilus_dolphin/nautilus/adaptive_circuit_breaker.py ACB — shared, correct
nautilus_dolphin/nautilus_dolphin/nautilus/ob_features.py OBF — shared, correct
Any other file Not touched

4. GREEN's Current State vs BLUE

4.1 What's Now Identical

Subsystem Status Notes
vel_div formula PARITY v50 - v750 in both systems. _normalize_ng7_scan() computes identically.
MC_BASE_CFG PARITY All 31 parameters match BLUE gold spec.
Engine kwargs (via green.yml) PARITY 24 engine parameters match BLUE's ENGINE_KWARGS.
D_LIQ engine PARITY Both use create_d_liq_engine()LiquidationGuardEngine(soft=8x, hard=9x).
ACBv6 PARITY Same AdaptiveCircuitBreaker, same NPZ paths, same w750 percentile logic.
OBF PARITY Both use HZOBProvider in live mode, MockOBProvider in backtest. Same gold biases.
vol_ok gate PARITY Rolling 50-bar BTC dvol > VOL_P60_THRESHOLD = 0.00009868.
IRP asset selection PARITY min_irp_alignment=0.0 (no filter) in both.
Direction confirm PARITY dc_lookback_bars=7, dc_min_magnitude_bps=0.75, dc_skip_contradicts=True.
Exit management PARITY fixed_tp_pct=0.0095, stop_pct=1.0, max_hold_bars=250.
Leverage PARITY min=0.5, D_LIQ soft=8.0, abs=9.0. leverage_convexity=3.0.
Position sizing PARITY fraction=0.20, same alpha layers, same bucket boost, same streak mult, same trend mult.
Survival Stack PARITY Both compute Rm → posture via SurvivalStack.
Stablecoin filter PARITY Both block _STABLECOIN_SYMBOLS at entry.
MC-Forewarner PARITY Same models_dir, same base config vector.
Adaptive Exit Engine PARITY Both load and run AE in shadow mode (no real exits).
NG7 normalization PARITY Both promote NG7 nested → flat with v50-v750.
Hibernate protection PARITY Both arm per-bucket TP+SL, re-label exits, finalize posture.
Fee model PARITY sp_maker_entry_rate=0.62, sp_maker_exit_rate=0.50, both use_sp_fees=True.
Seed PARITY Both use seed=42.
Direction PARITY Both short_only.

4.2 What's Intentionally Different (GREEN-specific)

Subsystem Difference Why
Nautilus Strategy GREEN is a Strategy subclass; BLUE is pure Python GREEN runs inside Nautilus BacktestEngine/TradingNode, receives on_bar() callbacks
Nautilus order submission GREEN calls self.submit_order() via _exec_submit_entry/_exit GREEN executes through Nautilus matching engine (paper/sandbox)
V7 RT exit engine GREEN has AlphaExitEngineV7; BLUE does not GREEN-only experiment — vol-normalized MAE + bounce model RT exits at 100ms cadence
RT exit manager GREEN has RealTimeExitManager at 100ms Sub-scan-cadence TP monitoring using live Nautilus bid/ask
Scan timer GREEN uses 500µs Nautilus timer; BLUE uses HZ entry listener directly Architecture difference — Nautilus can't be called from HZ thread
CH output GREEN writes strategy="green"; BLUE writes strategy="blue" Output isolation
HZ output GREEN writes DOLPHIN_PNL_GREEN, DOLPHIN_STATE_GREEN; BLUE writes _BLUE Output isolation
bar_idx sync GREEN inherits bar_idx from BLUE's engine_snapshot Ensures vol_ok warmup is satisfied immediately on GREEN startup
Portfolio capital GREEN reads from Nautilus Portfolio Ledger; BLUE from engine internal Nautilus tracks fills natively
Price feed GREEN uses Nautilus live prices (via cache.quote_tick); BLUE uses eigen scan prices GREEN gets better fill prices from exchange adapter

4.3 Data Sources (Shared)

GREEN reads from the same Hazelcast instance and data paths as BLUE:

Data Source Map/Path
Eigenvalue scans DOLPHIN_FEATURES["latest_eigen_scan"] Same HZ map, same NG8 scanner output
ACB boost/beta DOLPHIN_FEATURES["acb_boost"] Same HZ map, same acb_processor_service.py
ExF macro DOLPHIN_FEATURES["exf_latest"] Same HZ map, same exf_fetcher_flow.py
OBF universe DOLPHIN_FEATURES_SHARD_00..09 Same HZ maps, same obf_universe_service.py
MC-Forewarner DOLPHIN_FEATURES["mc_forewarner_latest"] Same HZ map
Posture DOLPHIN_SAFETY Same HZ CP AtomicReference
Eigenvalues (backfill) /mnt/ng6_data/eigenvalues/ Same NPZ files
Bucket assignments adaptive_exit/models/bucket_assignments.pkl Same pkl file
MC models nautilus_dolphin/mc_results/models/ Same pkl models

5. Test Suite

5.1 File

Path: /mnt/dolphinng5_predict/nautilus_dolphin/tests/test_green_blue_parity.py Lines: 540 Tests: 104

5.2 Test Classes

# Class Tests What It Verifies
1 TestMCBaseCfgParity 33 Every key in _MC_BASE_CFG matches BLUE gold spec (30 parametrized + 3 targeted)
2 TestAlgoVersion 3 ALGO_VERSION is v2_gold_fix_v50-v750, is string, is not v1
3 TestVelDivFormula 5 v50-v750 is correct; v50-v150 is different (v1 bug); NG7 normalize uses v750
4 TestVolOkParity 8 VOL_P60_THRESHOLD=0.00009868; BTC_VOL_WINDOW=50; high vol passes; low vol fails; empty/missing BTC returns True; formula matches BLUE exactly
5 TestBucketSlPctParity 10 All 7 buckets + default present and match BLUE values
6 TestGreenYmlParity 27 All 24 engine params in green.yml match BLUE; direction=short_only; strategy_name=green; HZ maps are GREEN-specific
7 TestHibernateProtectionParity 7 Methods exist; fields initialized; noop when no position; re-label map correct
8 TestE2EReplayParity 2 Full-day replay on known-good date produces finite positive capital and >0 trades
9 TestOutputSeparation 4 strategy_name=green; HZ PNL/STATE maps contain "GREEN"

5.3 Run Command

source /home/dolphin/siloqy_env/bin/activate
cd /mnt/dolphinng5_predict
python -m pytest nautilus_dolphin/tests/test_green_blue_parity.py -v -p no:cacheprovider

5.4 Last Run Result

104 passed in 47.79s

6. What GREEN Does NOT Have (By Design)

These are intentional differences, not gaps:

  1. BLUE's DolphinLiveTrader class — GREEN doesn't need it; it's the BLUE-specific pure-Python daemon. GREEN runs as a Nautilus Strategy.

  2. BLUE's position_state CH table — GREEN doesn't persist open positions to CH for restart recovery. This is a Nautilus-managed lifecycle.

  3. BLUE's exf_listener in the main loop — GREEN gets ACB updates through _on_acb_event (same HZ listener), but doesn't have a separate on_exf_update entry listener. The ACB listener already carries EXF fields.

  4. BLUE's _rollover_day ACB pre-warming — GREEN handles day transitions differently (inside _on_scan_timer and on_bar).

  5. BLUE's capital_checkpoint disk fallback — GREEN uses Nautilus Portfolio as the capital authority in live mode.


7. Migration Checklist for Future Agents

Before modifying GREEN code, verify:

  • Changes to nautilus_dolphin/nautilus_dolphin/nautilus/dolphin_actor.py maintain parity with prod/nautilus_event_trader.py
  • Run python -m pytest nautilus_dolphin/tests/test_green_blue_parity.py -v -p no:cacheprovider — all 104 must pass
  • Changes to shared engine code (esf_alpha_orchestrator, proxy_boost_engine, etc.) affect both BLUE and GREEN
  • GREEN's _MC_BASE_CFG must always match BLUE's MC_BASE_CFG exactly
  • Never modify prod/nautilus_event_trader.py or prod/configs/blue.yml
  • GREEN outputs must always go to DOLPHIN_PNL_GREEN, DOLPHIN_STATE_GREEN, strategy="green" in CH
  • vel_div is always v50 - v750 — never v50 - v150
  • _BUCKET_SL_PCT must stay synchronized with BLUE
  • VOL_P60_THRESHOLD must stay synchronized with BLUE

End of GREEN→BLUE Parity Change Log — 2026-04-19 104/104 parity tests passing. GREEN algorithmic state: FULL PARITY with BLUE v2_gold_fix_v50-v750.