feat: Add Phase 4 advanced metrics and components
- 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
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user