# How to Create Custom Analyst Agents for the AI Hedge Fund Trading System

> Learn to create custom analyst agents for the AI Hedge Fund trading system. Implement and register your agent function to enhance trading strategies easily. Start building now!

- Repository: [Virat Singh/ai-hedge-fund](https://github.com/virattt/ai-hedge-fund)
- Tags: how-to-guide
- Published: 2026-03-09

---

**To create custom analyst agents in the AI Hedge Fund system, implement an agent function with the signature `(state: AgentState, agent_id: str)`, register it in the `ANALYST_CONFIG` dictionary in [`src/utils/analysts.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/utils/analysts.py), and the LangGraph workflow will automatically incorporate your new node.**

The AI Hedge Fund trading system uses a modular architecture where analyst agents generate trading signals that portfolio managers consume. When you create custom analyst agents, you extend the system's analytical capabilities by adding new nodes to the LangGraph workflow defined in the backend services.

## Understanding the Analyst Architecture

### The ANALYST_CONFIG Registry (src/utils/analysts.py)

The system maintains a single source of truth for all analyst agents in the `ANALYST_CONFIG` dictionary located in **src/utils/analysts.py**. This registry maps agent keys to metadata including `display_name`, `description`, `investing_style`, and the critical `agent_func` pointer that references your implementation.

When the backend graph is created in **app/backend/services/graph.py**, each entry in `ANALYST_CONFIG` is transformed into a LangGraph node via the `create_agent_function` wrapper implemented in **app/backend/services/agent_service.py**.

### Agent Function Signature and State Management

Every custom analyst must implement a function with the exact signature:

```python
def custom_agent(state: AgentState, agent_id: str = "custom_agent") -> dict:

```

The function receives:

- **state**: An `AgentState` dictionary containing `data` (shared workspace) and `metadata` (execution flags)
- **agent_id**: A unique string identifier injected by the graph builder

Your agent must write its trading signals to `state["data"]["analyst_signals"][agent_id]` using this structure:

```python
{
    "TICKER": {
        "signal": "bullish" | "bearish" | "neutral",
        "confidence": int,  # 0-100

        "reasoning": str
    }
}

```

The function must return a standard LangGraph output dictionary:

```python
return {"messages": [HumanMessage(content=json.dumps(results), name=agent_id)], "data": state["data"]}

```

### Graph Construction (app/backend/services/graph.py)

The graph builder automatically constructs nodes for every entry in `ANALYST_CONFIG`:

```python
analyst_nodes = {key: (f"{key}_agent", config["agent_func"])
                 for key, config in ANALYST_CONFIG.items()}

```

The `order` field in your config controls the default execution sequence and the visual ordering in the React Flow editor. The graph uses this ordering only for visualization and initial edge creation; actual execution follows the connections you draw in the front-end flow editor.

## Step-by-Step Implementation Guide

### Step 1: Create the Agent Function

Create a new Python file in **src/agents/** following the naming convention of existing agents (e.g., [`custom_alpha.py`](https://github.com/virattt/ai-hedge-fund/blob/main/custom_alpha.py)). Implement your analysis logic using the required signature:

```python
from src.graph.state import AgentState, show_agent_reasoning
from src.utils.llm import call_llm
from src.utils.progress import progress
from langchain_core.messages import HumanMessage
from pydantic import BaseModel, Field
from typing import Literal
import json

class CustomAlphaSignal(BaseModel):
    signal: Literal["bullish", "bearish", "neutral"]
    confidence: int = Field(description="Confidence 0-100")
    reasoning: str = Field(description="Brief justification")

def custom_alpha_agent(state: AgentState, agent_id: str = "custom_alpha_agent"):
    """Analyst that scores tickers based on EPS growth momentum."""
    data = state["data"]
    tickers = data["tickers"]
    
    results = {}
    
    for ticker in tickers:
        progress.update_status(agent_id, ticker, "Analyzing EPS growth")
        
        # Replace with actual data fetching logic

        eps_growth = 0.12  # Placeholder: 12% growth

        
        if eps_growth > 0.10:
            signal, confidence = "bullish", 80
        elif eps_growth < 0.02:
            signal, confidence = "bearish", 70
        else:
            signal, confidence = "neutral", 50
            
        reasoning = f"EPS growth of {eps_growth:.0%} indicates {'strong' if signal == 'bullish' else 'weak' if signal == 'bearish' else 'stable'} momentum"
        
        results[ticker] = {
            "signal": signal,
            "confidence": confidence,
            "reasoning": reasoning
        }
        
        progress.update_status(agent_id, ticker, "Done", analysis=json.dumps(results[ticker]))
    
    # Create message for LangGraph

    message = HumanMessage(content=json.dumps(results), name=agent_id)
    
    # Show reasoning if debug flag is set

    if state["metadata"].get("show_reasoning"):
        show_agent_reasoning(results, "Custom Alpha Analyst")
    
    # Store signals in shared state

    state["data"]["analyst_signals"][agent_id] = results
    progress.update_status(agent_id, None, "Done")
    
    return {"messages": [message], "data": state["data"]}

```

### Step 2: Register in ANALYST_CONFIG

Import your function and add a new entry to the dictionary in **src/utils/analysts.py**:

```python
from src.agents.custom_alpha import custom_alpha_agent

ANALYST_CONFIG = {
    # ... existing analysts (warren_buffett, valuation, etc.) ...

    
    "custom_alpha": {
        "display_name": "Custom Alpha Analyst",
        "description": "Rule-based analyst scoring tickers on EPS growth momentum",
        "investing_style": "Momentum / Growth",
        "agent_func": custom_alpha_agent,
        "type": "analyst",
        "order": 17,  # Controls UI ordering; use value after existing analysts

    },
}

```

The `order` field determines where your analyst appears in the default execution sequence and the React Flow editor. Use a unique integer higher than existing analysts to append to the end, or insert between existing values to position earlier.

### Step 3: Deploy and Connect via UI

Once you register the agent and restart the backend, the system automatically exposes your analyst in the flow editor:

1. **Restart the backend** – The graph builder in **app/backend/services/graph.py** reads the updated `ANALYST_CONFIG` on initialization.
2. **Open the flow editor** – Navigate to the React Flow interface in `app/frontend`.
3. **Drag your node** – The **Custom Alpha Analyst** appears in the sidebar using the `display_name` you configured.
4. **Wire connections** – Connect your analyst node to portfolio managers or other analysts as needed. The graph will route signals from your agent's `agent_id` to downstream consumers.
5. **Save the flow** – The backend persists the edge configuration and rebuilds the LangGraph workflow with your new node included.

## Key Files for Custom Analyst Development

| File | Purpose |
|------|---------|
| **src/utils/analysts.py** | Central registry containing `ANALYST_CONFIG` dictionary where all analysts are defined. |
| **src/agents/\*.py** | Individual agent implementations (e.g., [`warren_buffett.py`](https://github.com/virattt/ai-hedge-fund/blob/main/warren_buffett.py), [`valuation.py`](https://github.com/virattt/ai-hedge-fund/blob/main/valuation.py), and your new files). |
| **app/backend/services/graph.py** | Constructs the LangGraph workflow by iterating over `ANALYST_CONFIG` and creating nodes. |
| **app/backend/services/agent_service.py** | Provides `create_agent_function` wrapper that injects unique `agent_id` values into agent calls. |

These four locations are the only touchpoints required to create custom analyst agents. The rest of the system—including portfolio management, risk controls, and backtesting—automatically consumes signals from any analyst registered in the config.

## Summary

- **Register in `ANALYST_CONFIG`**: Add your agent's metadata and function pointer to **src/utils/analysts.py** to make it discoverable by the graph builder.
- **Implement the standard signature**: Your function must accept `(state: AgentState, agent_id: str)` and return `{"messages": [...], "data": state["data"]}` while writing signals to `state["data"]["analyst_signals"]`.
- **Use the wrapper**: The system automatically wraps your function via `create_agent_function` in **app/backend/services/agent_service.py** to inject unique IDs.
- **Deploy via UI**: After restarting the backend, drag your new analyst node from the sidebar in the React Flow editor and wire it to portfolio managers or other agents.

## Frequently Asked Questions

### What is the required function signature for a custom analyst agent?

Your agent function must implement the signature `(state: AgentState, agent_id: str) -> dict`. The `state` parameter contains the shared `AgentState` dictionary with `data` (including `tickers` and `analyst_signals`) and `metadata`. The `agent_id` is injected by the `create_agent_function` wrapper. You must return a dictionary with `messages` (containing LangChain message objects) and `data` (the updated state).

### How does the system handle agent execution order?

The `order` field in your `ANALYST_CONFIG` entry controls the default visual ordering in the React Flow editor and the sequence in which the graph builder adds start-node edges. However, the actual execution flow depends on the connections you draw in the front-end flow editor. You can wire your analyst to run in parallel with others or in sequence by connecting nodes accordingly.

### Can I connect custom analysts to existing portfolio managers?

Yes. Once registered, your custom analyst appears as a draggable node in the React Flow editor alongside built-in analysts like Warren Buffett or Valuation. You can connect your analyst node to any existing portfolio manager node (such as the default portfolio manager) using the visual editor. The portfolio manager will automatically read signals from your agent's `agent_id` key in the shared `analyst_signals` state dictionary.

### Do I need to restart the backend after adding a new analyst?

Yes, you must restart the backend service after modifying `ANALYST_CONFIG` in **src/utils/analysts.py** or adding new agent files. The graph builder in **app/backend/services/graph.py** reads the configuration dictionary during initialization to construct the LangGraph workflow. Once restarted, the new analyst appears in the flow editor immediately without requiring changes to the database or additional registration steps.