LLM Alpha & Unstructured Financial Data Mining
Reading What Management Says — and How Fast the Market Forgets It
🔊 Listen to Article (Chinese Narration)
⚠️ Reality Check
Two Sigma Research and Point72's Cubist Systematic Strategies run LLM inference over the full universe of SEC filings and transcripts within minutes of release, cross-referenced against proprietary historical tone baselines built over a decade. You don't have their infrastructure, their latency, or their labeled training data. This article shows you what's structurally true about how the market prices textual tone, and what a retail-scale version can actually capture:
- Off-the-shelf FinBERT models are freely available and score sentiment nearly as well as proprietary fine-tunes on standard benchmarks
- The Q&A section of an earnings call is unscripted and structurally more information-dense than prepared remarks — this is publicly documented, not proprietary
- Sentiment's predictive power decays fast — most of it is gone within days, which changes how a retail trader has to operationalize the signal
- We're adapting the published tone-extraction framework for a slower retail execution cycle, not replicating a millisecond-latency institutional pipeline
🎯 What You'll Learn
Every quarter, thousands of pages of 10-Ks, 10-Qs, and earnings call transcripts hit the tape — most of it never gets fully read by a human before the next data point arrives. You'll learn:
- Why unstructured text is underpriced: The volume of filings and transcripts outpaces analyst reading capacity by orders of magnitude
- Prepared Remarks vs. Q&A: Why the scripted portion of a call carries far less signal than the unscripted analyst Q&A
- Sentiment Decay Half-Life: Why the predictive value of a tone signal collapses over hours to days, and what that means for entry timing
- Satellite & Web-Scrape Signals: How alternative data confirms or contradicts what management is saying
- Python Implementation: A full FinBERT-based pipeline — filing ingestion, section splitting, sentiment scoring, decay-weighted signal construction
- Realistic Retail Deployment: Where retail latency actually lands on the decay curve, and how to build a strategy around that reality rather than fighting it
Table of Contents
- The Unstructured Data Problem
- Prepared Remarks vs. Q&A: Where the Signal Lives
- 10-Q/10-K Tone Extraction: MD&A and Risk Factors
- Sentiment Decay Curve: The Half-Life Problem
- Satellite & Web-Scrape Confirmation Signals
- Python Implementation: Full NLP Scoring Pipeline
- Retail Deployment: Where You Actually Land on the Curve
- Illustrative Performance
- Common Mistakes That Erase This Edge
- Your Action Plan
The Unstructured Data Problem
More Text Than Anyone Can Read
Every U.S.-listed company files a 10-K annually and a 10-Q quarterly, and most large-caps hold a live earnings call the same day or within a day or two of the release. Across the Russell 3000, that's thousands of documents and transcripts landing in overlapping windows every earnings season — each one running from dozens to hundreds of pages. No research desk, however large, reads all of it closely before the market has already reacted to the numbers.
Two Sigma Research and Point72's Cubist Systematic Strategies have both published on how they treat this as a structural inefficiency: the text is public, but the marginal cost of reading it carefully — for every name, every quarter, within minutes of release — is what actually creates the opportunity. An LLM doesn't get tired, doesn't skip the boring paragraph, and scores the fortieth filing of the day exactly as carefully as the first.
"The value isn't in reading text no human has read before — it's in reading all of it, consistently, at a speed no human research process can match."
— Summary characterization of Two Sigma Research and Point72/Cubist Systematic Strategies published commentary on NLP-driven alternative data research
What Changed: LLMs vs. Legacy NLP
Bag-of-words and dictionary-based sentiment scoring (Loughran-McDonald word lists, for example) have been used in academic finance since the 2000s, but they're crude — they can't tell "not disappointing" from "disappointing." Transformer-based models fine-tuned on financial text (FinBERT and its successors) understand negation, hedging language, and context in a way word-count methods never could. That's the specific capability upgrade institutional desks are now running at scale.
Legacy vs. LLM-Based Tone Scoring
| Approach | Example Failure Mode | LLM Handling |
|---|---|---|
| Dictionary word-count | "Not a disappointing quarter" scores negative (flags "disappointing") | Correctly reads negation as positive/neutral |
| Dictionary word-count | Can't detect hedging ("we remain cautiously optimistic, though...") | Weighs hedge phrases against directional claims |
| Dictionary word-count | Treats all sentences equally regardless of position/context | Attention mechanism weighs context across the passage |
Key Insight: This isn't a marginal accuracy improvement — it's the difference between a noisy proxy and a usable signal on genuinely ambiguous corporate language, which is most of it.
Prepared Remarks vs. Q&A: Where the Signal Lives
The Scripted Portion Tells You Less Than You Think
The opening "prepared remarks" section of an earnings call is written, reviewed by legal and IR, and rehearsed before the CEO and CFO ever speak. It is optimized to sound confident and on-message regardless of the underlying quarter. Academic research on earnings call linguistics (building on work popularized in accounting and finance literature since the early 2010s) has repeatedly found that the unscripted analyst Q&A session carries substantially more incremental information than the prepared portion — because executives can't fully script answers to questions they don't control.
Prepared Remarks vs. Q&A: Structural Differences
| Dimension | Prepared Remarks | Analyst Q&A |
|---|---|---|
| Authorship | Written and legal-reviewed in advance | Live, unscripted responses |
| Control | Company controls topic and framing entirely | Analysts steer toward the topics they're worried about |
| Hedging language density | Lower — smoothed by review process | Higher — hesitation, filler, deflection are harder to hide live |
| Signal-to-noise for tone models | Lower incremental signal | Higher incremental signal |
Key Insight: A tone model that scores the full transcript as one undifferentiated blob is diluting its best signal with its weakest. Splitting the transcript into sections before scoring is not optional — it's the single highest-leverage step in the pipeline.
Reading the Delta, Not Just the Level
The absolute tone score of a single call matters less than the delta versus that same management team's own historical baseline and versus the tone of the prepared remarks on the very same call. A large negative gap between confident prepared remarks and hedge-heavy Q&A answers — sometimes called a "tone divergence" — is a more specific signal than either section read in isolation: it suggests management is comfortable with the scripted narrative but visibly less comfortable defending it under unscripted questioning.
⚠️ Sector and Management-Style Baselines Matter
Some executives are naturally more hedged speakers than others, and some sectors (biotech awaiting trial data, for example) have structurally higher hedging language regardless of underlying business health. A raw cross-sectional tone score without a company-specific and sector-specific baseline will misclassify naturally cautious speakers as bearish every single quarter.
10-Q/10-K Tone Extraction: MD&A and Risk Factors
Where the Signal Concentrates in a Filing
A 10-K or 10-Q is mostly boilerplate — accounting disclosures, standard legal language, item numbering required by SEC rules. The two sections institutional NLP research consistently focuses on are Management's Discussion & Analysis (MD&A), where management explains results in their own words, and the Risk Factors section, where year-over-year additions, removals, and rewordings are tracked as a distinct signal from the sentiment score itself.
Filing Sections Worth Scoring
| Section | What It Reveals | Signal Type |
|---|---|---|
| MD&A | Management's own narrative explanation of results | Tone/sentiment score |
| Risk Factors (new/removed) | Newly disclosed risks or quietly dropped ones | Structural change detection, not sentiment |
| Risk Factors (reworded) | Softened or hardened language on existing risks | Edit-distance / similarity delta |
Key Insight: A brand-new risk factor appearing for the first time — particularly one about liquidity, a customer concentration, or a covenant — is often a more actionable signal than any sentiment score, because it's a disclosure event, not an interpretation.
Year-over-Year Similarity as a Signal in Its Own Right
Comparing this quarter's MD&A and Risk Factors text against the prior-year filing using a text-similarity metric (cosine similarity on sentence embeddings is the standard retail-accessible approach) surfaces material changes that a sentiment score alone would miss. A filing that is 95%+ textually similar to the prior year, boilerplate included, tells you management didn't feel the need to say anything new — which is itself informative, especially in a quarter where something changed operationally.
Sentiment Decay Curve: The Half-Life Problem
Why "Correct" Sentiment Isn't Enough
A tone signal can be directionally accurate and still be worthless to trade if it's stale by the time you can act on it. Published academic and industry research on earnings-call and filing sentiment has consistently found that the market's price reaction to textual tone is front-loaded — most of the incremental information gets absorbed into price within hours, not days, of release, with a long thin tail of residual drift that fades over the following sessions.
Illustrative Sentiment Signal Decay Curve
| Time Since Release | Approx. % of Total Price Impact Realized | Practical Implication |
|---|---|---|
| 0-3 hours (live call / same-day filing) | ~40-55% | Institutional low-latency desks capture most of this window |
| 3 hours - 1 trading day | ~65-80% cumulative | Sell-side notes and initial analyst reactions catch up |
| 1-3 days | ~85-95% cumulative | Remaining drift as slower capital repositions |
| 3+ days | Approaching 100%, thin residual tail | Signal is effectively consumed; noise dominates any remainder |
Key Insight: The commonly cited "half-life" of a raw sentiment signal — the time for half of its eventual price impact to be realized — lands somewhere in the range of a few hours to roughly a day for large, liquid, heavily-covered names, and can stretch toward the multi-day end of the range for smaller, less-covered names with slower analyst pickup. This is a structural estimate from published market-microstructure and event-study literature, not a fund-specific disclosed number, and it varies by name, sector, and market regime.
Modeling Decay as an Exponential
The standard way to operationalize a decay curve is to treat the signal's usable weight as an exponential function of elapsed time since release, so a trading system can decide how much conviction to still assign a stale-but-not-dead signal rather than treating it as binary (tradeable / not tradeable):
weight(t) = 2^(-t / half_life)
where:
t = hours elapsed since filing/call release
half_life = estimated hours for signal to lose half its value
(illustrative: 3-24 hours depending on name/liquidity)
⚠️ The Half-Life Is Not a Universal Constant
Treating every ticker with the same half-life is a common modeling shortcut that costs real accuracy. Large-cap, heavily-covered names get priced in fast because more capital is watching; small- and mid-caps with thin analyst coverage can retain a usable signal for longer simply because fewer participants are reading the filing quickly. A production system should estimate half-life per liquidity/coverage bucket, not apply one number market-wide.
Satellite & Web-Scrape Confirmation Signals
Text Tone Is Stronger When It Agrees With Physical-World Data
Both Two Sigma Research and Point72/Cubist have discussed using alternative data — satellite imagery, web-scraped pricing and job postings, app usage telemetry — not as standalone signals but as a way to corroborate or contradict what a tone model extracts from text. A confident MD&A section paired with confirming satellite parking-lot traffic or job-posting growth is a stronger combined signal than either alone; a confident MD&A paired with contradicting physical-world data is itself informative — it flags a filing whose tone may not match reality.
Alternative Data Confirmation Categories
| Data Type | What It Proxies | Confirms/Contradicts |
|---|---|---|
| Satellite parking lot / foot traffic counts | Retail and restaurant footfall trends | Same-store sales commentary tone |
| Job posting scrapes (hiring volume, role mix) | Expansion or contraction intent | Growth/hiring-freeze language in MD&A |
| Web-scraped e-commerce pricing/inventory | Demand and discounting pressure | Margin and pricing-power commentary |
| App store review volume/sentiment, download ranks | Consumer product engagement trend | User growth commentary tone |
Key Insight: None of these alternative signals is individually reliable — satellite coverage is patchy, scrapes break, sample sizes are small relative to a company's true footprint. Their value is as a corroboration layer on top of text tone, not a replacement for it.
⚠️ Retail Access to Alternative Data Is Real but Limited
Institutional desks license comprehensive satellite and web-scrape panels covering thousands of locations and SKUs. Retail-accessible equivalents exist (some satellite imagery providers offer limited free tiers; job-posting and app-ranking data is scrapeable at small scale) but cover a small fraction of the names and update far less frequently. Treat retail alt-data as spot-check corroboration on a handful of names you already follow closely — not a scalable standalone signal.
Python Implementation: Full NLP Scoring Pipeline
Full implementation: Transcript/filing ingestion, section splitting (prepared remarks vs. Q&A, MD&A vs. Risk Factors), FinBERT sentiment scoring, year-over-year similarity, decay-weighted signal aggregation.
Complete Python Code
"""
LLM Alpha: Unstructured Financial Data Mining Pipeline
Retail-adapted FinBERT tone extraction for filings and earnings calls
Author: Plan My Retire
Date: March 2026
"""
import re
import numpy as np
import pandas as pd
from datetime import datetime
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch
from sklearn.metrics.pairwise import cosine_similarity
from sentence_transformers import SentenceTransformer
class FinancialTextScorer:
"""
Scores financial text (transcripts, filings) for sentiment using
a FinBERT-family model, with section-aware weighting.
"""
def __init__(self, model_name="ProsusAI/finbert"):
self.tokenizer = AutoTokenizer.from_pretrained(model_name)
self.model = AutoModelForSequenceClassification.from_pretrained(model_name)
self.model.eval()
self.embedder = SentenceTransformer("all-MiniLM-L6-v2")
self.labels = ["positive", "negative", "neutral"]
def score_passage(self, text, max_length=512):
"""
Returns a signed sentiment score in [-1, 1] for a text passage.
Long passages are chunked and score-averaged.
"""
chunks = self._chunk_text(text, max_length)
if not chunks:
return 0.0
scores = []
for chunk in chunks:
inputs = self.tokenizer(chunk, return_tensors="pt", truncation=True,
max_length=max_length)
with torch.no_grad():
logits = self.model(**inputs).logits
probs = torch.softmax(logits, dim=1).numpy()[0]
# positive - negative, ignoring neutral mass
signed = probs[self.labels.index("positive")] - probs[self.labels.index("negative")]
scores.append(signed)
return float(np.mean(scores))
def _chunk_text(self, text, max_length):
words = text.split()
chunk_size = max_length - 20 # headroom for special tokens
return [" ".join(words[i:i + chunk_size])
for i in range(0, len(words), chunk_size)] if words else []
def embedding_similarity(self, text_a, text_b):
"""Cosine similarity between two passages, for YoY filing comparison."""
emb = self.embedder.encode([text_a, text_b])
return float(cosine_similarity([emb[0]], [emb[1]])[0][0])
class EarningsCallSplitter:
"""
Splits a raw earnings call transcript into prepared remarks
and analyst Q&A sections using standard transcript markers.
"""
QA_MARKERS = [
r"question-and-answer session",
r"questions? and answers?",
r"we will now (begin|open) the (question|q&a)",
r"first question",
]
def split(self, transcript_text):
lower = transcript_text.lower()
split_idx = None
for pattern in self.QA_MARKERS:
match = re.search(pattern, lower)
if match:
split_idx = match.start()
break
if split_idx is None:
# Fallback: assume last third is Q&A if no marker found
split_idx = int(len(transcript_text) * 0.66)
return {
"prepared_remarks": transcript_text[:split_idx],
"qa_section": transcript_text[split_idx:]
}
class FilingSectionExtractor:
"""
Extracts MD&A and Risk Factors sections from 10-K/10-Q text
using standard SEC item-number headers.
"""
SECTION_PATTERNS = {
"risk_factors": r"item\s+1a\.?\s+risk factors(.*?)item\s+1b",
"mda": r"item\s+7\.?\s+management.s discussion(.*?)item\s+7a",
}
def extract(self, filing_text):
lower = filing_text.lower()
sections = {}
for name, pattern in self.SECTION_PATTERNS.items():
match = re.search(pattern, lower, re.DOTALL)
sections[name] = filing_text[match.start():match.end()] if match else ""
return sections
class DecayWeightedSignal:
"""
Combines a raw tone score with an exponential decay weight
based on hours elapsed since release, to avoid treating
stale signals as equally tradeable to fresh ones.
"""
def __init__(self, half_life_hours=8.0):
self.half_life = half_life_hours
def weight(self, hours_elapsed):
if hours_elapsed < 0:
return 0.0
return 2 ** (-hours_elapsed / self.half_life)
def weighted_score(self, raw_score, release_time, as_of_time=None):
as_of_time = as_of_time or datetime.now()
hours_elapsed = (as_of_time - release_time).total_seconds() / 3600
return raw_score * self.weight(hours_elapsed)
class TonePipeline:
"""
End-to-end pipeline: raw transcript/filing text in,
decay-weighted composite tone score out.
"""
def __init__(self, half_life_hours=8.0):
self.scorer = FinancialTextScorer()
self.call_splitter = EarningsCallSplitter()
self.filing_extractor = FilingSectionExtractor()
self.decay = DecayWeightedSignal(half_life_hours)
def score_earnings_call(self, transcript_text, release_time, as_of_time=None):
sections = self.call_splitter.split(transcript_text)
prepared_score = self.scorer.score_passage(sections["prepared_remarks"])
qa_score = self.scorer.score_passage(sections["qa_section"])
# Q&A weighted more heavily -- higher incremental information content
composite_raw = 0.35 * prepared_score + 0.65 * qa_score
divergence = prepared_score - qa_score
return {
"prepared_remarks_score": prepared_score,
"qa_score": qa_score,
"tone_divergence": divergence,
"composite_raw": composite_raw,
"composite_decay_weighted": self.decay.weighted_score(
composite_raw, release_time, as_of_time
),
}
def score_filing(self, filing_text, prior_year_filing_text, release_time,
as_of_time=None):
sections = self.filing_extractor.extract(filing_text)
prior_sections = self.filing_extractor.extract(prior_year_filing_text)
mda_score = self.scorer.score_passage(sections["mda"])
risk_similarity = self.scorer.embedding_similarity(
sections["risk_factors"], prior_sections["risk_factors"]
)
return {
"mda_score": mda_score,
"risk_factors_yoy_similarity": risk_similarity,
"risk_factors_changed": risk_similarity < 0.85, # heuristic threshold
"composite_decay_weighted": self.decay.weighted_score(
mda_score, release_time, as_of_time
),
}
# Example usage
if __name__ == "__main__":
pipeline = TonePipeline(half_life_hours=8.0)
sample_transcript = """
Thank you for joining us today. We are pleased to report strong
results this quarter, with revenue growth accelerating...
[prepared remarks continue]
Question-and-Answer Session
Analyst: Can you walk us through the margin pressure in the segment?
CFO: Well, I think, um, there are some puts and takes there, and
we're cautiously monitoring the situation going into next quarter...
"""
release_time = datetime(2026, 3, 12, 16, 30) # 4:30 PM call
as_of = datetime(2026, 3, 12, 20, 0) # scoring 3.5 hours later
result = pipeline.score_earnings_call(sample_transcript, release_time, as_of)
print(f"Prepared remarks score: {result['prepared_remarks_score']:.3f}")
print(f"Q&A score: {result['qa_score']:.3f}")
print(f"Tone divergence (prepared - Q&A): {result['tone_divergence']:.3f}")
print(f"Decay-weighted composite: {result['composite_decay_weighted']:.3f}")
⚠️ Data and Latency Limitations for Retail
This pipeline requires timely access to transcripts and filings — free sources (SEC EDGAR full-text search, some free transcript aggregators) typically lag the live event by anywhere from minutes to hours, which matters enormously given the decay curve above. A production version needs a real-time transcript feed and EDGAR filing alert service to compete for any part of the same-day window; retail realistically operates further out on the decay curve than institutional desks, which changes the strategy's design rather than invalidating it.
Retail Deployment: Where You Actually Land on the Curve
Stop Competing in the First 3 Hours
Institutional low-latency desks with direct transcript feeds and dedicated inference infrastructure own the first few hours after release — that portion of the decay curve is not a realistic target for a retail pipeline running on delayed data and consumer-grade compute. The retail-viable version of this strategy targets the multi-day residual drift tail, where slower-moving capital is still repositioning and the signal, while smaller, is more accessible on a delay.
Retail-Viable Signal Window
| Window | Who Competes Here | Retail Viability |
|---|---|---|
| 0-3 hours post-release | Low-latency institutional desks | Not viable — data access and speed gap too large |
| Same day, post-close to next open | Sell-side analysts, faster discretionary funds | Marginal — requires same-day EDGAR/transcript access |
| 1-3 trading days | Slower-moving quant and fundamental capital | Most realistic retail entry window |
Key Insight: A retail strategy that tries to trade the first three hours with delayed data isn't capturing a smaller version of the institutional edge — it's trading noise after the real signal has already been arbitraged away. Sizing the strategy around the 1-3 day residual tail is the honest, executable version of this trade.
Focus on Under-Covered Names
Because the decay half-life stretches longer for names with thin analyst coverage, retail tone-extraction has a structurally better risk/reward on small- and mid-cap names than on the most heavily covered mega-caps, where the first-hour institutional reaction has typically already consumed most of the available price impact before a retail pipeline can act.
Illustrative Performance
The figures below are hypothetical illustrations of the payoff shape of a decay-weighted tone-divergence strategy targeting the 1-3 day residual window — they are not a track record and are not a promise of similar results. They exist to make the risk/reward shape concrete, not to suggest predictability.
Illustrative Post-Earnings Tone-Divergence Program (Under-Covered Names, 1-3 Day Hold)
- Hit rate: ~55-60% of trades directionally correct (modest edge, not a high-conviction signal in isolation)
- Average win: +1.5% to +3% over the hold window
- Average loss: -1% to -2.5% over the hold window
- Best performing subset: Large negative tone-divergence (confident prepared remarks, hedge-heavy Q&A) combined with a contradicting alt-data signal
Key takeaway: This is a modest, statistical edge that needs to be run across many names and quarters to show up reliably — not a signal to concentrate a portfolio around a handful of high-conviction single-name bets.
🚨 This Edge Is Small and Crowds Fast
Text-based sentiment alpha has been public knowledge in academic finance for over a decade and is now heavily arbitraged by well-capitalized desks. Any retail version of this strategy is working with a thinner, later-arriving slice of an already-competed-over signal. Treat modest, consistent edge across a diversified basket as success — not a large edge on any single name.
Common Mistakes That Erase This Edge
Mistake Checklist
- Scoring the full transcript as one blob: Diluting the high-signal Q&A section with the low-signal prepared remarks section.
- Ignoring management-specific baselines: Flagging naturally cautious speakers or hedge-heavy sectors (biotech, early-stage) as bearish every quarter.
- Treating the signal as fresh regardless of elapsed time: Trading a same-week filing with the same conviction as a same-hour one, ignoring the decay curve entirely.
- Competing in the wrong window: Trying to trade the first few hours post-release on delayed retail data, when that window belongs to low-latency institutional desks.
- Over-weighting a single alternative data point: Treating one patchy satellite or scrape signal as confirmation rather than as one noisy input among several.
- No out-of-sample validation across earnings cycles: Fitting a decay half-life or scoring threshold to one or two quarters of data and assuming it generalizes.
Your Action Plan
Phase 1: Build and Validate the Scoring Pipeline (4-8 Weeks)
Timeline: Before risking any capital, confirm the pipeline produces sensible, stable scores.
- Install FinBERT (Hugging Face, free) and run it against a set of historical transcripts with known subsequent price reactions
- Build the prepared-remarks/Q&A splitter and manually spot-check a sample of splits for accuracy
- Compute and journal a per-name tone baseline across several quarters before treating any single reading as a signal
Phase 2: Small, Decay-Aware Deployment (2-4 Months)
Timeline: Start with the smallest tradeable size, on the 1-3 day window only.
- Trade only the residual-tail window — skip the same-day reaction entirely; you're not equipped to compete for it
- Prioritize under-covered small/mid-caps where the decay half-life is longer and retail latency matters less
- Require tone-divergence and alt-data agreement before sizing a position — never trade text tone alone at full size
- Log every trade's realized decay behavior to refine your half-life estimates by liquidity bucket
Phase 3: Systematic Scaling (6+ Months)
Timeline: Only after a full earnings cycle (or more) of validated, small-size execution.
- Automate filing/transcript ingestion against a real-time EDGAR alert feed and a paid transcript provider
- Diversify across a basket of names rather than concentrating on high-conviction single names
- Re-validate decay half-life estimates each earnings season — market regime and coverage patterns shift over time
Recommended Reading
- Institutional Research:
- Two Sigma Research — published commentary on NLP and unstructured data in systematic investing
- Point72 / Cubist Systematic Strategies — public commentary on machine learning and alternative data pipelines
- Related PMR Articles:
- Goldman Sachs Alternative Data (companion piece on FinBERT and satellite/social signal ensembles)
- Point72 Cubist ML Pipeline (companion piece on the broader ML production pipeline this tone signal would feed into)
🎯 Final Thoughts
Unstructured text is one of the few data sources where the raw material is fully public and free — the edge lives entirely in reading it faster, more consistently, and with more context-awareness than the market's slower participants. That's a genuinely different kind of edge than a proprietary data feed: it's an edge in processing, not in access.
Key to survival: Be honest about which part of the decay curve you can actually compete on, diversify the signal across many names rather than concentrating on a few, and never let a modest statistical tone-tilt become a substitute for a properly diversified, low-cost retirement core. This is a satellite tactic layered on top of that core — never the core itself.
Start by scoring history, not live filings. Calibrate the pipeline against known outcomes before you ever trade a fresh signal.