diff --git a/cli/main.py b/cli/main.py index 2e06d50c..512a27dd 100644 --- a/cli/main.py +++ b/cli/main.py @@ -72,6 +72,7 @@ class MessageBuffer: "fundamentals_report": None, "investment_plan": None, "trader_investment_plan": None, + "risk_debate": None, "final_trade_decision": None, } @@ -113,6 +114,7 @@ class MessageBuffer: "fundamentals_report": "Fundamentals Analysis", "investment_plan": "Research Team Decision", "trader_investment_plan": "Trading Team Plan", + "risk_debate": "Risk Management Team Debate", "final_trade_decision": "Portfolio Management Decision", } self.current_report = ( @@ -162,6 +164,11 @@ class MessageBuffer: if self.report_sections["trader_investment_plan"]: report_parts.append("## Trading Team Plan") report_parts.append(f"{self.report_sections['trader_investment_plan']}") + + # Risk Management Team DebateĀ  + if self.report_sections["risk_debate"]: + report_parts.append("## Risk Management Team Debate") + report_parts.append(f"{self.report_sections['risk_debate']}") # Portfolio Management Decision if self.report_sections["final_trade_decision"]: @@ -520,12 +527,16 @@ def get_analysis_date(): ) -def display_complete_report(final_state): - """Display the complete analysis report with team-based panels.""" +def display_complete_report(final_state, report_dir, selections): + """Display the complete analysis report with team-based panels and save to markdown file.""" console.print("\n[bold green]Complete Analysis Report[/bold green]\n") + # Create full markdown report content + md_content = ["# Complete Analysis Report"] + # I. Analyst Team Reports analyst_reports = [] + md_content.append("## I. Analyst Team Reports") # Market Analyst Report if final_state.get("market_report"): @@ -537,6 +548,10 @@ def display_complete_report(final_state): padding=(1, 2), ) ) + md_content.append("### Market Analyst") + md_content.append(adjust_markdown_headers( + final_state["market_report"])) + md_content.append("") # Empty line for spacing # Social Analyst Report if final_state.get("sentiment_report"): @@ -548,6 +563,10 @@ def display_complete_report(final_state): padding=(1, 2), ) ) + md_content.append("### Social Analyst") + md_content.append(adjust_markdown_headers( + final_state["sentiment_report"])) + md_content.append("") # Empty line for spacing # News Analyst Report if final_state.get("news_report"): @@ -559,6 +578,9 @@ def display_complete_report(final_state): padding=(1, 2), ) ) + md_content.append("### News Analyst") + md_content.append(adjust_markdown_headers(final_state["news_report"])) + md_content.append("") # Empty line for spacing # Fundamentals Analyst Report if final_state.get("fundamentals_report"): @@ -570,6 +592,10 @@ def display_complete_report(final_state): padding=(1, 2), ) ) + md_content.append("### Fundamentals Analyst") + md_content.append(adjust_markdown_headers( + final_state["fundamentals_report"])) + md_content.append("") # Empty line for spacing if analyst_reports: console.print( @@ -585,6 +611,7 @@ def display_complete_report(final_state): if final_state.get("investment_debate_state"): research_reports = [] debate_state = final_state["investment_debate_state"] + md_content.append("## II. Research Team Decision") # Bull Researcher Analysis if debate_state.get("bull_history"): @@ -596,6 +623,10 @@ def display_complete_report(final_state): padding=(1, 2), ) ) + md_content.append("### Bull Researcher") + md_content.append(adjust_markdown_headers( + debate_state["bull_history"])) + md_content.append("") # Empty line for spacing # Bear Researcher Analysis if debate_state.get("bear_history"): @@ -607,6 +638,10 @@ def display_complete_report(final_state): padding=(1, 2), ) ) + md_content.append("### Bear Researcher") + md_content.append(adjust_markdown_headers( + debate_state["bear_history"])) + md_content.append("") # Empty line for spacing # Research Manager Decision if debate_state.get("judge_decision"): @@ -618,6 +653,10 @@ def display_complete_report(final_state): padding=(1, 2), ) ) + md_content.append("### Research Manager Decision") + md_content.append(adjust_markdown_headers( + debate_state["judge_decision"])) + md_content.append("") # Empty line for spacing if research_reports: console.print( @@ -631,6 +670,12 @@ def display_complete_report(final_state): # III. Trading Team Reports if final_state.get("trader_investment_plan"): + md_content.append("## III. Trading Team Plan") + md_content.append("### Trader") + md_content.append(adjust_markdown_headers( + final_state["trader_investment_plan"])) + md_content.append("") # Empty line for spacing + console.print( Panel( Panel( @@ -649,6 +694,7 @@ def display_complete_report(final_state): if final_state.get("risk_debate_state"): risk_reports = [] risk_state = final_state["risk_debate_state"] + md_content.append("## IV. Risk Management Team Decision") # Aggressive (Risky) Analyst Analysis if risk_state.get("risky_history"): @@ -660,6 +706,10 @@ def display_complete_report(final_state): padding=(1, 2), ) ) + md_content.append("### Aggressive (Risky) Analyst") + md_content.append(adjust_markdown_headers( + risk_state["risky_history"])) + md_content.append("") # Empty line for spacing # Conservative (Safe) Analyst Analysis if risk_state.get("safe_history"): @@ -671,6 +721,10 @@ def display_complete_report(final_state): padding=(1, 2), ) ) + md_content.append("### Conservative (Safe) Analyst") + md_content.append(adjust_markdown_headers( + risk_state["safe_history"])) + md_content.append("") # Empty line for spacing # Neutral Analyst Analysis if risk_state.get("neutral_history"): @@ -682,6 +736,10 @@ def display_complete_report(final_state): padding=(1, 2), ) ) + md_content.append("### Neutral Analyst") + md_content.append(adjust_markdown_headers( + risk_state["neutral_history"])) + md_content.append("") # Empty line for spacing if risk_reports: console.print( @@ -695,6 +753,11 @@ def display_complete_report(final_state): # V. Portfolio Manager Decision if risk_state.get("judge_decision"): + md_content.append("## V. Portfolio Manager Decision") + md_content.append("### Portfolio Manager") + md_content.append(adjust_markdown_headers( + risk_state["judge_decision"])) + console.print( Panel( Panel( @@ -709,6 +772,12 @@ def display_complete_report(final_state): ) ) + # Write the full markdown report to file + complete_report_filename = f"complete_report_by_{selections.get('shallow_thinker')}__{selections.get('deep_thinker')}.md" + complete_report_path = report_dir / complete_report_filename + with open(complete_report_path, "w") as f: + f.write("\n".join(md_content)) + def update_research_team_status(status): """Update status for all research team members and trader.""" @@ -993,6 +1062,7 @@ def run_analysis(): # Risk Management Team - Handle Risk Debate State if "risk_debate_state" in chunk and chunk["risk_debate_state"]: risk_state = chunk["risk_debate_state"] + risk_debate_content = "" # Update Risky Analyst status and report if ( @@ -1006,10 +1076,11 @@ def run_analysis(): "Reasoning", f"Risky Analyst: {risk_state['current_risky_response']}", ) - # Update risk report with risky analyst's latest analysis only + # Update risk report with adding risky analyst's latest analysis + risk_debate_content += f"### Risky Analyst Analysis\n{risk_state['current_risky_response']}\n" message_buffer.update_report_section( - "final_trade_decision", - f"### Risky Analyst Analysis\n{risk_state['current_risky_response']}", + "risk_debate", + risk_debate_content, ) # Update Safe Analyst status and report @@ -1024,10 +1095,11 @@ def run_analysis(): "Reasoning", f"Safe Analyst: {risk_state['current_safe_response']}", ) - # Update risk report with safe analyst's latest analysis only + # Update risk report with adding safe analyst's latest analysis + risk_debate_content += f"### Safe Analyst Analysis\n{risk_state['current_safe_response']}\n" message_buffer.update_report_section( - "final_trade_decision", - f"### Safe Analyst Analysis\n{risk_state['current_safe_response']}", + "risk_debate", + risk_debate_content, ) # Update Neutral Analyst status and report @@ -1042,10 +1114,11 @@ def run_analysis(): "Reasoning", f"Neutral Analyst: {risk_state['current_neutral_response']}", ) - # Update risk report with neutral analyst's latest analysis only + # Update risk report with adding neutral analyst's latest analysis + risk_debate_content += f"### Neutral Analyst Analysis\n{risk_state['current_neutral_response']}\n" message_buffer.update_report_section( - "final_trade_decision", - f"### Neutral Analyst Analysis\n{risk_state['current_neutral_response']}", + "risk_debate", + risk_debate_content, ) # Update Portfolio Manager status and final decision @@ -1095,7 +1168,7 @@ def run_analysis(): message_buffer.update_report_section(section, final_state[section]) # Display the complete final report - display_complete_report(final_state) + display_complete_report(final_state, report_dir, selections) update_display(layout) diff --git a/cli/utils.py b/cli/utils.py index 7b9682a6..8664fcdd 100644 --- a/cli/utils.py +++ b/cli/utils.py @@ -1,6 +1,6 @@ import questionary from typing import List, Optional, Tuple, Dict - +import re from cli.models import AnalystType ANALYST_ORDER = [ @@ -274,3 +274,23 @@ def select_llm_provider() -> tuple[str, str]: print(f"You selected: {display_name}\tURL: {url}") return display_name, url + + +def adjust_markdown_headers(markdown_text: str) -> str: + def replace_header(match): + hashes = match.group(1) + header_text = match.group(2) + header_level = len(hashes) + + if header_level <= 3: + new_hashes = "####" # Force at least h4 + else: + new_hashes = "#" * (header_level + 1) # Increase one # + + return f"{new_hashes} {header_text}" + + # Regex to match markdown headers from level 1 to 6 + adjusted_text = re.sub( + r"^(#{1,6})\s+(.*)", replace_header, markdown_text, flags=re.MULTILINE + ) + return adjusted_text \ No newline at end of file diff --git a/tradingagents/agents/utils/memory.py b/tradingagents/agents/utils/memory.py index 69b8ab8c..c3668161 100644 --- a/tradingagents/agents/utils/memory.py +++ b/tradingagents/agents/utils/memory.py @@ -2,7 +2,6 @@ import chromadb from chromadb.config import Settings from openai import OpenAI - class FinancialSituationMemory: def __init__(self, name, config): if config["backend_url"] == "http://localhost:11434/v1": @@ -69,7 +68,7 @@ class FinancialSituationMemory: if __name__ == "__main__": # Example usage - matcher = FinancialSituationMemory() + matcher = FinancialSituationMemory( name="test", config={"backend_url": "https://api.openai.com/v1",}) # Example data example_data = [ diff --git a/tradingagents/graph/trading_graph.py b/tradingagents/graph/trading_graph.py index 40cdff75..c6db55fd 100644 --- a/tradingagents/graph/trading_graph.py +++ b/tradingagents/graph/trading_graph.py @@ -95,7 +95,10 @@ class TradingAgentsGraph: self.tool_nodes = self._create_tool_nodes() # Initialize components - self.conditional_logic = ConditionalLogic() + self.conditional_logic = ConditionalLogic( + max_debate_rounds=self.config["max_debate_rounds"], + max_risk_discuss_rounds=self.config["max_risk_discuss_rounds"] + ) self.graph_setup = GraphSetup( self.quick_thinking_llm, self.deep_thinking_llm,