diff --git a/cli/main.py b/cli/main.py index e7bed4ee..c790db2e 100644 --- a/cli/main.py +++ b/cli/main.py @@ -22,16 +22,19 @@ from tradingagents.graph.trading_graph import TradingAgentsGraph from tradingagents.default_config import DEFAULT_CONFIG from cli.models import AnalystType from cli.utils import * +from tradingagents.i18n import get_lang console = Console() +lang = get_lang() app = typer.Typer( name="TradingAgents", - help="TradingAgents CLI: Multi-Agents LLM Financial Trading Framework", + help=lang["welcome"] + ": " + lang["framework_subtitle"], add_completion=True, # Enable shell completion ) + # Create a deque to store recent messages with a maximum length class MessageBuffer: def __init__(self, max_length=100): @@ -41,22 +44,22 @@ class MessageBuffer: self.final_report = None # Store the complete final report self.agent_status = { # Analyst Team - "Market Analyst": "pending", - "Social Analyst": "pending", - "News Analyst": "pending", - "Fundamentals Analyst": "pending", + lang["market_analyst"]: lang["pending"], + lang["social_analyst"]: lang["pending"], + lang["news_analyst"]: lang["pending"], + lang["fundamentals_analyst"]: lang["pending"], # Research Team - "Bull Researcher": "pending", - "Bear Researcher": "pending", - "Research Manager": "pending", + lang["bull_researcher"]: lang["pending"], + lang["bear_researcher"]: lang["pending"], + lang["research_manager"]: lang["pending"], # Trading Team - "Trader": "pending", + lang["trader"]: lang["pending"], # Risk Management Team - "Risky Analyst": "pending", - "Neutral Analyst": "pending", - "Safe Analyst": "pending", + lang["risky_analyst"]: lang["pending"], + lang["neutral_analyst"]: lang["pending"], + lang["safe_analyst"]: lang["pending"], # Portfolio Management Team - "Portfolio Manager": "pending", + lang["portfolio_manager"]: lang["pending"], } self.current_agent = None self.report_sections = { @@ -101,13 +104,13 @@ class MessageBuffer: if latest_section and latest_content: # Format the current section for display section_titles = { - "market_report": "Market Analysis", - "sentiment_report": "Social Sentiment", - "news_report": "News Analysis", - "fundamentals_report": "Fundamentals Analysis", - "investment_plan": "Research Team Decision", - "trader_investment_plan": "Trading Team Plan", - "final_trade_decision": "Portfolio Management Decision", + "market_report": lang["market_report"], + "sentiment_report": lang["sentiment_report"], + "news_report": lang["news_report"], + "fundamentals_report": lang["fundamentals_report"], + "investment_plan": lang["investment_plan"], + "trader_investment_plan": lang["trader_investment_plan"], + "final_trade_decision": lang["final_trade_decision"], } self.current_report = ( f"### {section_titles[latest_section]}\n{latest_content}" @@ -129,37 +132,37 @@ class MessageBuffer: "fundamentals_report", ] ): - report_parts.append("## Analyst Team Reports") + report_parts.append(f"## {lang['analyst_team']}") if self.report_sections["market_report"]: report_parts.append( - f"### Market Analysis\n{self.report_sections['market_report']}" + f"### {lang['market_report']}\n{self.report_sections['market_report']}" ) if self.report_sections["sentiment_report"]: report_parts.append( - f"### Social Sentiment\n{self.report_sections['sentiment_report']}" + f"### {lang['sentiment_report']}\n{self.report_sections['sentiment_report']}" ) if self.report_sections["news_report"]: report_parts.append( - f"### News Analysis\n{self.report_sections['news_report']}" + f"### {lang['news_report']}\n{self.report_sections['news_report']}" ) if self.report_sections["fundamentals_report"]: report_parts.append( - f"### Fundamentals Analysis\n{self.report_sections['fundamentals_report']}" + f"### {lang['fundamentals_report']}\n{self.report_sections['fundamentals_report']}" ) # Research Team Reports if self.report_sections["investment_plan"]: - report_parts.append("## Research Team Decision") + report_parts.append(f"## {lang['research_team']}") report_parts.append(f"{self.report_sections['investment_plan']}") # Trading Team Reports if self.report_sections["trader_investment_plan"]: - report_parts.append("## Trading Team Plan") + report_parts.append(f"## {lang['trading_team']}") report_parts.append(f"{self.report_sections['trader_investment_plan']}") # Portfolio Management Decision if self.report_sections["final_trade_decision"]: - report_parts.append("## Portfolio Management Decision") + report_parts.append(f"## {lang['portfolio_management']}") report_parts.append(f"{self.report_sections['final_trade_decision']}") self.final_report = "\n\n".join(report_parts) if report_parts else None @@ -188,9 +191,9 @@ def update_display(layout, spinner_text=None): # Header with welcome message layout["header"].update( Panel( - "[bold green]Welcome to TradingAgents CLI[/bold green]\n" + f"[bold green]{lang['welcome']}[/bold green]\n" "[dim]© [Tauric Research](https://github.com/TauricResearch)[/dim]", - title="Welcome to TradingAgents", + title=lang["welcome"], border_style="green", padding=(1, 2), expand=True, @@ -207,22 +210,22 @@ def update_display(layout, spinner_text=None): padding=(0, 2), # Add horizontal padding expand=True, # Make table expand to fill available space ) - progress_table.add_column("Team", style="cyan", justify="center", width=20) - progress_table.add_column("Agent", style="green", justify="center", width=20) - progress_table.add_column("Status", style="yellow", justify="center", width=20) + progress_table.add_column(lang["team"], style="cyan", justify="center", width=20) + progress_table.add_column(lang["agent"], style="green", justify="center", width=20) + progress_table.add_column(lang["status"], style="yellow", justify="center", width=20) # Group agents by team teams = { - "Analyst Team": [ - "Market Analyst", - "Social Analyst", - "News Analyst", - "Fundamentals Analyst", + lang["analyst_team"]: [ + lang["market_analyst"], + lang["social_analyst"], + lang["news_analyst"], + lang["fundamentals_analyst"], ], - "Research Team": ["Bull Researcher", "Bear Researcher", "Research Manager"], - "Trading Team": ["Trader"], - "Risk Management": ["Risky Analyst", "Neutral Analyst", "Safe Analyst"], - "Portfolio Management": ["Portfolio Manager"], + lang["research_team"]: [lang["bull_researcher"], lang["bear_researcher"], lang["research_manager"]], + lang["trading_team"]: [lang["trader"]], + lang["risk_management"]: [lang["risky_analyst"], lang["neutral_analyst"], lang["safe_analyst"]], + lang["portfolio_management"]: [lang["portfolio_manager"]], } for team, agents in teams.items(): @@ -231,14 +234,14 @@ def update_display(layout, spinner_text=None): status = message_buffer.agent_status[first_agent] if status == "in_progress": spinner = Spinner( - "dots", text="[blue]in_progress[/blue]", style="bold cyan" + "dots", text=f"[blue]{lang['in_progress']}[/blue]", style="bold cyan" ) status_cell = spinner else: status_color = { - "pending": "yellow", - "completed": "green", - "error": "red", + lang["pending"]: "yellow", + lang["completed"]: "green", + lang["error"]: "red", }.get(status, "white") status_cell = f"[{status_color}]{status}[/{status_color}]" progress_table.add_row(team, first_agent, status_cell) @@ -248,14 +251,14 @@ def update_display(layout, spinner_text=None): status = message_buffer.agent_status[agent] if status == "in_progress": spinner = Spinner( - "dots", text="[blue]in_progress[/blue]", style="bold cyan" + "dots", text=f"[blue]{lang['in_progress']}[/blue]", style="bold cyan" ) status_cell = spinner else: status_color = { - "pending": "yellow", - "completed": "green", - "error": "red", + lang["pending"]: "yellow", + lang["completed"]: "green", + lang["error"]: "red", }.get(status, "white") status_cell = f"[{status_color}]{status}[/{status_color}]" progress_table.add_row("", agent, status_cell) @@ -264,7 +267,7 @@ def update_display(layout, spinner_text=None): progress_table.add_row("─" * 20, "─" * 20, "─" * 20, style="dim") layout["progress"].update( - Panel(progress_table, title="Progress", border_style="cyan", padding=(1, 2)) + Panel(progress_table, title=lang["progress"], border_style="cyan", padding=(1, 2)) ) # Messages panel showing recent messages and tool calls @@ -279,9 +282,7 @@ def update_display(layout, spinner_text=None): ) messages_table.add_column("Time", style="cyan", width=8, justify="center") messages_table.add_column("Type", style="green", width=10, justify="center") - messages_table.add_column( - "Content", style="white", no_wrap=False, ratio=1 - ) # Make content column expand + messages_table.add_column(lang["content"] if "content" in lang else "Content", style="white", no_wrap=False, ratio=1) # Combine tool calls and messages all_messages = [] @@ -326,12 +327,7 @@ def update_display(layout, spinner_text=None): ) layout["messages"].update( - Panel( - messages_table, - title="Messages & Tools", - border_style="blue", - padding=(1, 2), - ) + Panel(messages_table, title=lang["messages_tools"], border_style="blue") ) # Analysis panel showing current report @@ -339,7 +335,7 @@ def update_display(layout, spinner_text=None): layout["analysis"].update( Panel( Markdown(message_buffer.current_report), - title="Current Report", + title=lang["current_report"], border_style="green", padding=(1, 2), ) @@ -348,7 +344,7 @@ def update_display(layout, spinner_text=None): layout["analysis"].update( Panel( "[italic]Waiting for analysis report...[/italic]", - title="Current Report", + title=lang["current_report"], border_style="green", padding=(1, 2), ) @@ -380,9 +376,9 @@ def get_user_selections(): # 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 += f"[bold green]TradingAgents: {lang['framework_subtitle']} - CLI[/bold green]\n\n" + welcome_content += f"[bold]{lang['workflow_steps_title']}[/bold]\n" + welcome_content += lang['workflow_steps'] welcome_content += ( "[dim]Built by [Tauric Research](https://github.com/TauricResearch)[/dim]" ) @@ -392,8 +388,8 @@ def get_user_selections(): welcome_content, border_style="green", padding=(1, 2), - title="Welcome to TradingAgents", - subtitle="Multi-Agents LLM Financial Trading Framework", + title= lang["welcome"], + subtitle=lang["framework_subtitle"], ) console.print(Align.center(welcome_box)) console.print() # Add a blank line after the welcome box @@ -409,7 +405,7 @@ def get_user_selections(): # Step 1: Ticker symbol console.print( create_question_box( - "Step 1: Ticker Symbol", "Enter the ticker symbol to analyze", "SPY" + lang["step1_title"], lang["step1_prompt"], lang["default_ticker"] ) ) selected_ticker = get_ticker() @@ -418,8 +414,8 @@ def get_user_selections(): default_date = datetime.datetime.now().strftime("%Y-%m-%d") console.print( create_question_box( - "Step 2: Analysis Date", - "Enter the analysis date (YYYY-MM-DD)", + lang["step2_title"], + lang["step2_prompt"], default_date, ) ) @@ -428,7 +424,7 @@ def get_user_selections(): # Step 3: Select analysts console.print( create_question_box( - "Step 3: Analysts Team", "Select your LLM analyst agents for the analysis" + lang["step3_title"], lang["step3_prompt"] ) ) selected_analysts = select_analysts() @@ -439,7 +435,7 @@ def get_user_selections(): # Step 4: Research depth console.print( create_question_box( - "Step 4: Research Depth", "Select your research depth level" + lang["step4_title"], lang["step4_prompt"] ) ) selected_research_depth = select_research_depth() @@ -447,11 +443,11 @@ def get_user_selections(): # Step 5: Thinking agents console.print( create_question_box( - "Step 5: Thinking Agents", "Select your thinking agents for analysis" + lang["step5_title"], lang["step5_prompt"] ) ) - selected_shallow_thinker = select_shallow_thinking_agent() - selected_deep_thinker = select_deep_thinking_agent() + # selected_shallow_thinker = select_shallow_thinking_agent() + # selected_deep_thinker = select_deep_thinking_agent() return { "ticker": selected_ticker, diff --git a/cli/utils.py b/cli/utils.py index c3865253..9c6631cc 100644 --- a/cli/utils.py +++ b/cli/utils.py @@ -2,6 +2,9 @@ import questionary from typing import List, Optional, Tuple, Dict from cli.models import AnalystType +from tradingagents.i18n import get_lang + +lang = get_lang() ANALYST_ORDER = [ ("Market Analyst", AnalystType.MARKET), @@ -14,8 +17,8 @@ ANALYST_ORDER = [ def get_ticker() -> str: """Prompt the user to enter a ticker symbol.""" ticker = questionary.text( - "Enter the ticker symbol to analyze:", - validate=lambda x: len(x.strip()) > 0 or "Please enter a valid ticker symbol.", + lang["step1_prompt"], + validate=lambda x: len(x.strip()) > 0 or lang["ticker_validate"], style=questionary.Style( [ ("text", "fg:green"), @@ -46,9 +49,8 @@ def get_analysis_date() -> str: return False date = questionary.text( - "Enter the analysis date (YYYY-MM-DD):", - validate=lambda x: validate_date(x.strip()) - or "Please enter a valid date in YYYY-MM-DD format.", + lang["step2_prompt"], + validate=lambda x: validate_date(x.strip()) or lang["date_validate"], style=questionary.Style( [ ("text", "fg:green"), @@ -67,12 +69,12 @@ def get_analysis_date() -> str: def select_analysts() -> List[AnalystType]: """Select analysts using an interactive checkbox.""" choices = questionary.checkbox( - "Select Your [Analysts Team]:", + lang["step3_prompt"], choices=[ - questionary.Choice(display, value=value) for display, value in ANALYST_ORDER + questionary.Choice(lang.get(display.replace(" ", "_").lower(), display), value=value) for display, value in ANALYST_ORDER ], - instruction="\n- Press Space to select/unselect analysts\n- Press 'a' to select/unselect all\n- Press Enter when done", - validate=lambda x: len(x) > 0 or "You must select at least one analyst.", + instruction=lang["analyst_instruction"], + validate=lambda x: len(x) > 0 or lang["analyst_validate"], style=questionary.Style( [ ("checkbox-selected", "fg:green"), @@ -95,17 +97,17 @@ def select_research_depth() -> int: # Define research depth options with their corresponding values DEPTH_OPTIONS = [ - ("Shallow - Quick research, few debate and strategy discussion rounds", 1), - ("Medium - Middle ground, moderate debate rounds and strategy discussion", 3), - ("Deep - Comprehensive research, in depth debate and strategy discussion", 5), + (lang["depth_shallow"], 1), + (lang["depth_medium"], 3), + (lang["depth_deep"], 5), ] choice = questionary.select( - "Select Your [Research Depth]:", + lang["step4_prompt"], choices=[ questionary.Choice(display, value=value) for display, value in DEPTH_OPTIONS ], - instruction="\n- Use arrow keys to navigate\n- Press Enter to select", + instruction=lang["depth_instruction"], style=questionary.Style( [ ("selected", "fg:yellow noinherit"), diff --git a/tradingagents/default_config.py b/tradingagents/default_config.py index 5bb2548c..0a3fa234 100644 --- a/tradingagents/default_config.py +++ b/tradingagents/default_config.py @@ -16,4 +16,6 @@ DEFAULT_CONFIG = { "max_recur_limit": 100, # Tool settings "online_tools": True, + # Language settings + "language": "zh", # 支持 'zh' 或 'en' } diff --git a/tradingagents/graph/trading_graph.py b/tradingagents/graph/trading_graph.py index bbd45071..da292fe3 100644 --- a/tradingagents/graph/trading_graph.py +++ b/tradingagents/graph/trading_graph.py @@ -55,9 +55,14 @@ class TradingAgentsGraph: ) # Initialize LLMs - self.deep_thinking_llm = ChatOpenAI(model=self.config["deep_think_llm"]) + self.deep_thinking_llm = ChatOpenAI( + model=self.config["deep_think_llm"], + base_url = self.config["base_url"], + ) self.quick_thinking_llm = ChatOpenAI( - model=self.config["quick_think_llm"], temperature=0.1 + model=self.config["quick_think_llm"], + base_url = self.config["base_url"], + temperature=0.1 ) self.toolkit = Toolkit(config=self.config) diff --git a/tradingagents/i18n/__init__.py b/tradingagents/i18n/__init__.py new file mode 100644 index 00000000..d9731803 --- /dev/null +++ b/tradingagents/i18n/__init__.py @@ -0,0 +1,12 @@ +import importlib +from tradingagents.default_config import DEFAULT_CONFIG + +def get_lang(): + lang_code = DEFAULT_CONFIG.get("language", "zh") + try: + lang_module = importlib.import_module(f"tradingagents.i18n.{lang_code}") + return lang_module.LANG + except Exception: + # fallback to zh + from .zh import LANG + return LANG \ No newline at end of file diff --git a/tradingagents/i18n/en.py b/tradingagents/i18n/en.py new file mode 100644 index 00000000..70a97507 --- /dev/null +++ b/tradingagents/i18n/en.py @@ -0,0 +1,65 @@ +LANG = { + "welcome": "Welcome to TradingAgents CLI", + "analyst_team": "Analyst Team", + "market_analyst": "Market Analyst", + "social_analyst": "Social Analyst", + "news_analyst": "News Analyst", + "social_media_analyst": "Social Media Analyst", + "fundamentals_analyst": "Fundamentals Analyst", + "research_team": "Research Team", + "bull_researcher": "Bull Researcher", + "bear_researcher": "Bear Researcher", + "research_manager": "Research Manager", + "trading_team": "Trading Team", + "trader": "Trader", + "risk_management": "Risk Management", + "risky_analyst": "Risky Analyst", + "neutral_analyst": "Neutral Analyst", + "safe_analyst": "Safe Analyst", + "portfolio_management": "Portfolio Management", + "portfolio_manager": "Portfolio Manager", + "market_report": "Market Report", + "sentiment_report": "Sentiment Report", + "news_report": "News Report", + "fundamentals_report": "Fundamentals Report", + "investment_plan": "Investment Plan", + "trader_investment_plan": "Trader Investment Plan", + "final_trade_decision": "Final Trade Decision", + "team": "Team", + "agent": "Agent", + "status": "Status", + "progress": "Progress", + "messages_tools": "Messages & Tools", + "current_report": "Current Report", + "pending": "Pending", + "completed": "Completed", + "error": "Error", + "in_progress": "In Progress", + "time": "Time", + "type": "Type", + "content": "Content", + "tool": "Tool", + "spinner": "Loading", + "framework_subtitle": "Multi-Agents LLM Financial Trading Framework", + "workflow_steps_title": "Workflow Steps:", + "workflow_steps": "I. Analyst Team → II. Research Team → III. Trader → IV. Risk Management → V. Portfolio Management\n\n", + "step1_title": "Step 1: Ticker Symbol", + "step1_prompt": "Enter the ticker symbol to analyze", + "step2_title": "Step 2: Analysis Date", + "step2_prompt": "Enter the analysis date (YYYY-MM-DD)", + "step3_title": "Step 3: Analysts Team", + "step3_prompt": "Select your LLM analyst agents for the analysis", + "step4_title": "Step 4: Research Depth", + "step4_prompt": "Select your research depth level", + "step5_title": "Step 5: Thinking Agents", + "step5_prompt": "Select your thinking agents for analysis", + "default_ticker": "SPY", + "ticker_validate": "Please enter a valid ticker symbol.", + "date_validate": "Please enter a valid date in YYYY-MM-DD format.", + "analyst_instruction": "\n- Press Space to select/unselect analysts\n- Press 'a' to select/unselect all\n- Press Enter when done", + "analyst_validate": "You must select at least one analyst.", + "depth_shallow": "Shallow - Quick research, few debate and strategy discussion rounds", + "depth_medium": "Medium - Middle ground, moderate debate rounds and strategy discussion", + "depth_deep": "Deep - Comprehensive research, in depth debate and strategy discussion", + "depth_instruction": "\n- Use arrow keys to navigate\n- Press Enter to select", +} \ No newline at end of file diff --git a/tradingagents/i18n/zh.py b/tradingagents/i18n/zh.py new file mode 100644 index 00000000..b8952643 --- /dev/null +++ b/tradingagents/i18n/zh.py @@ -0,0 +1,65 @@ +LANG = { + "welcome": "欢迎使用TradingAgents CLI", + "analyst_team": "分析师团队", + "market_analyst": "市场分析师", + "social_media_analyst": "社会媒体分析师", + "social_analyst": "社交分析师", + "news_analyst": "新闻分析师", + "fundamentals_analyst": "基本面分析师", + "research_team": "研究团队", + "bull_researcher": "多头研究员", + "bear_researcher": "空头研究员", + "research_manager": "研究经理", + "trading_team": "交易团队", + "trader": "交易员", + "risk_management": "风险管理", + "risky_analyst": "激进分析师", + "neutral_analyst": "中性分析师", + "safe_analyst": "保守分析师", + "portfolio_management": "投资组合管理", + "portfolio_manager": "投资组合经理", + "market_report": "市场分析报告", + "sentiment_report": "情绪分析报告", + "news_report": "新闻分析报告", + "fundamentals_report": "基本面分析报告", + "investment_plan": "投资计划", + "trader_investment_plan": "交易员投资计划", + "final_trade_decision": "最终交易决策", + "team": "团队", + "agent": "代理人", + "status": "状态", + "progress": "进度", + "messages_tools": "消息与工具", + "current_report": "当前报告", + "pending": "待处理", + "completed": "已完成", + "error": "错误", + "in_progress": "进行中", + "time": "时间", + "type": "类型", + "content": "内容", + "tool": "工具", + "spinner": "加载中", + "framework_subtitle": "多智能体大模型金融交易框架", + "workflow_steps_title": "工作流步骤:", + "workflow_steps": "I. 分析师团队 → II. 研究团队 → III. 交易员 → IV. 风险管理 → V. 投资组合管理\n\n", + "step1_title": "步骤1:股票代码", + "step1_prompt": "请输入要分析的股票代码", + "step2_title": "步骤2:分析日期", + "step2_prompt": "请输入分析日期(YYYY-MM-DD)", + "step3_title": "步骤3:分析师团队", + "step3_prompt": "请选择用于分析的LLM分析师代理", + "step4_title": "步骤4:研究深度", + "step4_prompt": "请选择研究深度等级", + "step5_title": "步骤5:思考代理", + "step5_prompt": "请选择用于分析的思考代理", + "default_ticker": "SPY", + "ticker_validate": "请输入有效的股票代码。", + "date_validate": "请输入有效的日期,格式为YYYY-MM-DD。", + "analyst_instruction": "\n- 空格选择/取消分析师\n- 'a' 全选/取消全选\n- 回车确认", + "analyst_validate": "请至少选择一位分析师。", + "depth_shallow": "浅层 - 快速研究,少量辩论与策略讨论轮次", + "depth_medium": "中等 - 适中研究,适量辩论与策略讨论", + "depth_deep": "深度 - 全面研究,深入辩论与策略讨论", + "depth_instruction": "\n- 使用方向键选择\n- 回车确认", +} \ No newline at end of file