news-scanner
You hand over a watchlist and a time window. The skill pulls every news
event Massive has on those tickers in the window, derives a sentiment
score per ticker per article, measures whether the angle is novel or a
re-run, computes the stock's price reaction since publish, ranks events
by impact, and emits a stream of the top N.
This is the workflow a sell-side analyst runs at 6am to write the
morning note. Twenty headlines that actually moved a stock, with the
context (sentiment + novelty + reaction + volume anomaly + divergence
flag) to write about them in 30 minutes. Unlike a news terminal or RSS
reader, news-scanner ranks events by signal quality (price reaction ×
volume anomaly × novelty) rather than recency, and ships the
methodology with the output.
When to invoke
- An analyst is prepping the morning note and wants the overnight tape
ranked by impact
- A PM is asking "what's the news on my book today"
- The user says "scan news on NVDA TSLA AAPL", "what moved overnight",
or "any catalyst on my watchlist"
- A trader wants to spot price/news divergence (negative headline,
positive reaction = bad news already priced in)
What you need
- A watchlist of tickers (default: NVDA, TSLA, AAPL, SPY, META, NFLX)
- A time window in hours (default: last 24h)
MASSIVE_API_KEY exported in the environment
- Stocks Basic + Benzinga News add-on minimum
The skill runs at two fidelity tiers.
- Tier A (Benzinga sentiment + minute aggs): Benzinga News add-on
returns per-ticker
insights[] with a categorical sentiment label
("positive" / "negative" / "neutral") and sentiment_reasoning from
Benzinga's own NLP. Stocks Starter or higher gives reliable minute
aggregates for the reaction window. This is the default tier.
- Tier B (keyword fallback): Benzinga News add-on missing or the
Benzinga
insights field is empty. Sentiment falls back to a keyword
scorer (positive: beat, raise, partnership, upgrade; negative: cut,
miss, lawsuit, downgrade, recall, probe). Reaction calc still works
on Stocks Basic but uses 5-minute aggregates instead of 1-minute.
Documented in references/sentiment-scoring.md.
What you get back
Two output layers from one analysis.
Layer 1: canonical JSON matching output-schema.json.
Per-event fields: ticker, published_at, source, headline, url,
sentiment_score (in [-1, +1]), sentiment_source ("benzinga" or
"keyword"), novelty_score, novelty_band, reaction_pct_since_publish,
reaction_window_label, volume_anomaly_x, divergence_flag, context_line.
UIs, alert pipelines, and downstream agents consume this.
Layer 2: rendered stream in Bloomberg news-tape / Benzinga Pro
style. Three lines per event, optional ↳ continuation for context.
Format rules in references/rendering.md.
Compact, scanable, key:value pairs. Claude Code users read this.
How it works
- For each ticker in the watchlist, pull
/v2/reference/news?ticker={t}&published_utc.gte={window_start}&limit=50.
Dedupe by article_url across the merged set so a story syndicated
across publishers only appears once. See
references/news-sources-and-coverage.md.
- Score sentiment per (ticker, article). Prefer the Benzinga
insights
entry for that ticker if present (map "positive" → +0.7, "neutral" →
0, "negative" → -0.7); otherwise fall back to a keyword scorer over
the title and description. See
references/sentiment-scoring.md.
- Score novelty per (ticker, article). Bucket the last 7 days of
articles for the ticker; compute TF-IDF over titles + first-sentence
of description; cosine distance to nearest neighbor in the bucket.
Distance > 0.6 = high novelty (new angle), 0.3-0.6 = medium, < 0.3 =
low (already covered). See
references/novelty-detection.md.
- Compute the price reaction. Pull
/v2/aggs/ticker/{ticker}/range/5/minute/...
from publish minute through min(publish + 60 minutes, market close).
Reaction % = (close at window end / close at publish minute) - 1.
Volume anomaly = avg per-minute volume during the window /
prior-5-day same-time-of-day average per-minute volume.
- Flag price/news divergence per
references/price-news-divergence.md:
positive sentiment + negative reaction = priced in / sell-the-news;
negative sentiment + positive reaction = bad news already priced in.
- Rank by
impact = |reaction_pct| × volume_anomaly × novelty_score.
See references/impact-ranking.md.
Emit top N (default 15).
Foundations used
massive-api-patterns for REST auth, the
best-price fallback chain for spot, and rate-limit handling on the
per-ticker news fan-out
Output mode: stream
Stream mode is the format Bloomberg's news tape, Benzinga Pro's feed,
and Reuters Eikon use for incoming events. Each event is a
self-contained block; the reader scans top to bottom and stops when
they see one they want to act on. Inherited from
options-flow/references/rendering.md,
adapted for news per
references/rendering.md.
Endpoints used
GET /v2/reference/news?ticker={t}&published_utc.gte={iso}&limit=50:
Benzinga News. Returns results[] with id, title, description,
published_utc, article_url, tickers[], keywords[],
publisher.name, and a critical insights[] array with per-ticker
sentiment ("positive"/"negative"/"neutral") and sentiment_reasoning.
GET /v2/aggs/ticker/{ticker}/range/5/minute/{from}/{to}: minute (or
5-minute) aggregates for the underlying. Used to compute reaction %
and volume anomaly post-publish.
GET /v2/snapshot/locale/us/markets/stocks/tickers/{ticker}: spot
fallback chain for the "spot at publish" reference when minute aggs
are missing or stale.
Doesn't handle (yet)
- Sector / peer reaction. A positive NVDA story usually moves AMD and
AVGO too; the skill doesn't surface sympathy plays. Clean v2.
- Wire-service deduplication beyond URL match. Reuters → Bloomberg →
CNBC rewrites of the same story have different URLs and different
first sentences; TF-IDF catches most but not all. A more rigorous
story-clustering pass (LSH or sentence embeddings) is a v2 candidate.
- Real-time WebSocket streaming. v1 is REST-polled. The
massive-websockets foundation covers the live-stream pattern for a
future variant.
- Insider transactions, SEC filings, and FDA calendar items. These are
catalyst-class news that don't ship through the Benzinga News feed;
they live on the corporate-actions and reference endpoints. Separate
skill.
These are clean PR extensions and welcome contributions.
1---2name: news-scanner3description: Surface the day's news events that actually moved a stock. For each notable headline across a watchlist (or the broader market), render a Bloomberg news tape / Benzinga Pro-style stream with sentiment, novelty, and the post-publish price reaction. Ranked by impact, capped at top N (default 15-20). The 6am sell-side morning-note prep workflow.4---56# news-scanner78You hand over a watchlist and a time window. The skill pulls every news9event Massive has on those tickers in the window, derives a sentiment10score per ticker per article, measures whether the angle is novel or a11re-run, computes the stock's price reaction since publish, ranks events12by impact, and emits a stream of the top N.1314This is the workflow a sell-side analyst runs at 6am to write the15morning note. Twenty headlines that actually moved a stock, with the16context (sentiment + novelty + reaction + volume anomaly + divergence17flag) to write about them in 30 minutes. Unlike a news terminal or RSS18reader, news-scanner ranks events by signal quality (price reaction ×19volume anomaly × novelty) rather than recency, and ships the20methodology with the output.2122## When to invoke2324- An analyst is prepping the morning note and wants the overnight tape25 ranked by impact26- A PM is asking "what's the news on my book today"27- The user says "scan news on NVDA TSLA AAPL", "what moved overnight",28 or "any catalyst on my watchlist"29- A trader wants to spot price/news divergence (negative headline,30 positive reaction = bad news already priced in)3132## What you need3334- A watchlist of tickers (default: NVDA, TSLA, AAPL, SPY, META, NFLX)35- A time window in hours (default: last 24h)36- `MASSIVE_API_KEY` exported in the environment37- Stocks Basic + Benzinga News add-on minimum3839The skill runs at two fidelity tiers.4041- **Tier A (Benzinga sentiment + minute aggs):** Benzinga News add-on42 returns per-ticker `insights[]` with a categorical sentiment label43 ("positive" / "negative" / "neutral") and `sentiment_reasoning` from44 Benzinga's own NLP. Stocks Starter or higher gives reliable minute45 aggregates for the reaction window. This is the default tier.46- **Tier B (keyword fallback):** Benzinga News add-on missing or the47 Benzinga `insights` field is empty. Sentiment falls back to a keyword48 scorer (positive: beat, raise, partnership, upgrade; negative: cut,49 miss, lawsuit, downgrade, recall, probe). Reaction calc still works50 on Stocks Basic but uses 5-minute aggregates instead of 1-minute.51 Documented in [`references/sentiment-scoring.md`](./references/sentiment-scoring.md).5253## What you get back5455Two output layers from one analysis.5657**Layer 1: canonical JSON** matching [`output-schema.json`](./output-schema.json).58Per-event fields: ticker, published_at, source, headline, url,59sentiment_score (in [-1, +1]), sentiment_source ("benzinga" or60"keyword"), novelty_score, novelty_band, reaction_pct_since_publish,61reaction_window_label, volume_anomaly_x, divergence_flag, context_line.62UIs, alert pipelines, and downstream agents consume this.6364**Layer 2: rendered stream** in Bloomberg news-tape / Benzinga Pro65style. Three lines per event, optional `↳` continuation for context.66Format rules in [`references/rendering.md`](./references/rendering.md).67Compact, scanable, key:value pairs. Claude Code users read this.6869## How it works70711. For each ticker in the watchlist, pull72 `/v2/reference/news?ticker={t}&published_utc.gte={window_start}&limit=50`.73 Dedupe by `article_url` across the merged set so a story syndicated74 across publishers only appears once. See75 [`references/news-sources-and-coverage.md`](./references/news-sources-and-coverage.md).762. Score sentiment per (ticker, article). Prefer the Benzinga `insights`77 entry for that ticker if present (map "positive" → +0.7, "neutral" →78 0, "negative" → -0.7); otherwise fall back to a keyword scorer over79 the title and description. See80 [`references/sentiment-scoring.md`](./references/sentiment-scoring.md).813. Score novelty per (ticker, article). Bucket the last 7 days of82 articles for the ticker; compute TF-IDF over titles + first-sentence83 of description; cosine distance to nearest neighbor in the bucket.84 Distance > 0.6 = high novelty (new angle), 0.3-0.6 = medium, < 0.3 =85 low (already covered). See86 [`references/novelty-detection.md`](./references/novelty-detection.md).874. Compute the price reaction. Pull `/v2/aggs/ticker/{ticker}/range/5/minute/...`88 from publish minute through min(publish + 60 minutes, market close).89 Reaction % = (close at window end / close at publish minute) - 1.90 Volume anomaly = avg per-minute volume during the window /91 prior-5-day same-time-of-day average per-minute volume.925. Flag price/news divergence per93 [`references/price-news-divergence.md`](./references/price-news-divergence.md):94 positive sentiment + negative reaction = priced in / sell-the-news;95 negative sentiment + positive reaction = bad news already priced in.966. Rank by `impact = |reaction_pct| × volume_anomaly × novelty_score`.97 See [`references/impact-ranking.md`](./references/impact-ranking.md).98 Emit top N (default 15).99100## Foundations used101102- [`massive-api-patterns`](../massive-api-patterns) for REST auth, the103 best-price fallback chain for spot, and rate-limit handling on the104 per-ticker news fan-out105106## Output mode: stream107108Stream mode is the format Bloomberg's news tape, Benzinga Pro's feed,109and Reuters Eikon use for incoming events. Each event is a110self-contained block; the reader scans top to bottom and stops when111they see one they want to act on. Inherited from112[`options-flow/references/rendering.md`](../options-flow/references/rendering.md),113adapted for news per114[`references/rendering.md`](./references/rendering.md).115116## Endpoints used117118- `GET /v2/reference/news?ticker={t}&published_utc.gte={iso}&limit=50`:119 Benzinga News. Returns `results[]` with `id`, `title`, `description`,120 `published_utc`, `article_url`, `tickers[]`, `keywords[]`,121 `publisher.name`, and a critical `insights[]` array with per-ticker122 `sentiment` ("positive"/"negative"/"neutral") and `sentiment_reasoning`.123- `GET /v2/aggs/ticker/{ticker}/range/5/minute/{from}/{to}`: minute (or124 5-minute) aggregates for the underlying. Used to compute reaction %125 and volume anomaly post-publish.126- `GET /v2/snapshot/locale/us/markets/stocks/tickers/{ticker}`: spot127 fallback chain for the "spot at publish" reference when minute aggs128 are missing or stale.129130## Doesn't handle (yet)131132- Sector / peer reaction. A positive NVDA story usually moves AMD and133 AVGO too; the skill doesn't surface sympathy plays. Clean v2.134- Wire-service deduplication beyond URL match. Reuters → Bloomberg →135 CNBC rewrites of the same story have different URLs and different136 first sentences; TF-IDF catches most but not all. A more rigorous137 story-clustering pass (LSH or sentence embeddings) is a v2 candidate.138- Real-time WebSocket streaming. v1 is REST-polled. The139 `massive-websockets` foundation covers the live-stream pattern for a140 future variant.141- Insider transactions, SEC filings, and FDA calendar items. These are142 catalyst-class news that don't ship through the Benzinga News feed;143 they live on the corporate-actions and reference endpoints. Separate144 skill.145146These are clean PR extensions and welcome contributions.