Setting Up the Five-Tier Rating Scale for Trade Decisions in TradingAgents

TradingAgents converts free-form LLM analyst reports into standardized trade directives using a five-tier rating scale consisting of BUY, OVERWEIGHT, HOLD, UNDERWEIGHT, and SELL.

The TradingAgents framework by TauricResearch leverages multi-agent LLM workflows to generate investment strategies, but raw natural language outputs require normalization into machine-readable actions. Implementing the five-tier rating scale for trade decisions ensures that verbose analyst narratives distill into consistent, actionable portfolio signals that downstream risk and portfolio management nodes can process programmatically.

Core Components of the Rating Workflow

The architecture centralizes rating extraction in three coordinated components:

Component Role Key Source
SignalProcessor Wraps the quick-thinking LLM to distill reports into single rating words tradingagents/graph/signal_processing.py (lines 13–28)
TradingAgentsGraph.process_signal Facade that forwards decisions to the processor and returns the rating tradingagents/graph/trading_graph.py (lines 90–93)
Trader node Generates natural-language proposals ending with FINAL TRANSACTION PROPOSAL tradingagents/agents/trader/trader.py (lines 33–41)

SignalProcessor

The SignalProcessor class in tradingagents/graph/signal_processing.py encapsulates the lightweight LLM responsible for parsing verbose analyst output. Lines 25–27 contain the critical system prompt that restricts the model to the five allowed ratings, ensuring the returned content attribute contains only BUY, OVERWEIGHT, HOLD, UNDERWEIGHT, or SELL.

TradingAgentsGraph.process_signal

Acting as the orchestration layer, the process_signal method in tradingagents/graph/trading_graph.py (lines 90–93) receives the raw final_trade_decision string and delegates extraction to the SignalProcessor. This method returns the canonical rating string that the graph state propagates to downstream agents.

Trader Node

The Trader agent produces the initial narrative containing the investment rationale. According to the source in tradingagents/agents/trader/trader.py (lines 33–41), this agent concludes its analysis with a FINAL TRANSACTION PROPOSAL section (e.g., BUY, HOLD, or SELL) stored in the graph state field final_trade_decision.

How the Five-Tier Rating Scale Is Defined

Unlike traditional enum-based systems, TradingAgents enforces the rating scale through prompt engineering rather than code constants. The SignalProcessor constructs a message list that explicitly enumerates the valid options:

messages = [
    (
        "system",
        "You are an efficient assistant that extracts the trading decision from analyst reports. "
        "Extract the rating as exactly one of: BUY, OVERWEIGHT, HOLD, UNDERWEIGHT, SELL. "
        "Output only the single rating word, nothing else.",
    ),
    ("human", full_signal),
]

Note: This prompt appears in tradingagents/graph/signal_processing.py at lines 25–27.

The quick-thinking LLM (self.quick_thinking_llm.invoke(messages).content) must select exactly one word from this closed set, effectively creating a constrained output space without requiring post-processing validation logic.

Where Ratings Are Consumed Downstream

Once extracted, the rating populates the final_trade_decision field defined in tradingagents/agents/utils/agent_states.py. This standardized value drives decision-making in two critical downstream nodes:

  • Risk Analyst: Queries final_trade_decision to determine whether the portfolio requires defensive positioning (e.g., UNDERWEIGHT) or can tolerate aggressive exposure (e.g., OVERWEIGHT).
  • Portfolio Manager: Uses the rating to calculate target position sizes and rebalance allocations according to the five-tier spectrum.

Practical Implementation Examples

Running the Graph and Retrieving a Rating

Execute the complete workflow to obtain a standardized rating for a specific ticker:

from tradingagents.graph.trading_graph import TradingAgentsGraph

# Initialize the graph (loads quick-thinking LLM from default_config.py)

graph = TradingAgentsGraph(debug=False)

# Execute for AAPL on specific date

final_state, rating = graph.propagate(company_name="AAPL", trade_date="2024-09-30")

print("Full decision text:", final_state["final_trade_decision"])
print("Extracted five-tier rating:", rating)

Example output:


Full decision text: FINAL TRANSACTION PROPOSAL: **OVERWEIGHT**
Extracted five-tier rating: OVERWEIGHT

Direct SignalProcessor Usage

For unit testing or ad-hoc analysis, invoke the processor directly without running the full graph:

from tradingagents.graph.signal_processing import SignalProcessor
from langchain_openai import ChatOpenAI

# Initialize the quick-thinking LLM (configured in default_config.py)

quick_llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
processor = SignalProcessor(quick_thinking_llm=quick_llm)

raw_report = """
Our technical analysis shows strong momentum, earnings beat, and rising volume.
We recommend increasing exposure to the stock.
"""

rating = processor.process_signal(raw_report)
print(rating)  # Output: BUY or OVERWEIGHT

Mapping Ratings to Portfolio Weights

Convert the categorical rating into numerical allocation targets:

def decide_position(rating: str) -> float:
    """Map the five-tier rating to a target portfolio weight."""
    mapping = {
        "BUY": 1.0,
        "OVERWEIGHT": 0.75,
        "HOLD": 0.5,
        "UNDERWEIGHT": 0.25,
        "SELL": 0.0,
    }
    return mapping.get(rating.upper(), 0.5)  # Default to HOLD weight

target_weight = decide_position(rating)
print(f"Target portfolio weight: {target_weight:.2f}")

Summary

  • TradingAgents uses a five-tier rating scale (BUY, OVERWEIGHT, HOLD, UNDERWEIGHT, SELL) to normalize LLM-generated analyst reports into machine-readable trade decisions.
  • The SignalProcessor in signal_processing.py enforces this scale through a constrained system prompt sent to the quick-thinking LLM.
  • TradingAgentsGraph.process_signal orchestrates the extraction, while the Trader node generates the raw narrative that gets rated.
  • Downstream Risk Analyst and Portfolio Manager nodes consume the standardized final_trade_decision field to adjust portfolio allocations and risk exposure.

Frequently Asked Questions

What are the exact five ratings used in TradingAgents?

The five-tier rating scale for trade decisions consists of BUY, OVERWEIGHT, HOLD, UNDERWEIGHT, and SELL. These words represent a spectrum from aggressive accumulation to full liquidation, allowing granular portfolio adjustments between neutral (HOLD) and directional bets.

Where is the five-tier rating scale defined in the codebase?

The scale is defined exclusively in the system prompt within tradingagents/graph/signal_processing.py (lines 25–27). Unlike hardcoded enums, the valid ratings exist as a comma-separated list inside the prompt string sent to the LLM, which constrains the model's output to these specific words.

Can I customize the rating scale to use different terms or fewer tiers?

Yes, but doing so requires modifying the system prompt in signal_processing.py and updating any downstream mapping logic (such as the decide_position function example). You must ensure that the Risk Analyst and Portfolio Manager nodes in tradingagents/agents/ can interpret your new rating vocabulary, as they reference final_trade_decision to determine portfolio tilts.

Which LLM configuration handles the rating extraction?

The SignalProcessor utilizes the quick-thinking LLM configuration defined in tradingagents/default_config.py. This is typically a lightweight model like gpt-4o-mini optimized for fast classification tasks, distinct from the heavier reasoning models used by the Researcher or Risk Analyst agents.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →