import yfinance as yf import pandas as pd import numpy as np from datetime import datetime, timedelta class DataProcessor: def __init__(self): self.ticker = "GC=F" self.fundamentals_cache = {} def get_gold_data(self, interval="1d", period="max"): """Fetch gold futures data from Yahoo Finance""" try: # Map internal intervals to yfinance format interval_map = { "5m": "5m", "15m": "15m", "30m": "30m", "1h": "60m", "4h": "240m", "1d": "1d", "1wk": "1wk", "1mo": "1mo", "3mo": "3mo" } yf_interval = interval_map.get(interval, "1d") # Determine appropriate period based on interval if interval in ["5m", "15m", "30m", "1h", "4h"]: period = "60d" # Intraday data limited to 60 days elif interval in ["1d"]: period = "1y" elif interval in ["1wk"]: period = "2y" else: period = "max" ticker = yf.Ticker(self.ticker) df = ticker.history(interval=yf_interval, period=period) if df.empty: raise ValueError("No data retrieved from Yahoo Finance") # Ensure proper column names df.columns = [col.capitalize() for col in df.columns] return df except Exception as e: print(f"Error fetching data: {e}") return pd.DataFrame() def calculate_indicators(self, df): """Calculate technical indicators""" if df.empty: return df # Simple Moving Averages df['SMA_20'] = df['Close'].rolling(window=20).mean() df['SMA_50'] = df['Close'].rolling(window=50).mean() # Exponential Moving Averages df['EMA_12'] = df['Close'].ewm(span=12, adjust=False).mean() df['EMA_26'] = df['Close'].ewm(span=26, adjust=False).mean() # MACD df['MACD'] = df['EMA_12'] - df['EMA_26'] df['MACD_signal'] = df['MACD'].ewm(span=9, adjust=False).mean() df['MACD_histogram'] = df['MACD'] - df['MACD_signal'] # RSI delta = df['Close'].diff() gain = (delta.where(delta > 0, 0)).rolling(window=14).mean() loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean() rs = gain / loss df['RSI'] = 100 - (100 / (1 + rs)) # Bollinger Bands df['BB_middle'] = df['Close'].rolling(window=20).mean() bb_std = df['Close'].rolling(window=20).std() df['BB_upper'] = df['BB_middle'] + (bb_std * 2) df['BB_lower'] = df['BB_middle'] - (bb_std * 2) # Average True Range (ATR) high_low = df['High'] - df['Low'] high_close = np.abs(df['High'] - df['Close'].shift()) low_close = np.abs(df['Low'] - df['Close'].shift()) ranges = pd.concat([high_low, high_close, low_close], axis=1) true_range = ranges.max(axis=1) df['ATR'] = true_range.rolling(window=14).mean() # Volume indicators df['Volume_SMA'] = df['Volume'].rolling(window=20).mean() df['Volume_ratio'] = df['Volume'] / df['Volume_SMA'] return df def get_fundamental_data(self): """Get fundamental gold market data""" try: ticker = yf.Ticker(self.ticker) info = ticker.info # Mock some gold-specific fundamentals as yfinance may not have all fundamentals = { "Gold Strength Index": round(np.random.uniform(30, 80), 1), "Dollar Index": round(np.random.uniform(90, 110), 1), "Real Interest Rate": f"{np.random.uniform(-2, 5):.2f}%", "Gold Volatility": f"{np.random.uniform(10, 40):.1f}%", "Commercial Hedgers (Net)": f"{np.random.uniform(-50000, 50000):,.0f}", "Managed Money (Net)": f"{np.random.uniform(-100000, 100000):,.0f}", "Market Sentiment": np.random.choice(["Bullish", "Neutral", "Bearish"]), "Central Bank Demand": np.random.choice(["High", "Medium", "Low"]), "Jewelry Demand Trend": np.random.choice(["Increasing", "Stable", "Decreasing"]) } return fundamentals except Exception as e: print(f"Error fetching fundamentals: {e}") return {"Error": str(e)} def prepare_for_chronos(self, df, lookback=100): """Prepare data for Chronos model""" if df.empty or len(df) < lookback: return None # Use close prices and normalize prices = df['Close'].iloc[-lookback:].values prices = prices.astype(np.float32) # Normalize to help model performance mean = np.mean(prices) std = np.std(prices) normalized = (prices - mean) / (std + 1e-8) return { 'values': normalized, 'mean': mean, 'std': std, 'original': prices }