- Add advanced metrics dashboard with trade analytics - Add new trading components (EntryTypeAnalysis, MultiDayPositionTracker, NewsEventTracker, etc.) - Add strategy mode selector and trend confirmation - Add risk automation panel and slippage correlation analysis - Add daily trading plan enhancements with modal components - Add custom hooks (useApi, useLocalStorage, useAdvancedTradeMetrics) - Add broker service integration and trading API - Add test setup and vitest configuration - Include parquet data files for live market data - Add comprehensive documentation in docs/ folder
154 lines
4.9 KiB
Python
154 lines
4.9 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Generate realistic historical OHLCV data for XAUUSD (Gold vs USD)
|
|
Saves data as parquet files in the live store format for testing.
|
|
"""
|
|
|
|
import os
|
|
import random
|
|
import math
|
|
from datetime import datetime, timedelta
|
|
from typing import List, Dict, Any
|
|
import pyarrow as pa
|
|
import pyarrow.parquet as pq
|
|
|
|
|
|
def generate_gold_prices(start_date: datetime, end_date: datetime, base_price: float = 2000.0) -> List[Dict[str, Any]]:
|
|
"""
|
|
Generate realistic 1-minute OHLCV data for gold prices.
|
|
|
|
Gold price characteristics:
|
|
- Base price around $2000/oz
|
|
- Daily volatility ~0.5-1.5%
|
|
- Higher volatility during market hours (London/New York overlap)
|
|
- Weekend gaps
|
|
- Trend following with mean reversion
|
|
"""
|
|
|
|
data = []
|
|
current_price = base_price
|
|
trend = 0.0 # Trend direction (-1 to 1)
|
|
volatility = 0.008 # Base volatility (0.8%)
|
|
|
|
current = start_date
|
|
while current <= end_date:
|
|
# Skip weekends (Saturday=5, Sunday=6)
|
|
if current.weekday() >= 5:
|
|
current += timedelta(minutes=1)
|
|
continue
|
|
|
|
# Market hours: 00:00-23:59 UTC (24/7 for forex, but lower volume on weekends)
|
|
hour = current.hour
|
|
|
|
# Adjust volatility based on market session
|
|
# London: 08:00-16:00 UTC, New York: 14:30-21:00 UTC
|
|
# Overlap (high volume): 14:30-16:00 UTC
|
|
if 14 <= hour < 16:
|
|
session_volatility = volatility * 1.5 # Higher volatility during overlap
|
|
elif 8 <= hour < 21:
|
|
session_volatility = volatility * 1.2 # Active session
|
|
else:
|
|
session_volatility = volatility * 0.7 # Low volume
|
|
|
|
# Random walk with trend and mean reversion
|
|
trend_change = random.gauss(0, 0.001) # Slow trend changes
|
|
trend = max(-0.5, min(0.5, trend + trend_change))
|
|
|
|
# Mean reversion to base price
|
|
reversion = (base_price - current_price) / base_price * 0.001
|
|
|
|
# Price change
|
|
price_change_pct = random.gauss(trend * 0.0001 + reversion, session_volatility)
|
|
price_change = current_price * price_change_pct
|
|
new_price = current_price + price_change
|
|
|
|
# Ensure reasonable bounds
|
|
new_price = max(1500, min(3000, new_price))
|
|
|
|
# Generate OHLC for 1-minute candle
|
|
high = new_price + abs(random.gauss(0, new_price * session_volatility * 0.5))
|
|
low = new_price - abs(random.gauss(0, new_price * session_volatility * 0.5))
|
|
open_price = current_price + random.gauss(0, new_price * session_volatility * 0.3)
|
|
close_price = new_price
|
|
|
|
# Ensure OHLC relationships
|
|
high = max(high, open_price, close_price)
|
|
low = min(low, open_price, close_price)
|
|
|
|
# Volume (simulated, higher during active hours)
|
|
base_volume = random.randint(50, 200)
|
|
if 14 <= hour < 16:
|
|
volume = int(base_volume * 2.5)
|
|
elif 8 <= hour < 21:
|
|
volume = int(base_volume * 1.8)
|
|
else:
|
|
volume = base_volume
|
|
|
|
bar = {
|
|
"time": int(current.timestamp()),
|
|
"open": round(open_price, 2),
|
|
"high": round(high, 2),
|
|
"low": round(low, 2),
|
|
"close": round(close_price, 2),
|
|
"volume": volume
|
|
}
|
|
|
|
data.append(bar)
|
|
current_price = close_price
|
|
current += timedelta(minutes=1)
|
|
|
|
return data
|
|
|
|
|
|
def save_to_parquet(data: List[Dict[str, Any]], symbol: str, timeframe: str, base_dir: str = "data/parquet/live"):
|
|
"""Save OHLCV data to parquet files partitioned by date."""
|
|
|
|
# Group by date
|
|
from collections import defaultdict
|
|
data_by_date = defaultdict(list)
|
|
|
|
for bar in data:
|
|
dt = datetime.utcfromtimestamp(bar["time"])
|
|
date_str = dt.strftime("%Y-%m-%d")
|
|
data_by_date[date_str].append(bar)
|
|
|
|
# Save each date partition
|
|
for date_str, bars in data_by_date.items():
|
|
# Sort by time
|
|
bars.sort(key=lambda x: x["time"])
|
|
|
|
# Create directory
|
|
partition_dir = os.path.join(base_dir, symbol, timeframe, f"date={date_str}")
|
|
os.makedirs(partition_dir, exist_ok=True)
|
|
|
|
# Convert to pyarrow table
|
|
table = pa.Table.from_pylist(bars)
|
|
|
|
# Save as parquet
|
|
file_path = os.path.join(partition_dir, f"historical-{int(datetime.utcnow().timestamp())}.parquet")
|
|
pq.write_table(table, file_path)
|
|
print(f"Saved {len(bars)} bars for {date_str} to {file_path}")
|
|
|
|
|
|
def main():
|
|
"""Generate 3 months of historical data."""
|
|
|
|
# Generate data for the last 90 days
|
|
end_date = datetime.utcnow()
|
|
start_date = end_date - timedelta(days=90)
|
|
|
|
print(f"Generating historical data from {start_date.date()} to {end_date.date()}")
|
|
|
|
# Generate data
|
|
data = generate_gold_prices(start_date, end_date)
|
|
|
|
print(f"Generated {len(data)} 1-minute bars")
|
|
|
|
# Save to parquet
|
|
save_to_parquet(data, "XAUUSD", "1m")
|
|
|
|
print("Historical data generation complete!")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |