180 lines
6.9 KiB
Python
180 lines
6.9 KiB
Python
|
|
import os
|
||
|
|
import sys
|
||
|
|
import json
|
||
|
|
import logging
|
||
|
|
import gc
|
||
|
|
import math
|
||
|
|
import uuid
|
||
|
|
from pathlib import Path
|
||
|
|
from datetime import datetime, timezone
|
||
|
|
import numpy as np
|
||
|
|
import pandas as pd
|
||
|
|
from tqdm import tqdm
|
||
|
|
|
||
|
|
# --- Project Paths ---
|
||
|
|
BASE_DIR = Path(r"C:\Users\Lenovo\Documents\- DOLPHIN NG HD HCM TSF Predict")
|
||
|
|
sys.path.insert(0, str(BASE_DIR))
|
||
|
|
sys.path.insert(0, str(BASE_DIR / 'nautilus_dolphin'))
|
||
|
|
|
||
|
|
from nautilus_trader.backtest.engine import BacktestEngine, BacktestEngineConfig
|
||
|
|
from nautilus_trader.model.identifiers import Venue, InstrumentId
|
||
|
|
from nautilus_trader.model.currencies import USDT
|
||
|
|
from nautilus_trader.model.objects import Money
|
||
|
|
from nautilus_trader.model.data import BarType, Bar
|
||
|
|
from nautilus_trader.model.enums import AccountType, OmsType
|
||
|
|
from nautilus_trader.model.instruments import Instrument
|
||
|
|
|
||
|
|
from nautilus_dolphin.nautilus.dolphin_actor import DolphinActor
|
||
|
|
from nautilus_dolphin.nautilus.proxy_boost_engine import create_boost_engine
|
||
|
|
|
||
|
|
# --- [GOLD STANDARD REPRO CONFIGURATION] ---
|
||
|
|
NATIVE_GOLD_CFG = dict(
|
||
|
|
initial_capital=25000.0,
|
||
|
|
vel_div_threshold=-0.02, vel_div_extreme=-0.05,
|
||
|
|
min_leverage=0.5, max_leverage=8.0,
|
||
|
|
leverage_convexity=3.0, fraction=0.20,
|
||
|
|
fixed_tp_pct=0.0095, stop_pct=1.0, max_hold_bars=250,
|
||
|
|
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,
|
||
|
|
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,
|
||
|
|
boost_mode='d_liq',
|
||
|
|
)
|
||
|
|
|
||
|
|
# --- Helpers ---
|
||
|
|
from prod.nautilus_native_backtest import _make_instrument, _make_bar, _FEATURE_STORE
|
||
|
|
|
||
|
|
def run_gold_repro():
|
||
|
|
print(f"\n[!] LAUNCHING GOLD REPRO V12 (THE FORTRESS): T=2155 | ROI=181.81% (Target)")
|
||
|
|
|
||
|
|
data_dir = BASE_DIR / "vbt_cache"
|
||
|
|
# Process exactly 56 days as per Gold Standard
|
||
|
|
parquet_files = sorted(data_dir.glob("202*.parquet"))[0:56]
|
||
|
|
|
||
|
|
if not parquet_files:
|
||
|
|
print(f"ERROR: No parquet files found in {data_dir}")
|
||
|
|
return
|
||
|
|
|
||
|
|
venue = Venue("BINANCE")
|
||
|
|
config = BacktestEngineConfig(trader_id="GOLD-REPRO-012")
|
||
|
|
engine = BacktestEngine(config=config)
|
||
|
|
|
||
|
|
# Starting capital
|
||
|
|
engine.add_venue(venue, OmsType.NETTING, AccountType.MARGIN, [Money(25000.0, USDT)])
|
||
|
|
|
||
|
|
actor_cfg = {
|
||
|
|
'initial_capital': 25000.0,
|
||
|
|
'paper_trade': {'initial_capital': 25000.0},
|
||
|
|
'native_mode': True,
|
||
|
|
'registered_assets': [],
|
||
|
|
'engine_cfg': NATIVE_GOLD_CFG
|
||
|
|
}
|
||
|
|
|
||
|
|
_FEATURE_STORE.clear()
|
||
|
|
all_bars = []
|
||
|
|
|
||
|
|
df0 = pd.read_parquet(parquet_files[0])
|
||
|
|
excluded = {'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'}
|
||
|
|
assets = [c for c in df0.columns if c not in excluded]
|
||
|
|
actor_cfg['registered_assets'] = assets
|
||
|
|
|
||
|
|
bar_type_map = {}
|
||
|
|
for sym in assets:
|
||
|
|
inst = _make_instrument(sym, venue)
|
||
|
|
if inst:
|
||
|
|
engine.add_instrument(inst)
|
||
|
|
bt_str = f"{sym}.BINANCE-5-SECOND-LAST-EXTERNAL"
|
||
|
|
bar_type_map[sym] = BarType.from_str(bt_str)
|
||
|
|
|
||
|
|
print(f"[*] Pre-loading {len(parquet_files)} days ({len(assets)} assets)...")
|
||
|
|
global_row_idx = 0
|
||
|
|
|
||
|
|
for pf in tqdm(parquet_files):
|
||
|
|
df = pd.read_parquet(pf)
|
||
|
|
columns = df.columns.tolist()
|
||
|
|
day_str = pf.stem
|
||
|
|
day_dt = datetime.strptime(day_str, '%Y-%m-%d').replace(tzinfo=timezone.utc)
|
||
|
|
day_start_ns = int(day_dt.timestamp() * 1e9)
|
||
|
|
|
||
|
|
vd_idx = columns.index('vel_div') if 'vel_div' in columns else -1
|
||
|
|
v50_idx = columns.index('v50_lambda_max_velocity') if 'v50_lambda_max_velocity' in columns else -1
|
||
|
|
v750_idx = columns.index('v750_lambda_max_velocity') if 'v750_lambda_max_velocity' in columns else -1
|
||
|
|
inst50_idx = columns.index('instability_50') if 'instability_50' in columns else -1
|
||
|
|
asset_indices = {sym: columns.index(sym) for sym in assets if sym in columns}
|
||
|
|
|
||
|
|
data = df.values.tolist()
|
||
|
|
for i, row in enumerate(data):
|
||
|
|
ts_ns = day_start_ns + i * 5 * 1_000_000_000
|
||
|
|
vol_ok = (global_row_idx >= 100)
|
||
|
|
|
||
|
|
# FORTRESS FIX: Sanitize features. Replace NaNs with reasonable defaults.
|
||
|
|
def _safe_float(val, default=0.0):
|
||
|
|
if val is None: return default
|
||
|
|
try:
|
||
|
|
f = float(val)
|
||
|
|
return f if math.isfinite(f) else default
|
||
|
|
except:
|
||
|
|
return default
|
||
|
|
|
||
|
|
_FEATURE_STORE[ts_ns] = {
|
||
|
|
'vel_div': _safe_float(row[vd_idx], 0.0) if vd_idx != -1 else 0.0,
|
||
|
|
'v50': _safe_float(row[v50_idx], 0.0) if v50_idx != -1 else 0.0,
|
||
|
|
'v750': _safe_float(row[v750_idx], 0.0) if v750_idx != -1 else 0.0,
|
||
|
|
'inst50': _safe_float(row[inst50_idx], 0.0) if inst50_idx != -1 else 0.0,
|
||
|
|
'vol_ok': vol_ok,
|
||
|
|
'row_i': i,
|
||
|
|
}
|
||
|
|
|
||
|
|
# Non-BTC assets first
|
||
|
|
for sym in assets:
|
||
|
|
if sym == 'BTCUSDT': continue
|
||
|
|
idx = asset_indices.get(sym)
|
||
|
|
if idx is not None:
|
||
|
|
p = row[idx]
|
||
|
|
if p is not None and p > 0 and math.isfinite(p):
|
||
|
|
bar = _make_bar(bar_type_map[sym], float(p), ts_ns)
|
||
|
|
if bar: all_bars.append(bar)
|
||
|
|
|
||
|
|
# BTC last (The "Heartbeat" of DolphinActor)
|
||
|
|
if 'BTCUSDT' in asset_indices:
|
||
|
|
p = row[asset_indices['BTCUSDT']]
|
||
|
|
if p is not None and p > 0 and math.isfinite(p):
|
||
|
|
bar = _make_bar(bar_type_map['BTCUSDT'], float(p), ts_ns)
|
||
|
|
if bar: all_bars.append(bar)
|
||
|
|
|
||
|
|
global_row_idx += 1
|
||
|
|
del df, data
|
||
|
|
gc.collect()
|
||
|
|
|
||
|
|
print(f"[RAM] Pre-load complete. {len(all_bars):,} bars synthesized.")
|
||
|
|
|
||
|
|
actor = DolphinActor(actor_cfg)
|
||
|
|
engine.add_strategy(actor)
|
||
|
|
|
||
|
|
# Inject all bars into the engine queue
|
||
|
|
engine.add_data(all_bars)
|
||
|
|
|
||
|
|
print(f"[NATIVE] Executing Gold-Repro V12 ({len(all_bars):,} bars)...")
|
||
|
|
t0 = datetime.now()
|
||
|
|
engine.run()
|
||
|
|
dur = (datetime.now() - t0).total_seconds()
|
||
|
|
|
||
|
|
stats = actor.engine.get_performance_summary()
|
||
|
|
print("\n" + "="*72)
|
||
|
|
print(f" GOLD REPRO RESULTS (V12 - FORTRESS)")
|
||
|
|
print(f" ROI : {stats['total_return']*100:+.2f}%")
|
||
|
|
print(f" Trades : {stats['total_trades']}")
|
||
|
|
print(f" Final Capital : ${stats['final_capital']:,.2f}")
|
||
|
|
print(f" Elapsed : {dur/60:.1f} min")
|
||
|
|
print("="*72)
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
os.environ['PYTHONIOENCODING'] = 'utf-8'
|
||
|
|
run_gold_repro()
|