Skip to content
Snippets Groups Projects
Commit fba2cf2b authored by li67@drexel.edu's avatar li67@drexel.edu
Browse files

done

parent 0e750698
No related branches found
No related tags found
No related merge requests found
Pipeline #8606 failed
# app.py
# --- Imports ---
# --- Imports ---
import pandas as pd
import traceback
from flask import Flask, render_template, request
from flask import Flask, render_template, request
from prophet import Prophet
from prophet.diagnostics import cross_validation, performance_metrics
from datetime import datetime
import json
import json
# --- Import data fetching functions ---
try:
from data import (fetch_crypto_price, fetch_gemini_historical_data,
fetch_current_fear_greed, fetch_historical_fear_greed,
fetch_reddit_sentiment)
from data import (fetch_crypto_price, fetch_gemini_historical_data,
fetch_current_fear_greed, fetch_historical_fear_greed,
fetch_reddit_sentiment)
......@@ -28,11 +22,9 @@ except ImportError as e:
def fetch_current_fear_greed(*args, **kwargs): return None
def fetch_historical_fear_greed(*args, **kwargs): return None
def fetch_reddit_sentiment(*args, **kwargs): return None
def fetch_reddit_sentiment(*args, **kwargs): return None
# --- Flask App Initialization ---
app = Flask(__name__)
app = Flask(__name__)
# --- Main Route ---
@app.route('/')
......@@ -41,8 +33,8 @@ def index():
print(f"\n--- Request received at {start_time.strftime('%Y-%m-%d %H:%M:%S')} ---")
# --- Configuration ---
symbol = request.args.get('symbol', request.args.get('symbol', 'BTCUSD') # Support dropdown selection) # Support dropdown selection
coin_gecko_id = {'BTCUSD': 'bitcoin', 'ETHUSD': 'ethereum', 'SOLUSD': 'solana'}.get(symbol, {'BTCUSD': 'bitcoin', 'ETHUSD': 'ethereum', 'SOLUSD': 'solana'}.get(symbol, 'bitcoin'))
symbol = request.args.get('symbol', 'BTCUSD') # Support dropdown selection
coin_gecko_id = {'BTCUSD': 'bitcoin', 'ETHUSD': 'ethereum', 'SOLUSD': 'solana'}.get(symbol, 'bitcoin')
timeframe = '1hr'
min_data_points = 25
......@@ -56,7 +48,6 @@ def index():
df_fng = None
fng_available = False
reddit_sentiment = None
reddit_sentiment = None
model = None
forecast = None
future = None
......@@ -71,18 +62,14 @@ def index():
data_confidence_score = 0
prediction_interval_penalty = 0
used_current_fng_for_future = False
used_current_fng_for_future = False
plot_data = None
error_message = None
sources = [] # List to store sources for frontend
sources = [] # List to store sources for frontend
# --- 1. FETCH DATA ---
try:
print("--- Fetching Data ---")
historical_data = fetch_gemini_historical_data(symbol, timeframe=timeframe)
if historical_data:
sources.append({"name": "Gemini API", "type": "Price Data", "reliability": "High"})
if historical_data:
sources.append({"name": "Gemini API", "type": "Price Data", "reliability": "High"})
current_price = fetch_crypto_price(coin_id=coin_gecko_id)
......@@ -94,30 +81,19 @@ def index():
if reddit_sentiment:
sources.append({"name": "Reddit (r/CryptoCurrency)", "type": "Social Media", "reliability": "Medium"})
print(f"Fetched: Current Price={current_price}, Current F&G={current_fng_value}, Reddit Sentiment={reddit_sentiment}")
current_fng_data = fetch_current_fear_greed()
current_fng_value = current_fng_data.get('value') if current_fng_data else None
if current_fng_value:
sources.append({"name": "Fear & Greed Index", "type": "Market Sentiment", "reliability": "Medium"})
reddit_sentiment = fetch_reddit_sentiment(subreddit="CryptoCurrency", query=coin_gecko_id, timeframe_hours=24)
if reddit_sentiment:
sources.append({"name": "Reddit (r/CryptoCurrency)", "type": "Social Media", "reliability": "Medium"})
print(f"Fetched: Current Price={current_price}, Current F&G={current_fng_value}, Reddit Sentiment={reddit_sentiment}")
except Exception as fetch_e:
error_message = "Error during initial data fetching."
print(f"!!! {error_message}: {fetch_e} !!!")
# --- 2. PROCESS PRICE/VOLUME DATA ---
if historical_data and len(historical_data) >= min_data_points:
if historical_data and len(historical_data) >= min_data_points:
try:
print("--- Processing Price/Volume data ---")
df = pd.DataFrame(historical_data, columns=['timestamp_ms', 'y', 'volume'])
df['ds'] = pd.to_datetime(df['timestamp_ms'], unit='ms')
df.dropna(subset=['ds', 'y'], inplace=True)
df.dropna(subset=['ds', 'y'], inplace=True)
if df.empty:
raise ValueError("No valid rows remaining after dropping NaNs in 'ds' or 'y'.")
raise ValueError("No valid rows remaining after dropping NaNs in 'ds' or 'y'.")
df_prophet = df[['ds', 'y', 'volume']].copy()
# Calculate base confidence score
......@@ -132,18 +108,6 @@ def index():
elif source['reliability'] == "Medium":
data_confidence_score += 5
print(f"Data Confidence: Base score {data_confidence_score} after price/vol and sources check.")
# Calculate base confidence score
data_confidence_score = 50 # Base for having minimum data
data_confidence_score += 5 * len(sources) # +5 per source
if len(df_prophet) > 100:
data_confidence_score += 10
# Add reliability bonus
for source in sources:
if source['reliability'] == "High":
data_confidence_score += 10
elif source['reliability'] == "Medium":
data_confidence_score += 5
print(f"Data Confidence: Base score {data_confidence_score} after price/vol and sources check.")
except Exception as e:
error_message = "Error processing historical price/volume data."
print(f"!!! {error_message}: {e} !!!")
......@@ -151,10 +115,6 @@ def index():
df_prophet = None
data_confidence_score = 0
else:
if historical_data is None:
error_message = f"Failed to fetch historical data from API for {symbol}/{timeframe}."
else:
error_message = f"Insufficient historical data ({len(historical_data)} points) for prediction."
if historical_data is None:
error_message = f"Failed to fetch historical data from API for {symbol}/{timeframe}."
else:
......@@ -171,17 +131,8 @@ def index():
sources=sources,
processing_time=f"{processing_time:.2f} sec",
last_updated=datetime.now().strftime("%Y-%m-%d %H:%M:%S %Z"))
return render_template('dashboard.html',
symbol=symbol,
error_message=error_message,
data_confidence_score=data_confidence_score,
plot_data=None,
sources=sources,
processing_time=f"{processing_time:.2f} sec",
last_updated=datetime.now().strftime("%Y-%m-%d %H:%M:%S %Z"))
# --- 2b. FETCH & ATTEMPT MERGE FEAR/GREED DATA ---
if df_prophet is not None:
if df_prophet is not None:
try:
start_date = df_prophet['ds'].min()
......@@ -194,27 +145,19 @@ def index():
df_prophet_with_date['ds_date'] = df_prophet_with_date['ds'].dt.normalize()
temp_merged = pd.merge(df_prophet_with_date, df_fng, on='ds_date', how='left')
temp_merged['fear_greed'] = temp_merged['fear_greed'].ffill().bfill()
if temp_merged['fear_greed'].isnull().any():
print("!!! Warning: Could not fill all missing Fear/Greed values. Filling remaining with 50. !!!")
temp_merged['fear_greed'].fillna(50, inplace=True)
if temp_merged['fear_greed'].isnull().any():
print("!!! Warning: Could not fill all missing Fear/Greed values. Filling remaining with 50. !!!")
temp_merged['fear_greed'].fillna(50, inplace=True)
df_merged = temp_merged[['ds', 'y', 'volume', 'fear_greed']].copy()
fng_available = True
data_confidence_score += 15
data_confidence_score += 15
print(f"Data Confidence: Added F&G bonus. Score = {data_confidence_score}")
else:
print("--- Proceeding without Fear/Greed data (fetch failed or no data). ---")
df_merged = df_prophet.copy()
fng_available = False
print("--- Proceeding without Fear/Greed data (fetch failed or no data). ---")
df_merged = df_prophet.copy()
fng_available = False
except Exception as e:
if not error_message:
error_message = "Error merging Fear/Greed data."
print(f"!!! {error_message}: {e} !!!")
traceback.print_exc()
......@@ -238,23 +181,6 @@ def index():
if df_merged is None:
df_merged = df_prophet.copy()
# --- 2c. MERGE REDDIT SENTIMENT ---
if df_prophet is not None and reddit_sentiment and 'average_score' in reddit_sentiment:
try:
df_merged['reddit_sentiment'] = reddit_sentiment['average_score']
# Add precision bonus based on sentiment variance
if 'variance' in reddit_sentiment:
variance = reddit_sentiment['variance']
if variance < 0.1: # Low variance = precise
data_confidence_score += 10
elif variance < 0.3:
data_confidence_score += 5
print(f"Data Confidence: Added Reddit sentiment precision bonus (variance={variance}). Score = {data_confidence_score}")
except Exception as e:
print(f"!!! Error merging Reddit sentiment: {e} !!!")
if df_merged is None:
df_merged = df_prophet.copy()
# --- 3. MODEL TRAINING & PREDICTION ---
if df_merged is not None:
try:
......@@ -270,21 +196,6 @@ def index():
df_merged['reddit_sentiment'] = pd.to_numeric(df_merged['reddit_sentiment'], errors='coerce').fillna(0)
model.add_regressor('reddit_sentiment')
initial_rows = len(df_merged)
df_merged.dropna(subset=['y'], inplace=True)
if len(df_merged) < initial_rows:
print(f"--- Warning: Dropped {initial_rows - len(df_merged)} rows due to NaN in 'y' before fitting. ---")
model = Prophet(interval_width=0.95)
if 'volume' in df_merged.columns and not df_merged['volume'].isnull().all():
df_merged['volume'] = pd.to_numeric(df_merged['volume'], errors='coerce').fillna(0)
model.add_regressor('volume')
if fng_available and 'fear_greed' in df_merged.columns and not df_merged['fear_greed'].isnull().all():
df_merged['fear_greed'] = pd.to_numeric(df_merged['fear_greed'], errors='coerce').fillna(50)
model.add_regressor('fear_greed')
if 'reddit_sentiment' in df_merged.columns and not df_merged['reddit_sentiment'].isnull().all():
df_merged['reddit_sentiment'] = pd.to_numeric(df_merged['reddit_sentiment'], errors='coerce').fillna(0)
model.add_regressor('reddit_sentiment')
initial_rows = len(df_merged)
df_merged.dropna(subset=['y'], inplace=True)
if len(df_merged) < initial_rows:
......@@ -292,20 +203,12 @@ def index():
if len(df_merged) < min_data_points:
raise ValueError(f"Insufficient data ({len(df_merged)} rows) remaining for model fitting after processing regressors.")
print(f"Fitting model on DataFrame with columns: {list(df_merged.columns)} ({len(df_merged)} rows)")
raise ValueError(f"Insufficient data ({len(df_merged)} rows) remaining for model fitting after processing regressors.")
print(f"Fitting model on DataFrame with columns: {list(df_merged.columns)} ({len(df_merged)} rows)")
model.fit(df_merged)
print("Making future dataframe...")
print("Making future dataframe...")
future = model.make_future_dataframe(periods=24, freq='h')
if 'volume' in model.regressors:
future['volume'] = df_merged['volume'].iloc[-1] if not df_merged['volume'].empty else 0
if 'fear_greed' in model.regressors:
if current_fng_value is not None:
if 'volume' in model.regressors:
future['volume'] = df_merged['volume'].iloc[-1] if not df_merged['volume'].empty else 0
if 'fear_greed' in model.regressors:
......@@ -318,17 +221,9 @@ def index():
if 'reddit_sentiment' in model.regressors:
future['reddit_sentiment'] = reddit_sentiment['average_score'] if reddit_sentiment and 'average_score' in reddit_sentiment else 0
print("Predicting...")
else:
future['fear_greed'] = df_merged['fear_greed'].iloc[-1] if not df_merged['fear_greed'].empty else 50
if 'reddit_sentiment' in model.regressors:
future['reddit_sentiment'] = reddit_sentiment['average_score'] if reddit_sentiment and 'average_score' in reddit_sentiment else 0
print("Predicting...")
forecast = model.predict(future)
# Prediction Interval Penalty
if forecast is not None and len(forecast) >= 24:
# Prediction Interval Penalty
if forecast is not None and len(forecast) >= 24:
pred_24h_row = forecast.iloc[-1]
......@@ -338,12 +233,6 @@ def index():
if pd.notna(yhat) and yhat != 0 and pd.notna(yhat_lower) and pd.notna(yhat_upper):
interval_width = yhat_upper - yhat_lower
relative_width = abs(interval_width / yhat)
if relative_width > 0.20:
prediction_interval_penalty = 20
elif relative_width > 0.10:
prediction_interval_penalty = 10
elif relative_width > 0.05:
prediction_interval_penalty = 5
if relative_width > 0.20:
prediction_interval_penalty = 20
elif relative_width > 0.10:
......@@ -353,26 +242,19 @@ def index():
data_confidence_score -= prediction_interval_penalty
print(f"Data Confidence: Applied Interval Penalty: -{prediction_interval_penalty}. Score = {data_confidence_score}")
# Extract predictions
# Extract predictions
if forecast is not None:
predicted_price_1h = forecast.iloc[-24].get('yhat') if len(forecast) >= 24 else None
predicted_price_12h = forecast.iloc[-13].get('yhat') if len(forecast) >= 13 else None
predicted_price_24h = forecast.iloc[-1].get('yhat') if len(forecast) >= 1 else None
predicted_price_1h = forecast.iloc[-24].get('yhat') if len(forecast) >= 24 else None
predicted_price_12h = forecast.iloc[-13].get('yhat') if len(forecast) >= 13 else None
predicted_price_24h = forecast.iloc[-1].get('yhat') if len(forecast) >= 1 else None
except Exception as e:
if not error_message:
error_message = "Prediction model fitting or forecasting failed."
print(f"!!! {error_message}: {e} !!!")
traceback.print_exc()
predicted_price_1h = predicted_price_12h = predicted_price_24h = None
forecast = None
model = None
forecast = None
model = None
data_confidence_score = max(0, data_confidence_score - 30)
# --- Clamp final confidence score ---
......@@ -390,15 +272,11 @@ def index():
df_p = performance_metrics(df_cv)
avg_mape = df_p['mape'].mean()
avg_mae = df_p['mae'].mean()
if pd.notna(avg_mape) and pd.notna(avg_mae):
accuracy_metrics = {'mape': avg_mape * 100, 'mae': avg_mae}
if pd.notna(avg_mape) and pd.notna(avg_mae):
accuracy_metrics = {'mape': avg_mape * 100, 'mae': avg_mae}
if pd.notna(avg_mape):
clamped_mape = max(0, min(avg_mape, 1.0))
historical_accuracy_percent = (1 - clamped_mape) * 100
clamped_mape = max(0, min(avg_mape, 1.0))
historical_accuracy_percent = (1 - clamped_mape) * 100
except Exception as cv_e:
print(f"!!! Cross-validation failed: {cv_e} !!!")
traceback.print_exc()
......@@ -406,23 +284,16 @@ def index():
error_message = "Accuracy cross-validation failed."
accuracy_metrics = None
historical_accuracy_percent = None
if not error_message:
error_message = "Accuracy cross-validation failed."
accuracy_metrics = None
historical_accuracy_percent = None
# --- 4. CALCULATE PERCENTAGE CHANGES ---
print("--- Calculating percentage changes ---")
if current_price is not None and current_price != 0:
try:
if predicted_price_1h is not None and predicted_price_1h is not None and pd.notna(predicted_price_1h):
if predicted_price_1h is not None and pd.notna(predicted_price_1h):
percentage_change_1h = ((predicted_price_1h - current_price) / current_price) * 100
if predicted_price_12h is not None and predicted_price_12h is not None and pd.notna(predicted_price_12h):
if predicted_price_12h is not None and pd.notna(predicted_price_12h):
percentage_change_12h = ((predicted_price_12h - current_price) / current_price) * 100
if predicted_price_24h is not None and predicted_price_24h is not None and pd.notna(predicted_price_24h):
if predicted_price_24h is not None and pd.notna(predicted_price_24h):
percentage_change_24h = ((predicted_price_24h - current_price) / current_price) * 100
except Exception as e:
print(f"!!! Error calculating percentage changes: {e} !!!")
......@@ -436,17 +307,11 @@ def index():
history_to_plot = df_merged.tail(history_points_to_plot)
if len(forecast) > len(df_merged):
future_plot_points = forecast.iloc[len(df_merged):][['ds', 'yhat', 'yhat_lower', 'yhat_upper']].copy()
future_plot_points = forecast.iloc[len(df_merged):][['ds', 'yhat', 'yhat_lower', 'yhat_upper']].copy()
else:
future_plot_points = pd.DataFrame(columns=['ds', 'yhat', 'yhat_lower', 'yhat_upper'])
future_plot_points = pd.DataFrame(columns=['ds', 'yhat', 'yhat_lower', 'yhat_upper'])
hist_dates = history_to_plot['ds']
future_dates = future_plot_points['ds']
if not future_dates.empty:
all_dates_dt = pd.concat([pd.Series(hist_dates), pd.Series(future_dates)], ignore_index=True).sort_values()
else:
all_dates_dt = hist_dates.sort_values()
if not future_dates.empty:
all_dates_dt = pd.concat([pd.Series(hist_dates), pd.Series(future_dates)], ignore_index=True).sort_values()
else:
......
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment