New: unified Python, Go & Rust SDKs — available now in beta.
Verdict Engine

Definition

What the Verdict Engine is — the in-house model that scores every artifact for actionability and sentiment, calibrated to realized price impact.

The Verdict Engine is Forecite's in-house scoring model. It reads a financial artifact — a headline, an 8-K, a wire release, a research note — and emits a single structured verdict: how likely the event is to move the underlying (actionability), and which way and how hard (sentiment). One model, one pass, one object you can threshold, route, or feed straight into a strategy.

It is the layer that turns an undifferentiated stream of text into a graded, machine-readable signal — and it is what every scored field in the feed and the /v1/score endpoint comes from.

Labelled by the market, not by humans

The defining design choice is the label. Generic NLP sentiment models score language — how positive the wording sounds. The Verdict Engine is supervised on outcomes: every training artifact carries its own ground truth — the realized forward return on the underlying in the window after it dropped (matched per-ticker, roughly a one-hour horizon).

This is distant / weak supervision from market reaction rather than crowd-sourced tags or analyst opinion. The consequence is subtle but decisive: a neutral-sounding going-concern paragraph buried in a 10-Q can be highly actionable, while a glowing, adjective-dense press release can be noise. Because the target is the price move and not the prose, the model learns the former and discounts the latter.

Regime drift

Markets re-price what matters. A guidance beat in 2022 is not worth the same as one in 2026. The Engine is recalibrated on rolling windows so its priors track the current regime instead of an averaged-out past — standard practice against concept drift in a non-stationary target.

One verdict, two heads

A verdict is factorized into two heads that are scored independently and never collapsed into a single opaque number:

HeadQuestionOutput
ActionabilityIs this a fresh, market-moving event — or a stale recap already priced in?0–100 score + boolean gate
SentimentWhich way, over which horizon, and how cleanly?signed direction + 0–100 conviction

Crucially, direction and conviction are orthogonal. A clearly bad-news 8-K is strongly directional and high-conviction; an ambiguous capital-raise can be mildly bullish yet low-conviction. Keeping them separate avoids the classic failure of one-dimensional sentiment where magnitude and certainty are conflated.

Interpretable by construction

Neither head is a black box. Each is a convex combination of orthogonal sub-scores, each rated 0–10 on its own merits, then aggregated with fixed weights that sum to 1. This is a deliberately interpretable, feature-attributed design: you can always see why a score is what it is.

Actionability

score = 10 × ( 0.30·novelty + 0.25·materiality + 0.20·surprise
             + 0.15·specificity + 0.10·directness )
Sub-scoreWeightCaptures
novelty0.30Genuinely new disclosure vs. recap of already-public info
materiality0.25Size of the event relative to this company's volatility
surprise0.20Unscheduled / unexpected vs. fully telegraphed
specificity0.15Hard, verifiable facts vs. vague, promotional language
directness0.10Is the ticker the primary subject, or mentioned in passing

Sentiment

conviction = 10 × ( 0.35·clarity + 0.35·magnitude + 0.30·consensus_gap )
FieldRangeCaptures
short_direction−5 … +5Intraday-to-days directional read
long_direction−5 … +5Weeks-to-quarters read (may diverge from short)
clarity0–10Unambiguously good/bad vs. mixed / two-sided
magnitude0–10Expected size of the move, regardless of direction
consensus_gap0–10Distance from prior expectations (surprise vs. consensus)

Why store the sub-scores?

The raw 0–10 sub-scores are persisted alongside every verdict. Because the final score is just a weighted sum of them, a re-weighting can be replayed over historical events without re-running inference — letting the weights be recalibrated against realized impact offline and backtested before they ship.

Reasoning before scoring

The Engine decodes reasoning-first: the model is constrained to emit its chain-of-thought before any numeric field. Forcing the rationale to precede the scores measurably improves calibration — the score is conditioned on an explicit analysis rather than produced reflexively — and it ships a human-readable reasoning + one-line comment with every verdict for auditability.

Output is schema-constrained: the decoder is bound to a strict typed schema, so a verdict is always well-formed JSON with every field in range. Malformed generations are rejected and surfaced as a typed error, never silently coerced.

A gated, two-pass cascade

Scoring runs as a cascade with early exit:

  1. Actionability pass. Title + lede are scored first (a tight context window — the actionability decision lives mostly in the lede).
  2. Gate. An item is actionable when score ≥ 60.
  3. Sentiment pass. Only if the gate passes (default if_actionable) — or always, on request — the full body is scored for direction and conviction.

The gate is an efficiency and a precision mechanism: the cheap, decisive pass filters the firehose so the deeper pass runs only where it pays off. Direction is decoded at temperature 0 (deterministic), while the actionability pass carries a small amount of stochasticity to avoid hard ties on borderline novelty/surprise.

Tiers, latency, provenance

  • Model tiers — a fast tier for high-volume / latency-critical scoring and a deeper tier for filings and research; the tier is auto-selected by artifact type and overridable per request.
  • Wire speed — headline to scored verdict in under 50 ms end-to-end, with inference itself in the low-milliseconds, sustained at thousands of verdicts per second.
  • Provenance — every verdict is stamped with the model version, processing duration, and a request id, so any score is fully reproducible and auditable.

The verdict object

{
  "actionability": {
    "is_actionable": true,
    "score": 88,
    "subscores": { "novelty": 9, "materiality": 9, "surprise": 8, "specificity": 9, "directness": 10 },
    "reasoning": "Brand-new Q3 disclosure; beat + raised guide is highly material and not telegraphed.",
    "comment": "Fresh, company-defining beat — clearly actionable."
  },
  "sentiment": {
    "short_direction": 4,
    "long_direction": 3,
    "conviction": 82,
    "score_legacy": 9,
    "subscores": { "clarity": 9, "magnitude": 8, "consensus_gap": 8 },
    "reasoning": "Unambiguous beat well above consensus; large, clean upside signal.",
    "comment": "Strong bullish intraday setup."
  },
  "meta": { "model": "…", "duration_ms": 31, "request_id": "…" }
}

sentiment is omitted entirely when the gate fails under if_actionable, and score_legacy maps short_direction onto a 0–10 scale (5 = neutral) for the feed's sentiment column.

Ready to consume it? See How to use.

On this page