From b8b2825783f490faeebfef5ddaf0178f70b2b0ba Mon Sep 17 00:00:00 2001 From: Yijia-Xiao Date: Sun, 22 Mar 2026 23:30:29 +0000 Subject: [PATCH] refactor: standardize portfolio manager, five-tier rating scale, fix analyst status tracking --- cli/main.py | 13 +++++++--- tradingagents/agents/__init__.py | 4 ++-- .../{risk_manager.py => portfolio_manager.py} | 24 ++++++++----------- tradingagents/graph/conditional_logic.py | 2 +- tradingagents/graph/reflection.py | 8 +++---- tradingagents/graph/setup.py | 18 +++++++------- tradingagents/graph/trading_graph.py | 8 +++---- 7 files changed, 40 insertions(+), 37 deletions(-) rename tradingagents/agents/managers/{risk_manager.py => portfolio_manager.py} (74%) diff --git a/cli/main.py b/cli/main.py index f26ae4c5..f975bac1 100644 --- a/cli/main.py +++ b/cli/main.py @@ -800,9 +800,11 @@ ANALYST_REPORT_MAP = { def update_analyst_statuses(message_buffer, chunk): - """Update all analyst statuses based on current report state. + """Update analyst statuses based on accumulated report state. Logic: + - Store new report content from the current chunk if present + - Check accumulated report_sections (not just current chunk) for status - Analysts with reports = completed - First analyst without report = in_progress - Remaining analysts without reports = pending @@ -817,11 +819,16 @@ def update_analyst_statuses(message_buffer, chunk): agent_name = ANALYST_AGENT_NAMES[analyst_key] report_key = ANALYST_REPORT_MAP[analyst_key] - has_report = bool(chunk.get(report_key)) + + # Capture new report content from current chunk + if chunk.get(report_key): + message_buffer.update_report_section(report_key, chunk[report_key]) + + # Determine status from accumulated sections, not just current chunk + has_report = bool(message_buffer.report_sections.get(report_key)) if has_report: message_buffer.update_agent_status(agent_name, "completed") - message_buffer.update_report_section(report_key, chunk[report_key]) elif not found_active: message_buffer.update_agent_status(agent_name, "in_progress") found_active = True diff --git a/tradingagents/agents/__init__.py b/tradingagents/agents/__init__.py index 8a169f22..1f03642c 100644 --- a/tradingagents/agents/__init__.py +++ b/tradingagents/agents/__init__.py @@ -15,7 +15,7 @@ from .risk_mgmt.conservative_debator import create_conservative_debator from .risk_mgmt.neutral_debator import create_neutral_debator from .managers.research_manager import create_research_manager -from .managers.risk_manager import create_risk_manager +from .managers.portfolio_manager import create_portfolio_manager from .trader.trader import create_trader @@ -33,7 +33,7 @@ __all__ = [ "create_neutral_debator", "create_news_analyst", "create_aggressive_debator", - "create_risk_manager", + "create_portfolio_manager", "create_conservative_debator", "create_social_media_analyst", "create_trader", diff --git a/tradingagents/agents/managers/risk_manager.py b/tradingagents/agents/managers/portfolio_manager.py similarity index 74% rename from tradingagents/agents/managers/risk_manager.py rename to tradingagents/agents/managers/portfolio_manager.py index d827c2e8..acdf940b 100644 --- a/tradingagents/agents/managers/risk_manager.py +++ b/tradingagents/agents/managers/portfolio_manager.py @@ -1,11 +1,8 @@ -import time -import json - from tradingagents.agents.utils.agent_utils import build_instrument_context -def create_risk_manager(llm, memory): - def risk_manager_node(state) -> dict: +def create_portfolio_manager(llm, memory): + def portfolio_manager_node(state) -> dict: instrument_context = build_instrument_context(state["company_of_interest"]) @@ -24,7 +21,7 @@ def create_risk_manager(llm, memory): for i, rec in enumerate(past_memories, 1): past_memory_str += rec["recommendation"] + "\n\n" - prompt = f"""As the Risk Management Judge, evaluate the debate between the Aggressive, Neutral, and Conservative analysts and deliver a final trading decision. + prompt = f"""As the Portfolio Manager, synthesize the risk analysts' debate and deliver the final trading decision. {instrument_context} @@ -37,19 +34,18 @@ def create_risk_manager(llm, memory): - **Underweight**: Reduce exposure, take partial profits - **Sell**: Exit position or avoid entry -**Guidelines:** -1. Extract the strongest points from each analyst, focusing on relevance to the current context. -2. Start with the trader's original plan: **{trader_plan}**, and refine it based on the analysts' insights. -3. Apply lessons from past decisions to strengthen this analysis: **{past_memory_str}** +**Context:** +- Trader's proposed plan: **{trader_plan}** +- Lessons from past decisions: **{past_memory_str}** **Required Output Structure:** 1. **Rating**: State one of Buy / Overweight / Hold / Underweight / Sell. -2. **Executive Summary**: A concise action plan covering entry strategy, position sizing, key risk levels, and time horizon. Keep this brief and actionable. -3. **Investment Thesis**: Detailed reasoning anchored in the debate and past reflections. +2. **Executive Summary**: A concise action plan covering entry strategy, position sizing, key risk levels, and time horizon. +3. **Investment Thesis**: Detailed reasoning anchored in the analysts' debate and past reflections. --- -**Analysts Debate History:** +**Risk Analysts Debate History:** {history} --- @@ -76,4 +72,4 @@ Be decisive and ground every conclusion in specific evidence from the analysts." "final_trade_decision": response.content, } - return risk_manager_node + return portfolio_manager_node diff --git a/tradingagents/graph/conditional_logic.py b/tradingagents/graph/conditional_logic.py index 7b1b1f90..48371793 100644 --- a/tradingagents/graph/conditional_logic.py +++ b/tradingagents/graph/conditional_logic.py @@ -59,7 +59,7 @@ class ConditionalLogic: if ( state["risk_debate_state"]["count"] >= 3 * self.max_risk_discuss_rounds ): # 3 rounds of back-and-forth between 3 agents - return "Risk Judge" + return "Portfolio Manager" if state["risk_debate_state"]["latest_speaker"].startswith("Aggressive"): return "Conservative Analyst" if state["risk_debate_state"]["latest_speaker"].startswith("Conservative"): diff --git a/tradingagents/graph/reflection.py b/tradingagents/graph/reflection.py index 33303231..85438595 100644 --- a/tradingagents/graph/reflection.py +++ b/tradingagents/graph/reflection.py @@ -110,12 +110,12 @@ Adhere strictly to these instructions, and ensure your output is detailed, accur ) invest_judge_memory.add_situations([(situation, result)]) - def reflect_risk_manager(self, current_state, returns_losses, risk_manager_memory): - """Reflect on risk manager's decision and update memory.""" + def reflect_portfolio_manager(self, current_state, returns_losses, portfolio_manager_memory): + """Reflect on portfolio manager's decision and update memory.""" situation = self._extract_current_situation(current_state) judge_decision = current_state["risk_debate_state"]["judge_decision"] result = self._reflect_on_component( - "RISK JUDGE", judge_decision, situation, returns_losses + "PORTFOLIO MANAGER", judge_decision, situation, returns_losses ) - risk_manager_memory.add_situations([(situation, result)]) + portfolio_manager_memory.add_situations([(situation, result)]) diff --git a/tradingagents/graph/setup.py b/tradingagents/graph/setup.py index 772efe7f..e0771c65 100644 --- a/tradingagents/graph/setup.py +++ b/tradingagents/graph/setup.py @@ -23,7 +23,7 @@ class GraphSetup: bear_memory, trader_memory, invest_judge_memory, - risk_manager_memory, + portfolio_manager_memory, conditional_logic: ConditionalLogic, ): """Initialize with required components.""" @@ -34,7 +34,7 @@ class GraphSetup: self.bear_memory = bear_memory self.trader_memory = trader_memory self.invest_judge_memory = invest_judge_memory - self.risk_manager_memory = risk_manager_memory + self.portfolio_manager_memory = portfolio_manager_memory self.conditional_logic = conditional_logic def setup_graph( @@ -101,8 +101,8 @@ class GraphSetup: aggressive_analyst = create_aggressive_debator(self.quick_thinking_llm) neutral_analyst = create_neutral_debator(self.quick_thinking_llm) conservative_analyst = create_conservative_debator(self.quick_thinking_llm) - risk_manager_node = create_risk_manager( - self.deep_thinking_llm, self.risk_manager_memory + portfolio_manager_node = create_portfolio_manager( + self.deep_thinking_llm, self.portfolio_manager_memory ) # Create workflow @@ -124,7 +124,7 @@ class GraphSetup: workflow.add_node("Aggressive Analyst", aggressive_analyst) workflow.add_node("Neutral Analyst", neutral_analyst) workflow.add_node("Conservative Analyst", conservative_analyst) - workflow.add_node("Risk Judge", risk_manager_node) + workflow.add_node("Portfolio Manager", portfolio_manager_node) # Define edges # Start with the first analyst @@ -176,7 +176,7 @@ class GraphSetup: self.conditional_logic.should_continue_risk_analysis, { "Conservative Analyst": "Conservative Analyst", - "Risk Judge": "Risk Judge", + "Portfolio Manager": "Portfolio Manager", }, ) workflow.add_conditional_edges( @@ -184,7 +184,7 @@ class GraphSetup: self.conditional_logic.should_continue_risk_analysis, { "Neutral Analyst": "Neutral Analyst", - "Risk Judge": "Risk Judge", + "Portfolio Manager": "Portfolio Manager", }, ) workflow.add_conditional_edges( @@ -192,11 +192,11 @@ class GraphSetup: self.conditional_logic.should_continue_risk_analysis, { "Aggressive Analyst": "Aggressive Analyst", - "Risk Judge": "Risk Judge", + "Portfolio Manager": "Portfolio Manager", }, ) - workflow.add_edge("Risk Judge", END) + workflow.add_edge("Portfolio Manager", END) # Compile and return return workflow.compile() diff --git a/tradingagents/graph/trading_graph.py b/tradingagents/graph/trading_graph.py index 306f7f38..c8cd7492 100644 --- a/tradingagents/graph/trading_graph.py +++ b/tradingagents/graph/trading_graph.py @@ -99,7 +99,7 @@ class TradingAgentsGraph: self.bear_memory = FinancialSituationMemory("bear_memory", self.config) self.trader_memory = FinancialSituationMemory("trader_memory", self.config) self.invest_judge_memory = FinancialSituationMemory("invest_judge_memory", self.config) - self.risk_manager_memory = FinancialSituationMemory("risk_manager_memory", self.config) + self.portfolio_manager_memory = FinancialSituationMemory("portfolio_manager_memory", self.config) # Create tool nodes self.tool_nodes = self._create_tool_nodes() @@ -117,7 +117,7 @@ class TradingAgentsGraph: self.bear_memory, self.trader_memory, self.invest_judge_memory, - self.risk_manager_memory, + self.portfolio_manager_memory, self.conditional_logic, ) @@ -283,8 +283,8 @@ class TradingAgentsGraph: self.reflector.reflect_invest_judge( self.curr_state, returns_losses, self.invest_judge_memory ) - self.reflector.reflect_risk_manager( - self.curr_state, returns_losses, self.risk_manager_memory + self.reflector.reflect_portfolio_manager( + self.curr_state, returns_losses, self.portfolio_manager_memory ) def process_signal(self, full_signal):