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.
This commit is contained in:
hjnormey
2026-04-21 16:58:38 +02:00
commit 01c19662cb
643 changed files with 260241 additions and 0 deletions

View File

@@ -0,0 +1,295 @@
"""
56-Day Nautilus DolphinActor Backtest
========================================
Exercises the FULL Nautilus trading core:
- Nautilus BacktestEngine kernel (not just NDAlphaEngine direct)
- DolphinActor.on_bar() → begin_day() → step_bar() cycle
- Nautilus order submission (Strategy A Direct path)
- ACBv6 Hz listener wired inside actor
- MC Forewarner gate via DolphinForewarner
- Capital continuity tracked in memory across 56 days
This is the harness the user asked for: VBT vectors → Nautilus trading core.
Architecture:
Per-day loop:
BacktestEngine.run() with one synthetic midnight bar
→ DolphinActor.on_bar() fires once
→ actor loads vbt_cache_klines/{date}.parquet (replay mode)
→ iterates 1439 rows via NDAlphaEngine.step_bar()
→ submits Nautilus orders per signal (gracefully no-ops for unregistered instruments)
→ NDAlphaEngine.capital is authoritative for P&L
Capital carries forward: next day engine starts with prior day's capital.
Gold reference: ROI=+181.81%, Trades=2155, DD=17.65% (Windows, full stack)
Expected (post-regression): ROI~+54-111%, Trades~1959-2145
"""
import sys
import time
import json
from pathlib import Path
from datetime import datetime, timezone
_PROD_DIR = Path(__file__).resolve().parent
_HCM_DIR = _PROD_DIR.parent
_ND_DIR = _HCM_DIR / 'nautilus_dolphin'
sys.path.insert(0, str(_HCM_DIR))
sys.path.insert(0, str(_ND_DIR))
VBT_KLINES_DIR = _HCM_DIR / 'vbt_cache'
MC_MODELS_DIR = str(_ND_DIR / 'mc_results' / 'models')
RUN_LOGS_DIR = _ND_DIR / 'run_logs'
RUN_LOGS_DIR.mkdir(parents=True, exist_ok=True)
WINDOW_START = '2025-12-31'
WINDOW_END = '2026-02-25'
INITIAL_CAPITAL = 25000.0
# Gold-calibrated vol threshold — must be identical across all codepaths.
VOL_P60_THRESHOLD = 0.00026414
def _safe_capital(val):
if val is None: return 25000.0
try:
import numpy as np
f = float(val)
if not np.isfinite(f): return 25000.0
return f
except:
return 25000.0
# Champion engine config — nested under 'engine' key as DolphinActor expects
CHAMPION_ENGINE_CFG = dict(
boost_mode='d_liq',
vel_div_threshold=-0.02, vel_div_extreme=-0.05,
min_leverage=0.5, max_leverage=5.0, leverage_convexity=3.0,
fraction=0.20, fixed_tp_pct=0.0095, stop_pct=1.0, max_hold_bars=120,
use_direction_confirm=True, dc_lookback_bars=7, dc_min_magnitude_bps=0.75,
dc_skip_contradicts=True, dc_leverage_boost=1.0, dc_leverage_reduce=0.5,
use_asset_selection=True, min_irp_alignment=0.0, # gold spec: no IRP filter
use_sp_fees=True, use_sp_slippage=True,
sp_maker_entry_rate=0.62, sp_maker_exit_rate=0.50,
use_ob_edge=True, ob_edge_bps=5.0, ob_confirm_rate=0.40,
lookback=100, use_alpha_layers=True, use_dynamic_leverage=True, seed=42,
)
MC_BASE_CFG = {
'trial_id': 0, 'vel_div_threshold': -0.020, 'vel_div_extreme': -0.050,
'use_direction_confirm': True, 'dc_lookback_bars': 7,
'dc_min_magnitude_bps': 0.75, 'dc_skip_contradicts': True,
'dc_leverage_boost': 1.00, 'dc_leverage_reduce': 0.50,
'vd_trend_lookback': 10, 'min_leverage': 0.50, 'max_leverage': 5.00,
'leverage_convexity': 3.00, 'fraction': 0.20, 'use_alpha_layers': True,
'use_dynamic_leverage': True, 'fixed_tp_pct': 0.0095, 'stop_pct': 1.00,
'max_hold_bars': 120, 'use_sp_fees': True, 'use_sp_slippage': True,
'sp_maker_entry_rate': 0.62, 'sp_maker_exit_rate': 0.50,
'use_ob_edge': True, 'ob_edge_bps': 5.00, 'ob_confirm_rate': 0.40,
'ob_imbalance_bias': -0.09, 'ob_depth_scale': 1.00,
'use_asset_selection': True, 'min_irp_alignment': 0.0, 'lookback': 100, # gold spec
'acb_beta_high': 0.80, 'acb_beta_low': 0.20, 'acb_w750_threshold_pct': 60,
}
def get_parquet_files():
all_pq = sorted(VBT_KLINES_DIR.glob('*.parquet'))
return [p for p in all_pq
if 'catalog' not in str(p)
and WINDOW_START <= p.stem <= WINDOW_END]
def run_one_day_nautilus(run_date: str, initial_capital: float, vol_p60: float, preload_dates: list, assets: list) -> dict:
"""Run one day through Nautilus BacktestEngine + DolphinActor."""
from nautilus_trader.backtest.engine import BacktestEngine, BacktestEngineConfig
from nautilus_trader.model.identifiers import Venue
from nautilus_trader.model.data import Bar, BarType
from nautilus_trader.model.objects import Price, Quantity, Money, Currency
from nautilus_trader.model.enums import OmsType, AccountType
from nautilus_trader.core.datetime import dt_to_unix_nanos
from nautilus_trader.test_kit.providers import TestInstrumentProvider
from nautilus_dolphin.nautilus.dolphin_actor import DolphinActor
BAR_TYPE_STR = 'BTCUSDT.BINANCE-5-SECOND-LAST-EXTERNAL'
actor_cfg = {
'engine': dict(CHAMPION_ENGINE_CFG),
'paper_trade': {'initial_capital': initial_capital},
'posture_override': 'APEX',
'live_mode': False, # replay mode: actor loads parquet internally
'run_date': run_date, # tells _run_replay_day() which day to process
'bar_type': BAR_TYPE_STR,
'mc_models_dir': MC_MODELS_DIR,
'mc_base_cfg': MC_BASE_CFG,
'venue': 'BINANCE',
'vol_p60': vol_p60,
'acb_preload_dates': preload_dates,
'assets': assets,
'parquet_dir': 'vbt_cache',
}
actor_cfg['engine']['initial_capital'] = initial_capital
be_cfg = BacktestEngineConfig(trader_id='DOLPHIN-NAUTILUS-BT-001')
engine = BacktestEngine(config=be_cfg)
actor = DolphinActor(config=actor_cfg)
engine.add_strategy(actor)
venue = Venue('BINANCE')
usdt = Currency.from_str('USDT')
engine.add_venue(
venue=venue,
oms_type=OmsType.HEDGING,
account_type=AccountType.MARGIN,
base_currency=usdt,
starting_balances=[Money(str(_safe_capital(initial_capital)), usdt)],
)
# Register BTCUSDT instrument (matching parquet asset naming exactly)
instrument = TestInstrumentProvider.default_fx_ccy('BTCUSDT', venue)
engine.add_instrument(instrument)
# One synthetic midnight bar — triggers DolphinActor.on_bar() once
bar_type = BarType.from_str(BAR_TYPE_STR)
dt_event = datetime.strptime(run_date, '%Y-%m-%d').replace(
hour=0, minute=0, second=5, tzinfo=timezone.utc
)
bars = [Bar(
bar_type=bar_type,
open=Price.from_str('10000.00000'),
high=Price.from_str('10000.00000'),
low=Price.from_str('10000.00000'),
close=Price.from_str('10000.00000'),
volume=Quantity.from_str('1'),
ts_event=dt_to_unix_nanos(dt_event),
ts_init=dt_to_unix_nanos(dt_event),
)]
engine.add_data(bars)
t0 = time.time()
try:
engine.run()
except Exception as e:
print(f' [WARN] Engine.run() raised: {e}')
result = {
'date': run_date,
'capital': initial_capital,
'pnl': 0.0,
'trades': 0,
'elapsed_s': round(time.time() - t0, 3),
'stale_state_events': actor._stale_state_events,
}
if actor.engine is not None:
result['capital'] = _safe_capital(getattr(actor.engine, 'capital', initial_capital))
result['pnl'] = result['capital'] - float(initial_capital)
result['trades'] = len(getattr(actor.engine, 'trade_history', []))
# Dispose to free resources
engine.dispose()
return result
def run_backtest():
print('=' * 70)
print('56-DAY NAUTILUS DOLPHIN ACTOR BACKTEST - FULL TRADING CORE')
print(f'Window: {WINDOW_START} -> {WINDOW_END}')
print('=' * 70)
parquet_files = get_parquet_files()
if not parquet_files:
print(f'ERROR: No parquets in {VBT_KLINES_DIR} for window')
return None
print(f' Parquet files: {len(parquet_files)} ({parquet_files[0].stem} -> {parquet_files[-1].stem})')
print(f' DolphinActor: replay mode, loads parquets from {VBT_KLINES_DIR}')
print(f' Engine: D_LIQ_GOLD (8x soft / 9x hard)')
print(f' MC Forewarner: {MC_MODELS_DIR}')
print()
# 1. Vol threshold — gold standard constant (5y BTC calibration, matches live trader)
import pandas as pd
import numpy as np
vol_p60 = VOL_P60_THRESHOLD
print(f"[*] Vol threshold: {vol_p60:.8f} (gold standard — consistent with live trader)")
# 2. Extract Asset List from first parquet
first_df = pd.read_parquet(parquet_files[0])
all_assets = [c for c in first_df.columns if c not in {
'timestamp', 'scan_number', 'v50_lambda_max_velocity',
'v150_lambda_max_velocity', 'v300_lambda_max_velocity',
'v750_lambda_max_velocity', 'vel_div', 'instability_50', 'instability_150'
}]
print(f"[*] Trading Asset List initialized ({len(all_assets)} assets)")
t_total = time.time()
capital = INITIAL_CAPITAL
daily_caps = []
daily_pnls = []
total_trades_all = 0
all_window_dates = [pf.stem for pf in parquet_files]
print(f' {"Day":<6} {"Date":<12} {"Capital":>12} {"Trades":>7} {"DayPnL":>10} {"Stale":>6}')
print(f' {"-"*6} {"-"*12} {"-"*12} {"-"*7} {"-"*10} {"-"*6}')
from decimal import Decimal
capital_dec = Decimal(str(INITIAL_CAPITAL))
for i, pf in enumerate(parquet_files):
ds = pf.stem
result = run_one_day_nautilus(ds, float(capital_dec), vol_p60, all_window_dates, all_assets)
current_cap_f = result['capital']
day_pnl_f = result['pnl']
day_trades = result['trades']
capital_dec += Decimal(str(round(day_pnl_f, 8)))
total_trades_all += day_trades
daily_caps.append(float(capital_dec))
daily_pnls.append(day_pnl_f)
if i == 0 or i == len(parquet_files) - 1 or (i + 1) % 10 == 0 or abs(day_pnl_f) > 500:
elapsed = time.time() - t_total
print(f' {i+1:<6} {ds:<12} ${float(capital_dec):>11,.2f} {day_trades:>7} '
f'{day_pnl_f:>+10.2f} {result["stale_state_events"]:>6} [{elapsed:.0f}s]')
# == Metrics ============================================================
import numpy as np
elapsed_total = time.time() - t_total
final_capital = float(capital_dec) # Decimal accumulator — correct final value
roi = (final_capital - INITIAL_CAPITAL) / INITIAL_CAPITAL * 100.0
caps_arr = np.array(daily_caps)
roll_max = np.maximum.accumulate(caps_arr)
dd_arr = (roll_max - caps_arr) / roll_max * 100.0
max_dd = float(np.max(dd_arr)) if len(dd_arr) > 0 else 0.0
daily_rets = np.array(daily_pnls) / INITIAL_CAPITAL
sharpe = float(np.mean(daily_rets) / (np.std(daily_rets) + 1e-12) * np.sqrt(252))
print(f'\n {"="*60}')
print(f' RESULT (Nautilus DolphinActor path):')
print(f' ROI={roi:+.2f}% | Trades={total_trades_all} | Capital=${final_capital:,.2f}')
print(f' MaxDD={max_dd:.2f}% | Sharpe={sharpe:.2f}')
print(f' Elapsed: {elapsed_total:.1f}s')
print(f'\n GOLD REF (D_LIQ_GOLD): ROI=+181.81%, T=2155, DD=17.65%')
print(f' REALISTIC (w/ slippage): ROI~+111%, T~1959')
print(f' {"="*60}')
result = dict(
roi=round(roi, 2), trades=total_trades_all,
dd=round(max_dd, 2), sharpe=round(sharpe, 2),
capital=round(final_capital, 2),
window=f'{WINDOW_START}:{WINDOW_END}',
days=len(parquet_files),
elapsed_s=round(elapsed_total, 1),
engine='Nautilus DolphinActor + D_LIQ_GOLD (8x/9x)',
run_ts=datetime.now(timezone.utc).isoformat(),
)
out_path = RUN_LOGS_DIR / f'nautilus_actor_56day_{datetime.now().strftime("%Y%m%d_%H%M%S")}.json'
out_path.write_text(json.dumps(result, indent=2))
print(f' Saved: {out_path}')
return result
if __name__ == '__main__':
run_backtest()