feat: add trade planner for stop loss and take profit

This commit is contained in:
kevin-bruton 2025-09-28 19:49:04 +02:00
parent 356a563813
commit 1a23037f9f
17 changed files with 235 additions and 21 deletions

View File

@ -49,7 +49,7 @@
## TradingAgents Framework
TradingAgents is a multi-agent trading framework that mirrors the dynamics of real-world trading firms. By deploying specialized LLM-powered agents: from fundamental analysts, sentiment experts, and technical analysts, to trader, risk management team, the platform collaboratively evaluates market conditions and informs trading decisions. Moreover, these agents engage in dynamic discussions to pinpoint the optimal strategy.
TradingAgents is a multi-agent trading framework that mirrors the dynamics of real-world trading firms. By deploying specialized LLM-powered agents: from fundamental analysts, sentiment experts, and trade planners, to trader, risk management team, the platform collaboratively evaluates market conditions and informs trading decisions. Moreover, these agents engage in dynamic discussions to pinpoint the optimal strategy.
<p align="center">
<img src="assets/schema.png" style="width: 100%; height: auto;">
@ -63,7 +63,7 @@ Our framework decomposes complex trading tasks into specialized roles. This ensu
- Fundamentals Analyst: Evaluates company financials and performance metrics, identifying intrinsic values and potential red flags.
- Sentiment Analyst: Analyzes social media and public sentiment using sentiment scoring algorithms to gauge short-term market mood.
- News Analyst: Monitors global news and macroeconomic indicators, interpreting the impact of events on market conditions.
- Technical Analyst: Utilizes technical indicators (like MACD and RSI) to detect trading patterns and forecast price movements.
- Trade Planner: Utilizes technical indicators (like MACD and RSI) to detect trading patterns and forecast price movements.
<p align="center">
<img src="assets/analyst.png" width="100%" style="display: inline-block; margin: 0 2%;">
@ -101,15 +101,19 @@ git clone https://github.com/TauricResearch/TradingAgents.git
cd TradingAgents
```
Create a virtual environment in any of your favorite environment managers:
Create a virtual environment in any of your favorite environment managers. Here are some indications if you've installed `uv`:
```bash
conda create -n tradingagents python=3.13
conda activate tradingagents
uv venv
```
Activate the virtual environment:
```bash
venv/Scripts/activate.bat
```
Install dependencies:
```bash
pip install -r requirements.txt
uv sync
```
### Required APIs

View File

@ -788,7 +788,12 @@ def run_analysis():
config["llm_provider"] = selections["llm_provider"].lower()
config["user_position"] = selections["user_position"]
config["cost_per_trade"] = selections["cost_per_trade"]
print("\nConfiguration:")
for key, value in config.items():
print(f" {key}: {value}")
print("")
# Initialize the graph
graph = TradingAgentsGraph(
[analyst.value for analyst in selections["analysts"]], config=config, debug=True

43
main.py
View File

@ -1,22 +1,55 @@
from dotenv import load_dotenv
load_dotenv()
from rich.panel import Panel
from rich.console import Console
from rich.align import Align
from tradingagents.graph.trading_graph import TradingAgentsGraph
from tradingagents.default_config import DEFAULT_CONFIG
# Create a custom config
config = DEFAULT_CONFIG.copy()
config["llm_provider"] = "google" # Use a different model
config["backend_url"] = "https://generativelanguage.googleapis.com/v1" # Use a different backend
config["deep_think_llm"] = "gemini-2.0-flash" # Use a different model
config["quick_think_llm"] = "gemini-2.0-flash" # Use a different model
config["ticker"] = "F"
config['analysis_date'] = "2025-09-28"
config["llm_provider"] = "openrouter" # Use a different model
#config["backend_url"] = "https://generativelanguage.googleapis.com/v1" # Use a different backend
config["backend_url"] = "https://openrouter.ai/api/v1"
config["deep_think_llm"] = "qwen/qwen3-235b-a22b:free" # Use a different model
config["quick_think_llm"] = "x-ai/grok-4-fast:free" # Use a different model
config["max_debate_rounds"] = 1 # Increase debate rounds
config["online_tools"] = True
config["cost_per_trade"] = 0.0
with open("./cli/static/welcome.txt", "r", encoding="utf-8") as f:
welcome_ascii = f.read()
# Create welcome box content
welcome_content = f"{welcome_ascii}\n"
welcome_content += "[bold green]TradingAgents: Multi-Agents LLM Financial Trading Framework - CLI[/bold green]\n\n"
welcome_content += "[bold]Workflow Steps:[/bold]\n"
welcome_content += "I. Analyst Team -> II. Research Team -> III. Trader -> IV. Risk Management -> V. Portfolio Management\n\n"
welcome_content += (
"[dim]Built by [Tauric Research](https://github.com/TauricResearch)[/dim]"
)
# Create and center the welcome box
welcome_box = Panel(
welcome_content,
border_style="green",
padding=(1, 2),
title="Welcome to TradingAgents",
subtitle="Multi-Agents LLM Financial Trading Framework",
)
console = Console()
console.print(Align.center(welcome_box))
console.print() # Add a blank line after the welcome box
# Initialize with custom config
ta = TradingAgentsGraph(debug=True, config=config)
# forward propagate
_, decision = ta.propagate("NVDA", "2024-05-10")
_, decision = ta.propagate(config["ticker"], config["analysis_date"])
print(decision)
# Memorize mistakes and reflect

Binary file not shown.

View File

@ -0,0 +1,66 @@
# Stop-Loss Feature Implementation Plan
This document outlines the plan for implementing a stop-loss feature in the TradingAgents project.
## 1. Overview
The goal is to enhance the trading agents' capabilities by requiring a stop-loss price level for every trade recommendation. This will improve risk management and provide more concrete trading plans. An optional take-profit level can also be included.
## 2. Recommended Architecture: New Trade Planner Agent
After investigating the existing architecture, the recommended approach is to introduce a new, dedicated **Trade Planner Agent**. This approach is favored over modifying existing agents for the following reasons:
* **Modularity and Separation of Concerns:** It keeps the responsibilities of each agent clear. The new agent will specialize in technical analysis, while other agents, like the `risk_manager`, can focus on their core competencies.
* **Expertise:** A dedicated agent can be specifically prompted and potentially fine-tuned to become an expert in technical analysis, leading to more accurate stop-loss and take-profit levels.
* **Scalability:** It will be easier to add more sophisticated technical analysis logic in the future without complicating the existing agents.
The new workflow will be as follows:
1. **Analyst Team:** Gathers and analyzes data (no changes).
2. **Researcher Team:** Debates the findings and creates an investment plan (no changes).
3. **Trade Planner Agent (New):** Receives the market data and investment plan, and calculates the stop-loss and (optionally) take-profit levels.
4. **Risk Management Team:** Assesses the risk of the proposed trade, now also considering the stop-loss level.
5. **Trader Agent:** Makes the final trading decision, incorporating the stop-loss and take-profit levels into the final transaction proposal.
## 3. Implementation Details
### 3.1. Create the Trade Planner Agent
* **File:** `tradingagents/agents/managers/trade_planner.py`
* **Function:** `create_trade_planner_agent`
* **Logic:**
* The agent will take the `market_report` and `investment_plan` from the state as input.
* It will use a detailed prompt that instructs the LLM to act as a trade planner.
* The prompt will guide the LLM to determine stop-loss and take-profit levels based on technical indicators such as:
* Support and resistance levels
* Moving averages
* Fibonacci retracement levels
* Volume analysis
* The prompt will specify the desired output format, which should be a JSON object with `stop_loss` and `take_profit` keys.
### 3.2. Update the Graph
* **File:** `tradingagents/graph/trading_graph.py`
* **Changes:**
* Instantiate the new `trade_planner_agent`.
* Add a new node for the agent in the `LangGraph` setup.
* The new node will be placed after the `research_manager` and before the `risk_manager`.
### 3.3. Update Existing Agents
* **`risk_manager.py`:**
* The prompt for the `risk_manager` will be updated to include the `stop_loss` level in its context. This will allow the risk manager to provide a more comprehensive risk assessment.
* **`trader.py`:**
* The prompt for the `trader` agent will be updated to include the `stop_loss` and `take_profit` levels.
* The final output of the trader agent, the "FINAL TRANSACTION PROPOSAL", must include the stop-loss level.
### 3.4. Update Agent State
* **File:** `tradingagents/agents/utils/agent_states.py`
* **Changes:**
* Add `stop_loss: float` and `take_profit: float` fields to the `AgentState` dataclass. This will allow the new price levels to be passed between agents in the graph.
## 4. Next Steps
The next step is to implement the changes described in this document. This will involve creating the new agent, updating the graph, and modifying the existing agents and state.

View File

@ -6,6 +6,7 @@ from .analysts.fundamentals_analyst import create_fundamentals_analyst
from .analysts.market_analyst import create_market_analyst
from .analysts.news_analyst import create_news_analyst
from .analysts.social_media_analyst import create_social_media_analyst
from .managers.trade_planner import create_trade_planner_agent
from .researchers.bear_researcher import create_bear_researcher
from .researchers.bull_researcher import create_bull_researcher
@ -37,5 +38,6 @@ __all__ = [
"create_risk_manager",
"create_safe_debator",
"create_social_media_analyst",
"create_trade_planner_agent",
"create_trader",
]

View File

@ -21,8 +21,15 @@ def create_fundamentals_analyst(llm, toolkit):
]
system_message = (
"You are a researcher tasked with analyzing fundamental information over the past week about a company. Please write a comprehensive report of the company's fundamental information such as financial documents, company profile, basic company financials, company financial history, insider sentiment and insider transactions to gain a full view of the company's fundamental information to inform traders. Make sure to include as much detail as possible. Do not simply state the trends are mixed, provide detailed and finegrained analysis and insights that may help traders make decisions."
+ " Make sure to append a Markdown table at the end of the report to organize key points in the report, organized and easy to read.",
"You are a researcher tasked with analyzing fundamental information over the past week about a company. "
"Please write a comprehensive report of the company's fundamental information such as "
"financial documents, company profile, basic company financials, company financial history, "
"insider sentiment and insider transactions to gain a full view of the company's fundamental information to inform traders. "
"Make sure to include as much detail as possible. "
"Do not simply state the trends are mixed. "
"Provide detailed and finegrained analysis and insights that may help traders make decisions. "
"Make sure to append a Markdown table at the end of the report to organize key points in the report, "
"organized and easy to read.",
)
prompt = ChatPromptTemplate.from_messages(

View File

@ -22,7 +22,8 @@ def create_market_analyst(llm, toolkit):
]
system_message = (
"""You are a trading assistant tasked with analyzing financial markets. Your role is to select the **most relevant indicators** for a given market condition or trading strategy from the following list. The goal is to choose up to **8 indicators** that provide complementary insights without redundancy. Categories and each category's indicators are:
"""
You are a trading assistant tasked with analyzing financial markets. Your role is to select the **most relevant indicators** for a given market condition or trading strategy from the following list. The goal is to choose the indicators that provide complementary insights without redundancy. Categories and each category's indicators are:
Moving Averages:
- close_50_sma: 50 SMA: A medium-term trend indicator. Usage: Identify trend direction and serve as dynamic support/resistance. Tips: It lags price; combine with faster indicators for timely signals.
@ -46,6 +47,16 @@ Volatility Indicators:
Volume-Based Indicators:
- vwma: VWMA: A moving average weighted by volume. Usage: Confirm trends by integrating price action with volume data. Tips: Watch for skewed results from volume spikes; use in combination with other volume analyses.
Support and Resistance Indicators:
- supertrend_lower: Lower Band of the SuperTrend. Usage: Identify trend direction and serve as dynamic support/resistance. Tips: It lags price; combine with faster indicators for timely signals.
- supertrend_upper: Upper Band of the SuperTrend. Usage: Identify trend direction and serve as dynamic support/resistance. Tips: It lags price; combine with faster indicators for timely signals.
- Pivot Points: Pivot Points: Key points used to identify potential entry points. Usage: Identify potential entry points for long/short positions. Tips: Watch for confirmation with other indicators.
- Donchian Chanells: Donchian Channels: A range of high and low prices over a specified period. Usage: Identify potential entry points for long/short positions. Tips: Watch for confirmation with other indicators.
Bullish and Bearish Candlestick Patterns:
- bullish_candlestick: Bullish Candlestick Pattern: A bullish candlestick pattern. Usage: Identify potential entry points for long positions. Tips: Watch for confirmation with other indicators.
- bearish_candlestick: Bearish Candlestick Pattern: A bearish candlestick pattern. Usage: Identify potential entry points for short positions. Tips: Watch for confirmation with other indicators.
- Select indicators that provide diverse and complementary information. Avoid redundancy (e.g., do not select both rsi and stochrsi). Also briefly explain why they are suitable for the given market context. When you tool call, please use the exact name of the indicators provided above as they are defined parameters, otherwise your call will fail. Please make sure to call get_YFin_data first to retrieve the CSV that is needed to generate indicators. Write a very detailed and nuanced report of the trends you observe. Do not simply state the trends are mixed, provide detailed and finegrained analysis and insights that may help traders make decisions."""
+ """ Make sure to append a Markdown table at the end of the report to organize key points in the report, organized and easy to read."""
)

View File

@ -14,6 +14,8 @@ def create_risk_manager(llm, memory):
fundamentals_report = state["news_report"]
sentiment_report = state["sentiment_report"]
trader_plan = state["investment_plan"]
stop_loss = state.get("stop_loss")
take_profit = state.get("take_profit")
curr_situation = f"{market_research_report}\n\n{sentiment_report}\n\n{news_report}\n\n{fundamentals_report}"
past_memories = memory.get_memories(curr_situation, n_matches=2)
@ -38,7 +40,8 @@ Guidelines for Decision-Making:
1. **Summarize Key Arguments**: Extract the strongest points from each analyst, focusing on relevance to the context.
2. **Provide Rationale**: Support your recommendation with direct quotes and counterarguments from the debate.
3. **Refine the Trader's Plan**: Start with the trader's original plan, **{trader_plan}**, and adjust it based on the analysts' insights.
4. **Learn from Past Mistakes**: Use lessons from **{past_memory_str}** to address prior misjudgments and improve the decision you are making now to make sure you don't make a wrong decision that loses money.
4. **Incorporate Technical Analysis**: The Trade Planner has proposed a stop-loss of **{stop_loss}** and a take-profit of **{take_profit}**. You must consider these levels in your final recommendation.
5. **Learn from Past Mistakes**: Use lessons from **{past_memory_str}** to address prior misjudgments and improve the decision you are making now to make sure you don't make a wrong decision that loses money.
Deliverables:
- A clear and actionable recommendation.

View File

@ -0,0 +1,57 @@
import json
def create_trade_planner_agent(llm, toolkit):
def trade_planner_node(state) -> dict:
market_report = state["market_report"]
investment_plan = state["investment_plan"]
if toolkit.config["online_tools"]:
tools = [
toolkit.get_YFin_data_online,
toolkit.get_stockstats_indicators_report_online,
]
else:
tools = [
toolkit.get_YFin_data,
toolkit.get_stockstats_indicators_report,
]
prompt = f'''You are a trade planner. Your role is to determine the stop-loss and take-profit levels for a given investment plan.
Analyze the following market report and investment plan to determine the optimal stop-loss and take-profit levels. You should use the available tools to get the latest market data and calculate technical indicators.
**Market Report:**
{market_report}
**Investment Plan:**
{investment_plan}
Use technical indicators such as Pivots, ATR, support and resistance levels, Donchian Channels, SuperTrend, etc., as well as risk factors to determine the stop-loss and take-profit levels.
Based on your analysis, provide the stop-loss and take-profit levels in a JSON format. For example:
{{
"stop_loss": 150.00,
"take_profit": 180.00
}}
The stop-loss level is mandatory. The take-profit level is optional.
Do not provide any other information or explanation.
'''
response = llm.invoke(prompt)
try:
levels = json.loads(response.content)
stop_loss = levels.get("stop_loss")
take_profit = levels.get("take_profit")
except (json.JSONDecodeError, AttributeError):
stop_loss = None
take_profit = None
return {
"stop_loss": stop_loss,
"take_profit": take_profit,
}
return trade_planner_node

View File

@ -25,9 +25,12 @@ def create_trader(llm, memory):
user_position = state.get("user_position", "none")
cost_per_trade = state.get("cost_per_trade", 0.0)
stop_loss = state.get("stop_loss")
take_profit = state.get("take_profit")
context = {
"role": "user",
"content": f"Based on a comprehensive analysis by a team of analysts, here is an investment plan tailored for {company_name}. This plan incorporates insights from current technical market trends, macroeconomic indicators, and social media sentiment. Use this plan as a foundation for evaluating your next trading decision.\n\nProposed Investment Plan: {investment_plan}\n\nLeverage these insights to make an informed and strategic decision.",
"content": f"Based on a comprehensive analysis by a team of analysts, here is an investment plan tailored for {company_name}. This plan incorporates insights from current technical market trends, macroeconomic indicators, and social media sentiment. Use this plan as a foundation for evaluating your next trading decision.\n\nProposed Investment Plan: {investment_plan}\n\nThe Trade Planner has proposed a stop-loss of **{stop_loss}** and a take-profit of **{take_profit}**. You must consider these levels in your final recommendation.\n\nLeverage these insights to make an informed and strategic decision.",
}
messages = [

View File

@ -76,3 +76,5 @@ class AgentState(MessagesState):
RiskDebateState, "Current state of the debate on evaluating risk"
]
final_trade_decision: Annotated[str, "Final decision made by the Risk Analysts"]
stop_loss: Annotated[Optional[float], "Stop loss price level"] = None
take_profit: Annotated[Optional[float], "Take profit price level"] = None

View File

@ -12,7 +12,7 @@ class FinancialSituationMemory:
# Use a good general-purpose model for financial text
self.embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
self.embedding_type = "local"
print(f" Using local embeddings with sentence-transformers for {name}")
print(f" Using local embeddings with sentence-transformers for {name}") # ✅
except ImportError:
print("⚠️ sentence-transformers not found. Install with: pip install sentence-transformers")
print("Falling back to ChromaDB's default embeddings...")

View File

@ -3,13 +3,13 @@ import os
DEFAULT_CONFIG = {
"project_dir": os.path.abspath(os.path.join(os.path.dirname(__file__), ".")),
"results_dir": os.getenv("TRADINGAGENTS_RESULTS_DIR", "./results"),
"data_dir": "/Users/yluo/Documents/Code/ScAI/FR1-data",
"data_dir": "./data",
"data_cache_dir": os.path.join(
os.path.abspath(os.path.join(os.path.dirname(__file__), ".")),
"dataflows/data_cache",
),
# LLM settings
"llm_provider": "openai", # "openai" or "gemini"
"llm_provider": "openai", # "openai"/"gemini"/"openrouter"/"ollama"
"deep_think_llm": "o4-mini",
"quick_think_llm": "gpt-4o-mini",
"backend_url": "https://api.openai.com/v1",

View File

@ -43,6 +43,14 @@ class ConditionalLogic:
return "tools_fundamentals"
return "Msg Clear Fundamentals"
def should_continue_technical(self, state: AgentState):
"""Determine if technical analysis should continue."""
messages = state["messages"]
last_message = messages[-1]
if last_message.tool_calls:
return "tools_technical"
return "Msg Clear Technical"
def should_continue_debate(self, state: AgentState) -> str:
"""Determine if debate should continue."""

View File

@ -98,6 +98,7 @@ class GraphSetup:
research_manager_node = create_research_manager(
self.deep_thinking_llm, self.invest_judge_memory
)
trade_planner_node = create_trade_planner_agent(self.quick_thinking_llm, self.toolkit)
trader_node = create_trader(self.quick_thinking_llm, self.trader_memory)
# Create risk analysis nodes
@ -123,6 +124,7 @@ class GraphSetup:
workflow.add_node("Bull Researcher", bull_researcher_node)
workflow.add_node("Bear Researcher", bear_researcher_node)
workflow.add_node("Research Manager", research_manager_node)
workflow.add_node("Trade Planner", trade_planner_node)
workflow.add_node("Trader", trader_node)
workflow.add_node("Risky Analyst", risky_analyst)
workflow.add_node("Neutral Analyst", neutral_analyst)
@ -172,7 +174,8 @@ class GraphSetup:
"Research Manager": "Research Manager",
},
)
workflow.add_edge("Research Manager", "Trader")
workflow.add_edge("Research Manager", "Trade Planner")
workflow.add_edge("Trade Planner", "Trader")
workflow.add_edge("Trader", "Risky Analyst")
workflow.add_conditional_edges(
"Risky Analyst",

View File

@ -180,6 +180,16 @@ class TradingAgentsGraph:
self.toolkit.get_simfin_income_stmt,
]
),
"technical": ToolNode(
[
# online tools
self.toolkit.get_YFin_data_online,
self.toolkit.get_stockstats_indicators_report_online,
# offline tools
self.toolkit.get_YFin_data,
self.toolkit.get_stockstats_indicators_report,
]
),
}
def propagate(self, company_name, trade_date, user_position="none", cost_per_trade=0.0):