agents/investor-personas/ — Famous Investor AI Personas

13 AI agents each embodying a legendary investor's philosophy

From Warren Buffett's value investing to Nassim Taleb's tail-risk analysis, each agent analyzes stocks through its persona's unique philosophical lens. All follow the same interface but with radically different system prompts and analysis priorities.

13
Investor Personas
13
Python Files
~6.5k
Lines
3
Signal Types

📚 What is this & why does it exist?

These 13 agents are the creative heart of the system. Each encodes decades of a real investor's published philosophy, books, and interview quotes into an LLM system prompt. The agent then uses this persona to interpret financial data — the same P/E ratio that excites a growth investor like Cathie Wood might horrify a value investor like Ben Graham. This diversity of perspective is the core insight: no single philosophy works in all markets.

💡 Real-world analogy

Imagine a book club where each member reads the same novel but interprets it through their professional lens. A psychologist focuses on character motivations (Buffett analyzes Portfolio Management" href="../management/index.html">management quality), a statistician tracks plot patterns (the technical analyst tracks price patterns), and a critic evaluates literary technique (Graham evaluates balance sheet quality). Same data, radically different interpretations — and the discussion between them produces richer understanding than any single perspective.

🤔 Why Is It Designed This Way?

The persona-agent pattern encodes domain expertise as natural-language system prompts rather than hard-coded rules. Each agent follows an identical structural contract — (state: AgentState) → {signal, confidence, reasoning} — but swaps in a different philosophical framework via its prompt. This design was chosen over a single agent with a "style" parameter because each investor's logic requires different sub-analyses: Buffett needs moat analysis while Taleb needs tail-risk metrics. Separating agents also allows LangGraph to run them in parallel across the fan-out phase, maximizing throughput. The trade-off is code duplication across agents, but the domain separation makes each agent independently testable and modifiable.

Design ChoiceWhat Was ChosenWhat Was RejectedWhy
Agent granularityOne file per personaSingle agent with style configEach persona needs unique sub-analyses (moat vs. tail-risk vs. disruption) that cannot be parameterized cleanly
Signal formatPydantic model with Literal typesFree-form string outputStructured output ensures downstream consumers (risk manager, portfolio manager) can reliably parse signals
LLM integrationPrompt-based persona + structured outputFine-tuned models per investorSystem prompts are cheaper to iterate, require no training data, and can be updated without retraining
Data fetchingAgent fetches its own dataShared data layer pre-fetches allEach persona needs different data (Burry wants insider trades; Taleb wants price volatility) so pre-fetching everything wastes API calls

Contents

Architecture

All 13 investor persona agents follow the same four-stage pipeline pattern. The LangGraph orchestrator fans out to all agents in parallel, each independently fetching data, building analysis, calling the LLM, and emitting a structured signal.

flowchart TD
  subgraph FanOut["LangGraph Fan-Out (parallel)"]
    WB["warren_buffett_agent"]
    BG["ben_graham_agent"]
    CW["cathie_wood_agent"]
    MB["michael_burry_agent"]
    NT["nassim_taleb_agent"]
    CM["charlie_munger_agent"]
    BA["bill_ackman_agent"]
    PL["peter_lynch_agent"]
    PF["phil_fisher_agent"]
    SD["stanley_druckenmiller_agent"]
    AD["aswath_damodaran_agent"]
    RJ["rakesh_jhunjhunwala_agent"]
    MP["mohnish_pabrai_agent"]
  end
  State["AgentState with tickers"] --> FanOut
  FanOut --> Signals["analyst_signals dict"]
  Signals --> RM["Risk Manager"]

Per-Agent Internal Pipeline

Each persona agent internally follows this four-step pattern, regardless of which investor it embodies:

flowchart LR
  A["1. Fetch Data"] --> B["2. Run Sub-Analyses"]
  B --> C["3. Build LLM Prompt"]
  C --> D["4. Parse Signal Output"]
  A -.- A1["get_financial_metrics\nget_market_cap\nsearch_line_items"]
  B -.- B1["analyze_fundamentals\nanalyze_moat\nanalyze_consistency\netc."]
  C -.- C1["System prompt encodes\ninvestor philosophy"]
  D -.- D1["Pydantic model:\nsignal + confidence\n+ reasoning"]

Key Classes & Functions

Class / FunctionFileInvesting Style & Focus
warren_buffett_agentwarren_buffett.pyValue investing: moat analysis, management quality, intrinsic value vs. market price, margin of safety
ben_graham_agentben_graham.pyClassic value investing: earnings stability, financial strength, Graham Number, net-net valuation
cathie_wood_agentcathie_wood.pyDisruptive innovation: breakthrough tech, rapid adoption curves, massive TAM, R&D intensity
michael_burry_agentmichael_burry.pyDeep-value contrarian: insider trades, news sentiment, free cash flow, short opportunities
nassim_taleb_agentnassim_taleb.pyAntifragility and tail risk: price volatility, barbell strategy, convexity, fat-tail distributions
charlie_munger_agentcharlie_munger.pyRational thinking: quality businesses, competitive advantages, mental models, long-term compounding
bill_ackman_agentbill_ackman.pyActivist investing: management influence, value unlock, strategic activism, contrarian positions
peter_lynch_agentpeter_lynch.pyGrowth at reasonable price: PEG ratio, "buy what you know", understandable businesses, 10-baggers
phil_fisher_agentphil_fisher.pyScuttlebutt research: strong management, innovative products, long-term growth, qualitative analysis
stanley_druckenmiller_agentstanley_druckenmiller.pyMacro investing: macroeconomic trends, currency/commodity bets, top-down analysis, large positions
aswath_damodaran_agentaswath_damodaran.pyValuation dean: intrinsic value modeling, rigorous DCF, financial metrics, academic rigor
rakesh_jhunjhunwala_agentrakesh_jhunjhunwala.pyEmerging markets: macroeconomic insights, high-growth sectors, domestic opportunities, the Big Bull
mohnish_pabrai_agentmohnish_pabrai.pyDhandho investing: low risk high uncertainty, copycat strategy, margin of safety, concentrated bets
growth_analyst_agentgrowth_agent.pyGrowth analysis: revenue acceleration, earnings momentum, valuation multiples, growth trajectory

Common Signal Contract

Every persona agent, regardless of which investor it embodies, must produce output conforming to this contract. The signal is always one of three values, confidence is always 0-100, and reasoning is free-form text explaining the investment thesis. This uniformity is what allows the risk manager and portfolio manager to treat all 13 agents identically.

class InvestorSignal(BaseModel):
    signal: Literal["bullish", "bearish", "neutral"]
    confidence: int = Field(description="Confidence level 0-100")
    reasoning: str = Field(description="Investment thesis reasoning")

Sub-Analysis Scoring Pattern

Each persona agent runs multiple domain-specific sub-analyses. While the exact analyses differ (Buffett runs 7, Graham runs 3, Taleb runs 4+), they all follow the same scoring pattern: each sub-analysis returns a dict with a score key and domain-specific details. Scores are aggregated, normalized against max_possible_score, and then passed to the LLM for final interpretation through the persona's philosophical lens.

Data Structures

TypeNameDescription
PydanticWarrenBuffettSignalOutput schema: signal (bullish/bearish/neutral), confidence (0-100 int), reasoning (str)
PydanticBenGrahamSignalOutput schema: signal (Literal), confidence (float), reasoning (str)
PydanticCathieWoodSignalOutput schema: same three fields, tailored for disruptive innovation analysis
PydanticMichaelBurrySignalOutput schema with confidence as float 0-100 for deep-value contrarian signals
PydanticNassimTalebSignalOutput schema: confidence as int Field(0-100) for tail-risk analysis
TypedDictAgentStateShared LangGraph state containing data, metadata, messages, and analyst_signals
dictanalysis_dataPer-ticker dict aggregating sub-analysis scores (fundamental, moat, consistency, etc.)
dictANALYST_CONFIGRegistry mapping agent keys to display names, descriptions, investing styles, and agent functions

Interactions

DirectionPartnerInterfaceDescription
→Financial APIget_financial_metrics()Fetches TTM/annual financial metrics for each ticker via the Financial Datasets API
→Financial APIsearch_line_items()Retrieves specific accounting line items (revenue, FCF, net income, etc.) with period/limit controls
→Financial APIget_market_cap()Gets current market capitalization for margin-of-safety calculations
→LLM Servicecall_llm()Sends structured prompt to LLM with persona system prompt; returns parsed Pydantic signal
→News/Insider APIget_insider_trades(), get_company_news()Used by Burry and Taleb agents for contrarian and sentiment signals
→Price APIget_prices(), prices_to_df()Used by Taleb agent for volatility and tail-risk analysis on price data
←LangGraph Statestate["data"]["analyst_signals"]Each agent writes its signal dict to the shared state for downstream consumption
←Progress Trackerprogress.update_status()Reports step-by-step progress to the UI for real-time status updates

Configuration

Knob / ParameterDefaultDescription
period"ttm" or "annual"Financial data period; Buffett uses TTM, Graham uses annual for stability analysis
limit5-10Number of historical periods to fetch; more periods enable trend analysis
agent_id"{name}_agent"Unique identifier for progress tracking and signal storage in state
show_reasoningmetadata flagWhen true, calls show_agent_reasoning() to display analysis to the user
FINANCIAL_DATASETS_API_KEYfrom stateAPI key for the Financial Datasets service, extracted from agent state

Directory Map

Source tree for investor-personas
src/agents/
├── warren_buffett.py            ← The Oracle of Omaha: moat, management, intrinsic value
├── ben_graham.py                ← Father of Value Investing: earnings stability, Graham Number
├── cathie_wood.py               ← Queen of Growth: disruptive innovation, TAM analysis
├── michael_burry.py             ← The Big Short: deep-value contrarian, insider trades
├── nassim_taleb.py              ← Black Swan: antifragility, tail risk, convexity
├── charlie_munger.py            ← Rational Thinker: quality businesses, mental models
├── bill_ackman.py               ← Activist Investor: strategic activism, value unlock
├── peter_lynch.py               ← 10-Bagger Hunter: PEG ratio, buy what you know
├── phil_fisher.py               ← Scuttlebutt Investor: management quality, innovation
├── stanley_druckenmiller.py     ← Macro Investor: macro trends, large directional bets
├── aswath_damodaran.py          ← Dean of Valuation: rigorous DCF, intrinsic value
├── rakesh_jhunjhunwala.py       ← Big Bull of India: emerging markets, growth sectors
├── mohnish_pabrai.py            ← Dhandho Investor: low risk high uncertainty, concentrated
└── growth_agent.py              ← Growth Analyst: revenue/earnings acceleration

📑 Annotated Code Walkthrough

The most important code in this sub-module: Warren Buffett's core agent function, which exemplifies the pattern all 13 persona agents follow.

warren_buffett_agent — Core Analysis Pipeline

def warren_buffett_agent(state: AgentState, agent_id: str = "warren_buffett_agent"):
    """Analyzes stocks using Buffett's principles and LLM reasoning."""
    data = state["data"]
    end_date = data["end_date"]
    tickers = data["tickers"]
    api_key = get_api_key_from_state(state, "FINANCIAL_DATASETS_API_KEY")
    analysis_data = {}
    buffett_analysis = {}

    for ticker in tickers:
        # Step 1: Fetch raw financial data
        progress.update_status(agent_id, ticker, "Fetching financial metrics")
        metrics = get_financial_metrics(ticker, end_date, period="ttm", limit=10, api_key=api_key)

        # Step 2: Run Buffett-specific sub-analyses
        fundamental_analysis = analyze_fundamentals(metrics)
        consistency_analysis = analyze_consistency(financial_line_items)
        moat_analysis = analyze_moat(metrics)
        pricing_power_analysis = analyze_pricing_power(financial_line_items, metrics)
        book_value_analysis = analyze_book_value_growth(financial_line_items)
        mgmt_analysis = analyze_management_quality(financial_line_items)
        intrinsic_value_analysis = calculate_intrinsic_value(financial_line_items)

        # Step 3: Aggregate sub-analysis scores
        total_score = (
            fundamental_analysis["score"] +
            consistency_analysis["score"] +
            moat_analysis["score"] +
            mgmt_analysis["score"] +
            pricing_power_analysis["score"] +
            book_value_analysis["score"]
        )

        # Step 4: Calculate margin of safety (Buffett's key metric)
        margin_of_safety = None
        intrinsic_value = intrinsic_value_analysis["intrinsic_value"]
        if intrinsic_value and market_cap:
            margin_of_safety = (intrinsic_value - market_cap) / market_cap

        # Step 5: Send to LLM with Buffett persona prompt
        buffett_output = generate_buffett_output(
            ticker=ticker, analysis_data=analysis_data[ticker],
            state=state, agent_id=agent_id,
        )

        # Step 6: Store in standard signal format
        buffett_analysis[ticker] = {
            "signal": buffett_output.signal,
            "confidence": buffett_output.confidence,
            "reasoning": buffett_output.reasoning,
        }

    # Write signals to shared state
    state["data"]["analyst_signals"][agent_id] = buffett_analysis

Line-by-line:

  • Line 1-2: Standard signature — every persona agent takes AgentState and a default agent_id. The ID is used as the key in analyst_signals so downstream consumers know which persona produced which signal.
  • Line 6: API key extraction from state allows different API keys per run (multi-tenant), rather than relying on environment variables.
  • Lines 12-13: Buffett uses TTM (trailing twelve months) with 10 periods — he cares about recent performance trends, not just the latest quarter.
  • Lines 16-22: The six sub-analyses are Buffett-specific. Other agents have entirely different analyses (Taleb has analyze_tail_risk, Cathie Wood has analyze_disruptive_potential). This is why each persona needs its own file.
  • Lines 30-32: Margin of safety — Buffett's most famous concept. He only buys when intrinsic value exceeds market price by a significant margin. This raw metric is passed to the LLM for interpretation.
  • Lines 35-38: The LLM call is where the persona really lives. generate_buffett_output wraps the analysis data in a system prompt that channels Buffett's voice and priorities.
  • Lines 41-44: Output is normalized to the standard {signal, confidence, reasoning} format that the risk manager and portfolio manager expect from all agents.

⚠️ Edge Cases & Boundaries

Edge CaseWhat HappensWhere in CodeRisk Level
Missing financial metricsSub-analysis functions return zero scores; LLM receives incomplete data and may produce low-confidence signalsAll agents: metrics fetch check🔴 High
API key not in stateget_api_key_from_state() raises or returns None, causing all data fetches to fail for the agentAgent entry: line 6🔴 High
Market cap is NoneMargin of safety is set to None and intrinsic value comparison is skippedwarren_buffett.py:103-106🟡 Medium
LLM returns malformed outputPydantic validation fails; the agent does not produce a signal for that tickergenerate_*_output functions🟡 Medium
No insider trades availableBurry and Taleb agents get empty lists; sentiment analysis produces neutral signalsmichael_burry.py:70, nassim_taleb.py:76🟢 Low
Empty tickers listThe for-loop body never executes; agent returns empty analysis dictAll agents: for ticker in tickers🟢 Low

Code Examples

Example: Ben Graham's Score-Based Signal Mapping

Graham's agent demonstrates the simpler scoring approach where scores directly map to signals without an LLM call for the initial assessment, though the LLM refines the final output:

# Aggregate scoring from three sub-analyses
total_score = (
    earnings_analysis["score"] +
    strength_analysis["score"] +
    valuation_analysis["score"]
)
max_possible_score = 15  # 5 per analysis

# Direct score-to-signal mapping (deterministic)
if total_score >= 0.7 * max_possible_score:     # >= 10.5
    signal = "bullish"
elif total_score <= 0.3 * max_possible_score:    # <= 4.5
    signal = "bearish"
else:
    signal = "neutral"

Example: Cathie Wood's Disruptive Innovation Focus

Note how Cathie Wood's agent fetches R&D and capex data that most other agents don't need — her analysis is uniquely focused on innovation spending:

financial_line_items = search_line_items(
    ticker,
    [
        "revenue", "gross_margin", "operating_margin",
        "research_and_development",   # Unique to Cathie Wood
        "capital_expenditure",         # Innovation investment proxy
        "operating_expense",           # R&D as % of opex
    ],
    end_date, period="annual", limit=5, api_key=api_key,
)

Data Flow

How a single ticker flows through one persona agent from state input to signal output:

sequenceDiagram
  participant S as AgentState
  participant A as Persona Agent
  participant API as Financial API
  participant LLM as LLM Service
  participant Out as analyst_signals

  S->>A: tickers, end_date, api_key
  loop For each ticker
    A->>API: get_financial_metrics(ticker)
    API-->>A: metrics list
    A->>API: search_line_items(ticker)
    API-->>A: line items
    A->>API: get_market_cap(ticker)
    API-->>A: market cap
    A->>A: Run sub-analyses (moat, fundamentals, etc.)
    A->>LLM: Persona prompt + analysis_data
    LLM-->>A: WarrenBuffettSignal(signal, confidence, reasoning)
  end
  A->>Out: analyst_signals[agent_id] = analysis