File size: 3,744 Bytes
03d7347 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | 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
|