File size: 4,441 Bytes
5a72e87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b0d3471
 
 
 
 
 
 
2f114de
b0d3471
2f114de
 
 
 
 
b0d3471
 
2f114de
 
b0d3471
 
 
 
 
5a72e87
2f114de
5a72e87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#+--------------------------------------------------------------------------------------------+
# Model for Stock Price Prediction via Linear Regression
# Using today vs tomorrow analysis
# app.py for deployment on Hugging Face
# 
# Written by: Prakash R. Kota
# Location: East Greenbush, NY
#
#
# Based on: 2a_HF_linear_model_app.ipynb
# and Based on: 1a_HF_linear_model.ipynb
#
# Written on: 25 Mar 2025
# Last update: 25 Mar 2025
#+--------------------------------------------------------------------------------------------+

import os
import joblib
import yfinance as yf
import pandas as pd
import numpy as np
from datetime import datetime
from sklearn.metrics import mean_absolute_percentage_error
from sklearn.linear_model import LinearRegression
import gradio as gr

# --- Global Variables --- #
Stock = "NVDA"
model_path = f"./model/{Stock}_lr_model.pkl"
start_date = "2020-01-01"
test_start_date = "2025-01-01"
today = datetime.today().strftime('%Y-%m-%d')

# --- Load Model --- #
if not os.path.exists(model_path):
    raise FileNotFoundError(f"Model not found at {model_path}. Please train and save it first.")

model = joblib.load(model_path)

def run_prediction():
    # --- Download Data --- #
    data = yf.download(Stock, start=start_date, end=today)[["Close"]]
    data.dropna(inplace=True)
    data.index = pd.to_datetime(data.index)

    # --- Create X and y for prediction --- #
    xactual = data[:-1]["Close"].values.reshape(-1, 1)  # Today's close
    ytrue = data[1:]["Close"]                           # Tomorrow's close (Series)
    dates = data[1:].index

    # Keep only test range (2025+)
    mask = dates >= test_start_date
    xactual = xactual[mask]
    ytrue = ytrue[mask]
    dates = dates[mask]

    # Predict
    typred = model.predict(xactual)

    # --- Build DataFrame --- #
    pred_df = pd.DataFrame({
        "Date": dates,
        "Actual Close": ytrue.squeeze().values,
        "Predicted Close": typred.flatten()
    })

    # --- Calculate Metrics --- #
    pred_df["% Error Raw"] = ((pred_df["Predicted Close"] - pred_df["Actual Close"]) / pred_df["Actual Close"]) * 100

    # Compute MAPE and SAPE first
    mape = np.mean(np.abs(pred_df["% Error Raw"]))
    sape = np.std(np.abs(pred_df["% Error Raw"]))

    # Format the columns
    pred_df["Actual Close"] = pred_df["Actual Close"].round(2)
    pred_df["Predicted Close"] = pred_df["Predicted Close"].round(2)
    pred_df["% Error"] = pred_df["% Error Raw"].apply(lambda x: f"$ {x:+.2f}")
    pred_df.drop(columns=["% Error Raw"], inplace=True)

    # Add MAPE Range per row to table
    pred_df["±MAPE Range"] = pred_df["Predicted Close"].apply(
        lambda x: f"${x * (1 - mape/100):.2f} to ${x * (1 + mape/100):.2f}"
    )


    # --- Next Day Prediction --- #
    latest_close = float(data["Close"].iloc[-1])
    latest_date = data.index[-1].strftime("%Y-%m-%d")
    next_pred = float(model.predict(np.array([[latest_close]]))[0])
    next_date = (data.index[-1] + pd.tseries.offsets.BDay(1)).strftime("%Y-%m-%d")
    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    # Expected Ranges
    mape_range = (next_pred * (1 - mape/100), next_pred * (1 + mape/100))
    sape_range = (next_pred * (1 - sape/100), next_pred * (1 + sape/100))

    summary = f"""
Prediction for {Stock} made at {now}:
Last available date: {latest_date}, Close = ${latest_close:.2f}
Predicted closing price for next trading day ({next_date}): ${next_pred:.2f}
Expected range (±MAPE): ${mape_range[0]:.2f} to ${mape_range[1]:.2f}
Expected range (±SAPE):  ${sape_range[0]:.2f} to ${sape_range[1]:.2f}
"""

    # Sort and format
    pred_df = pred_df.sort_values("Date", ascending=False)
    pred_df["Date"] = pred_df["Date"].dt.strftime("%Y-%m-%d")

    return summary, pred_df

# --- Gradio Interface --- #
description = f"""<h3>Linear Regression Stock Prediction</h3>
<p>This app loads a trained linear regression model for <b>{Stock}</b> and predicts the next trading day's close based on the last available price. 
It also shows historical prediction accuracy from 2025 onward.</p>"""

demo = gr.Interface(
    fn=run_prediction,
    inputs=[],
    outputs=[
        gr.Textbox(label="Prediction Summary", lines=6),
        gr.Dataframe(label="Prediction Table (2025+)", wrap=True)
    ],
    title="Stock Prediction using Linear Regression",
    description=description,
    allow_flagging="never"
)

if __name__ == "__main__":
    demo.launch()