LLM Alpha Mining: hoe haal je handelssignalen uit earnings calls en financiële documenten
Er gaat een grap rond op Wall Street: "De meest waardevolle informatie in een earnings call is niet wat de CEO zei, maar hoe hij het zei." Wanneer Tim Cook zegt "we zijn voorzichtig optimistisch" in plaats van het "we zijn zeer tevreden" van vorig jaar — dat is geen taalspelletje, dat is een signaal dat honderden miljoenen dollars waard is.
Al decennialang proberen kwantitatieve fondsen het extraheren van deze signalen te systematiseren. Eerst telden ze de frequentie van "positieve" en "negatieve" woorden met behulp van woordenboeken. Toen zetten ze BERT in. En nu hebben we GPT-4o, Claude en open-source LLM's die de subtiliteiten van bedrijfsjargon kunnen ontleden met een nauwkeurigheid die zelfs de onderzoekers zelf schrik aanjaagt.
Laten we uiteenzetten hoe je een volwaardige pipeline bouwt om handelssignalen uit earnings calls te halen — van het verkrijgen van het transcript tot het backtesten van cumulatieve abnormale rendementen.
Waarom earnings calls een goudmijn zijn voor alpha
Post-Earnings Announcement Drift: de anomalie die niet wil sterven
In 1968 ontdekten Ball en Brown iets vreemds: nadat de kwartaalresultaten zijn gepubliceerd, blijven aandelen nog 60-90 dagen doordrijven in de richting van de "verrassing". Ze noemden het Post-Earnings Announcement Drift (PEAD). Sindsdien is er meer dan een halve eeuw verstreken, zijn er honderden papers geschreven, is de anomalie vanuit tientallen invalshoeken verklaard — en toch werkt het nog steeds.
PEAD is een van de meest hardnekkige marktanomalieën in de geschiedenis van de financiën. Een portfoliostrategie van "koop positieve verrassing, verkoop negatieve verrassing" heeft historisch gezien 10-25% jaarlijks overrendement opgeleverd. Waarom heeft de markt dit niet weggearbitreerd? Verschillende redenen:
- Beperkte aandacht van beleggers — wanneer 200 bedrijven in dezelfde week rapporteren, is het fysiek onmogelijk om alle transcripten te lezen
- Cognitieve complexiteit — een earnings call duurt 45-60 minuten, en het cruciale signaal kan verstopt zitten in één enkele zin in de 38e minuut van de Q&A-sessie
- Taalambiguïteit — de CFO zegt "we navigeren door tegenwind", en zonder context is het onduidelijk of dit een zachte waarschuwing is of standaard hedging
Hier komen LLM's het toneel op. Voor het eerst hebben we een instrument dat 500 transcripten op een avond kan verwerken, terwijl het nuances opvangt die zelfs ervaren analisten zouden missen.
PEAD.txt: tekst is belangrijker dan cijfers
Onderzoekers van de Federal Reserve Bank of Philadelphia (Meursault, Liang, Routledge, Scanlon) publiceerden het PEAD.txt-paper, dat aannames over de waarde van tekstuele informatie omverwierp. Ze bouwden een tekstgebaseerde analoog van de standaard earnings surprise — SUE.txt — die de numerieke earnings-waarde helemaal niet gebruikt.
Het resultaat? SUE.txt genereert een drift die twee keer zo groot is als de klassieke PEAD. Bovendien: in de afgelopen jaren, terwijl de klassieke PEAD gebaseerd op numerieke verrassingen vrijwel is verdwenen (de markt heeft geleerd), blijft de tekstuele drift significant. De markt heeft geleerd cijfers snel te verwerken, maar worstelt nog steeds met tekstinterpretatie.
Dit is het fundamentele argument voor een op NLP gebaseerde benadering van earnings calls.
Van sentiment naar semantiek: evolutie van de aanpak

Eerste generatie: bag-of-words en woordenboeken (2000-2015)
Alles begon met het Loughran-McDonald-woordenboek (2011) — een woordenlijst gelabeld als "positief," "negatief," "onzeker," en "geschilgevoelig." Het idee was elegant in zijn eenvoud: tel het percentage negatieve woorden in een 10-K-indiening en handel daarop.
Het probleem? Het woord "outstanding" betekent in een financiële context vaker "openstaande schuld" dan "uitstekend resultaat." Het woord "risico" in Risk Management is geen negatief signaal — het is een procesbeschrijving. Standaard NLP-sentimentwoordenboeken presteerden gênant slecht op financiële teksten.
Loughran en McDonald creëerden een gespecialiseerd woordenboek, wat de situatie verbeterde, maar het fundamentele probleem bleef: bag-of-words begrijpt geen context. "We zijn er niet in geslaagd de verwachtingen niet te halen" — er staan hier twee "negatieve" woorden, maar de betekenis is positief.
Tweede generatie: FinBERT en transformers (2019-2023)
In 2019 publiceerde Dogu Araci FinBERT — BERT fijn afgesteld op financiële teksten van Reuters TRC2. De resultaten waren indrukwekkend: een verbetering van 14 procentpunten op de Financial PhraseBank-dataset ten opzichte van de state-of-the-art. FinBERT begreep context: "outstanding" naast "debt" — negatief, naast "performance" — positief.
Maar FinBERT had een beperking: een contextvenster van 512 tokens. Een earnings call beslaat 8.000-12.000 woorden. Opsplitsen in chunks en het sentiment middelen betekent het verlies van de semantiek tussen paragrafen. De CEO zou kunnen beginnen met optimisme, om vervolgens terloops problemen in de toeleveringsketen te noemen tijdens de Q&A. FinBERT analyseert elke chunk onafhankelijk en mist dit contrast.
Derde generatie: LLM's met lange context (2023-heden)
GPT-4, Claude, Gemini met contextvensters van 128K-1M tokens veranderden de spelregels. Nu kun je een volledig transcript in één keer laden en vragen stellen die begrip van het hele document vereisen.
De sleutelstudie — Lopez-Lira & Tang (2023) "Can ChatGPT Forecast Stock Price Movements?" Op meer dan 50.000 krantenkoppen liet GPT-4 een trefzekerheid van ~90% zien bij het voorspellen van de richting van de eerste marktreactie en voorspelde het significant de daaropvolgende drift, vooral voor small-capbedrijven en negatief nieuws. Eerdere modellen (GPT-1, GPT-2, BERT) vertoonden deze vaardigheid niet — voorspellend vermogen ontstaat als een opkomende eigenschap van grote modellen.
BloombergGPT (2023) — een model met 50 miljard parameters getraind op Bloombergs financiële corpus — toonde verbeteringen in financiële NER, nieuwsclassificatie en sentimentanalyse. FinGPT — het open-source alternatief — behaalt 89% nauwkeurigheid op financiële sentimenttaken met behulp van een datagerichte aanpak en RAG.
MarketSenseAI, dat GPT-4 gebruikt met Chain-of-Thought en In-Context Learning voor S&P 100-analyse, liet een overrendement van 10-30% zien en cumulatieve rendementen tot 72% over 15 maanden testen. Ja, deze cijfers moeten met voorzichtigheid worden bekeken (backtest ≠ live trading), maar de trend is duidelijk.
Datapipeline: waar haal je de data vandaan
SEC EDGAR: de officiële bron
Voor Amerikaanse aandelen is de primaire bron SEC EDGAR. Earnings calls worden meestal niet rechtstreeks ingediend, maar gerelateerde documenten zijn beschikbaar:
- 8-K-indieningen (Item 2.02 — Resultaten van Operaties) — persberichten met resultaten, vaak inclusief exhibit 99 met het transcript
- 10-Q / 10-K — kwartaal- en jaarverslagen met Management Discussion & Analysis (MD&A) — ook een waardevolle tekstbron
- DEF 14A — volmachtverklaringen met informatie over managementbeloningen
from edgar import Company
company = Company("AAPL")
filings = company.get_filings(form="8-K")
for filing in filings.latest(10):
if "2.02" in str(filing.items):
doc = filing.document()
text = doc.text() # Full text with exhibits
print(f"{filing.filing_date}: {len(text)} chars")
Seeking Alpha en commerciële API's
Earnings call-transcripten zijn een apart product. Seeking Alpha was historisch gezien de belangrijkste gratis bron, maar beperkt nu de toegang. Commerciële opties:
- Seeking Alpha Premium API — volledige transcripten met sprekerlabels
- AlphaVantage Earnings API — gratis tier met beperkingen
- Financial Modeling Prep — transcripten + fundamentals
- Earnings Call Edge / Motley Fool Transcripts — alternatieve bronnen
Crypto: governance calls en DAO-voorstellen
Hier wordt het interessant. Grote DeFi-protocollen houden hun equivalent van earnings calls:
- Uniswap — governance calls, community calls, opnames op YouTube
- Aave — maandelijkse community calls + governance-forumvoorstellen
- MakerDAO — governance calls + uitgebreide forumdiscussies
- Compound — governance-voorstellen met gedetailleerde discussies
Crypto-call-transcripten zijn meestal ongestructureerd. De oplossing — OpenAI's Whisper voor het transcriberen van YouTube-opnames:
import openai
from yt_dlp import YoutubeDL
def transcribe_governance_call(youtube_url: str) -> str:
"""Download audio from YouTube and transcribe via Whisper."""
ydl_opts = {
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': 'mp3',
'preferredquality': '64', # Low bitrate is sufficient for speech
}],
'outtmpl': '/tmp/governance_call.%(ext)s',
}
with YoutubeDL(ydl_opts) as ydl:
ydl.download([youtube_url])
client = openai.OpenAI()
with open("/tmp/governance_call.mp3", "rb") as audio_file:
transcript = client.audio.transcriptions.create(
model="whisper-1",
file=audio_file,
response_format="verbose_json",
timestamp_granularities=["segment"]
)
return transcript.text
De kosten van transcriptie via de Whisper API: 0,36. Voor een self-hosted optie — Whisper Large-v3 Turbo transcribeert een bestand van 60 minuten in ~17 seconden (216x realtime) op een moderne GPU.
LLM-promptingstrategieën: van naïef tot productieklaar

Strategie 1: direct sentiment (zwak)
De naïefste aanpak — het model rechtstreeks vragen:
"Is deze earnings call positief of negatief voor de aandelenkoers?"
Werkt dit? Ja, verrassend genoeg. Lopez-Lira & Tang toonden aan dat zelfs zo'n primitieve prompt statistisch significante voorspellingen oplevert. Maar er zijn problemen:
- Binaire output — je verliest gradaties. "Catastrofe" en "milde teleurstelling" krijgen hetzelfde label
- Geen uitleg — het is onduidelijk waarop het model zijn beslissing baseerde
- Instabiliteit — een herhaalde run kan een ander antwoord geven
Strategie 2: gestructureerde extractie met Chain-of-Thought (sterk)
Het idee: in plaats van een enkel getal, een gestructureerde set signalen extraheren, waarbij het model wordt gedwongen elke stap te verklaren.
from pydantic import BaseModel, Field
from openai import OpenAI
from enum import Enum
from typing import Optional
class SentimentLevel(str, Enum):
VERY_BEARISH = "very_bearish"
BEARISH = "bearish"
NEUTRAL = "neutral"
BULLISH = "bullish"
VERY_BULLISH = "very_bullish"
class GuidanceSurprise(BaseModel):
"""Deviation of forward guidance from consensus expectations."""
revenue_guidance_vs_consensus: Optional[float] = Field(
None, description="% deviation of revenue guidance from consensus"
)
margin_guidance_direction: Optional[str] = Field(
None, description="expanding / stable / contracting"
)
key_quote: str = Field(
description="Verbatim quote with guidance"
)
reasoning: str = Field(
description="CoT: why this guidance matters"
)
class ConfidenceMetrics(BaseModel):
"""Management confidence metrics."""
hedge_word_count: int = Field(
description="Number of hedge words: 'approximately', 'potentially', 'subject to'"
)
forward_looking_ratio: float = Field(
description="Ratio of forward-looking statements to total statements"
)
q_and_a_evasion_count: int = Field(
description="Number of questions where CEO/CFO gave an evasive answer"
)
ceo_vs_cfo_sentiment_delta: float = Field(
description="Sentiment difference between CEO and CFO (-1 to 1). Divergence is a red flag"
)
class CompetitiveIntelligence(BaseModel):
"""Mentions of competitors and market position."""
competitors_mentioned: list[str] = Field(
description="List of mentioned competitors"
)
market_share_claims: list[str] = Field(
description="Market share claims"
)
new_product_signals: list[str] = Field(
description="Signals about new products/services"
)
class ManagementSignals(BaseModel):
"""Management signals."""
turnover_risk: SentimentLevel = Field(
description="Risk of key management turnover"
)
tone_shift_from_previous: Optional[str] = Field(
None, description="How tone changed compared to last quarter"
)
insider_language_flags: list[str] = Field(
description="Marker phrases: 'exploring strategic alternatives', 'right-sizing', etc."
)
class EarningsCallAnalysis(BaseModel):
"""Complete earnings call analysis."""
ticker: str
quarter: str
overall_sentiment: SentimentLevel
sentiment_score: float = Field(description="from -1.0 to 1.0")
guidance_surprise: GuidanceSurprise
confidence_metrics: ConfidenceMetrics
competitive_intel: CompetitiveIntelligence
management_signals: ManagementSignals
key_risks: list[str]
key_catalysts: list[str]
one_line_summary: str
def analyze_earnings_call(transcript: str, ticker: str, quarter: str) -> EarningsCallAnalysis:
"""
Extract structured signals from an earnings call.
Cost: ~$0.15-0.30 per call (GPT-4o, ~10K tokens input).
"""
client = OpenAI()
system_prompt = """You are a senior equity research analyst with 20 years of experience.
Analyze the following earnings call transcript and extract structured trading signals.
IMPORTANT INSTRUCTIONS:
1. Use Chain-of-Thought reasoning for each field — explain WHY before giving the value
2. Focus on DEVIATIONS from expectations, not absolute statements
3. Pay special attention to Q&A section — management is less scripted there
4. Compare management's language to typical corporate hedging baseline
5. Flag any "strategic alternatives", "right-sizing", or other euphemisms
6. Score sentiment relative to market expectations, not in absolute terms
HEDGE WORDS TO COUNT: approximately, potentially, subject to, may, might,
could, uncertain, challenging, headwinds, navigate, prudent, cautious,
evolving, dynamic, unprecedented, transitional"""
completion = client.beta.chat.completions.parse(
model="gpt-4o",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Ticker: {ticker}\nQuarter: {quarter}\n\n{transcript}"}
],
response_format=EarningsCallAnalysis,
temperature=0.1, # Low temperature for reproducibility
)
return completion.choices[0].message.parsed
Let op een paar kernpunten:
Pydantic-schema — OpenAI Structured Outputs garandeert 100% schemanaleving. Geen "sorry, I cannot parse the JSON" meer. Elk veld heeft een beschrijving die dienstdoet als een mini-prompt voor een specifiek aspect van de analyse.
Chain-of-Thought binnen het schema — de velden reasoning en key_quote dwingen het model om "zijn werk te tonen." Dit verbetert niet alleen de kwaliteit (het model wordt gedwongen een specifiek citaat te vinden voordat het een oordeel velt), maar creëert ook een audit trail voor toezichthouders.
Temperatuur 0,1 — we willen geen creativiteit. We hebben reproduceerbaarheid nodig. Bij temperatuur 0 raakt het model soms "vast" in patronen; 0,1 is het optimale compromis.
Strategie 3: few-shot met historische voorbeelden
Nog krachtiger — het model voorbeelden geven van eerdere earnings calls met daadwerkelijke marktreacties:
few_shot_examples = """
EXAMPLE 1:
Transcript excerpt: "We are cautiously optimistic about the second half...
While we continue to navigate macro headwinds, our pipeline remains robust."
Actual market reaction: -3.2% (next day)
Analysis: Despite surface-level positivity, "cautiously optimistic" is a
DOWNGRADE from previous quarter's "very confident". Five hedge words in
two sentences. Market read through the hedging.
EXAMPLE 2:
Transcript excerpt: "Frankly, demand has exceeded our ability to supply.
We're expediting CapEx to address this."
Actual market reaction: +7.8% (next day)
Analysis: "Frankly" signals genuine surprise even from management.
Accelerated CapEx on demand = strong confidence. No hedging language.
"""
Few-shot-voorbeelden helpen het model te kalibreren: het leert dat "cautiously optimistic" in Wall Street-taal niet positief is — het is een zacht negatief. Zonder voorbeelden kan de LLM woorden letterlijk interpreteren.
Vier soorten signalen
1. Guidance Surprise
Het meest directe signaal. Een bedrijf geeft een prognose (guidance) voor het volgende kwartaal/jaar, en de markt reageert op de afwijking van de consensus. Een LLM kan guidance extraheren, zelfs wanneer het management deze vaag communiceert:
- "We expect revenues in the range of..." — directe guidance, gemakkelijk te parsen
- "We feel comfortable with current Street estimates" — impliciete bevestiging van consensus
- "There are puts and takes relative to consensus" — impliciet risicosignaal
Een LLM begrijpt alle drie de formuleringen; regex begrijpt alleen de eerste.
2. Confidence Metrics: hedge-word-dichtheid
Dit is mijn favoriete signaal omdat het contra-intuïtief is. De essentie: managers zijn mensen met juridische scholing en paranoïde juridische afdelingen. Wanneer het goed gaat, staan ze zichzelf toe specifiek te zijn. Wanneer er problemen broeien — beginnen ze te hedgen.
Te volgen metrics:
| Metric | Beschrijving | Bearish signaal |
|---|---|---|
| Hedge-word-dichtheid | Aandeel hedge-woorden per 1.000 woorden | > 15 per 1.000 woorden |
| Certainty ratio | Verhouding "will/expect" versus "may/could" | < 1,5 |
| Q&A ontwijkingspercentage | % vragen zonder direct antwoord | > 30% |
| CEO/CFO-delta | Toonverschil tussen CEO en CFO | > 0,3 op schaal [-1, 1] |
Het laatste punt is bijzonder interessant. De CEO is een verhalenverteller — zijn taak is een mooi plaatje te schetsen. De CFO is degene die verantwoording aflegt aan de accountants. Wanneer de CEO zegt "transformative growth ahead" en de CFO onmiddellijk aanvult met "while maintaining disciplined cost management" — signaleert deze divergentie interne spanningen.
3. Competitive Intelligence
Een LLM kan concurrentvermeldingen uit een transcript extraheren, zelfs wanneer het management directe namen vermijdt. "The largest player in the market" — dat is geen mysterie voor GPT-4 als het de sector kent.
Handelssignaal: als bedrijf A concurrent B in een negatieve context noemt tijdens zijn earnings call ("we're taking share from..."), is dat niet alleen een signaal voor A (long) maar ook voor B (short). Een pairs trade.
4. Management Turnover-signalen
Markerzinnen voor managementveranderingen of strategische koerswijzigingen:
- "Exploring strategic alternatives" — waarschijnlijke verkoop van het bedrijf
- "Right-sizing our operations" — massaontslagen
- "The board has initiated a comprehensive review" — de CEO vertrekt binnenkort
- "We're bringing in fresh perspectives" — het huidige team is gefaald
Elk van deze zinnen heeft een statistisch significante correlatie met de daaropvolgende koersdynamiek. Een LLM kan ze detecteren met nul valse positieven — omdat het context begrijpt, in tegenstelling tot regex, dat "strategic alternatives" zou kunnen oppikken in een productlijnbeschrijving.
Backtesting: Event Study-methodologie

We genereren signalen — geweldig. Maar werken ze? De standaard verificatiemethode is een Event Study met de berekening van Cumulative Abnormal Returns (CAR).
Methodologie
- Het event definiëren — de datum van de earnings call
- Schattingsvenster — [-250, -30] handelsdagen vóór het event om "normale" rendementen te schatten
- Event-venster — [-1, +60] dagen rondom het event
- Normale rendementen berekenen via het marktmodel:
- Abnormaal rendement — het verschil tussen het werkelijke en het "normale" rendement
- CAR — cumulatieve som van abnormale rendementen over het event-venster
import numpy as np
import pandas as pd
from scipy import stats
from dataclasses import dataclass
@dataclass
class EventStudyResult:
car: np.ndarray # Cumulative abnormal returns by day
t_stats: np.ndarray # t-statistics for each day
avg_car_3d: float # CAR[-1, +1]
avg_car_30d: float # CAR[-1, +30]
avg_car_60d: float # CAR[-1, +60]
p_value_3d: float
p_value_30d: float
n_events: int
def run_event_study(
returns: pd.DataFrame, # Daily stock returns (columns = tickers)
market_returns: pd.Series, # Daily market index returns
events: pd.DataFrame, # DataFrame with columns: [ticker, date, signal_score]
estimation_window: int = 220,
gap: int = 30,
event_window: tuple = (-1, 60),
) -> EventStudyResult:
"""
Event study to evaluate the predictive power of LLM signals.
Sort events by signal_score, form long/short portfolios,
calculate CAR and test statistical significance.
"""
all_cars = []
for _, event in events.iterrows():
ticker = event['ticker']
event_date = event['date']
if ticker not in returns.columns:
continue
try:
event_idx = returns.index.get_loc(event_date, method='ffill')
except KeyError:
continue
est_start = event_idx - estimation_window - gap
est_end = event_idx - gap
if est_start < 0:
continue
y = returns.iloc[est_start:est_end][ticker].values
x = market_returns.iloc[est_start:est_end].values
mask = ~(np.isnan(y) | np.isnan(x))
if mask.sum() < 60: # Minimum 60 observations
continue
y_clean, x_clean = y[mask], x[mask]
slope, intercept, _, _, _ = stats.linregress(x_clean, y_clean)
residual_std = np.std(y_clean - (intercept + slope * x_clean))
ev_start = event_idx + event_window[0]
ev_end = event_idx + event_window[1] + 1
if ev_end > len(returns):
continue
actual = returns.iloc[ev_start:ev_end][ticker].values
market = market_returns.iloc[ev_start:ev_end].values
expected = intercept + slope * market
ar = actual - expected
car = np.cumsum(ar)
all_cars.append(car)
if not all_cars:
raise ValueError("No valid events found")
min_len = min(len(c) for c in all_cars)
all_cars = np.array([c[:min_len] for c in all_cars])
mean_car = np.mean(all_cars, axis=0)
std_car = np.std(all_cars, axis=0) / np.sqrt(len(all_cars))
t_stats = mean_car / (std_car + 1e-10)
offset = -event_window[0] # Shift to event date
car_3d = mean_car[min(offset + 1, min_len - 1)] if min_len > offset + 1 else mean_car[-1]
car_30d = mean_car[min(offset + 30, min_len - 1)] if min_len > offset + 30 else mean_car[-1]
car_60d = mean_car[min(offset + 60, min_len - 1)] if min_len > offset + 60 else mean_car[-1]
n = len(all_cars)
p_3d = 2 * (1 - stats.t.cdf(abs(car_3d / (np.std([c[min(offset+1, min_len-1)] for c in all_cars]) / np.sqrt(n) + 1e-10)), df=n-1))
p_30d = 2 * (1 - stats.t.cdf(abs(car_30d / (np.std([c[min(offset+30, min_len-1)] for c in all_cars]) / np.sqrt(n) + 1e-10)), df=n-1))
return EventStudyResult(
car=mean_car,
t_stats=t_stats,
avg_car_3d=car_3d,
avg_car_30d=car_30d,
avg_car_60d=car_60d,
p_value_3d=p_3d,
p_value_30d=p_30d,
n_events=n,
)
def backtest_llm_signals(
llm_signals: pd.DataFrame, # [ticker, date, sentiment_score]
returns: pd.DataFrame,
market_returns: pd.Series,
):
"""Backtest: long top-quintile signals, short bottom-quintile."""
llm_signals['quintile'] = pd.qcut(
llm_signals['sentiment_score'], 5, labels=[1, 2, 3, 4, 5]
)
long_events = llm_signals[llm_signals['quintile'] == 5].copy()
short_events = llm_signals[llm_signals['quintile'] == 1].copy()
long_result = run_event_study(returns, market_returns, long_events)
short_result = run_event_study(returns, market_returns, short_events)
print(f"LONG portfolio (top quintile LLM sentiment):")
print(f" CAR[0,+3]: {long_result.avg_car_3d:+.2%} (p={long_result.p_value_3d:.4f})")
print(f" CAR[0,+30]: {long_result.avg_car_30d:+.2%} (p={long_result.p_value_30d:.4f})")
print(f" N events: {long_result.n_events}")
print(f"\nSHORT portfolio (bottom quintile LLM sentiment):")
print(f" CAR[0,+3]: {short_result.avg_car_3d:+.2%} (p={short_result.p_value_3d:.4f})")
print(f" CAR[0,+30]: {short_result.avg_car_30d:+.2%} (p={short_result.p_value_30d:.4f})")
print(f" N events: {short_result.n_events}")
ls_3d = long_result.avg_car_3d - short_result.avg_car_3d
ls_30d = long_result.avg_car_30d - short_result.avg_car_30d
print(f"\nLONG-SHORT spread:")
print(f" CAR[0,+3]: {ls_3d:+.2%}")
print(f" CAR[0,+30]: {ls_30d:+.2%}")
Wat we verwachten te zien
Op basis van bestaand onderzoek, realistische CAR's voor LLM-signalen:
| Venster | Long-portfolio | Short-portfolio | L/S-spread |
|---|---|---|---|
| [0, +1] | +0,8% — +1,5% | -0,5% — -1,2% | 1,3% — 2,7% |
| [0, +30] | +1,5% — +3,0% | -1,0% — -2,5% | 2,5% — 5,5% |
| [0, +60] | +2,0% — +4,0% | -1,5% — -3,5% | 3,5% — 7,5% |
De belangrijkste indicator is statistische significantie. Met p < 0,01 en N > 200 events kun je spreken van een robuust signaal. Met p > 0,05 — kan het ruis zijn.
Productie-implementatie: van Jupyter naar prod
Real-time pipeline-architectuur
YouTube/Audio Stream
│
▼
┌─────────────────┐ ┌──────────────────┐
│ Whisper │───▶│ Transcript │
│ Transcription │ │ Buffer │
│ (streaming) │ │ (Redis Stream) │
└─────────────────┘ └──────────────────┘
│
┌─────────┴─────────┐
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Real-time │ │ Full-call │
│ Chunk Anal. │ │ Analysis │
│ (every 5min) │ │ (after call │
│ │ │ ends) │
└──────────────┘ └──────────────┘
│ │
▼ ▼
┌──────────────────────────────┐
│ Signal Aggregator │
│ (confidence-weighted merge) │
└──────────────────────────────┘
│
▼
┌──────────────────────────────┐
│ Trading Engine │
│ (position sizing, risk mgmt)│
└──────────────────────────────┘
Kostenanalyse: hoeveel kost één earnings call
Laten we de economie van het verwerken van één earnings call in productie uiteenzetten:
| Component | Kosten | Latentie |
|---|---|---|
| Whisper API-transcriptie (60 min) | $0,36 | ~17 sec (Turbo) |
| GPT-4o gestructureerde extractie | $0,15-0,30 | ~8-15 sec |
| GPT-4o real-time chunk-analyse (x12) | $1,80-3,60 | ~5 sec elk |
| Embedding voor RAG-opslag | $0,01 | <1 sec |
| Totaal (volledige pipeline) | $2,30-4,30 | ~30 sec totaal |
Wacht, de schatting was $30-50 per call. Waar komen die cijfers vandaan? Het hangt af van het model en de aanpak:
- Budgetoptie (GPT-4o-mini, enkele pass): $0,50-1,00
- Standaardoptie (GPT-4o, gestructureerde extractie + chunk-analyse): $2-5
- Premiumoptie (GPT-4o, meerdere passes, cross-validatie, historische vergelijking): $15-30
- Hedgefonds-niveau (meerdere modellen + menselijke review + real-time streaming): $30-50+
Voor een kwantitatief fonds dat 500 tickers verhandelt, bedragen de kosten voor het verwerken van een earnings-seizoen (~2.000 calls over 6 weken) 10.000 met de standaardoptie. Gezien een gemiddelde alpha per positie van 1-3% — is de ROI astronomisch.
Latentie: de race om milliseconden
In de HFT-wereld is latentie alles. Maar voor op earnings gebaseerde strategieën is de situatie anders:
- Een earnings call duurt 45-60 minuten — je hebt tijd
- PEAD strekt zich uit over 60 dagen — geen noodzaak om in de eerste seconde in te stappen
- De belangrijkste dislocatie vindt plaats in de eerste 30 minuten na afloop van de call
De optimale strategie is tweefasig:
- Fase 1 (real-time): analyseer chunks van 5 minuten tijdens de call, waarbij een voorlopig signaal wordt gevormd
- Fase 2 (post-call): volledige analyse van het hele transcript 2-5 minuten na afloop
Fase 1 biedt een voorsprong van 5-10 minuten op marktdeelnemers die wachten tot de call eindigt. Voor mid-capaandelen is dit genoeg.
Uitbreiding naar crypto: DeFi governance en DAO-voorstellen
De cryptomarkt is een ideaal testterrein voor LLM alpha mining. Hier is waarom:
- Minder institutionele spelers — wat meer inefficiëntie betekent om te benutten
- Governance = earnings call — DAO-beslissingen beïnvloeden de tokenomics rechtstreeks
- 24/7-markt — je kunt de reactie onmiddellijk verhandelen
- Publieke data — alle voorstellen en stemmingen zijn on-chain
Soorten crypto-events voor analyse
Governance Proposals (Aave, Compound, Uniswap)
Een voorstel wijzigt protocolparameters — rentetarieven, onderpandfactoren, fee switches. Een LLM kan de economische impact beoordelen:
crypto_analysis_prompt = """Analyze this DeFi governance proposal.
Extract:
1. Economic impact on token holders (positive/negative/neutral)
2. TVL impact estimate (increase/decrease/stable + magnitude)
3. Competitive positioning vs other protocols
4. Risk factors introduced by the proposal
5. Historical precedent (similar proposals in other protocols)
6. Likely voting outcome based on forum discussion sentiment
Proposal: {proposal_text}
Forum discussion: {discussion_text}
"""
Protocol Update Announcements
Wanneer Uniswap v4 met hooks aankondigt, of Aave GHO lanceert — dat is het equivalent van een productlancering in TradFi. Een LLM kan narratief momentum en technische relevantie beoordelen.
Treasury Reports
Grote DAO's hebben treasuries ter waarde van honderden miljoenen. Kwartaal-treasuryrapporten zijn een directe analoog van earnings. Runway, burn rate, diversificatie — allemaal vatbaar voor LLM-analyse.
Specifieke kenmerken van crypto-signalen
In tegenstelling tot TradFi geldt in crypto:
- On-chain data bevestigt of weerspreekt het narratief — je kunt kruisverwijzen wat er op governance calls wordt gezegd met werkelijke protocolmetrics (TVL, volume, actieve gebruikers)
- Whale wallets als insider trading — grote wallet-bewegingen na governance-discussies gaan vaak vooraf aan stemmingen
- Sentiment-amplificatie via CT (Crypto Twitter) — een signaal van een governance call kan worden versterkt of onderdrukt door Twitter-narratieven
Valkuilen en beperkingen
Hallucinaties: wanneer het model cijfers verzint
Een LLM kan guidance "extraheren" die niet in het transcript stond. Dit is vooral gevaarlijk bij het analyseren van hedge-word-dichtheid: het model kan meer of minder woorden tellen dan er daadwerkelijk bestaan.
Oplossing: tweefasige verificatie. De LLM extraheert, deterministische code verifieert. Voor hedge-woorden — regex-telling parallel aan LLM-beoordeling. Afwijking > 20% — markeren voor handmatige review.
import re
HEDGE_WORDS = [
r'\bapproximately\b', r'\bpotentially\b', r'\bsubject to\b',
r'\bmay\b', r'\bmight\b', r'\bcould\b', r'\buncertain\b',
r'\bchallenging\b', r'\bheadwinds\b', r'\bnavigate\b',
r'\bprudent\b', r'\bcautious\b', r'\bevolving\b',
r'\bdynamic\b', r'\bunprecedented\b', r'\btransitional\b',
]
def verify_hedge_count(text: str, llm_count: int) -> dict:
"""Deterministic verification of LLM hedge word count."""
regex_count = sum(
len(re.findall(pattern, text, re.IGNORECASE))
for pattern in HEDGE_WORDS
)
deviation = abs(llm_count - regex_count) / (regex_count + 1)
return {
"llm_count": llm_count,
"regex_count": regex_count,
"deviation": deviation,
"needs_review": deviation > 0.2,
}
Beperkingen van het contextvenster
Zelfs 128K tokens zijn misschien niet genoeg als je het volgende wilt aanleveren:
- Huidig transcript (~10K tokens)
- Vorig transcript ter vergelijking (~10K)
- Analisten-consensusprognoses (~2K)
- Few-shot-voorbeelden (~3K)
- Systeemprompt (~1K)
Totaal ~26K — we passen er nog net in. Maar als je een 10-K-indiening (~80-120K tokens) toevoegt voor context — zit je al aan de grens. Oplossing: RAG om relevante chunks uit lange documenten op te halen.
Bias en systematische fouten
LLM's worden getraind op historische data waarin bepaalde zinsdelen geassocieerd werden met bepaalde uitkomsten. Maar de markt past zich aan:
- Als iedereen hedge-woorden begint te tellen met GPT-4, zullen managers hun taalgebruik veranderen
- Het model kan de betekenis van patronen uit trainingsdata overschatten (survivorship bias)
- Bedrijfstaal evolueert: "synergieën" betekende in 2010 iets anders dan in 2026
Crowded-trade-risico
Als 50 kwantitatieve fondsen dezelfde GPT-4 gebruiken om dezelfde transcripten te analyseren — degradeert het signaal. Een analogie: toen iedereen PEAD begon te verhandelen op numerieke verrassingen, kromp de anomalie. Hetzelfde zal gebeuren met tekstuele signalen, maar met vertraging:
- Nu (2026) — weinigen gebruiken LLM's systematisch voor earnings calls. Alpha is significant
- Over 2-3 jaar — brede adoptie, alpha neemt af
- Over 5 jaar — basale LLM-signalen worden een commodity, edge blijft alleen bestaan in aangepaste modellen en unieke data
Dit is de standaard levenscyclus van een alpha-signaal. Geniet ervan zolang het duurt.
In plaats van een conclusie: een actieplan
Als je wilt beginnen met het gebruik van LLM's voor earnings call-analyse, hier een minimaal haalbaar plan:
- Begin met gratis data — SEC EDGAR + EdgarTools voor 8-K/10-Q-indieningen
- Gebruik gestructureerde extractie — Pydantic-schema's via OpenAI Structured Outputs
- Backtest via event study — CAR op historische data, minimaal 200 events
- Voeg few-shot-voorbeelden toe — 5-10 gelabelde voorbeelden verbeteren de kwaliteit radicaal
- Verifieer deterministisch — LLM extraheert, regex verifieert, mens auditeert
- Begin met mid-cap — meer alpha, minder concurrentie met grote fondsen
- Breid uit naar crypto — governance calls en DAO-voorstellen als onontgonnen terrein
Onthoud de kardinale regel van kwantitatieve analyse: als een signaal te mooi klinkt om waar te zijn — controleer het opnieuw. LLM's creëren een illusie van begrip, maar daarachter schuilt statistische patroonherkenning. Een krachtig instrument — maar een instrument, geen orakel.
Bibliografie
-
Ball, R., Brown, P. (1968). An Empirical Evaluation of Accounting Income Numbers. Journal of Accounting Research, 6(2), 159-178. — Eerste ontdekking van PEAD.
-
Bernard, V.L., Thomas, J.K. (1989). Post-Earnings-Announcement Drift: Delayed Price Response or Risk Premium? Journal of Accounting Research, 27, 1-36. — Canonieke PEAD-paper.
-
Loughran, T., McDonald, B. (2011). When Is a Liability Not a Liability? Textual Analysis, Dictionaries, and 10-Ks. Journal of Finance, 66(1), 35-65. — Financieel sentimentwoordenboek.
-
Araci, D. (2019). FinBERT: Financial Sentiment Analysis with Pre-trained Language Models. arXiv:1908.10063. — BERT voor financiële NLP, +14pp t.o.v. SOTA.
-
Wu, S. et al. (2023). BloombergGPT: A Large Language Model for Finance. arXiv:2303.17564. — Bloombergs 50B-parametermodel.
-
Yang, H. et al. (2023). FinGPT: Open-Source Financial Large Language Models. arXiv:2306.06031. — Open-source alternatief voor BloombergGPT, 89% nauwkeurigheid.
-
Lopez-Lira, A., Tang, Y. (2023). Can ChatGPT Forecast Stock Price Movements? Return Predictability and Large Language Models. arXiv:2304.07619. — GPT-4 voorspelt rendementen met ~90% trefzekerheid.
-
Meursault, V., Liang, P.J., Routledge, B., Scanlon, M.M. (2023). PEAD.txt: Post-Earnings-Announcement Drift Using Text. Journal of Financial and Quantitative Analysis. — Tekstgebaseerde PEAD is twee keer zo groot als numerieke PEAD.
-
Fatouros, G. et al. (2024). Can Large Language Models Beat Wall Street? Evaluating GPT-4's Impact on Financial Decision-Making with MarketSenseAI. Neural Computing and Applications. — GPT-4-framework met 10-30% overrendement op S&P 100.
-
Chen, Y. et al. (2025). GPT-Signal: Generative AI for Semi-automated Feature Engineering in the Alpha Research Process. arXiv:2410.18448. — Automatische generatie van handelssignalen via LLM.
-
Zhang, X. et al. (2025). Can LLMs Hit Moving Targets? Tracking Evolving Signals in Corporate Disclosures. arXiv:2510.03195. — Detectie van "bewegende doelen" in bedrijfspublicaties.
-
Chen, Z. et al. (2025). Large Language Models in Equity Markets: Applications, Techniques, and Insights. Frontiers in Artificial Intelligence. — Overzicht van 84 LLM-onderzoeken in finance.
Dit artikel is educatief van aard en vormt geen beleggingsadvies. Elke hier beschreven handelsstrategie vereist grondige backtesting en risicobeheer voordat deze wordt ingezet met echt kapitaal.
Auteurs
Trading-systems engineer
Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.