← all research

Free yfinance Ticker.news (keyword lexicon proxy for FinBERT) on a 20-name S&P pilot. Signal = average headline sentiment per symbol per day (title+summary), cross-sectional top decile long, hold 5 trading days, equal-weight, net 10bps next_open (validation/framework.py:118, lag>=1).

Fetched 200 headlines (2026-06-19 .. 2026-08-29) across 20 symbols; signal panel 8 symbols, 23 daily cells.

VariantWindowRet (cum)Sharpe(252)MaxDDpCorrMktCorrEqMomResidSharpe
News top decile (hold5, keyword)recent 90d+0.00%0.000.00%1.000nannan0.00
News top decile (hold5, keyword)full+0.00%0.000.00%1.000nannan0.00
EqMom baseline (hold21)full+3165.97%0.86-54.71%0.0001.001.000.00

Verdict (pilot, n is tiny — not significant by construction)

  • News top decile (hold5, keyword) recent 90d: REJECT (p=1.000, Sharpe 0.00, corrEqMom nan, resid 0.00) — pilot sample too short for free yfinance history; pipeline works but edge is unverified.

Limitations & upgrade path

  • Depth: yfinance returns ~10 headlines per ticker, covering days-weeks, not years. So a long IS/OOS (2016) backtest is impossible free — the 90d recent window above is the honest pilot window. A real test needs a paid archive (RavenPack, Benzinga, Alpaca News news history with as_of lag).

  • Sentiment: keyword lexicon is a noisy FinBERT proxy. Production upgrade: pip install transformers then pipeline('sentiment-analysis', model='ProsusAI/finbert', tokenizer='ProsusAI/finbert') — same title+summary input, output label in {positive,negative,neutral} with score; map to +score/-score. The rest of the pipeline (daily panel -> cross_section_portfolio_net) is unchanged.

  • Timing: headlines are intraday (pubDate has hour). This pilot buckets by calendar date and uses lag1 (hold5 with shift(1)), so a headline at 10am on D is tradable at D+1 open — no look-ahead. A true intraday news bot would need a minutely loop like production/live_runner_alt.py:30 (00:15 UTC daily is too slow for headline alpha which decays in minutes).

  • Mic check: see news['title'] sample below for sanity (first 5 scored headlines):

    • 2026-08-28 MMM: score +0.00 — Carlisle (CSL) Down 2.5% Since Last Earnings Report: Can It Rebound?
    • 2026-08-28 MMM: score +0.00 — 2 Cash-Producing Stocks for Long-Term Investors and 1 We Question
    • 2026-08-27 MMM: score +1.00 — Will 3M Be Able to Sustain Margin Momentum Amid Cost Pressures?
    • 2026-08-27 MMM: score +0.00 — What’s Going On With 3M (MMM)?
    • 2026-08-26 MMM: score +0.00 — Can Honeywell Technologies Boost Margin Performance Amid Cost Pressures?

Next step if pilot shows promise

  1. Subscribe to Alpaca News API (or Benzinga) for historical headlines with as_of lag, 2) swap keyword scorer for FinBERT, 3) promote to an intraday production/news_sleeve.py (minutely loop, own .env.news account, fail-closed like pead_sleeve.py:1).