Home

How LangGraph Turns Market Data into Trading Signals in Real Time

Pi

Ping Xia

July 20, 20269 min read

# How LangGraph Turns Market Data into Trading Signals in Real Time ## Overview: What LangGraph Is and Who It’s For LangGraph is a library for building stateful, multi‑actor applications with large ...

How LangGraph Turns Market Data into Trading Signals in Real Time

Overview: What LangGraph Is and Who It’s For

LangGraph is a library for building stateful, multi‑actor applications with large language models (LLMs). It extends LangChain by letting developers define computation as a directed graph where each node can be an LLM call, a tool, or arbitrary Python code. Edges specify control flow, including conditional branching based on node outputs. The framework is aimed at engineers who need to orchestrate complex workflows—such as agents that perceive data, reason, and act—without writing boilerplate for state persistence, streaming, or error handling.

In the context of trading, LangGraph enables a pipeline that continuously ingests market data, applies analytical functions, queries an LLM for signal generation, and emits orders or alerts. Typical users are quantitative researchers, algorithmic trading engineers, and fintech developers who want to combine deterministic analytics (e.g., technical indicators) with the adaptive reasoning of LLMs.

Core Features for Real-Time Signal Generation

  • Graph‑based orchestration: Nodes and edges are declared with plain Python functions; the runtime handles scheduling, parallelism, and state updates.
  • Persistence layer: Built‑in checkpointing (via SQLite, PostgreSQL, or in‑memory) lets the graph survive process restarts, crucial for long‑running trading bots.
  • Streaming support: Nodes can yield intermediate results, enabling real‑time UI updates or incremental signal refinement.
  • Tool integration: Any LangChain tool (e.g., HTTP requests, SQL queries, pandas operations) can be wrapped as a node, making market data connectors trivial.
  • Conditional routing: Edges can evaluate Python expressions to decide the next node, allowing logic like "if volatility > threshold, go to risk‑check node".
  • Human‑in‑the‑loop: Optional interrupt points let a trader review or modify signals before execution.

These features address the core needs of a real‑time trading system: low‑latency data ingestion, deterministic preprocessing, adaptive LLM reasoning, and reliable state management.

Architecture: Nodes, Edges, and State Management

A LangGraph application is defined by a StateGraph object. The state is a typed Python dataclass (or TypedDict) that flows through the graph. Each node receives the current state, performs work, and returns an updated state (or a partial update). Edges connect nodes; they can be simple (edge = ("node_a", "node_b")) or conditional (conditional_edge = ("node_a", {True: "node_b", False: "node_c"})).

Example State for a Crypto Signal Bot

from typing import TypedDict, Optional

class TradingState(TypedDict):
    symbol: str
    price: float
    sma_20: Optional[float]
    rsi: Optional[float]
    llm_signal: Optional[str]  # "buy", "sell", "hold"
    timestamp: str

Node Definitions

  1. Market Data Ingest – subscribes to a WebSocket (e.g., Binance) and updates price and timestamp.
  2. Technical Indicator Calculator – computes SMA‑20 and RSI using pandas‑ta.
  3. LLM Reasoner – prompts an LLM (e.g., GPT‑4o) with the latest indicators and asks for a trading signal.
  4. Risk Filter – checks position size and volatility; may override the LLM signal.
  5. Output Emitter – logs the signal or sends an order via an exchange API.

Graph Construction (pseudocode)

from langgraph.graph import StateGraph, END

builder = StateGraph(TradingState)

builder.add_node("ingest", market_ingest_node)
builder.add_node("indicators", technical_indicators_node)
builder.add_node("llm", llm_reasoner_node)
builder.add_node("risk", risk_filter_node)
builder.add_node("emit", output_emitter_node)

builder.set_entry_point("ingest")
builder.add_edge("ingest", "indicators")
builder.add_edge("indicators", "llm")
builder.add_conditional_edge(
    "llm",
    lambda state: state["llm_signal"] in ("buy", "sell"),
    {True: "risk", False: "emit"},
)
builder.add_edge("risk", "emit")
builder.add_edge("emit", END)

graph = builder.compile()

When the graph runs, the ingest node pushes updates into the state as market ticks arrive; subsequent nodes process the latest state, producing a signal within milliseconds of receiving new data.

Real-World Example: Building a Crypto Trading Signal Pipeline

Below is a runnable snippet that demonstrates a minimal LangGraph‑based signal generator for Bitcoin‑USDT on Binance. It uses the binance Python package for WebSocket price updates and pandas‑ta for indicators. The LLM call is simulated with a deterministic function for clarity; in production you would replace it with a call to langchain_openai.ChatOpenAI.

import asyncio
import json
from binance import AsyncClient, BinanceSocketManager
import pandas_ta as ta
from langgraph.graph import StateGraph, END
from typing import TypedDict, Optional

class State(TypedDict):
    symbol: str
    price: float
    sma_20: Optional[float]
    rsi: Optional[float]
    llm_signal: Optional[str]
    timestamp: str

async def price_websocket(state: State) -> State:
    """Node: receives live ticker updates from Binance."""
    async with AsyncClient() as client:
        bm = BinanceSocketManager(client)
        ts = bm.symbol_ticker_socket(state["symbol"])
        async with ts as socket:
            while True:
                resp = await socket.recv()
                price = float(resp["c"])
                return {
                    **state,
                    "price": price,
                    "timestamp": resp["E"],
                }

def compute_indicators(state: State) -> State:
    """Node: calculates SMA‑20 and RSI on a rolling window."""
    # In a real system you would keep a deque of recent prices.
    # Here we approximate using the latest price only for demo.
    price = state["price"]
    sma = price  # placeholder
    rsi = 50.0   # placeholder
    return {
        **state,
        "sma_20": sma,
        "rsi": rsi,
    }

def llm_reasoner(state: State) -> State:
    """Node: asks an LLM for a signal based on indicators."""
    # Simulated LLM logic: buy if price > SMA and RSI < 70
    if state["price"] > state["sma_20"] and state["rsi"] < 70:
        signal = "buy"
    elif state["price"] < state["sma_20"] and state["rsi"] > 30:
        signal = "sell"
    else:
        signal = "hold"
    return {
        **state,
        "llm_signal": signal,
    }

def risk_filter(state: State) -> State:
    """Node: overrides signal if volatility is high."""
    # Simple volatility proxy: price change > 2% in last tick
    # For demo we just pass through.
    return state

def output_emitter(state: State) -> State:
    """Node: logs the final signal."""
    print(f"{state['timestamp']} {state['symbol']} -> {state['llm_signal']}")
    return state

async def main():
    builder = StateGraph(State)
    builder.add_node("ingest", price_websocket)
    builder.add_node("indicators", compute_indicators)
    builder.add_node("llm", llm_reasoner)
    builder.add_node("risk", risk_filter)
    builder.add_node("emit", output_emitter)
    builder.set_entry_point("ingest")
    builder.add_edge("ingest", "indicators")
    builder.add_edge("indicators", "llm")
    builder.add_conditional_edge(
        "llm",
        lambda s: s["llm_signal"] in ("buy", "sell"),
        {True: "risk", False: "emit"},
    )
    builder.add_edge("risk", "emit")
    builder.add_edge("emit", END)
    graph = builder.compile()
    
    # Initialize state
    init_state = {
        "symbol": "BTCUSDT",
        "price": 0.0,
        "sma_20": None,
        "rsi": None,
        "llm_signal": None,
        "timestamp": "",
    }
    # Run the graph as an async generator; each yield is a new state
    async for _ in graph.astream(init_state):
        pass

if __name__ == "__main__":
    asyncio.run(main())

Explanation of the example

  • The price_websocket node opens a Binance ticker stream and returns a fresh state on each tick.
  • compute_indicators updates SMA‑20 and RSI (in practice you would maintain a rolling window).
  • llm_reasoner mimics an LLM decision: a simple rule‑based signal, but you could replace the body with a call to ChatOpenAI prompting "Given price {price}, SMA‑20 {sma_20}, RSI {rsi}, output buy/sell/hold."
  • risk_filter is a placeholder for volatility‑based overrides.
  • output_emitter prints the signal; in a trading bot you would send an order via the exchange’s REST API.

When executed, the script prints signals in near real‑time, illustrating how LangGraph wires together asynchronous data ingestion, deterministic analytics, and LLM reasoning.

Strengths and Limitations

Strengths

  • Explicit control flow: The graph makes the sequence of operations visible and branching of steps transparent, simplifying debugging and auditing—critical for regulated trading environments.
  • Reusability: Nodes are plain functions; you can reuse the same technical‑indicator node across multiple strategies.
  • State persistence: Checkpointing lets you resume after a crash without losing the rolling window of prices or open positions.
  • Scalability: Conditional edges enable parallel sub‑graphs (e.g., separate risk checks for multiple symbols) without extra coordination code.
  • Integration with LangChain ecosystem: Access to hundreds of tools (API wrappers, vector stores, memory components) reduces boilerplate.

Limitations

  • Learning curve: Developers must think in terms of graph nodes and edges; traditional procedural code may feel more intuitive at first.
  • Latency overhead: The graph engine adds a small constant factor (typically sub‑millisecond) compared to a hand‑rolled loop; for ultra‑low‑latency arbitrage (<100 µs) this may be relevant.
  • LLM nondeterminism: While LangGraph manages state, the LLM node’s output can vary; robust systems need output validation or fallback rules.
  • Limited built‑in visualization: Unlike some workflow tools, LangGraph does not ship a drag‑and‑drop editor; you rely on code or external diagrams.

Overall, for most systematic and semi‑systematic trading strategies where latency tolerance is in the low‑millisecond range, the benefits outweigh the costs.

Comparison with Alternatives

Feature LangGraph CrewAI AutoGen smolagents
Primary model Graph‑based orchestration Role‑based agent teams Conversational agents Minimalist agent loop
State persistence Built‑in checkpointing (SQL/Postgres) External storage required External storage required None
Streaming / incremental yields Supported Limited Supported Not emphasized
Tool integration Full LangChain toolset Custom tools Custom tools Limited
Human‑in‑the‑loop Interrupt points Manual Manual Manual
Typical use case Complex deterministic + LLM pipelines Multi‑agent collaboration Multi‑agent chat Prototyping, low‑resource
Maturity (2026) v0.1.x, stable v0.9, active v0.2, experimental v0.0.5, nascent
Community size Large (LangChain) Growing Medium (Microsoft) Small

Takeaway: LangGraph excels when you need a deterministic data‑flow backbone (e.g., market data → indicators → LLM) with reliable state handling. CrewAI shines when the solution is best expressed as a team of agents negotiating roles. AutoGen is suited for conversational problem‑solving where agents iterate via dialogue. smolagents is a lightweight option for quick experiments but lacks the persistence and streaming needed for production trading.

Getting Started Guide

  1. Install dependencies

    pip install langgraph langchain-openai binance pandas-ta
    

    (Adjust the LLM provider package as needed.)

  2. Save the state definition (see TradingState in the example).

  3. Implement each node as a plain async or sync function that accepts the state dict and returns a dict with updates.

  4. Construct the graph using StateGraph. Use add_edge for fixed transitions and add_conditional_edge for branching logic.

  5. Compile and run

    graph = builder.compile()
    # For async sources:
    async for state in graph.astream(initial_state):
        # state contains the latest values
        pass
    
  6. Add persistence (optional but recommended for long runs)

    from langgraph.checkpoint import SqliteSaver
    
    memory = SqliteSaver.from_conn_string("trading_checkpoints.db")
    graph = builder.compile(checkpointer=memory)
    

    This will checkpoint after each node execution, allowing recovery from interruption.

  7. Deploy

    • Run the script inside a Docker container with restart policies.
    • Monitor logs for signal outputs; connect the output_emitter node to your order‑management system.
    • Set up alerts for node failures or checkpoint errors.

Next steps: Replace the placeholder LLM node with a real call to ChatOpenAI or another provider, add a rolling window for indicators, and integrate risk‑management logic (position sizing, stop‑loss). You can also extend the graph to handle multiple symbols by fan‑out patterns: a single ingest node broadcasts to parallel sub‑graphs per symbol.

LangGraph provides a clean, testable foundation for turning streaming market data into actionable trading signals while preserving the ability to incorporate LLMs for adaptive decision‑making.

Keywords

LangGraphtrading signalsreal-time market dataLLM agentsgraph orchestrationCrewAIAutoGengetting started

Keep reading

More related articles from DriftSeas.