Spaces:
Build error
Build error
| import yfinance as yf | |
| import pandas as pd | |
| import numpy as np | |
| from datetime import datetime, timedelta | |
| class DataProcessor: | |
| def __init__(self): | |
| self.fundamentals_cache = {} | |
| def get_market_data(self, ticker="GC=F", interval="1d"): | |
| """Fetch market data from Yahoo Finance for a given ticker""" | |
| try: | |
| 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") | |
| if interval in ["5m", "15m", "30m", "1h", "4h"]: | |
| period = "60d" | |
| elif interval in ["1d"]: | |
| period = "1y" | |
| elif interval in ["1wk"]: | |
| period = "2y" | |
| else: | |
| period = "max" | |
| ticker_obj = yf.Ticker(ticker) | |
| df = ticker_obj.history(interval=yf_interval, period=period) | |
| if df.empty: | |
| raise ValueError(f"No data retrieved from Yahoo Finance for {ticker}") | |
| df.columns = [col.capitalize() for col in df.columns] | |
| return df | |
| except Exception as e: | |
| print(f"Error fetching data for {ticker}: {e}") | |
| return pd.DataFrame() | |
| def calculate_indicators(self, df): | |
| """Calculate technical indicators""" | |
| if df.empty: | |
| return df | |
| # Simple Moving Averages (5, 20 as requested) | |
| df['SMA_5'] = df['Close'].rolling(window=5).mean() | |
| df['SMA_20'] = df['Close'].rolling(window=20).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 (12, 26, 9) | |
| 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'] | |
| # Split histogram into positive and negative for plotting | |
| df['MACD_bar_positive'] = df['MACD_histogram'].where(df['MACD_histogram'] > 0, 0) | |
| df['MACD_bar_negative'] = df['MACD_histogram'].where(df['MACD_histogram'] < 0, 0) | |
| # 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'] | |
| # Stochastic Oscillator (14, 3) | |
| low_14 = df['Low'].rolling(window=14).min() | |
| high_14 = df['High'].rolling(window=14).max() | |
| df['%K'] = 100 * (df['Close'] - low_14) / (high_14 - low_14) | |
| df['%D'] = df['%K'].rolling(window=3).mean() | |
| df['%SD'] = df['%D'].rolling(window=3).mean() | |
| df['UL'] = 70 # Upper limit | |
| df['DL'] = 30 # Lower limit | |
| return df | |
| def get_fundamental_data(self, ticker="GC=F"): | |
| """Get fundamental gold market data (now generalized/mocked)""" | |
| try: | |
| if ticker == "BTC-USD": | |
| fundamentals = { | |
| "Crypto Volatility Index": round(np.random.uniform(50, 150), 1), | |
| "Dominance Index": f"{np.random.uniform(40, 60):.2f}%", | |
| "Fear & Greed Index": np.random.choice(["Extreme Fear", "Fear", "Neutral", "Greed", "Extreme Greed"]), | |
| "Hash Rate Trend": np.random.choice(["Increasing", "Stable", "Decreasing"]), | |
| "Institutional Flow (Net)": f"{np.random.uniform(-100, 100):,.0f}M USD", | |
| "Market Sentiment": np.random.choice(["Bullish", "Neutral", "Bearish"]), | |
| } | |
| else: | |
| fundamentals = { | |
| "Gold Strength Index": round(np.random.uniform(30, 80), 1), | |
| "Dollar Index (DXY)": 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"]), | |
| } | |
| 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 | |
| prices = df['Close'].iloc[-lookback:].values | |
| prices = prices.astype(np.float32) | |
| mean = np.mean(prices) | |
| std = np.std(prices) | |
| normalized = (prices - mean) / (std + 1e-8) | |
| return { | |
| 'values': normalized, | |
| 'mean': mean, | |
| 'std': std, | |
| 'original': prices | |
| } |