|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import os |
|
|
|
|
|
os.environ["YFINANCE_NO_CACHE"] = "1" |
|
os.environ["XDG_CACHE_HOME"] = "/tmp/xfake_cache" |
|
if not os.path.exists("/tmp/xfake_cache"): |
|
os.makedirs("/tmp/xfake_cache", exist_ok=True) |
|
|
|
import yfinance as yf |
|
import gradio as gr |
|
import numpy as np |
|
import pandas as pd |
|
|
|
from datetime import datetime |
|
from pandas.tseries.offsets import BDay |
|
from tabulate import tabulate |
|
|
|
|
|
from tensorflow.keras.models import load_model |
|
from sklearn.preprocessing import MinMaxScaler |
|
import joblib |
|
|
|
import sklearn |
|
import tensorflow as tf |
|
import shutil |
|
from pytz import timezone |
|
from pandas.tseries.offsets import BDay |
|
import hashlib |
|
import yfinance as yf |
|
import time |
|
import gc |
|
|
|
import threading |
|
lock = threading.Lock() |
|
|
|
|
|
|
|
|
|
model_dir = "./model" |
|
|
|
NN_model = load_model(os.path.join(model_dir, "NN_CPU_model.keras")) |
|
|
|
NN_model.compile(optimizer="adam", loss="mse") |
|
NN_model.predict(np.zeros((1, 5))) |
|
|
|
scaler_X = joblib.load(os.path.join(model_dir, "scaler_X.pkl")) |
|
scaler_y = joblib.load(os.path.join(model_dir, "scaler_y.pkl")) |
|
|
|
def safe_download(*args, retries=3, delay=1, **kwargs): |
|
for i in range(retries): |
|
try: |
|
df = yf.download(*args, **kwargs) |
|
if not df.empty: |
|
return df |
|
except Exception as e: |
|
print(f"[Attempt {i+1}] yfinance error: {e}") |
|
time.sleep(delay) |
|
raise RuntimeError("yfinance download failed after retries.") |
|
|
|
|
|
def predict_stock(): |
|
|
|
with lock: |
|
|
|
|
|
print("YFINANCE_NO_CACHE =", os.getenv("YFINANCE_NO_CACHE")) |
|
|
|
|
|
now_est = datetime.now(timezone("US/Eastern")) |
|
print("Current Eastern Time:", now_est) |
|
print("Trying to fetch data up to:", now_est.strftime('%Y-%m-%d')) |
|
|
|
|
|
cache_path = os.path.expanduser("~/.cache/py-yfinance") |
|
if os.path.exists(cache_path): |
|
print("Clearing yfinance cache...") |
|
shutil.rmtree(cache_path) |
|
|
|
Stock = "NVDA" |
|
start_date = "2020-01-01" |
|
train_end_date = "2024-12-31" |
|
|
|
|
|
today = now_est.strftime('%Y-%m-%d') |
|
|
|
|
|
|
|
try: |
|
full_data = safe_download( |
|
tickers=Stock, |
|
start=start_date, |
|
end=today, |
|
interval="1d", |
|
auto_adjust=False, |
|
actions=False, |
|
progress=False, |
|
threads=True |
|
) |
|
|
|
if full_data.empty: |
|
print("yfinance returned empty data for:", today) |
|
return "Error: Stock data not available at this time. Please try again shortly.", pd.DataFrame() |
|
|
|
except Exception as e: |
|
print("yfinance error:", e) |
|
return "Error: Could not fetch stock data. Please try again later.", pd.DataFrame() |
|
|
|
features = ["Open", "High", "Low", "Close", "Volume"] |
|
|
|
X_scaled = scaler_X.transform(full_data[features]) |
|
y = full_data["Close"].values.reshape(-1, 1) |
|
y_scaled = scaler_y.transform(y) |
|
|
|
X_all = X_scaled[:-1] |
|
y_all = y_scaled[1:].flatten() |
|
dates_all = full_data.index[1:] |
|
|
|
test_mask = dates_all > pd.to_datetime(train_end_date) |
|
X_test, y_test = X_all[test_mask], y_all[test_mask] |
|
dates_test = dates_all[test_mask] |
|
|
|
|
|
y_pred_scaled = NN_model.predict(X_test).flatten() |
|
y_pred = scaler_y.inverse_transform(y_pred_scaled.reshape(-1, 1)).flatten() |
|
y_true = scaler_y.inverse_transform(y_test.reshape(-1, 1)).flatten() |
|
|
|
last_date = full_data.index[-1] |
|
last_close_price = float(full_data["Close"].iloc[-1].item()) |
|
X_input = X_scaled[-1].reshape(1, -1) |
|
|
|
next_day_pred_scaled = NN_model.predict(X_input) |
|
next_day_pred = scaler_y.inverse_transform(next_day_pred_scaled)[0][0] |
|
|
|
mape = np.mean(np.abs((y_true - y_pred) / y_true)) * 100 |
|
std_ape = np.std(np.abs((y_true - y_pred) / y_true)) * 100 |
|
|
|
mape_margin = next_day_pred * (mape / 100) |
|
sae_margin = next_day_pred * (std_ape / 100) |
|
|
|
next_date = (last_date + BDay(1)).date() |
|
|
|
summary_lines = [ |
|
f"Prediction for {Stock}:", |
|
f"Last available date: {last_date.date()}, Close = ${last_close_price:.2f}", |
|
f"Predicted closing price for next trading day ({next_date}): ${next_day_pred:.2f}", |
|
f"Expected range (±MAPE): ${next_day_pred - mape_margin:.2f} to ${next_day_pred + mape_margin:.2f}", |
|
f"Expected range (±SAE): ${next_day_pred - sae_margin:.2f} to ${next_day_pred + sae_margin:.2f}" |
|
] |
|
summary = "\n".join(summary_lines) |
|
|
|
prediction_df = pd.DataFrame({ |
|
'Date': dates_test, |
|
'Actual Close': y_true, |
|
'Predicted Close': y_pred |
|
}) |
|
|
|
prediction_df['% Error'] = ((prediction_df['Actual Close'] - prediction_df['Predicted Close']) / prediction_df['Actual Close']) * 100 |
|
prediction_df['% Error'] = prediction_df['% Error'].map(lambda x: f"{x:+.2f}%") |
|
prediction_df['±MAPE Range'] = prediction_df['Predicted Close'].apply( |
|
lambda x: f"${x * (1 - mape/100):.2f} to ${x * (1 + mape/100):.2f}" |
|
) |
|
|
|
prediction_df['Date'] = prediction_df['Date'].dt.strftime("%Y-%m-%d") |
|
prediction_df['Actual Close'] = prediction_df['Actual Close'].map(lambda x: f"${x:.2f}") |
|
prediction_df['Predicted Close'] = prediction_df['Predicted Close'].map(lambda x: f"${x:.2f}") |
|
prediction_df = prediction_df.sort_values("Date", ascending=False) |
|
|
|
headers = ["Prediction For Date", "Actual Close", "Predicted Close", "% Error", "±MAPE Range"] |
|
table = tabulate(prediction_df.values, headers=headers, tablefmt="plain") |
|
|
|
|
|
|
|
assert not np.any(np.isnan(X_scaled[-1].reshape(1, -1))), "NaNs detected in input!" |
|
assert X_scaled[-1].reshape(1, -1).shape == (1, X_scaled.shape[1]), f"Unexpected shape: {X_scaled[-1].reshape(1, -1).shape}" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
gc.collect() |
|
|
|
import hashlib |
|
|
|
def md5(fname): |
|
with open(fname, "rb") as f: |
|
return hashlib.md5(f.read()).hexdigest() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return summary, prediction_df[["Date", "Actual Close", "Predicted Close", "% Error", "±MAPE Range"]] |
|
|
|
|
|
|
|
|
|
""" |
|
demo = gr.Interface( |
|
fn=predict_stock, |
|
inputs=[], |
|
outputs=[ |
|
gr.Textbox(label="Prediction Summary", lines=6), |
|
gr.Dataframe(headers=["Prediction For Date", "Actual Close", "Predicted Close", "% Error", "±MAPE Range"], label="Prediction Table (2025+)") |
|
], |
|
title="📈 NVDA Stock Predictor", |
|
description="This app uses a Dense Neural Network to predict NVDA's next trading day's closing price.", |
|
live=True #<-- changed to True for live queuing |
|
) |
|
|
|
demo.launch() |
|
""" |
|
|
|
with gr.Blocks() as demo: |
|
|
|
gr.Markdown("## 📊 NVDA Stock Prediction Using Neural Networks") |
|
|
|
|
|
output_summary = gr.Textbox(label="Prediction Summary", lines=6) |
|
output_table = gr.Dataframe(headers=["Prediction For Date", "Actual Close", "Predicted Close", "% Error", "±MAPE Range"], label="Prediction Table (2025+)") |
|
|
|
|
|
generate_btn = gr.Button("Generate") |
|
generate_btn.click(fn=predict_stock, inputs=[], outputs=[output_summary, output_table]) |
|
|
|
|
|
gr.HTML("""<p style='margin-top: 20px;'><a href="https://prakashkota.com/2025/04/09/ai-ml-in-finance-how-a-lightweight-neural-network-forecasts-nvdas-next-stock-price-move/" target="_blank"> |
|
👉 Read the full article here - AI/ML in Finance: How a Lightweight Neural Network Forecasts NVDA’s Next Stock Price Move</a></p>""") |
|
|
|
|
|
gr.HTML(""" |
|
<p style='font-size: 14px; color: gray; line-height: 1.5;'> |
|
<strong>Disclaimer:</strong> The information provided in this article and through the linked prediction model is for educational and informational purposes only. |
|
It does not constitute financial, investment, or trading advice, and should not be relied upon as such.<br><br> |
|
Any decisions made based on the model's output are solely at the user’s own risk. I make no guarantees regarding the accuracy, completeness, or reliability of the predictions. |
|
I am not responsible for any financial losses or gains resulting from the use of this model.<br><br> |
|
Always consult with a licensed financial advisor before making any investment decisions. |
|
</p> |
|
""") |
|
|
|
demo.launch(share=True) |