- Created data/examples/download_ml_training_data.rs using reqwest + Databento HTTP API - Downloaded 90 days × 4 symbols (ES.FUT, NQ.FUT, ZN.FUT, 6E.FUT) - Files saved to test_data/real/databento/ml_training/ - Total: 360 files, 15 MB compressed DBN format - Used existing Rust pattern from download_nq_fut.rs - API key loaded from .env file - 100% success rate (360/360 files) - Ready for ML training benchmarks Next: Create simplified training benchmark for RTX 3050 Ti GPU measurements
173 lines
5.7 KiB
Python
173 lines
5.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Verify the downloaded 6E data quality and provide detailed analysis.
|
|
"""
|
|
import os
|
|
import databento as db
|
|
import sys
|
|
from datetime import datetime
|
|
|
|
def main():
|
|
data_file = "/home/jgrusewski/Work/foxhunt/test_data/real/databento/6EH4_ohlcv-1m_2024-01-02_to_2024-01-31.dbn"
|
|
|
|
if not os.path.exists(data_file):
|
|
print(f"ERROR: File not found: {data_file}")
|
|
sys.exit(1)
|
|
|
|
print("=" * 70)
|
|
print("6E (Euro FX Futures) Data Quality Verification")
|
|
print("=" * 70)
|
|
print(f"\nFile: {data_file}")
|
|
print(f"Size: {os.path.getsize(data_file) / (1024*1024):.2f} MB")
|
|
print()
|
|
|
|
# Load data
|
|
store = db.DBNStore.from_file(data_file)
|
|
|
|
# Analyze records
|
|
records = []
|
|
for record in store:
|
|
records.append(record)
|
|
|
|
print(f"Total Records: {len(records):,}")
|
|
print()
|
|
|
|
if not records:
|
|
print("❌ No records found in file!")
|
|
sys.exit(1)
|
|
|
|
# Analyze first and last records
|
|
first = records[0]
|
|
last = records[-1]
|
|
|
|
print("=" * 70)
|
|
print("Time Range")
|
|
print("=" * 70)
|
|
print(f"First Timestamp: {first.ts_event}")
|
|
print(f"Last Timestamp: {last.ts_event}")
|
|
|
|
# Calculate duration
|
|
duration_ns = last.ts_event - first.ts_event
|
|
duration_days = duration_ns / (1e9 * 60 * 60 * 24)
|
|
print(f"Duration: {duration_days:.1f} days")
|
|
print()
|
|
|
|
# Price analysis
|
|
print("=" * 70)
|
|
print("Price Analysis")
|
|
print("=" * 70)
|
|
|
|
# Extract OHLCV data (prices in fixed-point, need to divide by 1e9)
|
|
opens = [r.open / 1e9 for r in records if hasattr(r, 'open')]
|
|
highs = [r.high / 1e9 for r in records if hasattr(r, 'high')]
|
|
lows = [r.low / 1e9 for r in records if hasattr(r, 'low')]
|
|
closes = [r.close / 1e9 for r in records if hasattr(r, 'close')]
|
|
volumes = [r.volume for r in records if hasattr(r, 'volume')]
|
|
|
|
if closes:
|
|
print(f"First Bar:")
|
|
print(f" O: {opens[0]:.5f} H: {highs[0]:.5f} L: {lows[0]:.5f} C: {closes[0]:.5f} V: {volumes[0]:,}")
|
|
print()
|
|
print(f"Last Bar:")
|
|
print(f" O: {opens[-1]:.5f} H: {highs[-1]:.5f} L: {lows[-1]:.5f} C: {closes[-1]:.5f} V: {volumes[-1]:,}")
|
|
print()
|
|
|
|
min_price = min(lows)
|
|
max_price = max(highs)
|
|
avg_price = sum(closes) / len(closes)
|
|
total_volume = sum(volumes)
|
|
avg_volume = total_volume / len(volumes)
|
|
|
|
print(f"Price Range:")
|
|
print(f" Min: {min_price:.5f}")
|
|
print(f" Max: {max_price:.5f}")
|
|
print(f" Average: {avg_price:.5f}")
|
|
print()
|
|
|
|
print(f"Volume:")
|
|
print(f" Total: {total_volume:,}")
|
|
print(f" Average: {avg_volume:,.0f} per bar")
|
|
print()
|
|
|
|
# EUR/USD sanity check (should be around 1.05-1.15)
|
|
if 1.00 < avg_price < 1.20:
|
|
print("✅ Prices look reasonable for EUR/USD")
|
|
else:
|
|
print(f"⚠️ WARNING: Unusual price range for EUR/USD")
|
|
print(f" Expected: 1.05-1.15, Got: {avg_price:.5f}")
|
|
print()
|
|
|
|
# Data quality checks
|
|
print("=" * 70)
|
|
print("Data Quality Checks")
|
|
print("=" * 70)
|
|
|
|
# Check for gaps (bars should be 1 minute apart)
|
|
gaps = []
|
|
for i in range(1, min(100, len(records))): # Check first 100 bars
|
|
time_diff = (records[i].ts_event - records[i-1].ts_event) / 1e9 / 60 # in minutes
|
|
if time_diff > 5: # More than 5 minutes
|
|
gaps.append((i, time_diff))
|
|
|
|
if gaps:
|
|
print(f"⚠️ Found {len(gaps)} gaps in first 100 bars:")
|
|
for idx, gap_minutes in gaps[:5]: # Show first 5
|
|
print(f" Bar {idx}: {gap_minutes:.1f} minute gap")
|
|
if len(gaps) > 5:
|
|
print(f" ... and {len(gaps) - 5} more")
|
|
else:
|
|
print("✅ No significant gaps detected in first 100 bars")
|
|
print()
|
|
|
|
# Check for zero volume bars
|
|
zero_volume_count = sum(1 for v in volumes if v == 0)
|
|
if zero_volume_count > 0:
|
|
pct = 100 * zero_volume_count / len(volumes)
|
|
print(f"⚠️ {zero_volume_count:,} bars ({pct:.1f}%) have zero volume")
|
|
else:
|
|
print("✅ All bars have non-zero volume")
|
|
print()
|
|
|
|
# Check for invalid prices (OHLC relationship)
|
|
invalid_bars = []
|
|
for i, r in enumerate(records[:1000]): # Check first 1000
|
|
if hasattr(r, 'open') and hasattr(r, 'high') and hasattr(r, 'low') and hasattr(r, 'close'):
|
|
o, h, l, c = r.open/1e9, r.high/1e9, r.low/1e9, r.close/1e9
|
|
if not (l <= o <= h and l <= c <= h):
|
|
invalid_bars.append(i)
|
|
|
|
if invalid_bars:
|
|
print(f"⚠️ {len(invalid_bars)} bars have invalid OHLC relationships")
|
|
else:
|
|
print("✅ All sampled bars have valid OHLC relationships (Low ≤ Open,Close ≤ High)")
|
|
print()
|
|
|
|
# Sample data display
|
|
print("=" * 70)
|
|
print("Sample Data (First 5 Bars)")
|
|
print("=" * 70)
|
|
print(f"{'Timestamp':<28} {'Open':>10} {'High':>10} {'Low':>10} {'Close':>10} {'Volume':>10}")
|
|
print("-" * 70)
|
|
for i in range(min(5, len(records))):
|
|
r = records[i]
|
|
if hasattr(r, 'open'):
|
|
print(f"{str(r.ts_event):<28} {r.open/1e9:>10.5f} {r.high/1e9:>10.5f} "
|
|
f"{r.low/1e9:>10.5f} {r.close/1e9:>10.5f} {r.volume:>10,}")
|
|
print()
|
|
|
|
# Final summary
|
|
print("=" * 70)
|
|
print("SUMMARY")
|
|
print("=" * 70)
|
|
print(f"✅ Successfully downloaded 6EH4 (Euro FX March 2024)")
|
|
print(f"✅ {len(records):,} OHLCV-1m bars spanning {duration_days:.1f} days")
|
|
print(f"✅ Price range: {min_price:.5f} - {max_price:.5f} (typical EUR/USD range)")
|
|
print(f"✅ Total volume: {total_volume:,} contracts")
|
|
print(f"✅ File size: {os.path.getsize(data_file) / (1024*1024):.2f} MB")
|
|
print(f"✅ Cost: $0.1093")
|
|
print()
|
|
print("🎯 Data is ready for backtesting!")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|