Build Real-Time Market-Moving News Alerts (Python Tutorial)

Kent Hudson

Kent Hudson

·

20 mins lezen

Build Real-Time Market-Moving News Alerts (Python Tutorial)

How to Build Real-Time Market-Moving News Alerts

A market-moving news alert is a real-time notification fired only when a news item is likely to move an asset's price — that is, when a credible, breaking story about a material entity carries genuine surprise, not merely positive or negative sentiment. The hard part is not fetching news; it is deciding which 1% of the firehose is worth waking a trading system for. This tutorial builds that decision engine in Python: a poll → score → dedupe → push pipeline with a rigorous, tunable scoring model.

This guide is for FinTech developers and algo-trading engineers building a real-time alert pipeline. By the end you will have runnable code that turns a raw news feed into market-moving news alerts, plus a latency budget so you know whether polling is fast enough for your strategy.

Disclosure: This tutorial is published by APITube (apitube.io), a news API provider, and the live examples call its API. The method is vendor-neutral — every signal it uses (breaking flag, entity tagging, source credibility, category, sentiment) is standard in modern financial news APIs, so the pipeline works with any feed that exposes those fields.

Key takeaways:

  • Sentiment is the wrong signal — score surprise and materiality, not polarity.
  • A weighted Market-Moving Score with a numeric threshold beats "alert on negative news."
  • The full pipeline is poll → score → dedupe → push, and it fits in ~100 lines of Python.
  • Know your latency budget: polling every 5s is fine for some strategies and far too slow for others.

Table of contents

Why sentiment is the wrong signal

Most "market-moving news alert" tutorials are really sentiment tutorials: fetch headlines, run a sentiment model, alert on strongly positive or negative scores. That is the single most common mistake in this space, and it is wrong for a structural reason.

By the time a story is unambiguously positive or negative, the move is already priced in. Markets react to surprise — new, credible, material information the market has not yet absorbed. A headline's emotional tone is a poor proxy for its informational surprise.

Here is a real example from the APITube feed. The headline "Nvidia plans $25B bond sale, its first in five years" scored a sentiment of −0.01 (neutral). By a sentiment-threshold alert, it never fires. Yet it is obviously market-relevant: the entity Nvidia appears with frequency 3, and the article is categorized under debt market, market and exchange, and interest rates. A first bond sale in five years from a megacap is material news — and a polarity filter is blind to it.

The shift in one line: stop asking "is this headline positive or negative?" and start asking "is this credible, breaking, material, and surprising?" Sentiment magnitude can be one input, but its direction is noise.

What actually makes news market-moving

News is market-moving when it combines five observable signals: entity materiality, a breaking flag, source credibility, a finance-relevant category, and a multi-source coverage spike — surprise about a material entity from a credible source, not sentiment direction. Drop polarity as the gatekeeper and that better signal set appears. The core data types a financial news API exposes — article metadata, entity/ticker tagging, sentiment, coverage-volume signals, and source attribution — map directly onto five observable signals of market-movement:

  1. Entity materiality — does the story name a tracked ticker/company, and how prominently (mention frequency)? An article mentioning your watchlist entity three times is more material than one mentioning it once.
  2. Breaking flag — is the feed marking this as urgent (is_breaking)? Breaking news is, by definition, the surprise the market hasn't absorbed.
  3. Source credibility — is the source authoritative (an Open PageRank score) rather than a low-quality reblog? Credible sources move prices; content farms don't.
  4. Finance-category match — is the article tagged with a market-relevant category (earnings, M&A, debt market, interest rates, regulation)? This filters lifestyle and PR noise.
  5. Coverage spike — how many distinct sources are clustering on the same story (story.id) in a short window? Rapid multi-source pickup is a materiality multiplier.

Notice what is not on the list as a gate: sentiment direction. We keep sentiment magnitude (|score|) as a minor input — a strongly-toned story is marginally more likely to move — but we never require a positive or negative sign.

The Market-Moving Score

Combine those signals into one weighted score from 0 to 100, then alert above a threshold. This is the decision framework the sentiment tutorials never give you. Tune the weights against your own labeled price-reaction data; the defaults below are a sensible starting point.

SignalCondition for full pointsWeightWhy
Entity materialityWatchlist entity, frequency ≥ 230Relevance to your book is the precondition
Breaking flagis_breaking == true20Direct surprise signal
Source credibilitysource.rankings.opr ≥ 520Credible sources move prices
Finance-category matchCategory in {earnings, M&A, debt, rates, regulation}20Filters non-market noise
Coverage spike≥ 3 distinct sources on the story.id in 10 min10Multi-source pickup = materiality
Sentiment magnitude (bonus)|score| ≥ 0.5+5Tone intensity, not direction

Read the resulting score in bands:

  • 70–100 — Fire alert. Material, credible, surprising. Push immediately.
  • 40–69 — Queue / digest. Relevant but not urgent; batch into a periodic digest.
  • 0–39 — Drop. Below the noise floor; log and ignore.

Applied to the Nvidia example: entity materiality (30) + finance-category match for debt/rates (20) + a credible source at OPR 5 (20) = 70, even with is_breaking false and neutral sentiment. It fires. A sentiment filter scored it ~0 and stayed silent. That gap is the whole point of the model.

Build it: the alert pipeline

The pipeline has four stages: poll the feed for new articles, score each one, dedupe so one story fires once, and push the survivors. Start with a single real request.

curl -s "https://api.apitube.io/v1/news/everything?api_key=YOUR_KEY&language.code=en&title=Nvidia&published_at.start=2026-06-16T00:00:00Z&per_page=50"

Each article carries the scoring signals directly:

{
  "results": [
    {
      "id": 3098112233,
      "title": "Nvidia plans $25B bond sale, its first in five years",
      "published_at": "2026-06-15T20:30:39.000Z",
      "is_breaking": false,
      "story": { "id": 3098112233 },
      "is_duplicate": false,
      "source": { "domain": "cryptobriefing.com", "rankings": { "opr": 5 } },
      "sentiment": { "overall": { "score": -0.01, "polarity": "neutral" } },
      "entities": [ { "name": "Nvidia", "type": "organization", "frequency": 3 } ],
      "categories": [ { "name": "debt market" }, { "name": "interest rates" } ]
    }
  ]
}

Now the scorer. It takes a raw article dict and returns a 0–100 score using the weights above:

WATCHLIST = {"Nvidia", "Apple", "Tesla", "Microsoft"}
FINANCE_CATS = {"earnings", "mergers and acquisitions", "debt market",
                "interest rates", "market and exchange", "regulation"}

def score_article(a: dict, coverage_count: int = 1) -> int:
    score = 0
    # 1. Entity materiality (30)
    for e in a.get("entities", []):
        if e.get("name") in WATCHLIST and e.get("frequency", 0) >= 2:
            score += 30
            break
    # 2. Breaking flag (20)
    if a.get("is_breaking"):
        score += 20
    # 3. Source credibility (20)
    if (a.get("source", {}).get("rankings", {}) or {}).get("opr", 0) >= 5:
        score += 20
    # 4. Finance-category match (20)
    cats = {c.get("name") for c in a.get("categories", [])}
    if cats & FINANCE_CATS:
        score += 20
    # 5. Coverage spike (10)
    if coverage_count >= 3:
        score += 10
    # Bonus: sentiment MAGNITUDE, not direction (+5)
    if abs((a.get("sentiment", {}).get("overall", {}) or {}).get("score", 0)) >= 0.5:
        score += 5
    return min(score, 100)

The poll loop tracks the last-seen timestamp and asks only for newer articles, so you never reprocess the same window. Dedup uses story.id (and the is_duplicate flag) so a story covered by twenty outlets fires one alert, not twenty.

import time, requests
from datetime import datetime, timezone

BASE = "https://api.apitube.io/v1/news/everything"
API_KEY = "YOUR_KEY"
ALERT_THRESHOLD = 70
POLL_SECONDS = 5

def fetch_since(ts_iso: str) -> list[dict]:
    r = requests.get(BASE, params={
        "api_key": API_KEY, "language.code": "en",
        "published_at.start": ts_iso, "per_page": 100,
    }, timeout=10)
    r.raise_for_status()
    return r.json().get("results", [])

def run():
    seen_stories: set = set()
    last_ts = datetime.now(timezone.utc).isoformat()
    while True:
        try:
            articles = fetch_since(last_ts)
            # coverage counts per story for the spike signal
            counts: dict = {}
            for a in articles:
                sid = (a.get("story") or {}).get("id")
                counts[sid] = counts.get(sid, 0) + 1
            for a in sorted(articles, key=lambda x: x["published_at"]):
                sid = (a.get("story") or {}).get("id")
                if sid in seen_stories or a.get("is_duplicate"):
                    continue
                s = score_article(a, counts.get(sid, 1))
                if s >= ALERT_THRESHOLD:
                    push_alert(a, s)
                    seen_stories.add(sid)
                last_ts = max(last_ts, a["published_at"])
        except Exception as e:
            print("poll error:", e)
        time.sleep(POLL_SECONDS)

Finally, dispatch. Push is just an outbound webhook — Slack, Discord, a trading bot, or your own service:

def push_alert(a: dict, score: int):
    entity = next((e["name"] for e in a.get("entities", [])
                   if e.get("name") in WATCHLIST), "—")
    payload = {"text": f"🚨 [{score}] {entity}: {a['title']}\n"
                       f"{a['source']['domain']} · {a['published_at']}"}
    requests.post("https://hooks.slack.com/services/XXX", json=payload, timeout=5)

That is the whole pipeline: a scorer, a poll loop with dedup, and a push. Everything else — more entities, smarter coverage windows, per-strategy thresholds — is tuning on top of this skeleton.

The latency budget: polling vs streaming

Polling every 5 seconds means your worst-case detection lag is your poll interval plus the feed's own publish latency. For a swing strategy holding hours to days, a 5–10 second lag is irrelevant. For a strategy where alpha lives in the first seconds after a headline, it is fatal.

Industry practitioners put the bar bluntly: anything above roughly 100 ms median is not a real-time feed for first-seconds strategies. Latency-focused providers advertise accordingly — TradingNews markets sub-200 ms WebSocket delivery, Finlight markets sub-second streams, and Marketaux reports tracking 200,000+ entities per minute. Match the transport to the strategy:

TransportTypical detection lagBest forTrade-off
Polling (5–10 s)secondsSwing, EOD, researchSimple; wastes calls on quiet windows
Polling (1 s)~1 s + publish latencyIntraday discretionaryHeavier rate-limit pressure
SSE / WebSocket streamsub-secondFirst-seconds, HFT-adjacentMore moving parts; reconnection logic

Unlike a fixed polling interval, which caps your detection lag at the interval length no matter how fast the news breaks, a server-push stream delivers each article the moment it is indexed — which means the same scoring model can serve a research backtester and a low-latency desk by swapping only the transport. APITube documents an SSE option ("stream articles in real time via Server-Sent Events") for the low-latency path; the exact stream endpoint isn't in the public docs excerpt, so confirm the path in the dashboard before wiring it. The scorer and dedup logic above are transport-agnostic: poll for the simple build, switch to SSE when milliseconds matter.

Going to production

A few hardening steps separate a demo loop from something you'd trust with capital:

  • Dedup across restarts. Persist seen_stories to Redis with a TTL, so a process restart doesn't re-fire yesterday's alerts.
  • Respect rate limits. Back off on HTTP 429; widen the poll interval during quiet hours and tighten it around market open and scheduled events (earnings, CPI, FOMC).
  • Make the threshold per-strategy. A market-maker might alert at 50; a long-horizon fund at 80. Keep the score, vary the gate.
  • Log every score, not just alerts. You can only tune the weights if you can later join scores against realized price moves.
  • Watch the watchlist. Resolve entities to tickers explicitly; "Apple" the company is not "apple" the fruit, and entity-type tagging (type == "organization") is your disambiguator.

If you are still choosing a feed, evaluate it on the signals this model needs — breaking flags, entity frequency, source credibility, category depth, and stream latency. Our Best Financial News API for Trading 2026 and the broader News API Buyer's Guide 2026 compare providers on exactly those axes.

Frequently asked questions

How do you get real-time financial news alerts?

You get real-time financial news alerts by continuously ingesting a news API and notifying on the items that matter. The simplest method is to poll an endpoint such as /news/everything every few seconds using a published_at.start cursor, score each new article, and push the high-scoring ones to a webhook. For sub-second delivery, use a Server-Sent Events or WebSocket stream instead of polling.

What makes news market-moving?

News is market-moving when it carries surprise about a material entity from a credible source — not merely when it is positive or negative. The strongest signals are breaking status, prominent mention of a tracked ticker, an authoritative source, a market-relevant category (earnings, M&A, rates), and a rapid multi-source coverage spike. Sentiment direction is a weak predictor because consensus tone is usually already priced in.

How fast does news need to be for trading?

It depends on the strategy. For swing and end-of-day strategies, a detection lag of several seconds is fine, so 5–10 second polling works. For strategies where alpha lives in the first seconds after a headline, anything above roughly 100 ms median latency is too slow, and you need a streaming (SSE/WebSocket) transport rather than polling.

How do you detect which news moves a stock?

Score each article on observable signals rather than guessing from the headline. Combine entity materiality (is your ticker named, how often), breaking flag, source credibility, finance-category match, and coverage spike into a weighted 0–100 score, then alert above a threshold. To validate the weights, log every score and later correlate it against the stock's realized price move in the minutes after publication.

Conclusion

Building real-time market-moving news alerts is less about plumbing and more about judgment: deciding what deserves to fire. The plumbing — poll, score, dedupe, push — is a hundred lines of Python. The judgment is refusing to equate sentiment with signal, and instead scoring surprise, materiality, credibility, and coverage. A neutral-sentiment headline about a $25B bond sale should wake your system; a glowing puff piece should not.

Start with the scorer and the poll loop above, log every score against realized moves, and tune the weights to your own book. Then, when your strategy demands it, swap polling for a stream without touching the scoring model.

Try Apitube free → apitube.io

Resources

APITube - News API

Gerelateerde artikelen

Earnings News Monitor: Build It in Python (2026)
Developer Guides

Earnings News Monitor: Build It in Python (2026)

Build an earnings news monitor in Python that fuses the earnings calendar with real-time company news over one watchlist — earnings-window framework + code.

How to Detect Misinformation by Cross-Referencing Sources
Developer Guides

How to Detect Misinformation by Cross-Referencing Sources

Detect misinformation by cross-referencing sources: score corroboration breadth, source authority, and bias diversity over a news API. Code included.

How to Create a News Summarization Pipeline with GPT
Developer Guides

How to Create a News Summarization Pipeline with GPT

Build a production GPT news summarization pipeline: chain selection, cost math at volume, hallucination grounding, prompt caching, and build-vs-buy.

Hoe nieuws-API-kosten te verlagen: 8 strategieën (bespaar 70-90%)</h4><h2></h2>
Developer Guides

Hoe nieuws-API-kosten te verlagen: 8 strategieën (bespaar 70-90%)</h4><h2></h2>

Een audit-eerst-playbook voor het verlagen van de uitgaven aan nieuws-API's met 70-90%: fingerprint uw aanroepen, rangschik cache TTL op basis van nieuws-snelheid, vervang dode polling door webhooks en bulk-export backfills. Kostenberekening per 100.000 code.

Wij gebruiken cookies

Door op "Accepteren" te klikken, gaat u akkoord met het opslaan van cookies op uw apparaat voor functionele en analytische doeleinden.