Files
DOLPHIN/nautilus_dolphin/run_param_sweep.py

171 lines
6.6 KiB
Python
Raw Permalink Normal View History

import sys, time, math, itertools
from pathlib import Path
import numpy as np
import pandas as pd
import gc
sys.path.insert(0, str(Path(__file__).parent))
from nautilus_dolphin.nautilus.alpha_orchestrator import NDAlphaEngine
from nautilus_dolphin.nautilus.adaptive_circuit_breaker import AdaptiveCircuitBreaker
from nautilus_dolphin.nautilus.ob_features import OBFeatureEngine
from nautilus_dolphin.nautilus.ob_provider import MockOBProvider
VBT_DIR = Path(r"C:\Users\Lenovo\Documents\- DOLPHIN NG HD HCM TSF Predict\vbt_cache")
META_COLS = {'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'}
parquet_files = sorted(VBT_DIR.glob("*.parquet"))
parquet_files = [p for p in parquet_files if 'catalog' not in str(p)]
print("Loading data...")
acb = AdaptiveCircuitBreaker()
date_strings = [pf.stem for pf in parquet_files]
acb.preload_w750(date_strings)
all_vols = []
for pf in parquet_files[:2]:
df = pd.read_parquet(pf)
if 'BTCUSDT' not in df.columns: continue
pr = df['BTCUSDT'].values
for i in range(60, len(pr)):
seg = pr[max(0,i-50):i]
if len(seg)<10: continue
diffs = np.zeros(len(seg)-1)
for j in range(len(seg)-1):
if seg[j] > 0: diffs[j] = (seg[j+1]-seg[j])/seg[j]
v = float(np.std(diffs))
if v > 0: all_vols.append(v)
vol_p60 = float(np.percentile(all_vols, 60))
pq_data = {}
for pf in parquet_files:
df = pd.read_parquet(pf)
ac = [c for c in df.columns if c not in META_COLS]
bp = df['BTCUSDT'].values if 'BTCUSDT' in df.columns else None
dv = np.full(len(df), np.nan)
if bp is not None:
for i in range(50, len(bp)):
seg = bp[max(0,i-50):i]
if len(seg)<10: continue
diffs = np.zeros(len(seg)-1)
for j in range(len(seg)-1):
if seg[j] > 0: diffs[j] = (seg[j+1]-seg[j])/seg[j]
dv[i] = float(np.std(diffs))
pq_data[pf.stem] = (df, ac, dv)
VD_THRESH = -0.02; VD_EXTREME = -0.05; CONVEXITY = 3.0
def strength_cubic(vel_div, threshold=-0.02):
if vel_div >= threshold: return 0.0
raw = (threshold - vel_div) / (threshold - VD_EXTREME)
return min(1.0, max(0.0, raw)) ** CONVEXITY
assets = ["BTCUSDT", "ETHUSDT", "BNBUSDT", "SOLUSDT"]
def evaluate_params(tp_bps, dc_lookback, dc_magnitude, min_irp, max_hold):
engine_kwargs = dict(
initial_capital=25000.0, 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=tp_bps/10000.0, stop_pct=1.0, max_hold_bars=max_hold,
use_direction_confirm=True, dc_lookback_bars=dc_lookback, dc_min_magnitude_bps=dc_magnitude,
dc_skip_contradicts=True, dc_leverage_boost=1.0, dc_leverage_reduce=0.5,
use_asset_selection=True, min_irp_alignment=min_irp,
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,
)
mock = MockOBProvider(
imbalance_bias=-0.09, depth_scale=1.0, assets=assets,
imbalance_biases={"BTCUSDT": -0.086, "ETHUSDT": -0.092, "BNBUSDT": +0.20, "SOLUSDT": +0.20},
)
ob_eng = OBFeatureEngine(mock)
ob_eng.preload_date("mock", assets)
engine = NDAlphaEngine(**engine_kwargs)
engine.set_ob_engine(ob_eng)
bar_idx = 0; ph = {}; dstats = []
# We evaluate on half the parquet files (about 25 days) to make the sweep fast enough
for pf in parquet_files[:25]:
ds = pf.stem; cs = engine.capital
engine.regime_direction = -1
engine.regime_dd_halt = False
acb_info = acb.get_dynamic_boost_for_date(ds, ob_engine=ob_eng)
engine.regime_size_mult = acb_info['boost']
df, acols, dvol = pq_data[ds]
bid = 0
for ri in range(len(df)):
row = df.iloc[ri]; vd = row.get("vel_div")
if vd is None or not np.isfinite(vd): bar_idx+=1; bid+=1; continue
prices = {}
for ac in acols:
p = row[ac]
if p and p > 0 and np.isfinite(p):
prices[ac] = float(p)
if ac not in ph: ph[ac] = []
ph[ac].append(float(p))
if len(ph[ac]) > 500: ph[ac] = ph[ac][-200:]
if not prices: bar_idx+=1; bid+=1; continue
vrok = False if bid < 100 else (np.isfinite(dvol[ri]) and dvol[ri] > vol_p60)
if acb_info['beta'] > 0:
ss = strength_cubic(float(vd))
engine.regime_size_mult = acb_info['boost'] * (1.0 + acb_info['beta'] * ss)
engine.process_bar(bar_idx=bar_idx, vel_div=float(vd), prices=prices,
vol_regime_ok=vrok, price_histories=ph)
bar_idx+=1; bid+=1
dstats.append({'date': ds, 'pnl': engine.capital - cs, 'cap': engine.capital})
tr = engine.trade_history
w = [t for t in tr if t.pnl_absolute > 0]; l = [t for t in tr if t.pnl_absolute <= 0]
wr = len(w) / len(tr) * 100 if tr else 0.0
pf = sum(t.pnl_absolute for t in w) / abs(sum(t.pnl_absolute for t in l)) if l else 999
roi = (engine.capital - 25000) / 25000 * 100
del engine
gc.collect()
return roi, wr, pf, len(tr)
print("Starting parameter sweep...")
params = {
'tp_bps': [89, 99, 109],
'dc_lookback': [5, 7, 9],
'dc_magnitude': [0.5, 0.75],
'min_irp': [0.40, 0.45],
'max_hold': [120, 150]
}
keys, values = zip(*params.items())
permutations = [dict(zip(keys, v)) for v in itertools.product(*values)]
print(f"Total combinations: {len(permutations)}")
best_wr = 0
best_roi = 0
for i, p in enumerate(permutations):
try:
t0 = time.time()
roi, wr, pf, trades = evaluate_params(
p['tp_bps'], p['dc_lookback'], p['dc_magnitude'], p['min_irp'], p['max_hold']
)
print(f"[{i+1}/{len(permutations)}] {p} -> ROI: {roi:+.2f}% | WR: {wr:.2f}% | PF: {pf:.2f} | Trades: {trades} [{time.time()-t0:.1f}s]")
if wr > best_wr:
best_wr = wr
print(f" *** NEW BEST WR: {wr:.2f}% ***")
if roi > best_roi:
best_roi = roi
print(f" *** NEW BEST ROI: {roi:+.2f}% ***")
except Exception as e:
print(f"Error on {p}: {e}")