LLM 알파 마이닝: 어닝스 콜과 금융 문서에서 트레이딩 시그널을 추출하는 방법
월스트리트에는 이런 농담이 있다: "어닝스 콜에서 가장 가치 있는 정보는 CEO가 무엇을 말했는지가 아니라, 어떻게 말했는지이다." 팀 쿡이 작년의 "우리는 매우 만족한다" 대신 "우리는 신중하게 낙관적이다"라고 말할 때 — 이것은 언어적 유희가 아니라 수억 달러 규모의 시그널이다.
수십 년간 퀀트 펀드는 이러한 시그널의 체계적 추출을 시도해 왔다. 처음에는 사전을 사용해 "긍정적"과 "부정적" 단어의 빈도를 세었다. 그다음 BERT를 투입했다. 그리고 이제 GPT-4o, Claude, 오픈소스 LLM이 등장해 연구자들조차 놀라게 할 정도의 정확도로 기업 언어의 미묘한 차이를 파싱할 수 있게 되었다.
어닝스 콜에서 트레이딩 시그널을 추출하는 완전한 파이프라인 구축 방법을 살펴보자 — 트랜스크립트 확보부터 누적 비정상 수익률의 백테스팅까지.
왜 어닝스 콜은 알파의 금맥인가
어닝스 공시 후 드리프트: 사라지지 않는 이상 현상
1968년 볼과 브라운은 이상한 현상을 발견했다: 분기 실적 발표 후 주가가 "서프라이즈" 방향으로 60-90일간 계속 드리프트하는 것이다. 이를 Post-Earnings Announcement Drift(PEAD)라 명명했다. 그로부터 반세기가 넘게 지났고, 수백 편의 논문이 작성되었으며, 이 이상 현상은 십여 가지 각도에서 설명되었지만 — 여전히 작동한다.
PEAD는 금융 역사상 가장 지속적인 시장 이상 현상 중 하나다. "긍정적 서프라이즈 매수, 부정적 서프라이즈 매도" 포트폴리오 전략은 역사적으로 연간 10-25%의 초과 수익률을 창출해왔다. 왜 시장은 이를 차익거래로 제거하지 못했을까? 여러 이유가 있다:
- 투자자의 제한된 주의력 — 200개 기업이 같은 주에 실적을 발표할 때 모든 트랜스크립트를 읽는 것은 물리적으로 불가능하다
- 인지적 복잡성 — 어닝스 콜은 45-60분간 진행되며, 핵심 시그널은 Q&A 세션 38분 째의 한 문장에 숨어 있을 수 있다
- 언어의 모호성 — CFO가 "we are navigating headwinds"라고 말해도 맥락 없이는 부드러운 경고인지 표준적 헤징인지 알 수 없다
바로 여기에서 LLM이 등장한다. 하룻밤에 500건의 트랜스크립트를 처리하면서도 경험 많은 애널리스트도 놓칠 수 있는 뉘앙스를 포착할 수 있는 도구를 우리는 처음으로 갖게 되었다.
PEAD.txt: 텍스트가 숫자보다 중요하다
필라델피아 연준의 연구자들(Meursault, Liang, Routledge, Scanlon)은 PEAD.txt 논문을 발표해 텍스트 정보의 가치에 대한 인식을 뒤집었다. 그들은 표준 어닝스 서프라이즈의 텍스트 아날로그인 SUE.txt를 구축했는데, 이것은 이익의 수치 값을 전혀 사용하지 않는다.
결과는? SUE.txt는 클래식 PEAD의 두 배에 달하는 드리프트를 생성한다. 더구나 최근 몇 년간 수치적 서프라이즈에 기반한 클래식 PEAD가 사실상 사라졌지만(시장이 학습했다), 텍스트 드리프트는 여전히 유의하다. 시장은 숫자를 빠르게 처리하는 법을 배웠지만, 텍스트 해석에는 여전히 어려움을 겪고 있다.
이것이 어닝스 콜에 대한 NLP 기반 접근법을 지지하는 근본적 논거다.
센티먼트에서 시맨틱스로: 접근법의 진화

1세대: Bag-of-Words와 사전 (2000-2015)
모든 것은 Loughran-McDonald 사전(2011)에서 시작되었다 — "긍정적", "부정적", "불확실", "소송 관련"으로 라벨링된 단어 목록이다. 아이디어는 단순하면서 우아했다: 10-K 보고서에서 부정적 단어의 비율을 세고 이를 기반으로 거래하라.
문제는? "outstanding"이라는 단어는 금융 맥락에서 "우수한 실적"보다 "미상환 부채"를 의미하는 경우가 더 많다. Risk Management에서의 "risk"는 부정적 시그널이 아니라 프로세스 설명이다. 표준 NLP 센티먼트 사전은 금융 텍스트에서 형편없이 작동했다.
Loughran과 McDonald는 전문 사전을 만들어 상황을 개선했지만, 근본적 문제는 남았다: bag-of-words는 맥락을 이해하지 못한다. "We did not fail to meet expectations" — 여기에는 두 개의 "부정적" 단어가 있지만 의미는 긍정적이다.
2세대: FinBERT와 트랜스포머 (2019-2023)
2019년 Dogu Araci는 FinBERT를 발표했다 — Reuters TRC2의 금융 텍스트로 파인튜닝된 BERT다. 결과는 인상적이었다: Financial PhraseBank 데이터셋에서 최첨단 대비 14 퍼센트 포인트 향상. FinBERT는 맥락을 이해했다: "outstanding"이 "debt" 옆에 있으면 부정적, "performance" 옆에 있으면 긍정적.
하지만 FinBERT에는 한계가 있었다: 512 토큰의 컨텍스트 윈도우. 어닝스 콜은 8,000-12,000 단어에 달한다. 청크로 나누어 센티먼트를 평균하면 문단 간 시맨틱스를 잃게 된다. CEO가 낙관적으로 시작한 후 Q&A에서 공급망 문제를 살짝 언급할 수 있다. FinBERT는 각 청크를 독립적으로 분석하기에 이 대비를 놓친다.
3세대: 긴 컨텍스트의 LLM (2023-현재)
GPT-4, Claude, Gemini는 128K-1M 토큰의 컨텍스트 윈도우로 게임의 규칙을 바꿨다. 이제 트랜스크립트 전체를 한 번에 로드하고 문서 전체의 이해가 필요한 질문을 할 수 있다.
핵심 연구 — Lopez-Lira & Tang (2023) "Can ChatGPT Forecast Stock Price Movements?" 50,000건 이상의 헤드라인에서 GPT-4는 시장 초기 반응 방향 예측에서 ~90% 적중률을 보였고, 특히 소형주와 부정적 뉴스에서 후속 드리프트를 유의하게 예측했다. 초기 모델(GPT-1, GPT-2, BERT)에는 이 능력이 없었다 — 예측력은 대형 모델의 창발적 속성으로 나타난다.
BloombergGPT (2023) — Bloomberg 금융 코퍼스로 훈련된 500억 파라미터 모델 — 금융 NER, 뉴스 분류, 센티먼트 분석에서 개선을 보였다. FinGPT — 오픈소스 대안 — 은 데이터 중심 접근법과 RAG로 금융 센티먼트 작업에서 89% 정확도를 달성했다.
GPT-4와 Chain-of-Thought, In-Context Learning을 S&P 100 분석에 활용한 MarketSenseAI는 15개월 테스트에서 10-30%의 초과 알파와 최대 72%의 누적 수익률을 보였다. 물론 이 수치는 신중하게 받아들여야 한다(백테스트 ≠ 실거래). 하지만 추세는 분명하다.
데이터 파이프라인: 데이터를 어디서 구할 것인가
SEC EDGAR: 공식 소스
미국 주식의 경우 주요 소스는 SEC EDGAR다. 어닝스 콜은 보통 직접 제출되지 않지만, 관련 문서는 이용 가능하다:
- 8-K filing (Item 2.02 — 영업 실적) — 실적 보도자료로, 트랜스크립트가 포함된 exhibit 99이 있는 경우가 많다
- 10-Q / 10-K — 분기 및 연간 보고서, Management Discussion & Analysis (MD&A) 포함 — 역시 가치 있는 텍스트 소스
- DEF 14A — 경영진 보수 정보를 포함한 위임장 진술서
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() # 첨부파일 포함 전체 텍스트
print(f"{filing.filing_date}: {len(text)} chars")
Seeking Alpha와 상용 API
어닝스 콜 트랜스크립트는 별도 상품이다. Seeking Alpha가 역사적으로 주요 무료 소스였지만, 현재는 접근을 제한하고 있다. 상용 옵션:
- Seeking Alpha Premium API — 발언자 라벨이 포함된 전체 트랜스크립트
- AlphaVantage Earnings API — 제한된 무료 티어
- Financial Modeling Prep — 트랜스크립트 + 펀더멘털 데이터
- Earnings Call Edge / Motley Fool Transcripts — 대안 소스
크립토: 거버넌스 콜과 DAO 제안
여기가 더 흥미롭다. 주요 DeFi 프로토콜은 어닝스 콜에 해당하는 행사를 진행한다:
- Uniswap — 거버넌스 콜, 커뮤니티 콜, YouTube 녹화
- Aave — 월간 커뮤니티 콜 + 거버넌스 포럼 제안
- MakerDAO — 거버넌스 콜 + 광범위한 포럼 토론
- Compound — 상세 토론이 포함된 거버넌스 제안
크립토 콜 트랜스크립트는 보통 구조화되어 있지 않다. 해결책 — OpenAI의 Whisper를 사용한 YouTube 녹화 트랜스크립션:
import openai
from yt_dlp import YoutubeDL
def transcribe_governance_call(youtube_url: str) -> str:
"""YouTube에서 오디오를 다운로드하고 Whisper로 트랜스크립션."""
ydl_opts = {
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': 'mp3',
'preferredquality': '64', # 음성에는 낮은 비트레이트로 충분
}],
'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
Whisper API를 통한 트랜스크립션 비용: 0.36. 셀프 호스팅의 경우 — Whisper Large-v3 Turbo는 최신 GPU에서 60분 파일을 약 17초에 트랜스크립션(실시간 대비 216배).
LLM 프롬프팅 전략: 단순에서 프로덕션급까지

전략 1: 직접 센티먼트 (약함)
가장 단순한 접근법 — 모델에게 직접 질문하기:
"Is this earnings call positive or negative for the stock price?"
이것이 효과가 있을까? 놀랍게도 그렇다. Lopez-Lira & Tang은 이렇게 원시적인 프롬프트도 통계적으로 유의한 예측을 생성한다는 것을 보여주었다. 하지만 문제가 있다:
- 이진 출력 — 그라데이션을 잃는다. "재앙"과 "가벼운 실망"이 같은 라벨을 받는다
- 설명 부재 — 모델이 무엇을 기반으로 결정했는지 알 수 없다
- 불안정성 — 재실행 시 다른 답변이 나올 수 있다
전략 2: Chain-of-Thought를 활용한 구조화된 추출 (강함)
아이디어: 단일 숫자 대신 구조화된 시그널 세트를 추출하되, 모델이 각 단계를 설명하도록 강제한다.
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):
"""포워드 가이던스와 컨센서스 예상의 편차."""
revenue_guidance_vs_consensus: Optional[float] = Field(
None, description="매출 가이던스의 컨센서스 대비 편차 %"
)
margin_guidance_direction: Optional[str] = Field(
None, description="expanding / stable / contracting"
)
key_quote: str = Field(
description="가이던스를 포함한 원문 인용"
)
reasoning: str = Field(
description="CoT: 이 가이던스가 왜 중요한가"
)
class ConfidenceMetrics(BaseModel):
"""경영진 신뢰도 지표."""
hedge_word_count: int = Field(
description="헤지 워드 수: 'approximately', 'potentially', 'subject to'"
)
forward_looking_ratio: float = Field(
description="전체 발언 중 미래 전망 발언의 비율"
)
q_and_a_evasion_count: int = Field(
description="CEO/CFO가 모호한 답변을 한 질문 수"
)
ceo_vs_cfo_sentiment_delta: float = Field(
description="CEO와 CFO의 센티먼트 차이 (-1 ~ 1). 괴리는 위험 신호"
)
class CompetitiveIntelligence(BaseModel):
"""경쟁사 언급과 시장 포지션."""
competitors_mentioned: list[str] = Field(
description="언급된 경쟁사 목록"
)
market_share_claims: list[str] = Field(
description="시장 점유율 주장"
)
new_product_signals: list[str] = Field(
description="신규 제품/서비스 시그널"
)
class ManagementSignals(BaseModel):
"""경영진 시그널."""
turnover_risk: SentimentLevel = Field(
description="핵심 경영진 교체 리스크"
)
tone_shift_from_previous: Optional[str] = Field(
None, description="전 분기 대비 어조의 변화"
)
insider_language_flags: list[str] = Field(
description="마커 문구: 'exploring strategic alternatives', 'right-sizing' 등"
)
class EarningsCallAnalysis(BaseModel):
"""어닝스 콜 완전 분석."""
ticker: str
quarter: str
overall_sentiment: SentimentLevel
sentiment_score: float = Field(description="-1.0에서 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:
"""
어닝스 콜에서 구조화된 시그널 추출.
비용: 호출당 약 $0.15-0.30 (GPT-4o, 입력 약 10K 토큰).
"""
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, # 재현성을 위한 낮은 temperature
)
return completion.choices[0].message.parsed
몇 가지 핵심 포인트에 주목하자:
Pydantic 스키마 — OpenAI Structured Outputs는 100% 스키마 준수를 보장한다. 더 이상 "sorry, I cannot parse the JSON"은 없다. 각 필드에는 분석의 특정 측면에 대한 미니 프롬프트 역할을 하는 description이 있다.
스키마 내 Chain-of-Thought — reasoning과 key_quote 필드는 모델이 "작업 과정을 보여주도록" 강제한다. 이는 품질을 향상시킬 뿐 아니라(모델이 판단 전에 구체적 인용을 찾아야 한다), 규제 당국을 위한 감사 추적도 생성한다.
Temperature 0.1 — 창의성은 필요 없다. 재현성이 필요하다. Temperature 0에서는 모델이 때때로 패턴에 "갇히는" 경우가 있다. 0.1이 최적의 절충점이다.
전략 3: 과거 사례를 활용한 Few-Shot
더 강력한 방법 — 과거 어닝스 콜의 예시와 실제 시장 반응을 모델에 제공:
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 예시는 모델 캘리브레이션에 도움이 된다: 월스트리트 언어에서 "cautiously optimistic"은 긍정적이지 않고 부드러운 부정적 신호라는 것을 학습한다. 예시 없이는 LLM이 단어를 문자 그대로 해석할 수 있다.
네 가지 시그널 유형
1. 가이던스 서프라이즈
가장 직접적인 시그널. 기업이 다음 분기/연도 전망(가이던스)을 제공하면 시장은 컨센서스와의 편차에 반응한다. 경영진이 모호하게 전달하더라도 LLM은 가이던스를 추출할 수 있다:
- "We expect revenues in the range of..." — 직접 가이던스, 파싱이 쉬움
- "We feel comfortable with current Street estimates" — 컨센서스의 암묵적 확인
- "There are puts and takes relative to consensus" — 암묵적 리스크 시그널
LLM은 세 가지 표현 모두를 이해한다; 정규식은 첫 번째만 이해한다.
2. 신뢰도 지표: 헤지 워드 밀도
이것은 직관에 반하기 때문에 내가 가장 좋아하는 시그널이다. 핵심은 이렇다: 경영진은 법적 교육을 받고 편집증적인 법무팀을 가진 사람들이다. 상황이 좋을 때는 구체적으로 말하는 것을 허용한다. 문제가 싹틀 때 — 헤징을 시작한다.
추적해야 할 지표:
| 지표 | 설명 | 약세 시그널 |
|---|---|---|
| 헤지 워드 밀도 | 1,000 단어당 헤지 워드 비율 | 1,000 단어당 15 초과 |
| 확실성 비율 | "will/expect" 대 "may/could" 비율 | < 1.5 |
| Q&A 회피율 | 직접 답변 없는 질문의 비율 | > 30% |
| CEO/CFO 괴리 | CEO와 CFO 어조의 차이 | [-1, 1] 스케일에서 > 0.3 |
마지막 항목이 특히 흥미롭다. CEO는 스토리텔러다 — 그의 역할은 아름다운 그림을 그리는 것이다. CFO는 감사인에게 책임지는 사람이다. CEO가 "transformative growth ahead"라고 말하고 CFO가 바로 "while maintaining disciplined cost management"를 끼워 넣을 때 — 이 괴리는 내부 긴장을 시사하는 시그널이다.
3. 경쟁 인텔리전스
경영진이 직접 이름을 피하더라도 LLM은 트랜스크립트에서 경쟁사 언급을 추출할 수 있다. "The largest player in the market" — 업계를 알고 있는 GPT-4에게는 수수께끼가 아니다.
트레이딩 시그널: A사가 어닝스 콜에서 경쟁사 B를 부정적 맥락으로 언급하면("we're taking share from..."), 이는 A(롱)뿐만 아니라 B(숏)에 대한 시그널이기도 하다. 페어 트레이드.
4. 경영진 교체 시그널
경영진 변경이나 전략적 전환을 나타내는 마커 문구:
- "Exploring strategic alternatives" — 회사 매각 가능성
- "Right-sizing our operations" — 대규모 감원
- "The board has initiated a comprehensive review" — CEO가 곧 떠날 것
- "We're bringing in fresh perspectives" — 현 팀이 실패했다
이 문구들 각각은 후속 가격 동향과 통계적으로 유의한 상관관계를 갖는다. LLM은 제로 오탐으로 이들을 감지할 수 있다 — 맥락을 이해하기 때문이다. 정규식은 제품 라인 설명 중 "strategic alternatives"를 잘못 잡을 수 있다.
백테스팅: 이벤트 스터디 방법론

시그널을 생성했다 — 훌륭하다. 하지만 작동하는가? 표준 검증 방법은 누적 비정상 수익률(CAR)을 계산하는 이벤트 스터디다.
방법론
- 이벤트 정의 — 어닝스 콜 날짜
- 추정 윈도우 — 이벤트 전 [-250, -30] 거래일로 "정상" 수익률 추정
- 이벤트 윈도우 — 이벤트 전후 [-1, +60]일
- 정상 수익률 계산 — 시장 모형:
- 비정상 수익률 — 실제 수익률과 "정상" 수익률의 차이
- CAR — 이벤트 윈도우 내 비정상 수익률의 누적합
import numpy as np
import pandas as pd
from scipy import stats
from dataclasses import dataclass
@dataclass
class EventStudyResult:
car: np.ndarray # 일별 누적 비정상 수익률
t_stats: np.ndarray # 각 일의 t-통계량
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, # 주식 일일 수익률 (columns = 티커)
market_returns: pd.Series, # 시장 지수 일일 수익률
events: pd.DataFrame, # DataFrame, columns: [ticker, date, signal_score]
estimation_window: int = 220,
gap: int = 30,
event_window: tuple = (-1, 60),
) -> EventStudyResult:
"""
LLM 시그널의 예측력을 평가하는 이벤트 스터디.
signal_score로 이벤트를 정렬, 롱/숏 포트폴리오 구성,
CAR 계산 및 통계적 유의성 테스트.
"""
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: # 최소 60 관측치
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] # 이벤트 날짜까지의 오프셋
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,
):
"""백테스트: 상위 오분위 롱, 하위 오분위 숏."""
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%}")
예상 결과
기존 연구에 기반한 LLM 시그널의 현실적 CAR:
| 윈도우 | 롱 포트폴리오 | 숏 포트폴리오 | L/S 스프레드 |
|---|---|---|---|
| [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% |
핵심 지표는 통계적 유의성이다. p < 0.01이고 N > 200 이벤트이면 견고한 시그널이라고 할 수 있다. p > 0.05이면 — 노이즈일 수 있다.
프로덕션 구현: 주피터에서 프로덕션까지
실시간 파이프라인 아키텍처
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)│
└──────────────────────────────┘
비용 분석: 어닝스 콜 하나에 얼마가 드는가
프로덕션에서 어닝스 콜 하나를 처리하는 경제성을 분석해보자:
| 컴포넌트 | 비용 | 지연시간 |
|---|---|---|
| Whisper API 트랜스크립션 (60분) | $0.36 | ~17초 (Turbo) |
| GPT-4o 구조화된 추출 | $0.15-0.30 | ~8-15초 |
| GPT-4o 실시간 청크 분석 (x12) | $1.80-3.60 | 각 ~5초 |
| RAG 저장용 Embedding | $0.01 | <1초 |
| 합계 (전체 파이프라인) | $2.30-4.30 | ~30초 |
잠깐, 콜당 $30-50이라는 수치는 어디서 온 것인가? 모델과 접근법에 따라 다르다:
- 이코노미 옵션 (GPT-4o-mini, 단일 패스): $0.50-1.00
- 스탠다드 옵션 (GPT-4o, 구조화된 추출 + 청크 분석): $2-5
- 프리미엄 옵션 (GPT-4o, 멀티 패스, 교차 검증, 과거 비교): $15-30
- 헤지펀드 등급 (멀티 모델 + 인간 리뷰 + 실시간 스트리밍): $30-50+
500 종목을 거래하는 퀀트 펀드의 경우, 어닝스 시즌(6주간 약 2,000 콜) 처리 비용은 스탠다드 옵션으로 10,000이다. 포지션당 평균 알파가 1-3%일 때 — ROI는 천문학적이다.
지연시간: 밀리초 경쟁
HFT 세계에서 지연시간은 전부다. 하지만 어닝스 기반 전략에서는 상황이 다르다:
- 어닝스 콜은 45-60분 지속된다 — 시간이 있다
- PEAD는 60일에 걸쳐 진행된다 — 첫 1초에 진입할 필요가 없다
- 주요 디슬로케이션은 콜 종료 후 처음 30분 내에 발생한다
최적 전략은 이중 단계다:
- 1단계 (실시간): 콜 중 5분 청크를 분석해 예비 시그널 형성
- 2단계 (콜 후): 종료 후 2-5분 내에 전체 트랜스크립트 완전 분석
1단계는 콜 종료를 기다리는 시장 참가자 대비 5-10분의 엣지를 제공한다. 중형주에는 이것으로 충분하다.
크립토로의 확장: DeFi 거버넌스와 DAO 제안
크립토 시장은 LLM 알파 마이닝의 이상적인 시험장이다. 그 이유:
- 기관 투자자가 적다 — 활용할 비효율성이 더 많다는 의미
- 거버넌스 = 어닝스 콜 — DAO 결정이 토큰노믹스에 직접 영향
- 24/7 시장 — 반응을 즉시 거래할 수 있다
- 공개 데이터 — 모든 제안과 투표가 온체인
분석 대상 크립토 이벤트 유형
거버넌스 제안 (Aave, Compound, Uniswap)
제안은 프로토콜 파라미터를 변경한다 — 이자율, 담보 계수, 수수료 스위치. LLM은 경제적 영향을 평가할 수 있다:
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}
"""
프로토콜 업데이트 발표
Uniswap이 hooks가 포함된 v4를 발표하거나, Aave가 GHO를 출시할 때 — 이는 전통 금융에서의 제품 출시에 해당한다. LLM은 내러티브 모멘텀과 기술적 중요성을 평가할 수 있다.
트레저리 보고서
대형 DAO는 수억 달러 규모의 트레저리를 보유하고 있다. 분기별 트레저리 보고서는 어닝스의 직접적 아날로그다. 런웨이, 번레이트, 분산투자 — 모두 LLM 분석에 적합하다.
크립토 시그널의 특수성
전통 금융과 달리 크립토에서는:
- 온체인 데이터가 내러티브를 확인하거나 반박한다 — 거버넌스 콜에서 말한 것을 프로토콜의 실제 지표(TVL, 거래량, 활성 사용자)와 대조할 수 있다
- 웨일 월렛은 인사이더 트레이딩에 해당 — 거버넌스 토론 후 대형 월렛의 이동이 종종 투표에 선행한다
- CT(Crypto Twitter)를 통한 센티먼트 증폭 — 거버넌스 콜의 시그널이 트위터 내러티브에 의해 증폭되거나 억제될 수 있다
함정과 한계
환각: 모델이 숫자를 지어낼 때
LLM은 트랜스크립트에 없는 가이던스를 "추출"할 수 있다. 헤지 워드 밀도 분석 시 특히 위험하다: 모델이 실제보다 많거나 적게 단어를 셀 수 있다.
해결책: 이중 단계 검증. LLM이 추출하고 결정론적 코드가 검증한다. 헤지 워드의 경우 — LLM 평가와 병행하여 정규식 카운팅. 편차 > 20% — 수동 검토 플래그.
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:
"""LLM의 헤지 워드 카운트를 결정론적으로 검증."""
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,
}
컨텍스트 윈도우 제한
다음을 입력하려면 128K 토큰도 부족할 수 있다:
- 현재 트랜스크립트 (~10K 토큰)
- 비교용 이전 트랜스크립트 (~10K)
- 애널리스트 컨센서스 전망 (~2K)
- Few-shot 예시 (~3K)
- System prompt (~1K)
합계 ~26K — 수용 가능하다. 하지만 맥락을 위해 10-K filing (~80-120K 토큰)을 추가하면 — 이미 경계선이다. 해결책: 긴 문서에서 관련 청크를 가져오는 RAG.
편향과 체계적 오류
LLM은 특정 문구가 특정 결과와 연관된 과거 데이터로 훈련된다. 하지만 시장은 적응한다:
- 모든 사람이 GPT-4로 헤지 워드를 세기 시작하면, 경영진은 언어를 바꿀 것이다
- 모델이 훈련 데이터의 패턴 중요성을 과대평가할 수 있다 (생존자 편향)
- 기업 언어는 진화한다: 2010년의 "synergies"와 2026년의 "synergies"는 다른 의미다
밀집 거래 리스크
50개 퀀트 펀드가 같은 GPT-4로 같은 트랜스크립트를 분석하면 — 시그널은 열화된다. 비유: 모든 사람이 수치적 서프라이즈로 PEAD를 거래하기 시작했을 때 이상 현상은 축소되었다. 텍스트 시그널도 같은 일이 일어나겠지만 시차가 있다:
- 현재 (2026) — 어닝스 콜에 LLM을 체계적으로 사용하는 곳이 적다. 알파가 상당하다
- 2-3년 후 — 광범위한 채택, 알파 감소
- 5년 후 — 기본 LLM 시그널은 상품화, 커스텀 모델과 고유 데이터에서만 엣지 유지
이것은 알파 시그널의 표준 생명주기다. 가능할 때 즐기자.
결론을 대신하여: 실행 전략
어닝스 콜 분석에 LLM을 사용하고 싶다면, 최소 실행 가능 계획은 다음과 같다:
- 무료 데이터로 시작하라 — SEC EDGAR + EdgarTools로 8-K/10-Q filing
- 구조화된 추출을 사용하라 — OpenAI Structured Outputs를 통한 Pydantic 스키마
- 이벤트 스터디로 백테스트하라 — 과거 데이터에서 CAR, 최소 200 이벤트
- Few-shot 예시를 추가하라 — 5-10개의 라벨링된 예시가 품질을 극적으로 향상시킨다
- 결정론적으로 검증하라 — LLM이 추출, 정규식이 검증, 인간이 감사
- 중형주부터 시작하라 — 더 많은 알파, 대형 펀드와의 경쟁이 적다
- 크립토로 확장하라 — 거버넌스 콜과 DAO 제안은 미개척 영역
퀀트 분석의 황금률을 기억하라: 시그널이 너무 좋아서 사실이 아닌 것 같으면 — 다시 한번 확인하라. LLM은 이해의 환상을 만들지만, 그 뒤에는 통계적 패턴 매칭이 있다. 강력한 도구이지만 — 도구일 뿐, 오라클이 아니다.
참고문헌
-
Ball, R., Brown, P. (1968). An Empirical Evaluation of Accounting Income Numbers. Journal of Accounting Research, 6(2), 159-178. — 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. — PEAD의 정전적 논문.
-
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. — 금융 센티먼트 사전.
-
Araci, D. (2019). FinBERT: Financial Sentiment Analysis with Pre-trained Language Models. arXiv:1908.10063. — 금융 NLP를 위한 BERT, SOTA 대비 +14pp.
-
Wu, S. et al. (2023). BloombergGPT: A Large Language Model for Finance. arXiv:2303.17564. — Bloomberg의 500억 파라미터 모델.
-
Yang, H. et al. (2023). FinGPT: Open-Source Financial Large Language Models. arXiv:2306.06031. — BloombergGPT의 오픈소스 대안, 89% 정확도.
-
Lopez-Lira, A., Tang, Y. (2023). Can ChatGPT Forecast Stock Price Movements? Return Predictability and Large Language Models. arXiv:2304.07619. — GPT-4가 ~90% 적중률로 수익률 예측.
-
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. — 텍스트 PEAD는 수치 PEAD의 2배.
-
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. — S&P 100에서 10-30% 초과 알파의 GPT-4 프레임워크.
-
Chen, Y. et al. (2025). GPT-Signal: Generative AI for Semi-automated Feature Engineering in the Alpha Research Process. arXiv:2410.18448. — LLM을 통한 트레이딩 시그널 자동 생성.
-
Zhang, X. et al. (2025). Can LLMs Hit Moving Targets? Tracking Evolving Signals in Corporate Disclosures. arXiv:2510.03195. — 기업 공시에서 "이동 표적" 탐지.
-
Chen, Z. et al. (2025). Large Language Models in Equity Markets: Applications, Techniques, and Insights. Frontiers in Artificial Intelligence. — 금융 분야 84개 LLM 연구 서베이.
이 글은 교육 목적으로 작성되었으며 투자 조언이 아닙니다. 여기서 설명하는 모든 트레이딩 전략은 실제 자본으로 운용하기 전에 철저한 백테스팅과 리스크 관리가 필요합니다.
MarketMaker.cc Team
퀀트 리서치 및 전략