refactor: standardize portfolio manager, five-tier rating scale, fix analyst status tracking

This commit is contained in:
Yijia-Xiao 2026-03-22 23:30:29 +00:00
parent 318adda0c6
commit b8b2825783
7 changed files with 40 additions and 37 deletions

View File

@ -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

View File

@ -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",

View File

@ -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

View File

@ -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"):

View File

@ -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)])

View File

@ -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()

View File

@ -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):