# How to Troubleshoot Agent Execution and Streaming Responses in AI Hedge Fund

> Troubleshoot AI Hedge Fund agent execution and streaming responses. Fix agent dictionaries, ensure event handler registration, and inspect FastAPI streaming logic for seamless operation.

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

---

**To troubleshoot agent execution and streaming responses in the AI Hedge Fund project, verify that agents return properly structured dictionaries with valid action strings and numeric quantities, ensure the `AgentProgress` handler is registered in the event generator to propagate updates through the asyncio queue, and inspect the FastAPI streaming logic for proper task lifecycle management and client-disconnect handling.**

The virattt/ai-hedge-fund repository implements a real-time backtesting engine that streams agent decisions via Server-Sent Events (SSE). When agents execute silently or streams hang without emitting data, the issue typically lies in the normalization pipeline, the progress tracking system, or the async generator lifecycle. Understanding the data flow from agent output to SSE client is essential for effective debugging.

## Understanding the Streaming Architecture

The application routes all streaming data through a specific pipeline:

`Agent → AgentProgress.update_status → progress.register_handler → asyncio.Queue → FastAPI StreamingResponse → SSE client`

If any link in this chain fails, the client sees a stalled or empty stream, and the back-test may silently skip days. The major components include:

- **FastAPI endpoints** (`/hedge-fund/run` and `/hedge-fund/backtest`) in [`app/backend/routes/hedge_fund.py`](https://github.com/virattt/ai-hedge-fund/blob/main/app/backend/routes/hedge_fund.py) that produce SSE streams and handle client disconnections
- **Progress tracker** ([`src/utils/progress.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/utils/progress.py)) that collects status updates via the global `AgentProgress` instance
- **Agent controller** ([`src/backtesting/controller.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/backtesting/controller.py)) that normalizes raw agent output into clean `decisions` maps
- **Trade executor** ([`src/backtesting/trader.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/backtesting/trader.py)) that converts normalized decisions into portfolio changes
- **Back-test engine** ([`src/backtesting/engine.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/backtesting/engine.py)) that drives the daily loop and pushes progress events

## Troubleshooting Agent Execution

When an agent appears to run but produces no trades or decisions, trace through these validation layers:

### Validate the Agent Signature

The agent must be a callable that accepts the exact parameters defined in `AgentController.run_agent`: `tickers`, `start_date`, `end_date`, `portfolio`, `model_name`, `model_provider`, and `selected_analysts`. Missing arguments or mismatched return types raise exceptions before any progress events are emitted.

### Check the Normalization Step

In [`src/backtesting/controller.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/backtesting/controller.py), the `run_agent` method coerces the `action` field to the `Action` enum and `quantity` to `float`. If an agent returns a non-dict or uses unexpected keys, the controller falls back to `"hold"` and `0`. Inspect the loop that builds `normalized_decisions` (lines 44-58)—exceptions here are silently swallowed, so enable logging inside the `except` blocks to see why a decision was ignored.

### Inspect Trade Execution

The `execute_trade` method in [`src/backtesting/trader.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/backtesting/trader.py) applies additional filters:

- Quantities less than or equal to 0 are immediately ignored (line 18)
- Action strings are converted to the `Action` enum (lines 22-25)
- If enum conversion fails, the trade defaults to **hold**

### Common Failure Patterns

- **Invalid action name**: Using `"Buy"` instead of `"buy"` causes the controller to fall back to `HOLD`
- **Quantity as string**: While the controller casts to `float`, a non-numeric string results in `0.0`
- **Missing price data**: The back-test engine skips days when data-fetch helpers return empty DataFrames (lines 27-31 in [`engine.py`](https://github.com/virattt/ai-hedge-fund/blob/main/engine.py))

Add temporary logging inside `run_agent` and `execute_trade` to surface raw agent output before normalization occurs.

## Debugging Streaming Responses

When the endpoint returns 200 OK but the stream contains only the initial `StartEvent` or hangs indefinitely, investigate these specific areas:

### Confirm SSE Endpoint Registration

Verify that [`app/main.py`](https://github.com/virattt/ai-hedge-fund/blob/main/app/main.py) properly imports and includes the router containing `/hedge-fund/run` and `/hedge-fund/backtest`. A 404 error indicates the FastAPI app isn't registering the routes from [`app/backend/routes/hedge_fund.py`](https://github.com/virattt/ai-hedge-fund/blob/main/app/backend/routes/hedge_fund.py).

### Event Generator Lifecycle

The async generator in [`hedge_fund.py`](https://github.com/virattt/ai-hedge-fund/blob/main/hedge_fund.py) starts by registering a progress handler via `progress.register_handler` and launching the core task (`run_graph_async` or `BacktestService.run_backtest_async`). If the handler registration at line 75 fails to execute, no events reach the queue, resulting in a silent stream that returns only headers.

### Progress Queue Handling

The generator awaits `progress_queue.get()` with a 1-second timeout (lines 12-15). If `AgentProgress.update_status` is never called for an agent, the queue blocks until timeout. Verify that `update_status` forwards updates to all registered handlers (line 61 in [`src/utils/progress.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/utils/progress.py)).

### Client Disconnect Detection

The `wait_for_disconnect` task polls `await request.receive()` and cancels the background task if it detects `"http.disconnect"` (lines 53-58). If the client disconnects early, the task cancellation triggers the `finally` block (lines 42-53), which removes the handler and cancels the background task again. Stray exceptions escaping this block can terminate the response prematurely without sending the final `CompleteEvent`.

### SSE Formatting Validation

Events are serialized via `.to_sse()`. If this method in [`app/backend/models/events.py`](https://github.com/virattt/ai-hedge-fund/blob/main/app/backend/models/events.py) produces malformed SSE (missing the `\n\n` delimiter), browsers will never fire the `message` event. Ensure the formatting follows the SSE specification precisely.

## Practical Debugging Examples

Use these snippets to isolate issues in your agent execution and streaming pipeline.

### Minimal Agent Implementation

Create a simple agent that always buys 10 shares to verify the normalization path:

```python
def simple_buy_agent(
    tickers, start_date, end_date, portfolio, model_name, model_provider, selected_analysts
):
    # Return a dict matching the expected schema exactly

    decisions = {t: {"action": "buy", "quantity": 10} for t in tickers}
    return {"decisions": decisions}

```

Test it directly through the controller:

```python
from src.backtesting.controller import AgentController

controller = AgentController()
output = controller.run_agent(
    simple_buy_agent,
    tickers=["AAPL", "MSFT"],
    start_date="2023-01-01",
    end_date="2023-01-31",
    portfolio=portfolio,                # a Portfolio instance

    model_name="gpt-4o",
    model_provider="openai",
    selected_analysts=None,
)
print(output)   # → {"decisions": {"AAPL": {"action": "buy", "quantity": 10}, ...}}

```

### Custom Progress Handler for Debugging

Register a temporary handler to surface all status updates during execution:

```python
from src.utils.progress import progress

def debug_handler(agent, ticker, status, analysis, timestamp):
    print(f"[{timestamp}] {agent}:{ticker or ''} → {status}")

# Register the handler (remains active until unregistered)

progress.register_handler(debug_handler)

# Run the back-test; all updates will print to stdout

engine = BacktestEngine(...)
engine.run_backtest()

```

### JavaScript SSE Client Implementation

Consume the stream in the browser to verify client-side behavior:

```javascript
const evtSource = new EventSource("/hedge-fund/run");
evtSource.onmessage = (e) => {
  const data = JSON.parse(e.data);
  console.log("Event:", data);
};

evtSource.onerror = (e) => {
  console.error("Stream error", e);
  evtSource.close();
};

```

If you receive only the initial `StartEvent`, check that the Python side calls `progress.update_status` at each agent milestone.

## Summary

- **Agent validation**: Verify agents return dictionaries with lowercase action strings (`"buy"`, `"sell"`, `"hold"`) and numeric quantities; the controller in [`src/backtesting/controller.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/backtesting/controller.py) silently neutralizes invalid outputs to `"hold"`/`0`
- **Progress pipeline**: Ensure `progress.register_handler` executes before the back-test begins, and confirm `AgentProgress.update_status` broadcasts updates to the asyncio queue in [`src/utils/progress.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/utils/progress.py)
- **Stream lifecycle**: Inspect the async generator in [`app/backend/routes/hedge_fund.py`](https://github.com/virattt/ai-hedge-fund/blob/main/app/backend/routes/hedge_fund.py) for proper task cancellation, client-disconnect detection via `request.receive()`, and the guaranteed emission of `CompleteEvent` before return
- **Trade execution**: Remember that `execute_trade` in [`src/backtesting/trader.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/backtesting/trader.py) ignores quantities ≤ 0 and requires valid `Action` enum conversion

## Frequently Asked Questions

### Why is my agent returning decisions but trades aren't executing?

Check the normalization logic in [`src/backtesting/controller.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/backtesting/controller.py) lines 44-58. If your agent returns `"Buy"` instead of `"buy"`, the `Action` enum conversion fails and defaults to `"hold"`. Additionally, verify in [`src/backtesting/trader.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/backtesting/trader.py) line 18 that your quantity is a positive number—quantities ≤ 0 are filtered out before execution.

### Why does the SSE stream stop after the StartEvent?

This indicates the asyncio queue is not receiving data. Verify that `progress.update_status` is being called for each agent iteration in [`src/utils/progress.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/utils/progress.py) line 61. If running custom agents, ensure they emit progress events or manually call the update method. Also confirm the handler registration at line 75 of [`app/backend/routes/hedge_fund.py`](https://github.com/virattt/ai-hedge-fund/blob/main/app/backend/routes/hedge_fund.py) executes without error.

### How do I detect when a client disconnects from the stream?

The `wait_for_disconnect` coroutine in [`app/backend/routes/hedge_fund.py`](https://github.com/virattt/ai-hedge-fund/blob/main/app/backend/routes/hedge_fund.py) (lines 53-58) polls `request.receive()` for the `"http.disconnect"` message. When detected, it triggers task cancellation. Log inside this block to track disconnections, and ensure your `finally` block (lines 42-53) properly cleans up handlers without raising secondary exceptions.

### What happens if an agent returns an invalid action string?

According to [`src/backtesting/controller.py`](https://github.com/virattt/ai-hedge-fund/blob/main/src/backtesting/controller.py), the controller attempts to coerce the action to the `Action` enum. If coercion fails (e.g., `"purchase"` or `"BUY"`), the decision silently falls back to `"hold"` with zero quantity. Always use exact lowercase strings matching the enum values defined in the codebase.