AI Hedge Fund 深度解析:用大模型构建你的智能投资组合

7 阅读27分钟

项目地址virattt/ai-hedge-fund ⭐ 56.6k
定位:教育研究用途的 AI 多智能体投资决策系统,不构成真实交易建议。


一、从一个问题开始:AI 能做投资分析吗?

如果让巴菲特、彼得·林奇、迈克尔·伯里同时研究同一只股票,然后由风险经理汇总意见、投资组合经理做最终决策——这是多少人梦寐以求的投资委员会。

这个项目的回答是:可以用 AI 来模拟这个流程

它将 19 位投资大师和专业分析师的思维方式"注入"大语言模型(LLM),构成一个多智能体协作的投资决策系统。每个 Agent 各司其职,最终由组合经理综合所有信号给出交易指令。


二、全局架构:一张图搞懂整个系统

┌─────────────────────────────────────────────────────────────────┐
│                        用户输入层                                │
│         CLI (main.py)  /  Web App (React + FastAPI)             │
└────────────────────────────┬────────────────────────────────────┘
                             │ 股票代码 / 日期范围 / 选用分析师
                             ▼
┌─────────────────────────────────────────────────────────────────┐
│                   LangGraph 状态机(工作流)                      │
│                                                                  │
│   start_node                                                     │
│       │                                                          │
│       ├──── warren_buffett_agent ────┐                           │
│       ├──── ben_graham_agent ────────┤                           │
│       ├──── cathie_wood_agent ───────┤                           │
│       ├──── michael_burry_agent ─────┤                           │
│       ├──── technical_analyst_agent ─┤  并行执行                 │
│       ├──── fundamentals_analyst ────┤                           │
│       ├──── sentiment_analyst ───────┤                           │
│       ├──── valuation_analyst ───────┤                           │
│       └──── ... (19个分析师) ─────────┘                          │
│                             │                                    │
│                             ▼                                    │
│                   risk_management_agent                          │
│                   (波动率 + 相关性调仓限额)                       │
│                             │                                    │
│                             ▼                                    │
│                   portfolio_manager                              │
│                   (综合信号 → 最终交易决策)                       │
└────────────────────────────┬────────────────────────────────────┘
                             │ buy / sell / short / cover / hold
                             ▼
                     输出交易决策 + 信号详情

关键设计:所有分析师 Agent 并行执行(通过 LangGraph 的边连接到同一个 risk_management_agent),互不阻塞,最后统一汇总。


三、核心概念解析

3.1 AgentState:流转于整个工作流的"公共黑板"

整个系统的数据共享靠一个 TypedDict

class AgentState(TypedDict):
    messages: Annotated[Sequence[BaseMessage], operator.add]   # 消息历史(追加)
    data: Annotated[dict[str, any], merge_dicts]               # 股票数据、信号(合并)
    metadata: Annotated[dict[str, any], merge_dicts]           # 模型配置、调试开关

理解这个结构至关重要

data 字段内部结构示意:
{
  "tickers": ["AAPL", "MSFT"],
  "portfolio": { "cash": 100000, "positions": {...} },
  "start_date": "2024-01-01",
  "end_date": "2024-03-01",
  "analyst_signals": {
      "warren_buffett_agent": {
          "AAPL": {"signal": "bullish", "confidence": 85, "reasoning": "..."},
          "MSFT": {"signal": "neutral", "confidence": 60, "reasoning": "..."}
      },
      "technical_analyst_agent": { ... },
      "risk_management_agent": {
          "AAPL": {"remaining_position_limit": 15000.0, "current_price": 182.5}
      }
  }
}

每个 Agent 读取 data,将自己的分析结果写入 analyst_signals[自己的名字],下游 Agent 可以读取所有上游的信号。这是一种黑板架构(Blackboard Pattern)


3.2 工作流是如何构建的?

# src/main.py - 核心工作流组装逻辑
def create_workflow(selected_analysts=None):
    workflow = StateGraph(AgentState)
    workflow.add_node("start_node", start)

    analyst_nodes = get_analyst_nodes()  # 从配置获取所有分析师
    if selected_analysts is None:
        selected_analysts = list(analyst_nodes.keys())

    # 注册选定的分析师节点
    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)  # start → 各分析师(并行)

    # 所有分析师 → 风险管理 → 投资组合经理 → 结束
    workflow.add_node("risk_management_agent", risk_management_agent)
    workflow.add_node("portfolio_manager", portfolio_management_agent)

    for analyst_key in selected_analysts:
        node_name = analyst_nodes[analyst_key][0]
        workflow.add_edge(node_name, "risk_management_agent")

    workflow.add_edge("risk_management_agent", "portfolio_manager")
    workflow.add_edge("portfolio_manager", END)
    workflow.set_entry_point("start_node")
    return workflow

可视化工作流执行顺序

start_node
    │
    ├─────────────────────────────────────────────────────────┐
    │                (以下并行,互不依赖)                        │
    ▼                                                         ▼
warren_buffett_agent                              technical_analyst_agent
    │                                                         │
    └──────────────────────┬──────────────────────────────────┘
                           ▼
                  risk_management_agent
                  (需要等所有分析师完成)
                           │
                           ▼
                   portfolio_manager
                           │
                           ▼
                          END

四、19 位分析师 Agent 详解

4.1 分析师配置表

┌──────────────────────────┬───────────────────────────────────────────────────┐
│ 分析师                    │ 投资风格                                            │
├──────────────────────────┼───────────────────────────────────────────────────┤
│ Warren Buffett           │ 价值投资,护城河,安全边际,DCF内在价值              │
│ Ben Graham               │ 价值投资鼻祖,深度低估,安全边际                    │
│ Charlie Munger           │ 优质企业,合理价格,长期持有                        │
│ Peter Lynch              │ 十倍股,"买你所了解的",PEG比率                    │
│ Cathie Wood              │ 颠覆性创新,高增长赛道                              │
│ Bill Ackman              │ 激进投资人,推动管理层变革                          │
│ Michael Burry            │ 逆向做空,深度基本面分析                            │
│ Mohnish Pabrai           │ Dhandho 哲学,低风险高回报                         │
│ Phil Fisher              │ 成长股,"小道消息"研究法(Scuttlebutt)             │
│ Nassim Taleb             │ 黑天鹅,尾部风险,反脆弱,哑铃策略                  │
│ Aswath Damodaran         │ 估值大师,故事+数字,严格财务建模                   │
│ Stanley Druckenmiller     │ 宏观投资,货币/大宗商品/利率                       │
│ Rakesh Jhunjhunwala      │ 印度大牛,新兴市场,宏观+行业                       │
│ Technical Analyst        │ 趋势跟踪+均值回归+动量+波动率+统计套利              │
│ Fundamentals Analyst     │ 财务报表,ROE/成长/健康度/估值多维评分              │
│ Valuation Analyst        │ 内在价值计算,多种估值模型                          │
│ Sentiment Analyst        │ 内幕交易,市场情绪                                  │
│ News Sentiment Analyst   │ 新闻舆情分析                                       │
│ Growth Analyst           │ 成长趋势,PEG,成长质量                             │
└──────────────────────────┴───────────────────────────────────────────────────┘

4.2 以 Warren Buffett Agent 为例深入剖析

这是最完整的分析师实现,包含 8 个维度的纯代码评分 + LLM 最终判断。

分析流程图

输入:ticker + 财务数据
         │
         ├─→ analyze_fundamentals()    ROE/负债/利润率/流动比率   → 0~10分
         ├─→ analyze_consistency()     盈利增长一致性             → 0~3分
         ├─→ analyze_moat()            护城河(ROE稳定性/毛利率) → 0~5分
         ├─→ analyze_pricing_power()   定价权(毛利扩张)         → 0~5分
         ├─→ analyze_book_value_growth() 每股账面价值增长         → 0~5分
         ├─→ analyze_management_quality() 回购/分红              → 0~2分
         └─→ calculate_intrinsic_value()  三阶段DCF估值
                    │
                    └─→ 计算安全边际 = (内在价值 - 市值) / 市值
         
         ↓  将所有分析结果打包为 facts dict
         
         generate_buffett_output()
         ↓  发送给 LLM(扮演巴菲特角色)
         ↓  返回 {signal, confidence, reasoning}
         ↓
输出:{"AAPL": {"signal": "bullish", "confidence": 78, "reasoning": "..."}}

三阶段 DCF 估值模型(代码简化展示):

# 第一阶段:高增长期(5年,上限8%)
# 第二阶段:过渡期(5年,上限4%)  
# 第三阶段:永续增长率(2.5%,对应长期GDP增速)
# 折现率:10%(保守)
# 额外保守系数:再打85折

intrinsic_value = (stage1_pv + stage2_pv + terminal_pv) * 0.85
margin_of_safety = (intrinsic_value - market_cap) / market_cap

4.3 Technical Analyst:纯算法的五维信号融合

技术分析师不调用 LLM,完全用数学计算信号,然后加权融合

策略        权重    核心指标
────────────────────────────────────────────────────────
趋势跟踪    25%    EMA(8/21/55) + ADX趋势强度
均值回归    20%    布林带 + Z-Score + RSI(14/28)
动量分析    25%    1/3/6个月动量 + 成交量确认
波动率分析  15%    历史波动率 + VIX机制 + ATR比率
统计套利    15%    Hurst指数 + 偏度/峰度

加权综合分 > 0.2 → bullish
加权综合分 < -0.2 → bearish
否则 → neutral

Hurst 指数解读(项目中实现):

H < 0.4  →  均值回归市场(适合布林带策略)
H ≈ 0.5  →  随机游走(难以预测)
H > 0.6  →  趋势市场(适合趋势跟踪策略)

五、风险管理:不只是"别买太多"

5.1 双重调整机制

Risk Manager 对每个股票计算允许的最大持仓额度,分两步调整:

步骤1:波动率调整
──────────────────────────────────────────
年化波动率 < 15%  →  可用仓位 = 组合 × 25%(低波,可多配)
年化波动率 15~30% →  可用仓位 = 组合 × 15~20%(标准)
年化波动率 30~50% →  可用仓位 = 组合 × 7.5~15%(高波,减配)
年化波动率 > 50%  →  可用仓位 = 组合 × 10%(极高波,最小配置)

步骤2:相关性调整(乘以相关性倍数)
──────────────────────────────────────────
与持仓平均相关性 ≥ 0.8  →  × 0.70(高相关,减少集中风险)
与持仓平均相关性 0.6~0.8 →  × 0.85
与持仓平均相关性 0.4~0.6 →  × 1.00(中性)
与持仓平均相关性 0.2~0.4 →  × 1.05(低相关,轻微加仓)
与持仓平均相关性 < 0.2   →  × 1.10(分散化奖励)

最终仓位上限 = min(波动率调整后限额 × 相关性倍数, 可用现金)

举例:组合总值 $100,000,NVDA 年化波动率 60%,与现有持仓平均相关 0.75:

  • 基础仓位:100,000×20100,000 × 20% × 0.50 = 10,000
  • 相关性调整:10,000×0.85=10,000 × 0.85 = 8,500
  • 最多允许投入 $8,500

六、Portfolio Manager:最终决策的守门人

组合经理不仅仅是汇总信号投票,它还做了确定性约束预计算

# 先用纯逻辑计算"物理上允许的操作"
allowed_actions = compute_allowed_actions(tickers, prices, max_shares, portfolio)

# 例如:
# {
#   "AAPL": {"buy": 45, "sell": 100, "hold": 0},  # 有100股可卖,最多再买45股
#   "MSFT": {"short": 30, "cover": 0, "hold": 0},  # 没有多头,可做空30股
# }

# 只有"hold"选项的股票直接预填,不浪费 LLM token
# 其余发给 LLM 做最终选择

发给 LLM 的提示极度精简(节省成本):

Signals:
{"AAPL":{"warren_buffett_agent":{"sig":"bullish","conf":78},"technical_analyst_agent":{"sig":"bearish","conf":65},...}}

Allowed:
{"AAPL":{"buy":45,"sell":100,"hold":0}}

Pick one allowed action per ticker, quantity ≤ max. Reasoning max 100 chars.

LLM 返回结构化输出(Pydantic 强制校验):

class PortfolioDecision(BaseModel):
    action: Literal["buy", "sell", "short", "cover", "hold"]
    quantity: int
    confidence: int   # 0-100
    reasoning: str    # 最多100字符

七、数据层与缓存

数据流向图

  FinancialDatasets.ai API
         │
         │ REST请求(带重试+限流处理)
         ▼
   src/tools/api.py
   ┌──────────────────────────────────────────────┐
   │ get_prices()         价格数据(OHLCV)         │
   │ get_financial_metrics() 财务指标(ROE/PE/...) │
   │ search_line_items()  财务行项目(DCF用途)     │
   │ get_insider_trades() 内幕交易记录              │
   │ get_company_news()   新闻数据                  │
   │ get_market_cap()     市值                      │
   └──────────────────────┬───────────────────────┘
                          │
                    内存缓存层
                  src/data/cache.py
                  ┌──────────────────────────────┐
                  │ Cache(单例,进程内)          │
                  │ - prices_cache               │
                  │ - financial_metrics_cache    │
                  │ - insider_trades_cache       │
                  │ - company_news_cache         │
                  └──────────────────────────────┘
                  相同参数第二次请求直接读缓存

数据模型(Pydantic 强类型):

class FinancialMetrics(BaseModel):
    ticker: str
    market_cap: float | None
    price_to_earnings_ratio: float | None
    return_on_equity: float | None
    debt_to_equity: float | None
    operating_margin: float | None
    revenue_growth: float | None
    # ... 40+ 个字段

八、LLM 支持矩阵

项目支持极其丰富的 LLM 选择,覆盖国内外主流:

提供商          示例模型                  API Key 环境变量
──────────────────────────────────────────────────────────
OpenAI          gpt-4.1, gpt-4o          OPENAI_API_KEY
Anthropic       claude-opus-4.5          ANTHROPIC_API_KEY
DeepSeek        deepseek-chat            DEEPSEEK_API_KEY
Google          gemini-2.0-flash         GOOGLE_API_KEY
Groq            llama-3.3-70b            GROQ_API_KEY
Kimi (月之暗面) moonshot-v1-8k           MOONSHOT_API_KEY
xAI             grok-3                   XAI_API_KEY
GigaChat        GigaChat                 GIGACHAT_API_KEY
Azure OpenAI    自定义部署               AZURE_OPENAI_API_KEY
OpenRouter      多模型路由               OPENROUTER_API_KEY
Ollama          本地部署任意模型         无需Key

本地部署(Ollama)示例

# 先安装并启动 ollama,下载模型
ollama pull llama3.1:8b

# 使用本地模型运行
poetry run python src/main.py --ticker AAPL --ollama

九、实战示例

示例1:入门 —— 快速跑起来(5分钟)

# 1. 克隆项目
git clone https://github.com/virattt/ai-hedge-fund.git
cd ai-hedge-fund

# 2. 安装依赖
poetry install

# 3. 配置 API Key(至少配置一个 LLM + 金融数据源)
cp .env.example .env
# 编辑 .env:
# OPENAI_API_KEY=sk-...
# FINANCIAL_DATASETS_API_KEY=fd-...

# 4. 运行,分析苹果公司
poetry run python src/main.py --ticker AAPL

# 输出示例:
# AAPL: action=buy, quantity=23, confidence=72
# Reasoning: Strong moat, FCF positive, slight overvaluation offset by growth

示例2:进阶 —— 多股票 + 指定分析师 + 显示推理过程

# 只用巴菲特 + 技术分析师分析三只科技股,并展示推理过程
poetry run python src/main.py \
    --ticker AAPL,MSFT,NVDA \
    --start-date 2024-01-01 \
    --end-date 2024-06-30 \
    --analysts warren_buffett technical_analyst \
    --show-reasoning

# 控制台输出会显示每个 Agent 的详细分析:
# ========== Warren Buffett Agent ==========
# AAPL: score=28/40, margin_of_safety=-0.18, moat=strong, signal=neutral
# MSFT: score=35/40, margin_of_safety=0.05, moat=excellent, signal=bullish

示例3:进阶 —— 回测历史策略表现

# 用 Michael Burry(逆向做空)+ 技术分析师,回测 2023 全年
poetry run python src/backtester.py \
    --ticker SPY,QQQ,TLT \
    --start-date 2023-01-01 \
    --end-date 2023-12-31 \
    --analysts michael_burry technical_analyst \
    --initial-cash 100000

# 回测每个交易日执行一次决策,最终输出:
# Sharpe Ratio: 1.23
# Max Drawdown: -8.5%
# Total Return: +34.2%
# Long/Short Ratio: 2.1

示例4:高级 —— 自定义新分析师

假设你想加入一个"A股专属分析师",步骤如下:

第一步:创建分析师文件 src/agents/a_share_analyst.py

from langchain_core.messages import HumanMessage
from langchain_core.prompts import ChatPromptTemplate
from pydantic import BaseModel, Field
from typing_extensions import Literal
from src.graph.state import AgentState, show_agent_reasoning
from src.tools.api import get_financial_metrics
from src.utils.llm import call_llm
from src.utils.progress import progress
import json

class AShareSignal(BaseModel):
    signal: Literal["bullish", "bearish", "neutral"]
    confidence: int = Field(description="置信度 0-100")
    reasoning: str = Field(description="分析理由")

def a_share_analyst_agent(state: AgentState, agent_id: str = "a_share_analyst_agent"):
    """A股专属分析师:关注北向资金、政策面、市盈率分位数"""
    data = state["data"]
    tickers = data["tickers"]
    end_date = data["end_date"]
    
    analysis_results = {}
    
    for ticker in tickers:
        progress.update_status(agent_id, ticker, "获取A股财务数据")
        metrics = get_financial_metrics(ticker, end_date, period="annual", limit=5)
        
        if not metrics:
            continue
        
        m = metrics[0]
        # A股特色指标:低PE + 高ROE + 低负债
        score = 0
        if m.price_to_earnings_ratio and m.price_to_earnings_ratio < 20:
            score += 2
        if m.return_on_equity and m.return_on_equity > 0.12:
            score += 2
        if m.debt_to_equity and m.debt_to_equity < 0.6:
            score += 1
        
        # 让 LLM 扮演"A股价值投资者"
        template = ChatPromptTemplate.from_messages([
            ("system", "你是一位专注A股市场的价值投资分析师,"
                       "关注政策导向、行业景气度和估值合理性。"),
            ("human", "股票 {ticker} 的评分是 {score}/5,PE={pe},ROE={roe}。"
                      "请给出投资信号(bullish/neutral/bearish)和理由。\n"
                      '返回JSON: {{"signal":"...","confidence":int,"reasoning":"..."}}')
        ])
        
        prompt = template.invoke({
            "ticker": ticker, "score": score,
            "pe": m.price_to_earnings_ratio,
            "roe": m.return_on_equity
        })
        
        result = call_llm(prompt, AShareSignal, agent_id, state)
        analysis_results[ticker] = result.model_dump()
    
    message = HumanMessage(content=json.dumps(analysis_results), name=agent_id)
    state["data"]["analyst_signals"][agent_id] = analysis_results
    
    return {"messages": [message], "data": state["data"]}

第二步:注册到配置 src/utils/analysts.py

from src.agents.a_share_analyst import a_share_analyst_agent

ANALYST_CONFIG["a_share_analyst"] = {
    "display_name": "A股分析师",
    "description": "A股价值投资专家",
    "investing_style": "关注政策导向、低估值、高ROE的A股标的",
    "agent_func": a_share_analyst_agent,
    "type": "analyst",
    "order": 19,
}

完成!现在可以用 --analysts a_share_analyst 使用它了。


示例5:高级 —— 通过 Python API 嵌入到自己的系统

from src.main import run_hedge_fund

# 构建投资组合初始状态
portfolio = {
    "cash": 500000.0,
    "margin_requirement": 0.5,
    "margin_used": 0.0,
    "positions": {
        "AAPL": {"long": 0, "short": 0, "long_cost_basis": 0.0,
                 "short_cost_basis": 0.0, "short_margin_used": 0.0},
        "TSLA": {"long": 0, "short": 0, "long_cost_basis": 0.0,
                 "short_cost_basis": 0.0, "short_margin_used": 0.0},
    },
    "realized_gains": {"AAPL": {"long": 0.0, "short": 0.0},
                       "TSLA": {"long": 0.0, "short": 0.0}},
}

# 只用 4 位分析师,用 claude-opus-4-5 模型
result = run_hedge_fund(
    tickers=["AAPL", "TSLA"],
    start_date="2024-01-01",
    end_date="2024-03-31",
    portfolio=portfolio,
    show_reasoning=True,
    selected_analysts=["warren_buffett", "cathie_wood",
                       "technical_analyst", "risk_management"],
    model_name="claude-opus-4-5",
    model_provider="Anthropic",
)

# 解析结果
decisions = result["decisions"]
signals = result["analyst_signals"]

for ticker, decision in decisions.items():
    print(f"{ticker}: {decision['action']} {decision['quantity']}股 "
          f"(置信度: {decision['confidence']}%)")
    print(f"  理由: {decision['reasoning']}")
    
# 查看各分析师的原始信号
for agent, ticker_signals in signals.items():
    for ticker, signal in ticker_signals.items():
        print(f"  [{agent}] {ticker}: {signal.get('signal')} "
              f"conf={signal.get('confidence')}")

示例6:高级 —— 启动 Web 应用界面

# 最简单的方式(推荐,适合非技术用户)
./run.sh   # Mac/Linux
run.bat    # Windows

# 手动方式
# 终端1:启动后端
cd app/backend
poetry run uvicorn main:app --reload
# 访问 http://localhost:8000/docs 查看 API 文档

# 终端2:启动前端
cd app/frontend
npm run dev
# 访问 http://localhost:5173 使用可视化界面

Web UI 提供:

  • 可视化选择分析师(勾选框)
  • 实时显示每个 Agent 的分析进度
  • 交互式图表展示投资组合历史
  • 回测结果的 Sharpe/Drawdown 图表

十、回测引擎深度解析

回测是检验策略的核心工具。引擎的执行逻辑:

初始化
  │  预拉取1年数据并缓存(避免回测中频繁 API 调用)
  ↓
按交易日循环 (每个工作日)
  │
  ├─→ 获取当日价格
  │
  ├─→ 调用 run_hedge_fund()(模拟当日决策)
  │       输入:lookback_start ~ 当日
  │       输出:各股票 action + quantity
  │
  ├─→ 执行交易(TradeExecutor)
  │       buy  → 扣减现金,增加多头持仓
  │       sell → 增加现金,减少多头持仓
  │       short → 计算保证金,增加空头持仓
  │       cover → 计算盈亏,减少空头持仓
  │
  ├─→ 计算当日组合价值(多头市值 - 空头市值 + 现金)
  │
  └─→ 输出每日明细表 + 更新性能指标
           Sharpe Ratio = 年化收益 / 年化波动率
           Sortino Ratio = 年化收益 / 下行波动率
           Max Drawdown  = 最大从峰值回撤比例
           Long/Short Ratio = 多头敞口 / 空头敞口

十一、项目目录结构速查

ai-hedge-fund/
├── src/
│   ├── main.py              # 入口:工作流组装 + CLI
│   ├── backtester.py        # 回测入口
│   │
│   ├── agents/              # 所有分析师 Agent(19个)
│   │   ├── warren_buffett.py
│   │   ├── technical_analyst.py
│   │   ├── risk_manager.py
│   │   ├── portfolio_manager.py
│   │   └── ...
│   │
│   ├── graph/
│   │   └── state.py         # AgentState 定义
│   │
│   ├── llm/
│   │   ├── models.py        # LLM 提供商适配层
│   │   ├── api_models.json  # 在线模型列表
│   │   └── ollama_models.json # 本地模型列表
│   │
│   ├── tools/
│   │   └── api.py           # 金融数据 API 封装 + 缓存
│   │
│   ├── data/
│   │   ├── models.py        # Pydantic 数据模型
│   │   └── cache.py         # 内存缓存
│   │
│   ├── backtesting/
│   │   ├── engine.py        # 回测引擎
│   │   ├── trader.py        # 交易执行
│   │   ├── metrics.py       # Sharpe/Drawdown 计算
│   │   └── portfolio.py     # 组合持仓管理
│   │
│   └── utils/
│       ├── analysts.py      # 分析师注册表(单一配置源)
│       ├── llm.py           # call_llm 工具函数(重试/结构化输出)
│       ├── progress.py      # 进度显示
│       └── display.py       # 终端格式化输出
│
├── app/                     # Web 应用
│   ├── backend/             # FastAPI 后端
│   └── frontend/            # React/Vite 前端
│
├── .env.example             # API Key 配置模板
└── pyproject.toml           # Poetry 依赖管理

十二、设计亮点与局限性

亮点

1. 代码与 LLM 的职责分离:数值计算(DCF、波动率、技术指标)全用纯 Python,LLM 只负责"判断"和"推理",而非计算。这保证了可重复性和可调试性。

2. Pydantic 强类型约束:所有 LLM 输出都有 Pydantic 模型校验,避免幻觉导致的格式错误破坏后续流程,并有多次重试 + 默认值兜底。

3. 确定性约束前置:组合经理先用纯逻辑算出"物理上允许的操作",再送给 LLM 选择,避免 LLM 输出不合法操作(如买入超过现金量)。

4. 极低 token 消耗设计:信号压缩为 {sig, conf},推理限制在 100 字符以内,预填充 hold 类型股票,大幅节省成本。

5. 高度可扩展:新增分析师只需两步——写分析函数 + 注册到 ANALYST_CONFIG

局限性

  • 金融数据依赖 FinancialDatasets.ai(需付费 Key),A 股数据覆盖有限
  • 内存缓存进程重启后丢失,高频回测会重复 API 调用
  • 回测不考虑滑点、手续费、市场冲击成本
  • "模仿"大师只是提示词策略,并非真正学习了其决策模式

十三、总结

ai-hedge-fund 是一个教学意义极强的工程实践项目,它展示了如何将 LangGraph 多智能体框架金融量化分析大语言模型三者有机结合。

核心架构用一句话概括:并行的专家 Agent 各自产出信号,经过量化风控约束,由组合经理 LLM 做最终决策

无论你是想学习多智能体系统设计、探索 LLM 在金融领域的应用,还是搭建自己的量化研究平台,这个项目都是极好的起点。

再次强调:本项目为教育目的,不构成任何投资建议,请勿用于真实交易。


十四、更多分析师深度解析

14.1 Nassim Taleb Agent:最复杂的风险哲学实现

Taleb Agent 是整个项目中维度最多、逻辑最独特的分析师,完全按照《反脆弱》的思想框架构建,共有 7 个子分析维度,总分 46 分:

维度                   满分   核心逻辑
──────────────────────────────────────────────────────────────────────
尾部风险 (Tail Risk)    8分   峰度/偏度/尾比率/最大回撤
反脆弱性 (Antifragility) 10分  净现金/低杠杆/利润率稳定性/FCF一致性
凸性 (Convexity)       10分   R&D期权性/上行/下行比率/现金期权
脆弱性检测 (Fragility)  8分   杠杆/利息覆盖率/盈利波动/利润缓冲
同行参与 (Skin in Game) 4分   内幕净买入/买卖比率
波动率机制 (Vol Regime)  6分   当前波动率/历史均值比/波动率的波动率
黑天鹅哨兵 (Black Swan) 4分   负面新闻占比/成交量异常/价格偏离

波动率机制评分中最有趣的一条逻辑——"低波动才是危险"(即"火鸡问题"):

波动率机制比值 < 0.70分  (注意!这是得分最低的情况!)
           "极端低波动率——火鸡问题(Thanksgiving Turkey),
            平静期积累的脆弱性随时可能爆发"

波动率机制比值 1.0~1.33分  (正常区间)
波动率机制比值 1.3~2.04分  (最高分!高波动对反脆弱者是机会)

这与传统风险管理完全相反——大多数人怕高波动,Taleb 认为高波动才是真实的,低波动才是风险压抑的信号

黑天鹅哨兵里的逆向逻辑(与 Michael Burry 如出一辙):

# 高负面新闻 + 无成交量恐慌 = 逆向机会
if neg_ratio > 0.4 and volume_spike < 1.5 and score < 4:
    score = min(score + 1, 4)
    reasoning.append("逆向机会——负面情绪未伴随恐慌抛售")

14.2 Michael Burry Agent:数字驱动的逆向做空者

Burry 的实现只有 4 个维度,但极度聚焦:

维度                满分   核心指标
────────────────────────────────────────────
价值分析            6分   FCF收益率(≥15%得4分)+ EV/EBIT(<6得2分)
资产负债表          3分   D/E < 0.5 + 净现金地位
内幕交易活动        2分   过去12个月净内幕买入量
逆向情绪            1分   ≥5条负面新闻(越被嫌弃越好)

LLM 提示词充分体现了 Burry 的人设风格:

  • "数字先行,极简表达"
  • "关注下行风险,债务是死穴"
  • "负面新闻墙 = 潜在机会(前提是基本面支撑)"

14.3 Cathie Wood Agent:增长赛道的评分体系

Wood 的分析分三层:

第一层:颠覆性潜力(满分5分,归一化自12分)
  ├─ 营收增长加速(+2)
  ├─ 最新营收增长率 >100%/50%/20%(+3/2/1)
  ├─ 毛利率扩张 >5%(+2)
  ├─ 毛利率绝对值 >50%(+2)
  ├─ 正向经营杠杆(+2)
  └─ R&D强度 >15%/8%/5%(+3/2/1)

第二层:创新增长(满分5分,归一化自15分)
  ├─ R&D支出增长 >50%(+3)
  ├─ R&D强度趋势上升(+2)
  ├─ FCF增长且一致正值(+3)
  ├─ 营业利润率 >15%(+3)
  └─ 资本开支强度 + 再投资率(+2+2)

第三层:估值(满分5分)
  └─ 高成长 DCF:20%增长率 + 25x终值倍数
     安全边际 >50% → +3, >20% → +1

总分 ≥ 70% → bullish,≤ 30% → bearish

Wood 用 20% 成长率 + 25 倍终值(而非 Buffett 的 4% + 戈登模型),这正是她乐于买入"贵但高增长"的标的而不在乎 P/E 的数学依据。


14.4 情绪分析 Agent:两信号源加权融合

情绪分析师完全不调用 LLM,纯规则:

信号来源          权重    数据
───────────────────────────────────────────────
内幕交易方向      30%    transaction_shares > 0 → bullish
                        transaction_shares < 0 → bearish
新闻情绪          70%    sentiment == "positive" → bullish
                        sentiment == "negative" → bearish
                        其他 → neutral

最终信号 = argmax(加权后多/空票数)
置信度 = max(加权多票, 加权空票) / 加权总票数 × 100%

内幕交易权重(30%)低于新闻(70%),原因是内幕交易可能有多种解读(员工期权行权 ≠ 看空),而新闻情绪覆盖面更广、样本量更大。


14.5 估值 Agent:四模型加权内在价值

Valuation Agent 是整个项目中估值模型最完整的 Agent,同时运行 4 种方法:

方法                    权重   核心公式
────────────────────────────────────────────────────────────────────
增强 DCF(三情境)       35%   熊/基/牛三场景 × (0.2/0.6/0.2)
                              WACC = 股权成本(CAPM) + 税后债务成本
Owner Earnings 法        35%   净利润 + D&A - 维护性资本开支 - 营运资本变化
                              (Buffett 的"真实盈利能力")
EV/EBITDA 历史中位数法   20%   用历史中位 EV/EBITDA 倍数估算当前隐含价值
剩余收益模型 (RIM)       10%   账面价值 + PV(超额收益) + PV(终值超额收益)
                              (Edwards-Bell-Ohlson 模型)

WACC 计算(代码中真实实现):

资本成本 (CAPM):  Ke = Rf + β × ERP = 4.5% + 1.0 × 6% = 10.5%(默认)
债务成本:         Kd = 由利息覆盖率推算(覆盖率越高,债务越便宜)
WACC = (E/V × Ke) + (D/V × Kd × (1-税率))
边界约束: max(6%, min(20%, WACC))   ← 防极端情况

DCF 三情境设计

                 成长率调整  WACC调整  终值调整
熊市情境(20%)   ×0.5       ×1.2     ×0.8
基准情境(60%)   ×1.0       ×1.0     ×1.0
牛市情境(20%)   ×1.5       ×0.9     ×1.2

加权内在价值 = 熊×0.2 + 基×0.6 + 牛×0.2

最终信号判断:

加权差距 > 15%  → bullish(低估超 15%)
加权差距 < -15% → bearish(高估超 15%)
否则           → neutral

十五、投资组合交易引擎:做多/做空的完整会计处理

回测引擎的 Portfolio 类实现了精确的持仓核算,这是整个系统能进行做空回测的基础。

15.1 五种操作的现金流向

操作       现金变化                    持仓变化
─────────────────────────────────────────────────────────────────────
BUY        -数量×价格                  多头持仓+数量,更新均价成本
SELL       +数量×价格                  多头持仓-数量,计入已实现盈亏
SHORT      +融券收入-保证金            空头持仓+数量,锁定保证金
COVER      -回补成本+释放保证金        空头持仓-数量,计入已实现盈亏
HOLD       无变化                      无变化

做空的现金流细节(与大多数教学项目不同,这里实现正确):

# 开空:收到融券收入,但同时锁定保证金
proceeds = price × quantity          # 融券收入(卖空所得)
margin_required = proceeds × 0.5     # 需冻结 50% 保证金(默认)
cash += proceeds                      # 收到融券款
cash -= margin_required               # 冻结保证金
margin_used += margin_required        # 记录已用保证金

# 平空:回购股票,释放保证金,计算盈亏
realized_gain = (avg_short_price - cover_price) × quantity  # 空头盈利
cash -= cover_cost                    # 回购花费
cash += margin_to_release             # 释放保证金
margin_used -= margin_to_release

15.2 净清算价值(NLV)计算

NLV = 现金
    + Σ(多头持仓量 × 当前价格)
    - Σ(空头持仓量 × 当前价格)   ← 空头是负债

注意:空头敞口是减项——若空头头寸亏损(股价上涨),组合价值会下降,这与真实市场行为一致。


十六、性能指标详解:如何评价策略好坏

指标              计算公式                        含义
──────────────────────────────────────────────────────────────────────
Sharpe Ratio    √252 × (超额日均收益 / 日收益标准差)
                > 1.0 优秀,> 2.0 卓越
                分母包含上行和下行波动,"惩罚"所有波动

Sortino Ratio   √252 × (超额日均收益 / 下行偏差)
                只惩罚下行波动,对策略更公平
                >> Sharpe 说明策略收益主要来自上行

Max Drawdown    (谷值 - 峰值) / 峰值 × 100%
                衡量最坏时刻的账面亏损
                -20% 以内较健康,-50% 以上很危险

基准对比        策略收益率 vs SPY 同期收益率
                正超额收益 = alpha 存在(是否真正有价值)

Long/Short Ratio 多头敞口 / 空头敞口
                > 1 = 净多头偏向,< 1 = 净空头偏向

无风险利率默认使用 4.34%(annual_rf_rate = 0.0434),对应美国当前较高利率环境。


十七、CLI 交互体验:questionary 的精妙使用

项目没有简单地用 argparse 做命令行,而是用 questionary 实现了交互式终端 UI

运行时用户体验:

? Select your AI analysts.
  Instructions:
  1. Press Space to select/unselect analysts.
  2. Press 'a' to select/unselect all.
  3. Press Enter when done.

 ◯ Aswath Damodaran
 ◉ Ben Graham           ← 绿色高亮,已选中
 ◯ Bill Ackman
 ◉ Warren Buffett       ← 绿色高亮,已选中
 ◯ Cathie Wood
 ...

? Select your LLM model:
  > gpt-4.1 (OpenAI)    ← 绿色箭头指向当前选项
    claude-opus-4-5 (Anthropic)
    deepseek-chat (DeepSeek)
    gemini-2.0-flash (Google)
    llama-3.3-70b (Groq)
    [自定义 Ollama 模型...]

运行时进度显示(rich.Live实时刷新):

⋯ Warren Buffett      [AAPL] Calculating intrinsic value
✓ Technical Analyst   [AAPL] Done
✓ Technical Analyst   [MSFT] Done
⋯ Ben Graham          [MSFT] Analyzing balance sheet
⋯ Risk Management     [AAPL] Calculating volatility- and correlation-adjusted limits

绿色 ✓ = 完成,黄色 ⋯ = 进行中,红色 ✗ = 错误,颜色来自 colorama + rich


十八、v2 目录:下一代架构预览

项目根目录有一个 v2/ 文件夹,是正在开发中的新版本骨架。从目录名可以推测方向:

v2/
└── (初始数据层)
    Create initial data layer (4 days ago)

这暗示 v2 将把数据层独立出来——从当前的进程内内存缓存,演进为持久化数据层(可能是 SQLite / PostgreSQL + Alembic 迁移),这样回测就不需要每次重新拉取 API 数据。pyproject.toml 里已经有 sqlalchemyalembic 依赖,印证了这个方向。


十九、完整依赖图谱

ai-hedge-fund 核心依赖关系

┌─────────────────────────────────────────────────────────────┐
│                     应用层                                   │
│  langchain / langgraph  ←→  pydantic  ←→  python-dotenv    │
├─────────────────────────────────────────────────────────────┤
│                   LLM 适配层                                 │
│  langchain-openai  langchain-anthropic  langchain-deepseek  │
│  langchain-groq    langchain-ollama     langchain-google     │
│  langchain-xai     langchain-gigachat   (OpenRouter via OAI) │
├─────────────────────────────────────────────────────────────┤
│                   数据与计算层                               │
│  pandas    numpy    scipy                                    │
│  requests  (→ financialdatasets.ai API)                     │
├─────────────────────────────────────────────────────────────┤
│                   Web 应用层                                 │
│  fastapi  uvicorn  sqlalchemy  alembic  httpx               │
├─────────────────────────────────────────────────────────────┤
│                   CLI / 展示层                               │
│  rich  colorama  tabulate  questionary  matplotlib          │
└─────────────────────────────────────────────────────────────┘

二十、常见问题与调试指南

Q1:运行报错 "FINANCIAL_DATASETS_API_KEY not found"

# 检查 .env 文件是否在项目根目录
ls -la | grep .env

# 确认内容
cat .env | grep FINANCIAL_DATASETS_API_KEY
# 应输出:FINANCIAL_DATASETS_API_KEY=your-key-here

Q2:回测速度很慢

原因:每个交易日都会调用 LLM 和 API,100 个交易日 × 3 只股票 × 平均 0.5 元/次 LLM = 150 元。

优化方案

# 只用 2 个轻量分析师(减少 LLM 调用次数)
poetry run python src/backtester.py \
    --ticker AAPL \
    --start-date 2024-01-01 \
    --end-date 2024-01-31 \   # 先只测一个月
    --analysts technical_analyst fundamentals_analyst \
    --model deepseek-chat     # DeepSeek 价格是 GPT-4 的 1/20

# 或使用本地 Ollama(零成本)
poetry run python src/backtester.py \
    --ticker AAPL \
    --ollama

Q3:LLM 输出解析失败

call_llm 有 3 次重试 + default_factory 兜底,失败时会打印:

Error in LLM call after 3 attempts: ...

常见原因:模型不支持 JSON mode(DeepSeek / Gemini),系统已自动 fallback 到 extract_json_from_response 解析 markdown 代码块。若仍失败,换用 OpenAI / Anthropic。

Q4:如何只运行单个分析师测试

# 直接在 Python 中调用单个 agent 函数
from src.agents.warren_buffett import warren_buffett_agent
from src.graph.state import AgentState
from langchain_core.messages import HumanMessage

state = AgentState(
    messages=[HumanMessage(content="test")],
    data={
        "tickers": ["AAPL"],
        "start_date": "2024-01-01",
        "end_date": "2024-03-01",
        "analyst_signals": {},
        "portfolio": {"cash": 100000, "positions": {}}
    },
    metadata={
        "show_reasoning": True,
        "model_name": "gpt-4.1",
        "model_provider": "OpenAI"
    }
)
result = warren_buffett_agent(state)
print(result["data"]["analyst_signals"]["warren_buffett_agent"])

Q5:如何给每个分析师用不同模型(节省成本)

这个功能在 Web App 后端已实现(通过 request.get_agent_model_config(agent_name) 按 agent 分配模型),CLI 目前统一使用同一模型。可以参照 src/utils/llm.pyget_agent_model_config 函数自行扩展。


二十一、二次开发路线图

根据代码结构,以下是从简到难的二次开发方向:

🟢 入门级(1~2天)

添加新的分析师 Agent:参照上文"示例4",任何股票分析思想都能封装成 Agent。

替换数据源:修改 src/tools/api.py,将 FinancialDatasets.ai 替换为 Yahoo Finance(yfinance)或 AKShare(A股):

# 用 yfinance 替换 get_prices 的 API 部分
import yfinance as yf
def get_prices_yf(ticker, start_date, end_date):
    df = yf.download(ticker, start=start_date, end=end_date)
    # 转换为 Price 对象列表...

🟡 进阶级(3~7天)

持久化缓存:将 src/data/cache.py 从内存缓存改为 SQLite:

import sqlite3
class PersistentCache:
    def __init__(self, db_path="cache.db"):
        self.conn = sqlite3.connect(db_path)
        # 建表、CRUD...

增加组合再平衡逻辑:在 portfolio_management_agent 中加入目标权重约束,比如单股不超过 20%,按信号强度自动分配权重。

添加止损机制:在 TradeExecutor 中加入止损检查:

# 如果亏损超过 15%,强制清仓
if (current_price - cost_basis) / cost_basis < -0.15:
    return self.apply_long_sell(ticker, position["long"], current_price)

🔴 高级(1~4周)

实时交易接入(谨慎!):将 run_hedge_fund 封装为定时任务,通过 Alpaca / Interactive Brokers API 执行真实交易。但强烈建议先在纸交易模式(Paper Trading)运行数月。

多智能体辩论机制:让看多分析师和看空分析师在组合经理决策前进行"辩论"——每轮将对方信号作为输入,产生新的反驳或确认,类似 Society of Mind

强化学习优化权重:将技术分析师的 5 个策略权重({trend: 0.25, momentum: 0.25, ...})变成可学习的参数,用回测收益作为奖励信号训练。


二十二、核心设计模式总结

读完全部代码,可以提炼出几个值得借鉴的工程模式:

模式一:算法评分 + LLM 判断的混合架构

不让 LLM 做计算,只让它做判断。所有数值分析(DCF、波动率、技术指标)在代码中完成,压缩为"事实摘要"(facts dict)送给 LLM,LLM 返回结构化的 {signal, confidence, reasoning}。这样既保留了 LLM 的模式识别能力,又避免了幻觉影响数值计算。

模式二:Pydantic 全链路类型约束

从 API 响应(PriceResponseFinancialMetricsResponse)到 LLM 输出(WarrenBuffettSignalPortfolioManagerOutput),全程 Pydantic 强校验。任何数据格式异常在边界处立刻暴露,不会在系统内部静默传播。

模式三:单例注册表 + 配置驱动

ANALYST_CONFIG 是整个系统的单一事实来源(Single Source of Truth)。CLI 的选择列表、API 的分析师枚举、工作流的节点注册,全部派生自这一个字典,不存在多处硬编码需要同步修改的问题。

模式四:进度作为副作用

progress.update_status() 被调用于每个 Agent 的每个步骤,既用于终端实时显示(rich.Live),又通过 register_handler 机制向 Web 后端推送 WebSocket 事件。进度状态与业务逻辑完全解耦,分析师代码无需知道自己运行在 CLI 还是 Web 环境。

模式五:确定性约束前置

compute_allowed_actions 先于 LLM 调用运行,计算出物理上合法的操作集合,传递给 LLM 作为约束。这是一种**约束满足(Constraint Satisfaction)**的设计,防止 LLM 输出不合法的指令(如买超现金、卖出不持有的股票)。


二十三、博客最终总结

这个项目是一个罕见地将金融理论、量化计算、多智能体系统、大语言模型四者真正融合的开源作品,而不是表面上的"LLM + 一点点数据"的玩具项目。

从代码质量看,它有很多值得称道的工程实践:Pydantic 全链路、单例配置驱动、确定性约束、重试容错、进度与业务解耦。从教学价值看,它几乎是 LangGraph 多智能体应用的最佳实战教材。

如果你要用这个项目做学习,推荐路径:

第一周:跑通 CLI,理解 AgentState 和工作流结构
第二周:阅读 warren_buffett.py,理解"算法评分+LLM判断"模式
第三周:深读 risk_manager.py 和 portfolio_manager.py,理解资金管理
第四周:跑一次完整回测,分析 Sharpe/Drawdown 结果
第五周:写自己的分析师 Agent,贡献 PR

最后再次重申:本项目为教育目的,所有分析仅供学习参考,不构成任何投资建议。市场有风险,投资需谨慎。