AI Hedge Fund(virattt/ai-hedge-fund)는 유명 투자자 13명의 투자 철학을 각각 하나의 LLM 에이전트로 코드화하고, 여기에 기술적·펀더멘털·밸류에이션·감성·뉴스감성·성장 분석을 맡는 6종의 정량 애널리스트를 더해, 총 19개의 분석 에이전트가 같은 종목을 동시에 평가하게 만드는 교육용 오픈소스 Python 프레임워크입니다. 각 에이전트가 낸 매수/매도/중립 신호를 리스크 매니저가 포지션 한도로 다듬고, 포트폴리오 매니저가 종합해 최종 주문을 만듭니다.
가장 먼저 못박아 둘 점: 이 시스템은 실제로 거래를 하지 않습니다. README가 굵은 글씨로 "educational purposes only(교육 목적 전용)"이라고 명시합니다. 즉 "돈 버는 봇"이 아니라, LLM 멀티에이전트 설계를 금융이라는 친숙한 소재로 배우는 교과서에 가깝습니다.
같은 애플(AAPL) 주식을 두고, 가치투자자 버핏은 "해자(moat)와 내재가치"를, 성장투자자 캐시 우드는 "파괴적 혁신"을, 공매도꾼 마이클 버리는 "거품과 하락 위험"을 봅니다. 13명이 각자의 철학으로 독립 분석한 뒤, 리스크 매니저가 "한 종목에 자산의 몇 %까지"라는 한도를 정하고, 포트폴리오 매니저가 모든 의견을 종합해 최종 결정을 내립니다.
핵심은 "한 명의 천재"가 아니라 "관점이 다른 여러 전문가"를 코드로 모아 놓았다는 점입니다. 그리고 그 전문가들을 연결하는 배선이 바로 LangGraph입니다.
AI Hedge Fund는 AI 에이전트와 핀테크라는 두 인기 주제의 교차점에 있고, 만든 사람(virattt)은 AI 빌더 커뮤니티에서 유명한 개발자입니다. 하지만 단순한 화제성을 넘어, "멀티에이전트 시스템을 어떻게 깔끔하게 설계하는가"의 모범답안이라는 점에서 학습 가치가 큽니다.
흔한 "GPT 트레이딩 봇"은 모델 하나에게 "이 주식 어때?"라고 묻고 끝입니다. 모델의 훈련 편향이 그대로 나오고, 숫자를 지어내는 환각이 섞여도 거르지 못하며, 포지션 한도 같은 리스크 관리도 없습니다. 무엇보다 "왜 그렇게 판단했는지"가 블랙박스라 배울 게 없습니다.
같은 종목을 19개 에이전트가 서로 다른 기준으로 평가해 편향을 분산합니다. 핵심은 "숫자 계산은 코드가, 판단은 LLM이" 나눠 맡는 구조입니다 — 버핏 에이전트는 ROE·해자·DCF를 파이썬으로 먼저 계산하고, LLM에는 그 요약된 사실(facts)만 넘겨 신호를 내게 합니다. 그래서 LLM이 숫자를 지어낼 여지가 줄어듭니다. 마지막에 리스크 매니저가 포지션 한도를 걸고 포트폴리오 매니저가 종합합니다.
| 비교 항목 | AI Hedge Fund | TradingAgents | 일반 GPT 트레이딩 봇 |
|---|---|---|---|
| 핵심 메타포 | 투자 거장 13인 페르소나 | 트레이딩 회사 조직(토론) | 단일 어시스턴트 |
| 에이전트 연결 방식 | LangGraph 팬아웃 → 수렴 | LangGraph 토론 + 반성 | 없음(단일 호출) |
| 숫자 vs 판단 | 결정론 계산 + LLM은 facts만 | 주로 LLM + 도구 호출 | 전부 LLM |
| LLM 프로바이더 | OpenAI/Anthropic/Groq/DeepSeek/Google/xAI/Ollama 등 8종 | 여러 종 | 보통 1종 |
| 리스크 관리 | 전용 노드(포지션 한도) | 3관점 리스크팀 | 없거나 단순 |
| 제공 형태 | CLI + 백테스터 + 웹앱(FastAPI/React) | CLI 중심 | 스크립트 |
요약하면, TradingAgents가 "토론으로 편향을 줄이는" 데 집중한다면, AI Hedge Fund는 "각기 다른 투자 철학을 병렬로 돌려 비교하고, 숫자 계산과 LLM 판단을 깔끔히 분리"하는 데 집중합니다. 둘 다 LangGraph 기반이라 함께 읽으면 멀티에이전트 설계의 두 갈래를 비교 학습할 수 있습니다.
한 문장으로: Python 3.11 + Poetry 위에서 LangChain/LangGraph가 에이전트를 엮고, Pydantic이 LLM 출력을 구조화하며, financialdatasets.ai가 재무 데이터를 공급합니다. 거기에 FastAPI + React 웹앱이 얹혀 있는 구조입니다.
| 카테고리 | 라이브러리 | 역할 |
|---|---|---|
| 에이전트 오케스트레이션 | langgraph 0.2.56 | StateGraph로 19개 에이전트의 병렬 흐름 정의·실행 |
| LLM 연동 | langchain + 8종 프로바이더 패키지 | OpenAI·Anthropic·Groq·DeepSeek·Ollama·Google·xAI·GigaChat 클라이언트 |
| 출력 구조화 | pydantic 2 | signal/confidence/reasoning 스키마 강제(환각 방지) |
| 데이터 처리 | pandas, numpy, matplotlib | 재무 시계열 가공, 백테스트 결과 시각화 |
| 금융 데이터 | financialdatasets.ai (HTTP API) | 재무지표·재무항목·시가총액 수집(API 키 필요) |
| CLI 경험 | questionary, rich, colorama, tabulate | 대화형 에이전트 선택, 실시간 진행 표시, 표 출력 |
| 구분 | 기술 | 역할 |
|---|---|---|
| 백엔드 | FastAPI + uvicorn | 헤지펀드·백테스터를 실행하는 REST API (포트 8000, /docs 자동 문서) |
| DB / 영속화 | SQLAlchemy + Alembic | 실행 이력·설정 저장, 마이그레이션 관리 |
| HTTP 클라이언트 | httpx | 비동기 외부 API 호출 |
| 프론트엔드 | React + Vite | 에이전트 흐름을 시각적으로 구성·실행하는 UI (포트 5173) |
| 원클릭 실행 | run.sh / run.bat | 의존성 점검·설치 후 프론트+백엔드 동시 기동, 브라우저 자동 오픈 |
pyproject.toml 한 파일로 파이썬 라이브러리 버전을 잠그고 격리된 가상환경을 만듭니다. poetry install 한 줄로 "내 컴퓨터에서만 되던데?" 문제를 줄여 줍니다.전체 흐름은 의외로 단순합니다. 시작 노드에서 선택된 애널리스트들에게 동시에(병렬) 분기(fan-out)했다가, 그 결과가 리스크 매니저 한 곳으로 모여(fan-in) 포트폴리오 매니저를 거쳐 끝납니다.
에이전트 목록은 src/utils/analysts.py의 ANALYST_CONFIG 딕셔너리 한 곳에서만 정의됩니다. 메인 코드는 이 딕셔너리를 읽어 노드와 엣지를 자동으로 만듭니다. 즉 에이전트를 추가하려면 이 딕셔너리에 한 줄 추가하면 끝 — 그래프 코드는 손대지 않습니다.
# src/main.py — create_workflow() 핵심 발췌
workflow = StateGraph(AgentState)
workflow.add_node("start_node", start)
analyst_nodes = get_analyst_nodes() # ANALYST_CONFIG에서 파생
for analyst_key in selected_analysts:
node_name, node_func = analyst_nodes[analyst_key]
workflow.add_node(node_name, node_func)
workflow.add_edge("start_node", node_name) # 시작 → 각 애널리스트 (fan-out)
workflow.add_node("risk_management_agent", risk_management_agent)
workflow.add_node("portfolio_manager", portfolio_management_agent)
for analyst_key in selected_analysts:
workflow.add_edge(analyst_nodes[analyst_key][0], "risk_management_agent") # fan-in
workflow.add_edge("risk_management_agent", "portfolio_manager")
workflow.add_edge("portfolio_manager", END)
여러 에이전트가 동시에 상태를 건드리면 충돌이 납니다. LangGraph는 이를 리듀서로 해결합니다 — 각 필드에 "여러 결과가 들어오면 어떻게 합칠지" 규칙을 붙입니다.
# src/graph/state.py
class AgentState(TypedDict):
messages: Annotated[Sequence[BaseMessage], operator.add] # 리스트 이어붙이기
data: Annotated[dict, merge_dicts] # 딕셔너리 병합
metadata: Annotated[dict, merge_dicts]
operator.add가 그 메시지들을 한 리스트로 이어 붙입니다. 데이터 딕셔너리는 merge_dicts로 합쳐집니다. 마치 여러 사람이 같은 구글 시트에 동시에 적어도 충돌 없이 합쳐지는 것과 같습니다.이 레포에서 가장 배울 점입니다. 워런 버핏 에이전트는 LLM에게 원시 재무제표를 던지지 않습니다. 먼저 파이썬 함수로 ROE·해자·경영진 품질·오너 어닝(owner earnings)·3단계 DCF 내재가치·안전마진을 결정론적으로 계산하고, 그 결과를 짧은 facts 묶음으로 정리한 뒤, LLM에게는 "이 사실들로 버핏이라면 매수/매도/중립 중 무엇이라 할까?"만 묻습니다.
# src/agents/warren_buffett.py — LLM에 넘기는 건 "계산된 사실"뿐
facts = {
"score": analysis_data.get("score"),
"fundamentals": fundamental_analysis["details"],
"moat": moat_analysis["details"],
"intrinsic_value": intrinsic_value_analysis["intrinsic_value"],
"margin_of_safety": margin_of_safety, # (내재가치 - 시총)/시총
}
# system 프롬프트: "You are Warren Buffett. Decide bullish/bearish/neutral
# using ONLY the provided facts ... Do not invent data. Return JSON only."
return call_llm(prompt=prompt, pydantic_model=WarrenBuffettSignal, ...)
숫자 계산이 코드에 있으니 같은 입력엔 같은 점수가 나오고(재현성), LLM은 "지어내지 말고 주어진 사실로만 판단하라"는 좁은 역할만 합니다. 문제가 생기면 "계산이 틀렸나, 판단이 틀렸나"를 분리해 추적할 수 있습니다.
src/utils/llm.py의 call_llm()은 어떤 프로바이더든 동일한 방식으로 호출합니다. JSON 모드를 지원하는 모델은 with_structured_output()로 Pydantic 스키마를 강제하고, 지원하지 않으면 응답에서 직접 JSON을 긁어냅니다(extract_json_from_response). 게다가 에이전트마다 다른 모델을 쓸 수 있고, 3회까지 재시도하며, 끝내 실패하면 default_factory로 안전한 기본 신호(중립)를 반환해 전체가 멈추지 않게 합니다.
# 호출 → 재시도 → 그래도 실패하면 안전한 기본값
for attempt in range(max_retries): # 기본 3회
try:
result = llm.invoke(prompt)
return result # 구조화 출력 성공
except Exception:
if attempt == max_retries - 1:
return default_factory() # 예: signal="neutral", confidence=50
구조를 읽는 한 가지 요령: "에이전트 = 노드"라는 등식만 기억하면 됩니다. src/agents/ 안의 파일 하나가 그래프의 노드 하나이고, 각 파일은 거의 같은 골격(데이터 수집 → 정량 분석 함수들 → facts 구성 → call_llm)을 따릅니다. 그래서 한 에이전트(예: warren_buffett.py)만 정독하면 나머지 12개는 빠르게 읽힙니다.
StateGraph로 병렬 분기와 수렴을 정의하는 법, add_node/add_edge/set_entry_point의 기본기, 그리고 리듀서(operator.add / merge_dicts)로 동시 갱신 충돌을 푸는 법을 배웁니다. TradingAgents가 "조건부 엣지로 토론 루프"를 보여준다면, 이 레포는 "정적 팬아웃의 깔끔한 정석"을 보여줍니다.
ANALYST_CONFIG 딕셔너리 하나가 표시 이름·설명·투자 스타일·함수·정렬 순서를 모두 담고, 그래프·CLI·API 응답이 전부 여기서 파생됩니다. "같은 정보를 두 곳에 적지 않는다"는 단일 진실원(single source of truth) 원칙의 좋은 예시입니다.
LLM에 raw 데이터를 던지지 않고 "계산된 사실(facts)"만 넘기는 패턴, "데이터를 지어내지 말라"는 프롬프트 가드, 점수화(scoring) 로직과 판단(LLM)의 책임 분리를 배웁니다. 이건 금융을 넘어 모든 LLM 앱에 일반화되는 안티-환각 설계입니다.
with_structured_output(method="json_mode")로 JSON 스키마 강제, JSON 미지원 모델을 위한 수동 파싱(extract_json_from_response), 재시도 후 default_factory로 안전한 기본값을 반환하는 실패해도 멈추지 않는(graceful degradation) 설계를 배웁니다.
버핏 에이전트 안에는 진짜 투자 이론이 코드로 들어 있습니다 — analyze_moat(ROE 일관성·마진 안정성으로 해자 추정), calculate_owner_earnings(순이익+감가상각−유지보수 CapEx−운전자본 변화), 3단계 DCF 내재가치, 안전마진. "가치투자가 실제로 어떻게 숫자가 되는가"를 코드로 배울 수 있습니다.
src/backtester.py와 backtesting/로 과거 기간을 재생하며 의사결정을 시뮬레이션합니다. 포트폴리오 상태(현금·롱/숏 포지션·마진·실현손익)를 어떻게 표현하는지, 룩어헤드 편향 없이 날짜를 전진시키는지를 배웁니다.
같은 코어 로직을 CLI와 웹앱이 공유하는 모노레포 구조, FastAPI로 코어를 REST API로 감싸기, SQLAlchemy/Alembic로 실행 이력 저장, run.sh로 비개발자도 원클릭 실행하게 만드는 배포 경험을 배웁니다.
| 항목 | 최소 | 권장 |
|---|---|---|
| Python | 3.11 | 3.11 (3.13+는 호환 이슈 가능) |
| 패키지 매니저 | Poetry | Poetry + (웹앱은 Node.js) |
| RAM | 4GB | 8GB+ |
| GPU | 불필요 (클라우드 API 기반) | Ollama 로컬 모델 시 권장 |
| LLM API 키 | 1개 이상(OpenAI/Anthropic/Groq/DeepSeek 등) | 복수 또는 Ollama |
| 금융 데이터 키 | FINANCIAL_DATASETS_API_KEY | 동일 |
| 네트워크 | 필수 (LLM + 금융데이터) | Ollama 사용 시 LLM은 오프라인 |
--ollama로 무료 로컬 모델을 쓰거나, 에이전트 부분집합·경량 모델로 구조를 익힌 뒤 고성능 모델로 올리는 것을 권장합니다. (참고: 시스템 기본 모델은 gpt-4.1로 지정돼 있습니다.)레포 클론 → poetry install → .env에 키 입력 → poetry run python src/main.py --ticker AAPL,MSFT,NVDA 실행. 같은 종목을 두고 워런 버핏·캐시 우드·마이클 버리가 각각 어떤 신호(bullish/bearish/neutral)와 confidence를 냈는지 비교해 봅니다.
결과물: 종목별 에이전트 의견 비교 노트
--ollama 플래그로 로컬 LLM을 써서 비용 0원으로 돌려 봅니다. --show-reasoning을 켜면 각 에이전트가 LLM에 넘긴 reasoning이 출력됩니다. 클라우드 모델 결과와 로컬 모델 결과의 품질 차이를 관찰합니다.
결과물: 클라우드 vs 로컬 모델 결과 비교
warren_buffett.py를 본떠 새 에이전트(예: "제시 리버모어" 추세추종, 또는 "코스톨라니")를 만듭니다. ① 정량 분석 함수 작성 → ② facts 구성 → ③ Pydantic Signal 스키마 + 페르소나 프롬프트로 call_llm → ④ ANALYST_CONFIG에 한 줄 등록. 그래프 코드는 건드리지 않아도 자동 연결됩니다.
결과물: 작동하는 커스텀 페르소나 에이전트 PR
poetry run python src/backtester.py --ticker AAPL --start-date 2024-01-01 --end-date 2024-03-01로 기간 백테스트를 돌립니다. 대화형 선택으로 "버핏+그레이엄만" vs "캐시 우드만" 등 에이전트 조합을 바꿔 가며 성과(수익률·낙폭)를 비교합니다.
결과물: 에이전트 조합별 백테스트 성과표
call_llm이 에이전트별 모델 설정을 지원한다는 점을 이용해, 종합 판단(포트폴리오 매니저)에는 고성능 모델, 개별 애널리스트에는 경량 모델을 붙여 비용/품질 트레이드오프를 측정합니다. 더 나아가 app/의 run.sh로 FastAPI+React 웹앱을 띄워, 브라우저 플로우 UI에서 같은 분석을 실행해 봅니다.
결과물: 모델 분리 비용 리포트 + 로컬 구동 웹앱
공식 문서로 StateGraph·노드·엣지·entry point 이해. 2~3개 노드의 간단한 워크플로우를 직접 만들어 보고, operator.add 리듀서로 메시지가 어떻게 쌓이는지 관찰합니다. 참고: langchain-ai.github.io/langgraph/
이 레포의 src/main.py create_workflow를 정독하며 병렬 분기→수렴 패턴을 따라 구현합니다. 동시 갱신 충돌을 리듀서로 푸는 연습을 합니다.
Pydantic 모델 + with_structured_output으로 LLM에 JSON 스키마를 강제하는 법, 재시도·기본값으로 실패를 흡수하는 법을 익힙니다. "raw 데이터 대신 facts만 넘기기" 패턴을 자신의 프로젝트에 적용해 봅니다.
src/llm/models.py와 call_llm 구조를 분석해, OpenAI·Anthropic·Ollama를 한 인터페이스로 다루는 래퍼를 직접 만들어 봅니다. JSON 모드 지원 여부 분기 처리도 구현합니다.
ROE·영업이익률·부채비율 같은 재무지표의 의미, 오너 어닝과 DCF로 내재가치를 추정하는 법, 안전마진 개념을 warren_buffett.py의 함수들과 함께 이해합니다. (투자 조언이 아니라 "이론이 코드가 되는 과정"을 보는 학습입니다.)
2개 페르소나 + 리스크/포트폴리오 노드로 축소판을 직접 구현합니다. financialdatasets.ai(또는 무료 데이터)로 한 종목을 분석하고 백테스트까지 돌려 봅니다.
| 키워드 | 설명 |
|---|---|
| LangGraph | LangChain의 워크플로우 오케스트레이션 프레임워크. 에이전트 흐름을 StateGraph로 정의 |
| StateGraph | 노드(에이전트)와 엣지(흐름)로 구성된 상태 기반 그래프 |
| AgentState | TypedDict로 정의된 워크플로우 공유 상태(messages/data/metadata) |
| Reducer | 병렬 갱신을 합치는 규칙. operator.add(리스트 연결), merge_dicts(딕셔너리 병합) |
| Fan-out / Fan-in | 한 노드에서 여러 노드로 병렬 분기(fan-out), 여러 노드가 한 곳으로 수렴(fan-in) |
| ANALYST_CONFIG | 모든 에이전트 정의를 담은 단일 진실원 딕셔너리. 그래프/CLI/API가 여기서 파생 |
| Structured Output | LLM 응답을 정해진 스키마(JSON)로 강제하는 기법 |
| json_mode | 모델이 항상 유효한 JSON만 출력하도록 하는 모드 |
| Pydantic | 파이썬 데이터 검증 라이브러리. 에이전트 신호 스키마 정의에 사용 |
| call_llm | 프로바이더 무관 LLM 호출 헬퍼. 구조화 출력 + 재시도 + 기본값 처리 |
| default_factory | LLM 호출이 끝내 실패할 때 반환하는 안전한 기본 신호(예: 중립) |
| Signal | 에이전트 출력. bullish/bearish/neutral + confidence(0-100) + reasoning |
| Margin of Safety | 안전마진. (내재가치 − 시가총액)/시가총액. 가치투자의 핵심 지표 |
| Owner Earnings | 버핏식 진짜 수익력. 순이익+감가상각−유지보수 CapEx−운전자본 변화 |
| DCF | Discounted Cash Flow. 미래 현금흐름을 현재가치로 할인해 내재가치 추정 |
| Moat | 경제적 해자. 경쟁사가 넘기 힘든 지속적 경쟁우위(높은 ROE 일관성 등으로 추정) |
| Risk Manager | 종목별 포지션 한도를 계산하는 fan-in 지점 노드 |
| Backtester | 과거 데이터로 전략을 재생·검증하는 도구(src/backtester.py) |
| financialdatasets.ai | 재무지표·재무항목·시가총액을 제공하는 금융 데이터 API |
| Poetry / FastAPI / Ollama | 의존성 관리 / 웹 API 프레임워크 / 로컬 LLM 실행 도구 |