Akshit Chaturvedi commited on
Commit ยท
a3a6afc
1
Parent(s): 59edb04
initial commit
Browse files- .github/workflows/sync-to-hf-space.yml +40 -0
- README.md +58 -0
- app.py +154 -0
- readme.md +0 -1
- requirements.txt +6 -0
.github/workflows/sync-to-hf-space.yml
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# .github/workflows/sync-to-hf-space.yml
|
| 2 |
+
name: Sync to Hugging Face Space
|
| 3 |
+
|
| 4 |
+
on:
|
| 5 |
+
push:
|
| 6 |
+
branches:
|
| 7 |
+
- main # Or whatever your default branch is
|
| 8 |
+
|
| 9 |
+
jobs:
|
| 10 |
+
sync:
|
| 11 |
+
runs-on: ubuntu-latest
|
| 12 |
+
steps:
|
| 13 |
+
- name: Checkout repository
|
| 14 |
+
uses: actions/checkout@v3
|
| 15 |
+
with:
|
| 16 |
+
fetch-depth: 0 # Fetch all history for accurate diffs
|
| 17 |
+
|
| 18 |
+
- name: Push to Hugging Face Space
|
| 19 |
+
env:
|
| 20 |
+
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
| 21 |
+
# Your Hugging Face username
|
| 22 |
+
HF_USERNAME: GuitarGeorge
|
| 23 |
+
# The ID of your Space (username/space-name)
|
| 24 |
+
SPACE_ID: GuitarGeorge/Neural-Prophet-Predictor
|
| 25 |
+
run: |
|
| 26 |
+
echo "Attempting to push to Hugging Face Space: $SPACE_ID"
|
| 27 |
+
# Add Hugging Face as a remote
|
| 28 |
+
git remote add hf_space "https://dummy:${HF_TOKEN}@huggingface.co/spaces/${SPACE_ID}"
|
| 29 |
+
|
| 30 |
+
# Check if the remote was added
|
| 31 |
+
git remote -v
|
| 32 |
+
|
| 33 |
+
# Configure Git user
|
| 34 |
+
git config --global user.email "action@github.com"
|
| 35 |
+
git config --global user.name "GitHub Action"
|
| 36 |
+
|
| 37 |
+
# Force push to ensure the Space mirrors GitHub
|
| 38 |
+
git push --force hf_space HEAD:refs/heads/main
|
| 39 |
+
|
| 40 |
+
echo "Push to Hugging Face Space completed."
|
README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Neural Prophet Stock Predictor
|
| 3 |
+
emoji: ๐ฎ
|
| 4 |
+
colorFrom: indigo
|
| 5 |
+
colorTo: blue
|
| 6 |
+
sdk: gradio
|
| 7 |
+
sdk_version: 5.29.1
|
| 8 |
+
app_file: app.py
|
| 9 |
+
python_version: 3.10
|
| 10 |
+
pinned: false
|
| 11 |
+
---
|
| 12 |
+
|
| 13 |
+
# NeuralProphet Stock Predictor ๐
|
| 14 |
+
|
| 15 |
+
## Project Goal
|
| 16 |
+
This project uses **NeuralProphet**, a PyTorch-based hybrid forecasting framework (inspired by Facebook Prophet and Neural Networks), to predict the **90-day forward price** for any valid stock ticker found on Yahoo Finance.
|
| 17 |
+
|
| 18 |
+
Unlike simple linear regressions, this model accounts for time-series specific components such as:
|
| 19 |
+
- **Trend Changes:** Detecting shifts in the stock's growth trajectory.
|
| 20 |
+
- **Seasonality:** analyzing Yearly and Weekly patterns (e.g., does the stock typically rise in January?).
|
| 21 |
+
|
| 22 |
+
## How to Use
|
| 23 |
+
|
| 24 |
+
### Using the UI
|
| 25 |
+
1. Enter a valid Ticker Symbol in the textbox (e.g., `AAPL` for Apple, `AZN.L` for AstraZeneca UK).
|
| 26 |
+
2. Click **"Analyze Stock"**.
|
| 27 |
+
3. Wait approximately 10-30 seconds for the model to train on-the-fly.
|
| 28 |
+
4. View the **ROI Verdict**, the **Forecast Chart**, and the **Seasonality breakdown**.
|
| 29 |
+
|
| 30 |
+
### Interpretation
|
| 31 |
+
- **Blue Line (Forecast):** The predicted price path.
|
| 32 |
+
- **Yearly Seasonality:** Shows which months are historically bullish or bearish for this specific asset.
|
| 33 |
+
- **Verdict:**
|
| 34 |
+
- ๐ข **STRONG BUY:** ROI > 10%
|
| 35 |
+
- ๐ข **BUY:** ROI > 2%
|
| 36 |
+
- ๐ก **HOLD:** ROI > -5%
|
| 37 |
+
- ๐ด **SELL:** ROI <= -5%
|
| 38 |
+
|
| 39 |
+
## Model Methodology
|
| 40 |
+
|
| 41 |
+
This application trains a *fresh* model every time you request a ticker. It does not use pre-trained weights because stock data changes daily.
|
| 42 |
+
|
| 43 |
+
1. **Data Fetching:** Downloads 3 years of daily historical data via `yfinance`.
|
| 44 |
+
2. **Preprocessing:** Cleans data and handles timezone formatting.
|
| 45 |
+
3. **Training:** Fits a NeuralProphet model with:
|
| 46 |
+
* Yearly Seasonality: Enabled
|
| 47 |
+
* Weekly Seasonality: Enabled
|
| 48 |
+
* Daily Seasonality: Disabled (Noise reduction)
|
| 49 |
+
* Learning Rate: 0.01
|
| 50 |
+
4. **Forecasting:** Projects 90 days into the future.
|
| 51 |
+
|
| 52 |
+
## Tech Stack
|
| 53 |
+
- **NeuralProphet:** For time-series forecasting.
|
| 54 |
+
- **YFinance:** For live market data.
|
| 55 |
+
- **Gradio:** For the web interface.
|
| 56 |
+
- **Plotly:** For interactive charting.
|
| 57 |
+
|
| 58 |
+
*Disclaimer: Stock market predictions are inherently uncertain. Do not trade based solely on these results.*
|
app.py
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
import torch
|
| 3 |
+
import logging
|
| 4 |
+
import warnings
|
| 5 |
+
import os
|
| 6 |
+
import yfinance as yf
|
| 7 |
+
import pandas as pd
|
| 8 |
+
from neuralprophet import NeuralProphet
|
| 9 |
+
import plotly.graph_objs as go
|
| 10 |
+
|
| 11 |
+
# --- STEP 1: CONFIGURATION & PATCHES ---
|
| 12 |
+
|
| 13 |
+
# Suppress messy logs
|
| 14 |
+
logging.getLogger("neuralprophet").setLevel(logging.ERROR)
|
| 15 |
+
logging.getLogger("pytorch_lightning").setLevel(logging.ERROR)
|
| 16 |
+
warnings.filterwarnings("ignore")
|
| 17 |
+
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
|
| 18 |
+
|
| 19 |
+
# Fix for PyTorch 2.6+ security check
|
| 20 |
+
original_load = torch.load
|
| 21 |
+
def patched_load(*args, **kwargs):
|
| 22 |
+
if 'weights_only' not in kwargs:
|
| 23 |
+
kwargs['weights_only'] = False
|
| 24 |
+
return original_load(*args, **kwargs)
|
| 25 |
+
torch.load = patched_load
|
| 26 |
+
|
| 27 |
+
# --- STEP 2: PREDICTION LOGIC ---
|
| 28 |
+
|
| 29 |
+
def predict_stock(ticker):
|
| 30 |
+
"""
|
| 31 |
+
Takes a ticker symbol, trains a NeuralProphet model,
|
| 32 |
+
and returns a textual report and two Plotly figures.
|
| 33 |
+
"""
|
| 34 |
+
ticker = ticker.strip().upper()
|
| 35 |
+
|
| 36 |
+
if not ticker:
|
| 37 |
+
return "โ ๏ธ Please enter a ticker symbol.", None, None
|
| 38 |
+
|
| 39 |
+
status_msg = f"โณ Analyzing {ticker}... Please wait (approx 10-30 seconds)."
|
| 40 |
+
print(f"Processing {ticker}")
|
| 41 |
+
|
| 42 |
+
try:
|
| 43 |
+
# 1. Get Data
|
| 44 |
+
data = yf.download(ticker, period="3y", interval="1d", progress=False)
|
| 45 |
+
|
| 46 |
+
# Handle cases where yfinance returns empty dataframe or multi-index columns
|
| 47 |
+
if data.empty:
|
| 48 |
+
return f"โ Could not find data for ticker '{ticker}'. Please check the symbol.", None, None
|
| 49 |
+
|
| 50 |
+
# Flatten MultiIndex if present (yfinance update quirk)
|
| 51 |
+
if isinstance(data.columns, pd.MultiIndex):
|
| 52 |
+
try:
|
| 53 |
+
# Attempt to extract just the Close column for the specific ticker
|
| 54 |
+
df = data.xs(ticker, axis=1, level=1)
|
| 55 |
+
if 'Close' in df.columns:
|
| 56 |
+
df = df[['Close']].reset_index()
|
| 57 |
+
else:
|
| 58 |
+
# Fallback for single level extraction
|
| 59 |
+
df = data['Close'].reset_index()
|
| 60 |
+
except:
|
| 61 |
+
# Brute force flatten
|
| 62 |
+
df = data.copy()
|
| 63 |
+
df.columns = ['_'.join(col).strip() for col in df.columns.values]
|
| 64 |
+
# Look for a column containing "Close"
|
| 65 |
+
close_col = [c for c in df.columns if "Close" in c][0]
|
| 66 |
+
df = df[[close_col]].reset_index()
|
| 67 |
+
else:
|
| 68 |
+
df = data[['Close']].reset_index()
|
| 69 |
+
|
| 70 |
+
# Rename for NeuralProphet
|
| 71 |
+
df.columns = ['ds', 'y']
|
| 72 |
+
|
| 73 |
+
# Ensure dates are timezone-naive
|
| 74 |
+
df['ds'] = df['ds'].dt.tz_localize(None)
|
| 75 |
+
|
| 76 |
+
if len(df) < 100:
|
| 77 |
+
return f"โ Not enough historical data found for {ticker} (Need > 100 days).", None, None
|
| 78 |
+
|
| 79 |
+
# 2. Train Model
|
| 80 |
+
m = NeuralProphet(
|
| 81 |
+
yearly_seasonality=True,
|
| 82 |
+
weekly_seasonality=True,
|
| 83 |
+
daily_seasonality=False,
|
| 84 |
+
learning_rate=0.01,
|
| 85 |
+
# Disable progress bars for clean logs
|
| 86 |
+
trainer_config={"enable_progress_bar": False}
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
m.fit(df, freq="D")
|
| 90 |
+
|
| 91 |
+
# 3. Predict 90 Days out
|
| 92 |
+
future = m.make_future_dataframe(df, periods=90)
|
| 93 |
+
forecast = m.predict(future)
|
| 94 |
+
|
| 95 |
+
# 4. Extract Metrics
|
| 96 |
+
current_price = df['y'].iloc[-1]
|
| 97 |
+
predicted_price = forecast['yhat1'].iloc[-1]
|
| 98 |
+
|
| 99 |
+
# Calculate ROI
|
| 100 |
+
roi = ((predicted_price - current_price) / current_price) * 100
|
| 101 |
+
|
| 102 |
+
# Generate Verdict
|
| 103 |
+
if roi > 10: verdict = "STRONG BUY ๐ข"
|
| 104 |
+
elif roi > 2: verdict = "BUY ๐ข"
|
| 105 |
+
elif roi > -5: verdict = "HOLD ๐ก"
|
| 106 |
+
else: verdict = "SELL ๐ด"
|
| 107 |
+
|
| 108 |
+
# 5. formatting Output Text
|
| 109 |
+
report = f"""
|
| 110 |
+
### ๐ Analysis Report: {ticker}
|
| 111 |
+
**Current Price:** {current_price:.2f}
|
| 112 |
+
**90-Day Target:** {predicted_price:.2f}
|
| 113 |
+
**Projected ROI:** {roi:.2f}%
|
| 114 |
+
**Verdict:** {verdict}
|
| 115 |
+
|
| 116 |
+
*Disclaimer: This is an AI-generated forecast based on historical trends. Not financial advice.*
|
| 117 |
+
"""
|
| 118 |
+
|
| 119 |
+
# 6. Generate Plots
|
| 120 |
+
# NeuralProphet plot() returns a plotly figure object
|
| 121 |
+
fig_forecast = m.plot(forecast)
|
| 122 |
+
fig_components = m.plot_components(forecast)
|
| 123 |
+
|
| 124 |
+
return report, fig_forecast, fig_components
|
| 125 |
+
|
| 126 |
+
except Exception as e:
|
| 127 |
+
import traceback
|
| 128 |
+
traceback.print_exc()
|
| 129 |
+
return f"โ An error occurred while processing {ticker}: {str(e)}", None, None
|
| 130 |
+
|
| 131 |
+
# --- STEP 3: GRADIO INTERFACE ---
|
| 132 |
+
|
| 133 |
+
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
| 134 |
+
gr.Markdown("# ๐ NeuralProphet Stock Predictor")
|
| 135 |
+
gr.Markdown("Enter a stock ticker (e.g., `AAPL`, `TSLA`, `AZN.L`) to generate a 90-day forecast.")
|
| 136 |
+
|
| 137 |
+
with gr.Row():
|
| 138 |
+
ticker_input = gr.Textbox(label="Ticker Symbol", placeholder="e.g. AZN.L", value="AZN.L")
|
| 139 |
+
submit_btn = gr.Button("Analyze Stock", variant="primary")
|
| 140 |
+
|
| 141 |
+
result_text = gr.Markdown(label="Verdict")
|
| 142 |
+
|
| 143 |
+
with gr.Row():
|
| 144 |
+
plot1 = gr.Plot(label="Price Forecast")
|
| 145 |
+
plot2 = gr.Plot(label="Seasonality Components")
|
| 146 |
+
|
| 147 |
+
submit_btn.click(
|
| 148 |
+
fn=predict_stock,
|
| 149 |
+
inputs=ticker_input,
|
| 150 |
+
outputs=[result_text, plot1, plot2]
|
| 151 |
+
)
|
| 152 |
+
|
| 153 |
+
if __name__ == "__main__":
|
| 154 |
+
demo.launch()
|
readme.md
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
|
|
|
|
|
|
requirements.txt
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
neuralprophet
|
| 2 |
+
yfinance
|
| 3 |
+
pandas
|
| 4 |
+
gradio
|
| 5 |
+
plotly
|
| 6 |
+
torch
|