Files
foxhunt/scripts/python/data/download_ml_training_data.py
jgrusewski 433af5c25d chore: Major codebase cleanup - remove deprecated files and organize structure
- Docker: Delete 23 deprecated Dockerfiles, fix CI/CD to use Dockerfile.foxhunt-build
- Config: Remove 36 .env files, keep 4 essential, delete config/environments/
- Docs: Archive 614 Wave D files to docs/archive/wave_d/, 95% reduction in root
- Scripts: Delete 56 deprecated scripts, keep 58 production-critical (49% reduction)
- Python: Organize 37 scripts into scripts/python/ subdirectories, delete ml/python/
- Build: Remove 1GB artifacts, delete old venvs, clean Python cache from git
- Migrations: Delete deprecated directory (4,432 lines), remove duplicate database/migrations/
- Infrastructure: Delete deployment/ (61 files), docs/scripts/ (8 files)

Total impact: ~2,500 files cleaned, 750MB+ space freed, zero production impact
All deleted scripts backed up to archives. runpod/ and tests/runpod/ preserved.
data_acquisition_service retained per user request.
2025-10-30 01:02:34 +01:00

367 lines
11 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Download 90 days of real market data from Databento for ML model training.
Downloads OHLCV-1m data for 4 major futures symbols:
- ES.FUT (E-mini S&P 500) - Stock index
- NQ.FUT (E-mini NASDAQ) - Tech index
- ZN.FUT (10-Year Treasury Note) - Fixed income
- 6E.FUT (Euro FX) - Currency
Target: 90 days × 4 symbols = ~180,000 bars
Estimated cost: ~$2.00 (based on Databento pricing)
Usage:
# Set API key
export DATABENTO_API_KEY='your-key-here'
# Run download
python3 download_ml_training_data.py
# Or specify custom date range
python3 download_ml_training_data.py --start-date 2024-01-01 --days 90
"""
import os
import sys
from datetime import datetime, timedelta, timezone
import argparse
import databento as db
# Configuration
API_KEY = os.getenv("DATABENTO_API_KEY")
OUTPUT_DIR = "test_data/real/databento/ml_training"
SCHEMA = "ohlcv-1m"
DATASET = "GLBX.MDP3"
# Symbols to download (futures contracts)
SYMBOLS = {
"ES.FUT": {
"name": "E-mini S&P 500",
"category": "Stock Index",
"description": "Most liquid equity index futures"
},
"NQ.FUT": {
"name": "E-mini NASDAQ-100",
"category": "Stock Index",
"description": "Tech-heavy equity index"
},
"ZN.FUT": {
"name": "10-Year Treasury Note",
"category": "Fixed Income",
"description": "Interest rate sensitivity"
},
"6E.FUT": {
"name": "Euro FX",
"category": "Currency",
"description": "EUR/USD exchange rate"
}
}
def parse_args():
"""Parse command line arguments."""
parser = argparse.ArgumentParser(description="Download ML training data from Databento")
parser.add_argument(
"--start-date",
type=str,
default="2024-01-02",
help="Start date (YYYY-MM-DD, default: 2024-01-02)"
)
parser.add_argument(
"--days",
type=int,
default=90,
help="Number of days to download (default: 90)"
)
parser.add_argument(
"--symbols",
type=str,
nargs="+",
default=list(SYMBOLS.keys()),
help="Symbols to download (default: all 4 symbols)"
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Preview downloads without executing"
)
return parser.parse_args()
def generate_date_range(start_date_str, num_days):
"""Generate list of trading days (excluding weekends)."""
start_date = datetime.strptime(start_date_str, "%Y-%m-%d")
dates = []
current = start_date
while len(dates) < num_days:
# Skip weekends (Saturday=5, Sunday=6)
if current.weekday() < 5:
dates.append(current.strftime("%Y-%m-%d"))
current += timedelta(days=1)
return dates
def estimate_cost(num_days, num_symbols):
"""Estimate download cost based on Databento pricing."""
# Rough estimate: $0.10-0.15 per symbol per day for 1-minute OHLCV
cost_per_symbol_day = 0.12
return num_days * num_symbols * cost_per_symbol_day
def download_symbol_data(client, symbol, date_str, output_dir):
"""Download data for a single symbol and date."""
try:
# Parse date (full trading day UTC)
start_date = datetime.strptime(date_str, "%Y-%m-%d").replace(tzinfo=timezone.utc)
end_date = start_date.replace(hour=23, minute=59, second=59)
# Build output filename
output_file = os.path.join(output_dir, f"{symbol}_{SCHEMA}_{date_str}.dbn")
# Skip if file already exists
if os.path.exists(output_file):
file_size_kb = os.path.getsize(output_file) / 1024
return {
"status": "skipped",
"reason": "File already exists",
"path": output_file,
"size_kb": file_size_kb
}
# Request data
data = client.timeseries.get_range(
dataset=DATASET,
symbols=[symbol],
schema=SCHEMA,
start=start_date.isoformat(),
end=end_date.isoformat(),
)
# Write to file
data.to_file(output_file)
# Verify data
file_size = os.path.getsize(output_file)
file_size_kb = file_size / 1024
try:
store = db.DBNStore.from_file(output_file)
df = store.to_df()
record_count = len(df)
if record_count == 0:
return {
"status": "warning",
"reason": "No records in file (holiday/no trading)",
"path": output_file,
"size_kb": file_size_kb,
"records": 0
}
return {
"status": "success",
"path": output_file,
"size_kb": file_size_kb,
"records": record_count,
"price_min": float(df['close'].min()),
"price_max": float(df['close'].max()),
"volume": int(df['volume'].sum())
}
except Exception as e:
return {
"status": "success",
"path": output_file,
"size_kb": file_size_kb,
"warning": f"Could not verify: {e}"
}
except Exception as e:
return {
"status": "error",
"reason": str(e)
}
def main():
"""Main download orchestration."""
args = parse_args()
print("=" * 80)
print("ML Training Data Download - Databento")
print("=" * 80)
print()
# Validate API key
if not API_KEY:
print("❌ ERROR: DATABENTO_API_KEY not found in environment!")
print()
print("Set it with:")
print(" export DATABENTO_API_KEY='your-key-here'")
print()
print("Get your API key from: https://databento.com/")
sys.exit(1)
# Generate date range
dates = generate_date_range(args.start_date, args.days)
# Estimate cost
estimated_cost = estimate_cost(len(dates), len(args.symbols))
# Preview
print(f"📊 Download Configuration:")
print(f" Start date: {args.start_date}")
print(f" Trading days: {len(dates)}")
print(f" Symbols: {len(args.symbols)} ({', '.join(args.symbols)})")
print(f" Schema: {SCHEMA}")
print(f" Dataset: {DATASET}")
print(f" Output: {OUTPUT_DIR}")
print()
print(f"📦 Total Downloads: {len(dates) * len(args.symbols)} files")
print(f"💰 Estimated Cost: ${estimated_cost:.2f}")
print()
# Symbol details
print("📋 Symbols to Download:")
for symbol in args.symbols:
if symbol in SYMBOLS:
info = SYMBOLS[symbol]
print(f"{symbol}: {info['name']} ({info['category']})")
print(f" {info['description']}")
else:
print(f"{symbol}: Unknown symbol")
print()
# Dry run exit
if args.dry_run:
print("🔍 DRY RUN: Preview complete. Add --no-dry-run to execute.")
print()
print("First 5 dates to download:")
for date in dates[:5]:
print(f"{date}")
print(f" ... ({len(dates) - 5} more dates)")
return
# Confirm before proceeding
print("⚠️ This will download data and incur costs (~${:.2f})".format(estimated_cost))
response = input("Proceed with download? (yes/no): ")
if response.lower() not in ["yes", "y"]:
print("Download cancelled.")
sys.exit(0)
print()
# Create output directory
os.makedirs(OUTPUT_DIR, exist_ok=True)
# Initialize Databento client
try:
client = db.Historical(API_KEY)
print("✅ Databento client initialized")
print()
except Exception as e:
print(f"❌ Failed to initialize Databento client: {e}")
sys.exit(1)
# Track progress
results = {
"success": [],
"skipped": [],
"warning": [],
"error": []
}
total_records = 0
total_size_kb = 0
# Download all combinations
total_downloads = len(args.symbols) * len(dates)
current_download = 0
for symbol in args.symbols:
print("-" * 80)
print(f"📥 Downloading: {symbol}")
if symbol in SYMBOLS:
print(f" {SYMBOLS[symbol]['name']} - {SYMBOLS[symbol]['category']}")
print("-" * 80)
print()
for date_str in dates:
current_download += 1
progress = (current_download / total_downloads) * 100
print(f"[{current_download}/{total_downloads} - {progress:.1f}%] {symbol} @ {date_str}...", end=" ")
result = download_symbol_data(client, symbol, date_str, OUTPUT_DIR)
status = result["status"]
results[status].append((symbol, date_str, result))
if status == "success":
records = result.get("records", 0)
size_kb = result.get("size_kb", 0)
total_records += records
total_size_kb += size_kb
print(f"{records} bars, {size_kb:.1f} KB")
elif status == "skipped":
print(f"⏭️ {result['reason']}")
elif status == "warning":
print(f"⚠️ {result['reason']}")
elif status == "error":
print(f"{result['reason']}")
print()
# Summary
print()
print("=" * 80)
print("📊 DOWNLOAD SUMMARY")
print("=" * 80)
print()
print(f"✅ Successful: {len(results['success'])}")
print(f"⏭️ Skipped: {len(results['skipped'])}")
print(f"⚠️ Warnings: {len(results['warning'])}")
print(f"❌ Errors: {len(results['error'])}")
print()
print(f"📊 Total Records: {total_records:,} bars")
print(f"💾 Total Size: {total_size_kb:,.1f} KB ({total_size_kb/1024:.1f} MB)")
print(f"💰 Estimated Cost: ${estimated_cost:.2f}")
print()
if results['error']:
print("❌ ERRORS:")
for symbol, date, result in results['error'][:10]:
print(f"{symbol} @ {date}: {result['reason']}")
if len(results['error']) > 10:
print(f" ... and {len(results['error']) - 10} more")
print()
# Success criteria
success_rate = len(results['success']) / total_downloads * 100 if total_downloads > 0 else 0
print("📋 NEXT STEPS:")
print("1. Run ML readiness validation with new data:")
print(" cargo test -p ml --test ml_readiness_validation_tests")
print()
print("2. Run training time benchmarks:")
print(" python3 benchmark_training_time.py")
print()
print("3. Calculate realistic training timeline from benchmarks")
print()
if success_rate >= 80:
print(f"✅ SUCCESS: Downloaded {success_rate:.1f}% of requested data!")
print(f" Ready for ML training benchmarks on RTX 3050 Ti")
elif success_rate >= 50:
print(f"⚠️ PARTIAL SUCCESS: Downloaded {success_rate:.1f}% of data")
print(f" May be sufficient for benchmarking, but consider re-downloading missing files")
else:
print(f"❌ ERROR: Only downloaded {success_rate:.1f}% of data")
print(f" Check errors above and retry")
sys.exit(1)
if __name__ == "__main__":
main()