mcdouglasx (OP)
|
 |
May 27, 2025, 12:50:23 AM |
|
Testing Google's AI. I decided to test how good it was at coding, and my overall experience was that it has improved but still leaves much to be desired. However, if you know a bit about programming, it can serve as a fundamental tool to complement ideas.
As a speculative exercise, I created this script for a brief experiment in the forum. The dynamic will be to make our speculative predictions and share their parameters to see, in a way, who was closest to the truth. The sentiment bar remains in the middle by default, meaning there is no sentiment or expectation, this represents a neutral position. The closer you get to the critical area, the more negative your sentiment becomes, indicating a downward trend. Conversely, as you approach the wonderful area, your sentiment becomes increasingly optimistic, translating into an upward-trending prediction. Of course, this is not meant to be taken seriously due to the high volatility of the market, NOR SHOULD IT BE USED AS A TOOL IN A REALISTIC ENVIRONMENT. You simply need to enter the estimated time, the percentage of sentiment used, and share the results. Requirements:- Python pip install yfinance pandas numpy statsmodels scikit-learn arch prophet code:BtcTalk_forecast.py: import tkinter as tk from tkinter import ttk, messagebox import yfinance as yf import pandas as pd import numpy as np from statsmodels.tsa.arima.model import ARIMA from statsmodels.tsa.statespace.sarimax import SARIMAX from sklearn.linear_model import LinearRegression from datetime import datetime, timedelta import threading import warnings
from arch import arch_model
try: from prophet import Prophet PROPHET_AVAILABLE = True except ImportError: PROPHET_AVAILABLE = False print("Warning: The 'prophet' library is not installed. Prophet predictions will not be available.") print("To install it, run: pip install prophet")
warnings.filterwarnings("ignore", category=UserWarning, module='statsmodels') warnings.filterwarnings("ignore", category=DeprecationWarning, module='numpy') warnings.filterwarnings("ignore", category=UserWarning, module='statsmodels.base.model')
class BitcoinPredictor: def __init__(self): self.ticker = "BTC-USD" self.data = None self.MAX_SENTIMENT_IMPACT_PERCENTAGE = 0.30
def fetch_data(self, period="max", interval="1d", progress_callback=None): try: if progress_callback: progress_callback("status", f"Starting historical data download for {self.ticker} (all available)...") progress_callback("progress", 10)
print(f"Fetching {period} data for {self.ticker} from Yahoo Finance...") self.data = yf.download(self.ticker, period=period, interval=interval)
if self.data.empty: if progress_callback: progress_callback("status", "No data obtained. Check connection or ticker.", is_error=True) print("No data obtained. Check ticker or internet connection.") return False
self.data.index = pd.to_datetime(self.data.index) self.data = self.data[~self.data.index.duplicated(keep='first')]
if progress_callback: progress_callback("progress", 100) progress_callback("status", "Data successfully obtained.") print("Data successfully obtained.") return True except Exception as e: if progress_callback: progress_callback("status", f"Error fetching data: {e}", is_error=True) print(f"Error fetching data: {e}") return False
def predict_price(self, interval_type, interval_value, sentiment_score=50.0): results = { "current_price": None, "sarima_prediction": None, "sarima_conf_int": None, "garch_volatility": None, "prophet_prediction": None, "prophet_conf_int": None, "linear_regression_prediction": None, "model_used": "N/A", "error": None, "adjusted_prediction": None, "sentiment_applied": True, "sentiment_value": None, "base_prediction_for_sentiment_avg": None }
if self.data is None or self.data.empty: results["error"] = "Error: No historical data available. Please fetch data first." return results
prices = self.data['Close'].dropna() if prices.empty: results["error"] = "Error: No valid closing prices in historical data." return results
results["current_price"] = prices.iloc[-1].item()
if interval_type == "day": forecast_steps_days = interval_value elif interval_type == "week": forecast_steps_days = interval_value * 7 elif interval_type == "month": forecast_steps_days = interval_value * 30 elif interval_type == "year": forecast_steps_days = interval_value * 365 else: results["error"] = "Unsupported time interval." return results
try: if len(prices) < 30: results["sarima_prediction"] = "Insufficient data for SARIMA." results["sarima_conf_int"] = "N/A" else: sarima_model = SARIMAX(prices, order=(5,1,0), seasonal_order=(1,1,0,7), enforce_stationarity=False, enforce_invertibility=False) sarima_model_fit = sarima_model.fit(disp=False) sarima_forecast = sarima_model_fit.get_forecast(steps=forecast_steps_days) sarima_predicted_mean = sarima_forecast.predicted_mean.iloc[-1].item() sarima_conf_int = sarima_forecast.conf_int(alpha=0.05).iloc[-1] results["sarima_prediction"] = sarima_predicted_mean results["sarima_conf_int"] = (sarima_conf_int.iloc[0].item(), sarima_conf_int.iloc[1].item())
try: residuals = sarima_model_fit.resid garch_model = arch_model(residuals, vol='Garch', p=1, q=1, rescale=True) garch_results = garch_model.fit(disp='off') garch_forecast = garch_results.variance.iloc[-1] forecasted_variance = garch_forecast.item() results["garch_volatility"] = np.sqrt(forecasted_variance) except Exception as e_garch: results["garch_volatility"] = f"GARCH Error: {e_garch}" print(f"GARCH Error: {e_garch}")
except Exception as e_sarima: results["sarima_prediction"] = f"SARIMA Error: {e_sarima}" results["sarima_conf_int"] = "N/A" results["garch_volatility"] = "N/A" print(f"SARIMA Error: {e_sarima}")
if PROPHET_AVAILABLE: try: df_prophet = prices.reset_index() df_prophet.columns = ['ds', 'y'] df_prophet['ds'] = pd.to_datetime(df_prophet['ds'])
prophet_model = Prophet( seasonality_mode='multiplicative', yearly_seasonality=True, weekly_seasonality=True, daily_seasonality=False ) prophet_model.fit(df_prophet)
future_dates = [prices.index[-1] + timedelta(days=i) for i in range(1, forecast_steps_days + 1)] future = pd.DataFrame({'ds': future_dates}) prophet_forecast = prophet_model.predict(future) prophet_predicted_price = prophet_forecast['yhat'].iloc[-1].item() prophet_lower_bound = prophet_forecast['yhat_lower'].iloc[-1].item() prophet_upper_bound = prophet_forecast['yhat_upper'].iloc[-1].item()
results["prophet_prediction"] = prophet_predicted_price results["prophet_conf_int"] = (prophet_lower_bound, prophet_upper_bound)
except Exception as e_prophet: results["prophet_prediction"] = f"Prophet Error: {e_prophet}" results["prophet_conf_int"] = "N/A" print(f"Prophet Error: {e_prophet}") else: results["prophet_prediction"] = "Prophet not available (library not installed)." results["prophet_conf_int"] = "N/A"
linear_regression_price = None if interval_type == "year": try: positive_prices = prices[prices > 0] log_prices = np.log(positive_prices) time_index_filtered = np.arange(len(positive_prices)).reshape(-1, 1)
if len(log_prices) > 1: model_lr = LinearRegression() model_lr.fit(time_index_filtered, log_prices)
future_days_offset = interval_value * 365 last_day_index_filtered = len(positive_prices) - 1 future_index = np.array([[last_day_index_filtered + future_days_offset]]) predicted_log_price = model_lr.predict(future_index)[0] linear_regression_price = np.exp(predicted_log_price).item() results["linear_regression_prediction"] = linear_regression_price else: results["linear_regression_prediction"] = "Insufficient data for Linear Regression." except Exception as e_lr: results["linear_regression_prediction"] = f"Linear Regression Error: {e_lr}" print(f"Linear Regression Error: {e_lr}") else: results["linear_regression_prediction"] = "Linear Regression not applicable for this interval." valid_predictions = [] model_names_used = []
if isinstance(results["sarima_prediction"], (float, int)): valid_predictions.append(results["sarima_prediction"]) model_names_used.append("SARIMA") if isinstance(results["prophet_prediction"], (float, int)): valid_predictions.append(results["prophet_prediction"]) model_names_used.append("Prophet") if isinstance(results["linear_regression_prediction"], (float, int)): valid_predictions.append(results["linear_regression_prediction"]) model_names_used.append("Log-Linear Regression")
base_prediction = None if valid_predictions: base_prediction = np.mean(valid_predictions) results["model_used"] = f"Average of: {', '.join(model_names_used)}" else: base_prediction = results["current_price"] results["model_used"] = "None (model error, using current price)"
results["base_prediction_for_sentiment_avg"] = base_prediction
if base_prediction is not None: normalized_sentiment = (sentiment_score - 50) / 50 adjustment_factor = normalized_sentiment * self.MAX_SENTIMENT_IMPACT_PERCENTAGE results["adjusted_prediction"] = base_prediction * (1 + adjustment_factor) results["sentiment_value"] = sentiment_score else: results["adjusted_prediction"] = base_prediction results["sentiment_value"] = None
return results
class BitcoinForecastApp: def __init__(self, master): self.master = master master.title("BtcTalk Sandbox: Bitcoin Price Experiment") master.geometry("700x850") master.resizable(False, False)
self.predictor = BitcoinPredictor() self.prediction_intervals = { "1 day": ("day", 1), "1 week": ("week", 1), "1 month": ("month", 1), "3 months": ("month", 3), "6 months": ("month", 6), "1 year": ("year", 1), "2 years": ("year", 2), "3 years": ("year", 3), "4 years": ("year", 4), "5 years": ("year", 5), } self.data_loaded = False self.style = ttk.Style()
self._create_widgets() self._load_initial_data_threaded()
def _display_message(self, message, is_error=False): if hasattr(self, 'status_label'): self.status_label.config(text=message, foreground='red' if is_error else 'gray') self.master.update_idletasks()
def _create_widgets(self): self.style.theme_use('clam') self.style.configure('TFrame', background='#f0f0f0') self.style.configure('TLabel', background='#f0f0f0', font=('Inter', 10)) self.style.configure('TButton', font=('Inter', 10, 'bold'), padding=10, relief='raised', borderwidth=2) self.style.map('TButton', background=[('active', '#e0e0e0')]) self.style.configure('TCombobox', font=('Inter', 10)) self.style.configure('Horizontal.TProgressbar', thickness=15) self.style.configure('Sentiment.Horizontal.TScale', background='lightgray', troughcolor='#AAAAAA') self.style.map('Sentiment.Horizontal.TScale', background=[('active', 'lightgray')])
main_frame = ttk.Frame(self.master, padding="20 20 20 20") main_frame.pack(fill=tk.BOTH, expand=True)
title_label = ttk.Label(main_frame, text="BtcTalk Sandbox: Bitcoin Price Experiment (BTC-USD)", font=('Inter', 16, 'bold'), foreground='#333') title_label.grid(row=0, column=0, columnspan=3, pady=(0, 20))
self.progress_frame = ttk.Frame(main_frame) self.progress_frame.grid(row=1, column=0, columnspan=3, pady=(10, 5), sticky="ew")
self.progress_bar = ttk.Progressbar(self.progress_frame, orient="horizontal", length=400, mode="determinate", style="Horizontal.TProgressbar") self.progress_bar.pack(pady=5, fill=tk.X, expand=True)
self.loading_status_label = ttk.Label(self.progress_frame, text="Loading data...", font=('Inter', 9, 'italic'), foreground='blue') self.loading_status_label.pack(pady=2)
ttk.Label(main_frame, text="Prediction Interval:").grid(row=2, column=0, padx=5, pady=5, sticky="w") self.interval_combobox = ttk.Combobox(main_frame, state="readonly", width=25) self.interval_combobox['values'] = list(self.prediction_intervals.keys()) self.interval_combobox.grid(row=2, column=1, columnspan=2, padx=5, pady=5, sticky="ew") self.interval_combobox.set("1 day")
sentiment_frame = ttk.LabelFrame(main_frame, text="Custom Sentiment Adjustment", padding="15 15 15 15") sentiment_frame.grid(row=3, column=0, columnspan=3, padx=10, pady=10, sticky="ew")
self.sentiment_label_info = ttk.Label(sentiment_frame, text="0=Critical (pessimistic) | 50=Neutral | 100=Wonderful (optimistic)", font=('Inter', 9, 'italic'), foreground='gray') self.sentiment_label_info.grid(row=0, column=0, columnspan=3, pady=(0,5), sticky="w")
self.critical_label = ttk.Label(sentiment_frame, text="0 (Critical)", foreground="black", font=('Inter', 9, 'bold')) self.critical_label.grid(row=1, column=0, padx=(5,0), pady=5, sticky="w")
self.sentiment_scale_var = tk.DoubleVar(value=50.0) self.sentiment_slider = ttk.Scale(sentiment_frame, from_=0, to=100, orient=tk.HORIZONTAL, variable=self.sentiment_scale_var, length=300, command=self._update_sentiment_label, style='Sentiment.Horizontal.TScale') self.sentiment_slider.grid(row=1, column=1, padx=5, pady=5, sticky="ew") self.sentiment_slider.bind("<ButtonRelease-1>", self._update_sentiment_label_on_release)
self.wonderful_label = ttk.Label(sentiment_frame, text="100 (Wonderful)", foreground="black", font=('Inter', 9, 'bold')) self.wonderful_label.grid(row=1, column=2, padx=(0,5), pady=5, sticky="e")
self.sentiment_value_display_label = ttk.Label(sentiment_frame, text="Sentiment: 50 (Neutral)", font=('Inter', 10, 'bold')) self.sentiment_value_display_label.grid(row=2, column=0, columnspan=3, padx=5, pady=5, sticky="w") sentiment_frame.grid_columnconfigure(1, weight=1)
self.predict_button = ttk.Button(main_frame, text="Predict Price", command=self._on_predict_click, state=tk.DISABLED) self.predict_button.grid(row=4, column=0, columnspan=3, pady=20)
results_frame = ttk.LabelFrame(main_frame, text="Detailed Forecast Results", padding="15 15 15 15") results_frame.grid(row=5, column=0, columnspan=3, padx=10, pady=10, sticky="ew")
self.results_text = tk.Text(results_frame, height=15, width=70, state="disabled", font=('Inter', 10), wrap=tk.WORD) self.results_text.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") results_frame.grid_rowconfigure(0, weight=1) results_frame.grid_columnconfigure(0, weight=1)
self.status_label = ttk.Label(main_frame, text="Waiting for data load...", font=('Inter', 9, 'italic'), foreground='gray') self.status_label.grid(row=6, column=0, columnspan=3, pady=(10, 0))
disclaimer_text = ("Disclaimer: Cryptocurrency price predictions are inherently speculative and should not be considered financial advice. " "Mathematical models are based on historical price data and cannot predict unpredictable future events or directly incorporate external factors " "(such as market sentiment, political actions, economic news, or regulatory changes). " "This application is for demonstrative and educational purposes only. Forecasted volatility is an estimate of risk, not a guarantee.") self.disclaimer_label = ttk.Label(main_frame, text=disclaimer_text, font=('Inter', 8, 'italic'), foreground='red', wraplength=650, justify=tk.LEFT) self.disclaimer_label.grid(row=7, column=0, columnspan=3, pady=(10, 0), sticky="ew")
main_frame.grid_columnconfigure(0, weight=1) main_frame.grid_columnconfigure(1, weight=1) main_frame.grid_columnconfigure(2, weight=1)
def _update_sentiment_label(self, val): sentiment_val = int(float(val)) if sentiment_val == 50: sentiment_text = "Neutral" elif sentiment_val > 50: sentiment_text = "Optimistic" else: sentiment_text = "Pessimistic" self.sentiment_value_display_label.config(text=f"Sentiment: {sentiment_val} ({sentiment_text})")
def _update_sentiment_label_on_release(self, event): sentiment_val = int(self.sentiment_scale_var.get()) if sentiment_val < 50: trough_color = '#FF6666' elif sentiment_val > 50: trough_color = '#66FF66' else: trough_color = '#AAAAAA'
self.style.configure('Sentiment.Horizontal.TScale', troughcolor=trough_color)
def _update_loading_progress(self, type, value, is_error=False): if hasattr(self, 'progress_bar') and hasattr(self, 'loading_status_label'): if type == "progress": self.progress_bar['value'] = value elif type == "status": self.loading_status_label.config(text=value, foreground='red' if is_error else 'blue') self.master.update_idletasks()
def _load_initial_data_threaded(self): self.predict_button.config(state=tk.DISABLED) self.interval_combobox.config(state=tk.DISABLED)
self._display_message("Starting Bitcoin historical data load...", False) threading.Thread(target=self._perform_data_load, daemon=True).start()
def _perform_data_load(self): success = self.predictor.fetch_data(period="max", progress_callback=self._update_loading_progress) self.master.after(0, self._after_data_load, success)
def _after_data_load(self, success): if success: self.data_loaded = True self._display_message("Bitcoin historical data loaded successfully. Ready to predict!", False) self.predict_button.config(state=tk.NORMAL) self.interval_combobox.config(state="readonly") self._update_sentiment_label_on_release(None)
else: self.data_loaded = False self._display_message("Error loading historical data. Application will not function correctly.", True) self.predict_button.config(state=tk.DISABLED) self.interval_combobox.config(state=tk.DISABLED) self.loading_status_label.config(text="ERROR: Failed to load data.")
def _on_predict_click(self): if not self.data_loaded: self._display_message("Historical data has not been loaded yet. Please wait.", True) return
selected_interval_text = self.interval_combobox.get() interval_type, interval_value = self.prediction_intervals.get(selected_interval_text)
if not interval_type: self._display_message("Please select a valid prediction interval.", True) return
sentiment_score = self.sentiment_scale_var.get()
self._display_message(f"Performing prediction for {selected_interval_text}...", False) self.predict_button.config(state=tk.DISABLED) self.sentiment_slider.config(state=tk.DISABLED) self.sentiment_value_display_label.config(state=tk.DISABLED) self.critical_label.config(state=tk.DISABLED) self.wonderful_label.config(state=tk.DISABLED)
try: prediction_results = self.predictor.predict_price( interval_type, interval_value, sentiment_score )
self.results_text.config(state="normal") self.results_text.delete(1.0, tk.END)
if prediction_results["error"]: output = f"Prediction Error: {prediction_results['error']}\n" self.results_text.insert(tk.END, output) self.results_text.config(state="disabled") self._display_message("Prediction completed with errors.", is_error=True) return
output = f"Current BTC-USD Price: ${prediction_results['current_price']:,.2f}\n" output += f"--------------------------------------------------\n\n"
if prediction_results["sarima_prediction"] is not None and not isinstance(prediction_results["sarima_prediction"], str): output += f"SARIMA Prediction for {selected_interval_text}: ${prediction_results['sarima_prediction']:,.2f}\n" if prediction_results["sarima_conf_int"] and isinstance(prediction_results["sarima_conf_int"], tuple): lower, upper = prediction_results["sarima_conf_int"] output += f" SARIMA Confidence Interval (95%): [${lower:,.2f}, ${upper:,.2f}]\n" if prediction_results["garch_volatility"] is not None and not isinstance(prediction_results["garch_volatility"], str): output += f" Forecasted Volatility (GARCH): {prediction_results['garch_volatility'] * 100:,.2f}% (standard deviation)\n" output += "\n" else: output += f"SARIMA: {prediction_results['sarima_prediction']}\n\n"
if prediction_results["prophet_prediction"] is not None and not isinstance(prediction_results["prophet_prediction"], str): output += f"Prophet Prediction for {selected_interval_text}: ${prediction_results['prophet_prediction']:,.2f}\n" if prediction_results["prophet_conf_int"] and isinstance(prediction_results["prophet_conf_int"], tuple): lower, upper = prediction_results["prophet_conf_int"] output += f" Prophet Confidence Interval (80% default): [${lower:,.2f}, ${upper:,.2f}]\n" output += "\n" else: output += f"Prophet: {prediction_results['prophet_prediction']}\n\n"
if prediction_results["linear_regression_prediction"] is not None: if not isinstance(prediction_results["linear_regression_prediction"], str): output += f"Log-Linear Regression Prediction for {selected_interval_text}: ${prediction_results['linear_regression_prediction']:,.2f}\n\n" else: output += f"Log-Linear Regression: {prediction_results['linear_regression_prediction']}\n\n" output += f"--- Custom Sentiment Adjusted Prediction ---\n" output += f"Sentiment applied: {int(prediction_results['sentiment_value'])} (0=Critical, 100=Wonderful)\n" output += f"Base for Adjustment (Average of Valid Models): ${prediction_results['base_prediction_for_sentiment_avg']:,.2f}\n" output += f"Final Adjusted Prediction: ${prediction_results['adjusted_prediction']:,.2f}\n" output += f"----------------------------------------------------------\n\n" output += f"--------------------------------------------------\n" output += f"Base Model(s) Used for Point Prediction: {prediction_results['model_used']}\n" output += "Based on historical data from Yahoo Finance.\n"
prices = self.predictor.data['Close'].dropna() if len(prices) > 1 and (prices.index[-1] - prices.index[0]).days > 365: first_price = prices.iloc[0].item() last_price_historical = prices.iloc[-1].item() num_years = (prices.index[-1] - prices.index[0]).days / 365.25 if first_price > 0 and num_years > 0: try: cagr = ((last_price_historical / first_price)**(1/num_years) - 1) * 100 output += f"Historical CAGR (last {num_years:.1f} years): {cagr:,.2f}%\n" except OverflowError: output += "Historical CAGR: Too large to calculate or inconsistent data.\n" else: output += "Historical CAGR: Insufficient data for calculation.\n" else: output += "Historical CAGR: Not applicable (less than 1 year of data or insufficient data).\n"
self.results_text.insert(tk.END, output) self.results_text.config(state="disabled") self._display_message("Prediction completed.")
except Exception as e: self._display_message(f"An unexpected error occurred during prediction: {e}. Check console for more details.", True) print(f"Prediction error detail: {e}") finally: self.predict_button.config(state=tk.NORMAL) self.sentiment_slider.config(state=tk.NORMAL) self.sentiment_value_display_label.config(state=tk.NORMAL) self.critical_label.config(state=tk.NORMAL) self.wonderful_label.config(state=tk.NORMAL) self.sentiment_label_info.config(state=tk.NORMAL)
if __name__ == "__main__": root = tk.Tk() app = BitcoinForecastApp(root) root.mainloop()
My Bet:
Both SARIMA and Prophet are widely used time series forecasting models for predicting Bitcoin prices. A study found that SARIMA performs better for daily Bitcoin predictions, while Prophet yields more accurate results for monthly predictions: A Comparative Study between the Facebook Prophet Model and SARIMA for Bitcoin Price Prediction
This script uses yfinance to collect Bitcoin's historical data.
|