금융 시장 분석 AI 에이전트 시스템을 개발하던 중, 트레이딩팀에서 이런 요청이 들어왔습니다:
"현재 데이터만으로는 부족해요. 과거 특정 시점의 재무제표를 기준으로 분석해서 우리 전략을 백테스팅하고 싶어요."
기존 시스템은 가장 최근 데이터만 분석할 수 있었는데, 백테스팅을 위해서는 특정 날짜 기준의 히스토리컬 분석이 필요했습니다.
우리 시스템은 LangGraph 기반의 멀티 에이전트 아키텍처로 구성되어 있었습니다:
# 기존 구조
SupervisorNode → USFinancialAnalyzerNode → AlphaVantage API
class USStockInput(BaseModel):
query: str = Field(
description="company name or ticker symbol (e.g., Apple, AAPL)"
)
단순히 회사명이나 티커만 받아서 최신 데이터만 분석하는 구조였습니다.
복잡한 구조 변경보다는 LLM의 자연어 이해 능력을 활용하기로 했습니다.
class USStockInput(BaseModel):
query: str = Field(
description="Query containing company name/ticker and optionally a specific date. "
"Examples: 'Apple', 'AAPL as of 2023-12-31', 'Microsoft in Q2 2023', "
"'Tesla 2022년 말 기준'"
)
def _extract_ticker_and_date(self, query: str) -> Tuple[Optional[str], Optional[str]]:
prompt = f"""
Extract the ticker symbol and analysis date from this query:
"{query}"
Date conversion examples:
- "Q1 2023" → "2023-03-31" (end of Q1)
- "Q2 2023" → "2023-06-30" (end of Q2)
- "2023년 말" → "2023-12-31"
- "as of 2023-12-31" → "2023-12-31"
Return format: TICKER|DATE or TICKER|CURRENT
"""
response = self.llm.invoke(prompt)
result = response.content.strip().upper()
if "|" in result:
ticker, date = result.split("|", 1)
return ticker, date if date != "CURRENT" else None
# 현재 분석 (기존과 동일)
"Apple 분석해줘"
"MSFT 재무상태는?"
# 특정 날짜 분석 (NEW!)
"Apple as of 2023-12-31"
"Microsoft 2023년 2분기 기준"
"Tesla 2022년 말 재무상태"
"NVDA Q3 2023 financial performance"
현재 분석:
애플(Apple Inc., Ticker: AAPL) 최신 재무제표 분석
- 기준일: 2024-09-30
- ROE: 138.0%, ROA: 23.8%
- 매출: $391,035백만
히스토리컬 분석:
📊 Historical Financial Analysis: AAPL as of 2023-12-31
- 기준일: 2023-09-30 (해당 날짜 기준 최신 데이터)
- ROE: 138.0%, ROA: 23.8%
- 매출: $383,285백만
*Note: 백테스팅 목적의 과거 시점 분석*
구현 과정에서 알게 된 중요한 사실:
2024-09-30 (Q4/연말) → 연간보고서
2024-06-30 (Q3) → 분기보고서
2024-03-31 (Q2) → 분기보고서
2023-12-31 (Q1) → 분기보고서
이로 인해 "분기별 백테스팅"보다는 "연도별 백테스팅"이 더 현실적임을 깨달았습니다.
문제: 사용자가 다양한 형태로 날짜를 표현
해결: LLM 프롬프트에 변환 예시를 충분히 제공
# 프롬프트에 포함된 변환 예시
- "Q1 2023" → "2023-03-31" (end of Q1)
- "Q2 2023" → "2023-06-30" (end of Q2)
- "2023년 말" → "2023-12-31"
문제: 히스토리컬 데이터 요청 시 응답 시간 증가
해결: 캐싱 시스템 활용
def _get_cached_or_fetch(self, endpoint: str, func, *args, **kwargs):
cache_key = endpoint
current_time = time.time()
if (cache_key in self.cache and
current_time - self.cache_timestamp.get(cache_key, 0) < self.base_cache_time):
return self.cache[cache_key]
result = func(*args, **kwargs)
self.cache[cache_key] = result
return result
문제: 과거 시점에 실제로 공개된 데이터만 사용해야 함
해결: 엄격한 날짜 필터링
# 타겟 날짜보다 이후에 공시된 데이터는 제외
if report_date <= target_dt:
valid_reports.append(report)

처음에는 복잡한 새 시스템을 구축하려 했지만, 기존 구조를 최대한 활용하는 방향이 더 효과적이었습니다.
복잡한 날짜 파싱 로직 대신 LLM 프롬프트로 해결한 것이 더 유연하고 확장 가능했습니다.
미국 재무제표 공시 주기를 이해하고 나서야 현실적인 백테스팅 전략을 수립할 수 있었습니다.