import yfinance as yf import pandas as pd import numpy as np def fetch_data(symbol, period='2y', interval='1d'): """ Fetch historical data for a given symbol. Symbols: BTC-USD, ETH-USD, GC=F (Gold), CL=F (Oil), SI=F (Silver) """ ticker = yf.Ticker(symbol) df = ticker.history(period=period, interval=interval) return df def add_indicators(df): """ Add technical indicators manually to avoid dependency issues with pandas_ta on Python 3.14. """ # EMA for trend df['EMA_20'] = df['Close'].ewm(span=20, adjust=False).mean() df['EMA_50'] = df['Close'].ewm(span=50, adjust=False).mean() # RSI for momentum delta = df['Close'].diff() gain = (delta.where(delta > 0, 0)).rolling(window=14).mean() loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean() # Handle division by zero safely rs = gain / loss.replace(0, np.finfo(float).eps) df['RSI'] = 100 - (100 / (1 + rs)) # MACD exp1 = df['Close'].ewm(span=12, adjust=False).mean() exp2 = df['Close'].ewm(span=26, adjust=False).mean() df['MACD'] = exp1 - exp2 df['MACD_Signal'] = df['MACD'].ewm(span=9, adjust=False).mean() # Bollinger Bands df['BB_Mid'] = df['Close'].rolling(window=20).mean() df['BB_Std'] = df['Close'].rolling(window=20).std() df['BB_Upper'] = df['BB_Mid'] + (df['BB_Std'] * 2) df['BB_Lower'] = df['BB_Mid'] - (df['BB_Std'] * 2) # ATR for volatility 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 = np.max(ranges, axis=1) df['ATR'] = true_range.rolling(window=14).mean() # Simple Candlestick Patterns # Hammer: Small body, long lower shadow, little/no upper shadow body = (df['Close'] - df['Open']).abs() range_ = df['High'] - df['Low'] # Hammer definition: lower shadow > 2 * body, upper shadow < body, body in upper 1/3 of range df['CDL_Hammer'] = (range_ > 3 * body) & \ ((df[['Open', 'Close']].min(axis=1) - df['Low']) > 2 * body) & \ ((df['High'] - df[['Open', 'Close']].max(axis=1)) < body) # Engulfing: Current body completely covers previous body with opposite direction prev_open = df['Open'].shift(1) prev_close = df['Close'].shift(1) # Bullish Engulfing bull_eng = (df['Close'] > df['Open']) & (prev_close < prev_open) & \ (df['Close'] > prev_open) & (df['Open'] < prev_close) # Bearish Engulfing bear_eng = (df['Close'] < df['Open']) & (prev_close > prev_open) & \ (df['Close'] < prev_open) & (df['Open'] > prev_close) df['CDL_Bull_Engulfing'] = bull_eng df['CDL_Bear_Engulfing'] = bear_eng df.dropna(inplace=True) return df def prepare_ml_data(df, window_size=10): """ Prepare sequences for the model. """ # Select numeric features only features = ['Open', 'High', 'Low', 'Close', 'Volume', 'EMA_20', 'EMA_50', 'RSI', 'MACD', 'ATR'] data = df[features].values # Target: Close price of the next day > current Close price target = (df['Close'].shift(-1) > df['Close']).astype(int).values X = [] y = [] for i in range(window_size, len(data) - 1): X.append(data[i-window_size:i]) y.append(target[i]) # Also return the latest window for prediction (we don't have target for this one) latest_window = data[len(data)-window_size:] return np.array(X), np.array(y), latest_window