import numpy as np
np.NaN = np.nan # For pandas_ta compatibility
import matplotlib.pyplot as plt
import requests
from datetime import datetime, timedelta
import pandas_ta as ta
import mplfinance as mpf
import xgboost as xgb
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error
import warnings
warnings.filterwarnings(‘ignore’)
class BitcoinTechnicalAnalysisML:
def __init__(self):
“””Set up our tools for predicting Bitcoin prices”””
self.data = None # Where we’ll store Bitcoin info
self.api_url = “https://api.coingecko.com/api/v3/coins/bitcoin/market_chart”
# Our crystal ball: a smart model that learns patterns, using the GPU for speed
self.model = xgb.XGBRegressor(n_estimators=100, tree_method=’hist’, device=’cuda’, random_state=42)
self.scaler = StandardScaler() # A tool to make numbers easier for the model
def fetch_bitcoin_data(self, days=365, vs_currency=’usd’):
“””Step 1: Grab Bitcoin’s price history, like checking a year’s worth of receipts”””
try:
params = {‘vs_currency’: vs_currency, ‘days’: days, ‘interval’: ‘daily’}
response = requests.get(self.api_url, params=params)
response.raise_for_status()
data = response.json()
df = pd.DataFrame({
‘Timestamp’: [x[0] for x in data[‘prices’]],
‘Close’: [x[1] for x in data[‘prices’]],
‘Volume’: [x[1] for x in data[‘total_volumes’]]
})
df[‘Timestamp’] = pd.to_datetime(df[‘Timestamp’], unit=’ms’)
df.set_index(‘Timestamp’, inplace=True)
df[‘High’] = df[‘Close’] * 1.02 # Guess the day’s high (a little above close)
df[‘Low’] = df[‘Close’] * 0.98 # Guess the day’s low (a little below close)
df[‘Open’] = df[‘Close’].shift(1) # Yesterday’s close is today’s open
self.data = df.dropna() # Remove any incomplete days
print(f”We’ve grabbed {len(self.data)} days of Bitcoin prices, from {self.data.index[0].strftime(‘%Y-%m-%d’)} to {self.data.index[-1].strftime(‘%Y-%m-%d’)}—like a year-long diary of Bitcoin’s ups and downs!”)
return self.data
except requests.exceptions.RequestException as e:
print(f”Oops! Couldn’t get the Bitcoin data because: {e}. Maybe the internet’s down?”)
return None
def calculate_indicators(self):
“””Step 2: Add clues to guess where Bitcoin’s price is heading”””
if self.data is None:
print(“Hold on! We need Bitcoin data first. Run fetch_bitcoin_data() to get it.”)
return None
df = self.data.copy()
print(“Now, we’re adding some smart clues—like checking Bitcoin’s mood, speed, and patterns—to help us predict its next move.”)
# Moving averages: Like smoothing out a bumpy road to see the trend
df[‘SMA7’] = ta.sma(df[‘Close’], length=7) # 7-day average
df[‘SMA25’] = ta.sma(df[‘Close’], length=25) # 25-day average
df[‘SMA50’] = ta.sma(df[‘Close’], length=50)
df[‘SMA99’] = ta.sma(df[‘Close’], length=99)
df[‘SMA200’] = ta.sma(df[‘Close’], length=200)
df[‘EMA12’] = ta.ema(df[‘Close’], length=12) # Quick 12-day trend
df[‘EMA26’] = ta.ema(df[‘Close’], length=26) # Slower 26-day trend
df[‘MA111’] = ta.sma(df[‘Close’], length=111)
df[‘MA350x2’] = ta.sma(df[‘Close’], length=350) * 2 # Long-term doubled
macd = ta.macd(df[‘Close’], fast=12, slow=26, signal=9) # Momentum checker
df[‘MACD’] = macd[‘MACD_12_26_9’]
df[‘MACD_Signal’] = macd[‘MACDs_12_26_9’]
df[‘MACD_Hist’] = macd[‘MACDh_12_26_9’]
df[‘SAR’] = ta.psar(df[‘High’], df[‘Low’], df[‘Close’])[‘PSARl_0.02_0.2’] # Trend direction
df[‘RSI’] = ta.rsi(df[‘Close’], length=14) # Is Bitcoin overexcited or sleepy?
stoch = ta.stoch(df[‘High’], df[‘Low’], df[‘Close’], k=14, d=3, smooth_k=3) # Speed gauge
df[‘StochK’] = stoch[‘STOCHk_14_3_3’]
df[‘StochD’] = stoch[‘STOCHd_14_3_3’]
bbands = ta.bbands(df[‘Close’], length=20, std=2) # Price range bands
df[‘BB_Upper’] = bbands[‘BBU_20_2.0’]
df[‘BB_Middle’] = bbands[‘BBM_20_2.0’]
df[‘BB_Lower’] = bbands[‘BBL_20_2.0’]
df[‘CCI’] = ta.cci(df[‘High’], df[‘Low’], df[‘Close’], length=14) # Overbought/oversold
df[‘OBV’] = ta.obv(df[‘Close’], df[‘Volume’]) # Volume trend
df[‘CMF’] = ta.adosc(df[‘High’], df[‘Low’], df[‘Close’], df[‘Volume’], fast=3, slow=10) # Money flow
df[‘ForceIndex’] = df[‘Close’].diff(1) * df[‘Volume’] # Price push
df[‘ForceIndex13’] = ta.ema(df[‘ForceIndex’], length=13)
df[‘ATR’] = ta.atr(df[‘High’], df[‘Low’], df[‘Close’], length=14) # Volatility
recent_high = df[‘High’].iloc[-100:].max() # Last 100 days’ peak
recent_low = df[‘Low’].iloc[-100:].min() # Last 100 days’ dip
df[‘Fib_0’] = recent_low
df[‘Fib_23.6’] = recent_low + 0.236 * (recent_high – recent_low) # Fibonacci levels
df[‘Fib_38.2’] = recent_low + 0.382 * (recent_high – recent_low)
df[‘Fib_50’] = recent_low + 0.5 * (recent_high – recent_low)
df[‘Fib_61.8’] = recent_low + 0.618 * (recent_high – recent_low)
df[‘Fib_100’] = recent_high
self.data = df
print(f”Done! We’ve added {len(df.columns)} clues—like Bitcoin’s mood swings and spending habits—to make our prediction smarter.”)
return df
def prepare_ml_data(self):
“””Step 3: Get our clues ready for the prediction machine”””
if self.data is None:
print(“Oops! We need the clues first. Run calculate_indicators() after fetching data.”)
return None
df = self.data.copy()
print(“We’re setting up the puzzle: tomorrow’s price is what we want to guess, using today’s clues.”)
df[‘Target’] = df[‘Close’].shift(-1) # Tomorrow’s price is our goal
df = df.dropna() # Skip days with missing pieces
features = [‘Open’, ‘High’, ‘Low’, ‘Close’, ‘Volume’, ‘SMA7’, ‘SMA25’, ‘SMA50’, ‘SMA99’, ‘SMA200’,
‘EMA12’, ‘EMA26’, ‘MA111’, ‘MA350x2’, ‘MACD’, ‘MACD_Signal’, ‘MACD_Hist’, ‘SAR’,
‘RSI’, ‘StochK’, ‘StochD’, ‘BB_Upper’, ‘BB_Middle’, ‘BB_Lower’, ‘CCI’, ‘OBV’,
‘CMF’, ‘ForceIndex’, ‘ForceIndex13’, ‘ATR’]
X = df[features] # Our clue pile
y = df[‘Target’] # The answer we’re after
X_scaled = self.scaler.fit_transform(X) # Make clues easier to compare, like putting them all in the same language
print(f”Puzzle ready! We have {len(X)} days to learn from, with {len(features)} clues each—like ingredients for a Bitcoin price recipe.”)
return X_scaled, y, X.index
def train_model(self, test_size=0.2):
“””Step 4: Teach our crystal ball to predict Bitcoin prices”””
X_scaled, y, dates = self.prepare_ml_data()
if X_scaled is None:
return None
X_train, X_test, y_train, y_test, dates_train, dates_test = train_test_split(
X_scaled, y, dates, test_size=test_size, shuffle=False
)
print(f”We’re teaching our prediction machine with {len(X_train)} days of history and testing it on the last {len(X_test)} days—like practicing with old weather forecasts before predicting tomorrow’s rain.”)
self.model.fit(X_train, y_train) # Let the machine learn the patterns
y_pred = self.model.predict(X_test) # Test its guesses
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
print(f”Training done! Our machine’s guesses were off by about ${rmse:.2f} on average (that’s the RMSE). The MSE ({mse:.2f}) is a bigger number showing the total error squared—smaller is better!”)
return X_test, y_test, y_pred, dates_test
def predict_next_day(self):
“””Step 5: Look into the future with our trained crystal ball”””
if self.data is None:
print(“Wait! We need data and clues first. Run the earlier steps.”)
return None
last_data = self.data.tail(1)
features = [‘Open’, ‘High’, ‘Low’, ‘Close’, ‘Volume’, ‘SMA7’, ‘SMA25’, ‘SMA50’, ‘SMA99’, ‘SMA200’,
‘EMA12’, ‘EMA26’, ‘MA111’, ‘MA350x2’, ‘MACD’, ‘MACD_Signal’, ‘MACD_Hist’, ‘SAR’,
‘RSI’, ‘StochK’, ‘StochD’, ‘BB_Upper’, ‘BB_Middle’, ‘BB_Lower’, ‘CCI’, ‘OBV’,
‘CMF’, ‘ForceIndex’, ‘ForceIndex13’, ‘ATR’]
X_last = last_data[features]
X_last_scaled = self.scaler.transform(X_last)
prediction = self.model.predict(X_last_scaled)[0]
last_date = self.data.index[-1]
next_date = last_date + timedelta(days=1)
print(f”Looking at yesterday ({last_date.strftime(‘%Y-%m-%d’)}, price was ${last_data[‘Close’].values[0]:.2f}), our crystal ball says tomorrow ({next_date.strftime(‘%Y-%m-%d’)}) will be ${prediction:.2f}. It’s using all those clues we gathered!”)
return prediction
def plot_predictions(self, X_test, y_test, y_pred, dates_test):
“””Step 6: Draw a picture of our guesses vs. reality”””
plt.figure(figsize=(14, 7))
plt.plot(dates_test, y_test, label=’Actual Price’, color=’blue’)
plt.plot(dates_test, y_pred, label=’Predicted Price’, color=’red’, linestyle=’–‘)
plt.title(‘Bitcoin Price Prediction (Our Smart Guess vs. What Really Happened)’)
plt.xlabel(‘Date’)
plt.ylabel(‘Price (USD)’)
plt.legend()
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
print(“Here’s a picture! The blue line is what Bitcoin actually did in the test days. The red dashed line is what our machine guessed. Closer lines mean better guesses!”)
if __name__ == “__main__”:
print(“Let’s predict Bitcoin’s next price, step-by-step, like baking a cake with a magic recipe!”)
btc = BitcoinTechnicalAnalysisML()
btc.fetch_bitcoin_data(days=365)
btc.calculate_indicators()
result = btc.train_model(test_size=0.2)
if result is not None:
X_test, y_test, y_pred, dates_test = result
btc.plot_predictions(X_test, y_test, y_pred, dates_test)
btc.predict_next_day()