Next-day price-direction forecasting for any Yahoo Finance ticker (shipped:
IndiGo INDIGO.NS, also tested on AAPL). Combines news-headline
embeddings with price/technical/HAR features, evaluated with a strict
time-ordered split (no look-ahead).
Target: y_t = 1[ Close_{t+1} > Close_t ]
Forked from nehasane/mantis_indigo_forecasting,
a single script that only used news embeddings (never price data) and split
train/test by row position instead of date, letting same-day headlines leak
across the split. This version adds real price/HAR features and a strict
date-ordered split, then tests 5 follow-up ideas for squeezing out more
accuracy (see Experiment log).
python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt
brew install libomp # macOS only, needed for xgboost/lightgbmShipped data runs fully offline:
python scripts/04_run_pipeline.py # defaults to INDIGO.NSFor a new ticker, run in order (each step needs network except the last):
python scripts/01_download_prices.py --ticker AAPL
python scripts/02_scrape_news.py --ticker AAPL # GDELT, free, no key
python scripts/03_embed_news.py --ticker AAPL
python scripts/04_run_pipeline.py --ticker AAPLexport MANTIS_TICKER=AAPL works instead of passing --ticker every time.
GDELT rate-limits to 1 request/5s. If you see repeated throttled warnings,
stop, wait ~5 min, and rerun — don't run two scrapes at once.
The model can skip days it's not confident about instead of guessing on every day — trades coverage for accuracy:
python scripts/05_confidence_report.py --ticker INDIGO.NS threshold accuracy coverage
(none) 0.578 100.0%
0.60 0.634 56.9%
data/raw/ per-ticker prices, headlines, embeddings
data/processed/ cleaned prices
scripts/ 01-04 pipeline steps, 05 confidence report
src/mantis/config.py ticker registry + all tunable constants
src/mantis/data/ download + clean prices/news/embeddings/index
src/mantis/features/ technical, HAR, news, market features -> feature table
src/mantis/models/ splits, metrics, model zoo, comparison
src/mantis/pipeline.py end-to-end run
tests/ leakage guardrails + ticker-scoping checks
Every artifact is ticker-scoped (indigo_*, aapl_*) so multiple tickers
coexist without clobbering each other.
Holdout accuracy sits near a coin flip (~0.50-0.58) under a leakage-free split — that's expected and honest. The original repo's higher numbers came from the leak this version removes.
| Ticker | Best model (feature set) | Holdout acc | Majority baseline |
|---|---|---|---|
| INDIGO.NS | lightgbm (all) | 0.578 | 0.502 |
| AAPL | lightgbm (price+har) | 0.542 | 0.527 |
Five ideas tested against the 0.578 INDIGO.NS baseline, one at a time:
| Idea | Result | Verdict |
|---|---|---|
| Longer horizon (predict 3/5/10 days out) | 0.581 at 5 days | Wash, kept default at 1 day |
| More news (scraper stopped at 2024, prices go to 2026) | 0.566 after adding 2025-26 headlines | Hurt, reverted |
| Only predict on confident days | 0.634 at 57% coverage | Real win, shipped as scripts/05 |
| Fuel-price proxy (crude oil futures) | 0.561 | Hurt, not adopted |
| Test ideas one at a time | — | Applied throughout; caught the two negative results above |
A sixth idea from earlier (broad market index as a feature) also hurt
accuracy (0.549). Left in the code as an opt-in flag
(include_market=True on build_feature_table, off by default) rather than
deleted, so it's reproducible.
Why more columns kept hurting: only ~1,100 rows of data, so extra features mostly just give tree models more noise to overfit. The one idea that worked didn't add data — it just let the model decline to guess when unsure.
python -m pytest -qChecks no look-ahead bias (train dates strictly precede test dates, walk-forward folds never see the future) and that ticker-scoped paths never collide.
| Symptom | Fix |
|---|---|
No raw prices at ... |
Run script 01 for that ticker first |
No news embeddings at ... |
Run scripts 02 then 03 |
GDELT throttled repeatedly |
Wait ~5 min, rerun, don't parallelize scrapes |
| XGBoost/LightGBM import errors on macOS | brew install libomp |