#!/usr/bin/env python """ NexQuant Multi-Asset Data Pipeline — Download + Test on expanded universe. Downloads DXY, Gold, S&P 500, Bund, EUR/USD extended history via yfinance. """ from __future__ import annotations import json, sys, time from pathlib import Path import numpy as np import pandas as pd import yfinance as yf sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) DATA_DIR = Path("git_ignore_folder/factor_implementation_source_data") DATA_DIR.mkdir(parents=True, exist_ok=True) # Multi-asset tickers (free via Yahoo Finance) ASSETS = { "EURUSD": "EURUSD=X", "DXY": "DX-Y.NYB", # US Dollar Index "GOLD": "GC=F", # Gold Futures "SPX": "^GSPC", # S&P 500 "BUND": "BUN24-EUX", # German Bund (approximate) "GBPUSD": "GBPUSD=X", "USDJPY": "USDJPY=X", "OIL": "CL=F", # Crude Oil } def download_asset(name: str, ticker: str, period: str = "max") -> pd.DataFrame: print(f" Downloading {name} ({ticker})...") try: data = yf.download(ticker, period=period, progress=False, auto_adjust=True) if data.empty: print(f" Empty — skipping") return None close = data["Close"] if isinstance(close, pd.DataFrame): close = close.iloc[:, 0] close.name = name print(f" {len(close):,} bars ({close.index[0].date()} - {close.index[-1].date()})") return close except Exception as e: print(f" Failed: {e}") return None def main(): print(f"\n{'='*60}") print(" NexQuant Multi-Asset Data Download") print(f"{'='*60}\n") all_data = {} for name, ticker in ASSETS.items(): series = download_asset(name, ticker) if series is not None and len(series) > 100: all_data[name] = series if not all_data: print("No data downloaded!") return # Build combined DataFrame df = pd.DataFrame(all_data).dropna(how="all") print(f"\nCombined data: {len(df):,} daily bars, {len(df.columns)} assets") print(f"Date range: {df.index[0].date()} - {df.index[-1].date()}") # Save to HDF5 h5_path = DATA_DIR / "multi_asset_daily.h5" df.to_hdf(h5_path, key="data", mode="w") print(f"Saved to {h5_path}") # Quick strategy test print(f"\n{'='*60}") print(" Quick Daily Strategy Test on Multi-Asset") print(f"{'='*60}") from rdagent.components.backtesting.vbt_backtest import backtest_signal_risk for asset in df.columns: c = df[asset].dropna() if len(c) < 500: continue # SMA 10/30 f = c.rolling(10).mean() s = c.rolling(30).mean() sig = pd.Series(0.0, index=c.index) sig[f > s] = 1 sig[f < s] = -1 r = backtest_signal_risk(c, sig.fillna(0), txn_cost_bps=2.14, wf_rolling=True) oos = r.get("wf_oos_sharpe_mean") or r.get("oos_sharpe", -999) oos_m = r.get("oos_monthly_return_pct", 0) or 0 status = "✅" if oos > 0 else " " print(f" {asset:<10} SMA10/30: OOS={oos:+8.2f} Mon={oos_m:+6.2f}% {status}") # Also test extended EUR/USD eurusd = df["EURUSD"].dropna() print(f"\n Extended EUR/USD: {len(eurusd):,} bars") c = eurusd f = c.rolling(10).mean() s = c.rolling(30).mean() sig = pd.Series(0.0, index=c.index) sig[f > s] = 1 sig[f < s] = -1 r = backtest_signal_risk(c, sig.fillna(0), txn_cost_bps=2.14, wf_rolling=True) oos = r.get("wf_oos_sharpe_mean") or r.get("oos_sharpe", -999) print(f" SMA10/30 extended: OOS={oos:+8.2f} Mon={r.get('oos_monthly_return_pct',0):+.2f}%") if __name__ == "__main__": main()