AI & Automations6 min read

Building a Multi‑Agent LLM Framework for Automated Financial Trading

Financial trading systems must reconcile low‑latency market data with sophisticated decision logic, a challenge that grows when you add large language models. I show how a modular multi‑agent architecture—leveraging the open‑source TradingAgents repo—turns LLM‑driven strategies into production‑grade trade execution.

PK
Piyush Kalsariya
Sep 8, 2026·Full-Stack & AI Engineer
Building a Multi‑Agent LLM Framework for Automated Financial Trading

Introduction

Automated trading has traditionally been the domain of rule‑based engines written in C++ or Java. The rise of large language models (LLMs) opens a new frontier: agents that can interpret news, earnings calls, and macro‑economic narratives, then translate that insight into actionable orders. The difficulty lies in wiring these high‑latency, probabilistic models into a low‑latency, fault‑tolerant trading stack.

In this post I walk through a multi‑agent LLM trading framework built on the open‑source TradingAgents reference implementation. I’ll expose the architectural layers, show concrete TypeScript/Node and Python snippets, and discuss production trade‑offs you need to know before moving from a research notebook to a live market feed.

---

Architecture Overview

At a high level the system consists of four concentric layers (see the mermaid diagram below):

``mermaid
1flowchart TD
2    subgraph A[Execution Engine (Node.js)]
3        direction LR
4        orderRouter[Order Router]
5        brokerAdapter[Broker Adapter]
6    end
7    subgraph B[Orchestration Layer (Python)]
8        agentMgr[Agent Manager]
9        scheduler[Task Scheduler]
10    end
11    subgraph C[Strategy Agents (Python/TS)]
12        newsAgent[News‑LLM Agent]
13        sentimentAgent[Sentiment Agent]
14        riskAgent[Risk‑Constraint Agent]
15    end
16    subgraph D[Market Data Ingestion (Go/TS)]
17        marketFeed[WebSocket Feed]
18        cache[Redis Cache]
19    end
20    marketFeed --> cache --> agentMgr --> newsAgent --> sentimentAgent --> riskAgent --> scheduler --> orderRouter --> brokerAdapter
  • Market Data Ingestion – Real‑time price ticks, order‑book depth, and news streams are normalized into a Redis‑backed event bus.
  • Strategy Agents – Each LLM‑powered micro‑service (e.g., a news summarizer) runs in an isolated Docker container and publishes intent messages.
  • Orchestration Layer – A Python ``AgentManager (based on asyncio + FastAPI) validates intents, resolves conflicts, and schedules execution.
  • Execution Engine – A Node.js/Next.js 15 serverless API receives vetted orders, routes them through a broker‑specific adapter, and persists audit logs.

---

Agent Orchestration Layer

The orchestration layer is the brain that keeps the system deterministic despite the stochastic nature of LLMs. It performs three critical functions:

  1. Intent Normalization – Convert free‑form LLM output into a typed TradeIntent schema.
  2. Conflict Resolution – If two agents suggest opposite positions, the RiskAgent arbitrates based on exposure limits.
  3. Scheduling – Use a priority queue to enforce latency SLAs (e.g., sub‑100 ms for high‑frequency signals).

Sample TradeIntent TypeScript definition

``typescript
1export interface TradeIntent {
2  /** Unique identifier for the originating agent */
3  agentId: string;
4  /** Symbol ticker, e.g. "AAPL" */
5  symbol: string;
6  /** Desired side: "buy" | "sell" */
7  side: 'buy' | 'sell';
8  /** Target quantity in shares */
9  quantity: number;
10  /** Confidence score from the LLM (0‑1) */
11  confidence: number;
12  /** Optional price limit; undefined for market orders */
13  limitPrice?: number;
14  /** Timestamp of generation (ISO) */
15  generatedAt: string;
16}

The Python side validates this schema with ``pydantic before publishing to the Redis stream.

---

Market Data Ingestion

Low latency is non‑negotiable. We use a Go micro‑service to maintain a persistent WebSocket connection to the exchange, decode binary FIX messages, and write a compact JSON payload to Redis Streams.

``bash
1# Run the market‑feed service
2docker run -d \
3  -e EXCHANGE_WS=wss://feed.example.com \
4  -p 8080:8080 \
5  ghcr.io/tauricresearch/trading‑agents/market‑feed:latest

A lightweight TypeScript client reads from the stream and pushes the data into a Next.js API route for real‑time UI dashboards.

---

Strategy Agents

Each strategy lives in its own container. The reference repo ships three agents:

| Agent | LLM Prompt | Output | Role |

|-------|------------|--------|------|

| ``NewsAgent | Summarize the latest earnings call for {{symbol}} and suggest a directional bias. | {"bias":"bullish","confidence":0.78} | Market sentiment extraction |

| SentimentAgent | Analyze Twitter sentiment for {{symbol}} over the last 5 minutes. | {"bias":"neutral","confidence":0.45} | Social‑media signal |

| RiskAgent | Given current portfolio exposure, compute a safe position size for {{symbol}}. | {"maxQty":120} | Risk cap |

Python stub for an LLM‑driven agent

``python
1import os, json, httpx
2from pydantic import BaseModel
3
4class LLMResponse(BaseModel):
5    bias: str
6    confidence: float
7
8async def run_agent(symbol: str) -> dict:
9    prompt = f"Summarize the latest earnings call for {symbol} and output a JSON with bias and confidence."
10    resp = await httpx.post(
11        os.getenv('OPENAI_ENDPOINT'),
12        json={"model": "gpt-4o-mini", "messages": [{"role": "user", "content": prompt}]},
13        headers={"Authorization": f"Bearer {os.getenv('OPENAI_KEY')}"},
14        timeout=5.0,
15    )
16    data = LLMResponse.parse_raw(resp.text)
17    return {"agentId": "news", "symbol": symbol, **data.dict()}

The agent publishes its JSON to the Redis stream ``agent:intents.

---

Execution Engine (Node.js)

The final step is turning a vetted TradeIntent into a broker‑specific order. We expose a Next.js API route (/api/trade) that receives the intent, signs the request with HMAC, and forwards it to the broker's REST endpoint.

``tsx
1// pages/api/trade.ts
2import type { NextApiRequest, NextApiResponse } from 'next';
3import { TradeIntent } from '@/types/trade';
4import crypto from 'crypto';
5import axios from 'axios';
6
7export default async function handler(req: NextApiRequest, res: NextApiResponse) {
8  const intent: TradeIntent = req.body;
9  // Simple validation
10  if (!intent.symbol || !intent.quantity) return res.status(400).json({ error: 'Invalid intent' });
11
12  const payload = {
13    symbol: intent.symbol,
14    side: intent.side,
15    qty: intent.quantity,
16    price: intent.limitPrice,
17    timestamp: Date.now(),
18  };
19
20  const signature = crypto
21    .createHmac('sha256', process.env.BROKER_SECRET!)
22    .update(JSON.stringify(payload))
23    .digest('hex');
24
25  try {
26    const brokerResp = await axios.post(process.env.BROKER_URL!, payload, {
27      headers: { 'X-Signature': signature },
28    });
29    res.status(200).json({ orderId: brokerResp.data.id });
30  } catch (e) {
31    console.error('Broker error', e);
32    res.status(502).json({ error: 'Broker unavailable' });
33  }
34}

All order events are persisted to a PostgreSQL audit table for compliance and later back‑testing.

---

Pros & Cons

| ✅ Pros | ❌ Cons |

|--------|--------|

| Modular – Each LLM agent can be swapped without touching the core pipeline. | Latency overhead – LLM inference (even with quantized models) adds 50‑150 ms, which may be too slow for ultra‑high‑frequency strategies. |

| Explainability – Agents return structured JSON with confidence scores, easing downstream risk checks. | Model drift – Prompt engineering must be revisited as market language evolves. |

| Scalable – Stateless containers let you horizontally scale agents based on CPU/GPU availability. | Operational complexity – Multiple runtimes (Python, Node, Go) increase DevOps surface area. |

| Rapid prototyping – New data sources (e.g., ESG reports) become a new agent in minutes. | Security surface – Exposing LLM APIs requires strict IAM and rate‑limiting to avoid denial‑of‑service. |

---

Production Takeaways

  1. Separate latency budgets – Keep the LLM path on a soft latency tier (e.g., 200 ms) and let the execution engine run on a hard tier (<50 ms). Use a fallback rule‑based agent when the LLM times out.
  2. Circuit‑breaker pattern – Wrap each agent call in a ``try/except (Python) or catch (TS) and emit a neutral intent if the service fails.
  3. Observability – Instrument every stage with OpenTelemetry traces; correlate the LLM response latency with order fill quality.
  4. Compliance logging – Store raw LLM prompts, responses, and the final TradeIntent in immutable storage (e.g., AWS S3 with Object Lock).
  5. Model versioning – Tag each container image with the exact LLM checkpoint hash; this makes back‑testing reproducible.

---

Conclusion

A multi‑agent LLM architecture bridges the gap between language‑centric insight generation and the deterministic world of automated trading. By decoupling market data ingestion, strategy agents, orchestration, and execution, you gain flexibility without sacrificing the low‑latency guarantees required in production markets. The open‑source TradingAgents repo provides a solid scaffolding; the patterns described here—typed intents, Redis‑backed event streams, and a Next.js execution layer—turn that scaffold into a battle‑tested, observable trading platform.

Happy coding, and may your signals be sharp!

Tags:#LLM#Financial Trading#Multi-Agent Systems#Full-Stack Architecture
PK

Written by Piyush Kalsariya

Full-stack software engineer and AI automation builder specializing in Next.js, Node.js, Python, Sanity CMS, and production LLM orchestration.