Pick a stock, check the news/metadata features you want a LightGBM model
trained on, and see predicted vs. actual closing prices for the last 30
trading days — live, on localhost. Every checkbox or ticker change
retrains the model from scratch.
- You pick a ticker from the 21-stock list and toggle any subset of:
vader_sentiment,loughran_mcdonald_sentiment,article_length,publish_day_of_week,publish_month,publish_year,company_name,industry,event_date. - News features come from
processed_ticker_data_enriched.csv(one row per article, columns:ticker, event_date, text, publish_date, prenews_rolling_vol_20d, prenews_rolling_vol_252d, prenews_volume_zscore_20d, prenews_momentum_5d, prenews_close, target_horizon_return, text_clean, vader_sentiment, loughran_mcdonald_sentiment, article_length, publish_day_of_week, publish_month, publish_year, company_name, industry). Rows are filtered to the chosen ticker with an exact match on thetickercolumn (not a fuzzy company-name match), then collapsed to one row per calendar day:vader_sentiment,loughran_mcdonald_sentiment,article_length→ averaged per day, alongside an auto-computedarticle_count.- Days with no articles forward-fill the last known values; days before
the first-ever article default to neutral
0. publish_day_of_week/publish_month/publish_yearare derived directly from the date (guaranteed consistent).company_name/industry, when checked, are read directly off the CSV rows for that ticker (not hardcoded) and held constant across days.
- Price features (always included, regardless of checkboxes) come
fresh from
yfinancefor every trading day in the window — not just article days: lagged returns (1/2/5-day), rolling mean/std of returns, and volume change.
Your CSV also has text, text_clean (raw/cleaned article text — not a
numeric or categorical model input), publish_date (when the article was
published, as opposed to event_date, which is what the app joins on),
and four prenews_* columns plus target_horizon_return
(prenews_rolling_vol_20d, prenews_rolling_vol_252d,
prenews_volume_zscore_20d, prenews_momentum_5d, prenews_close). Those
prenews_*/target_horizon_return columns look like they were computed
per-article from historical price data at CSV build time. This app
intentionally doesn't use them and instead computes its own lag/rolling
price features fresh from yfinance for every trading day (not just days
with an article) — mixing a per-article snapshot feature set with a
daily one would leave most trading days with no prenews_* value at all.
If you'd rather use your precomputed prenews_* columns instead of (or
alongside) the app's own price features, that's a straightforward addition
to SELECTABLE_FEATURES in config.py and the feature list in
model_pipeline.py — just say the word.
4. Train/test split: the model trains on all data through 32 days
ago, then is evaluated on the most recent 30 trading days (the
2-day gap is a buffer so lag features at the start of the test window
aren't computed from training data). This is a backtest — the model
predicts each day's return using only same-day/prior-day features, and
that's turned into a predicted close using each day's actual
previous close (so one bad day doesn't compound into the next).
5. The chart plots actual vs. predicted close for those 30 days, plus
MAE/RMSE.
git clone <your-repo-url>
cd stock-sentiment-predictor
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txtDrop your processed_ticker_data_enriched.csv in the project root, next to
app.py. It must contain at least ticker and event_date columns; any
of the other checkbox columns you want to use should also be present.
python app.pyOpen http://localhost:5000.
The 21-ticker dropdown list lives in config.py (TICKERS) purely for
display labels. The app filters your CSV by an exact match on the
ticker column, so as long as your CSV's ticker values are standard
symbols (AAPL, MSFT, ...) it'll just work. If a ticker in the dropdown
has no matching rows in your CSV, the app still runs — it falls back to
the dropdown label for company_name/"Unknown" for industry, uses
all-zero sentiment, and shows a warning banner saying so.
cd stock-sentiment-predictor
git init
git add .
git commit -m "Initial commit"
git branch -M main
git remote add origin https://github.com/<your-username>/<your-repo>.git
git push -u origin main(processed_ticker_data_enriched.csv isn't excluded by .gitignore — add
it yourself before committing if you want it in the repo, or leave it out
and note in your repo that users must supply their own.)
stock-sentiment-predictor/
├── app.py # Flask routes
├── model_pipeline.py # data loading, feature engineering, training
├── config.py # ticker list + feature list + windows
├── requirements.txt
├── templates/index.html
└── static/
├── css/style.css
└── js/main.js