From 2474f7ad589c52eab9a7930bacdcd3e7c39d5b60 Mon Sep 17 00:00:00 2001 From: test Date: Sat, 21 Mar 2026 21:27:37 +0900 Subject: [PATCH] feat: update CLI for Polymarket event input and display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace AnalystType enum: MARKET/FUNDAMENTALS → ODDS/EVENT - Replace get_ticker() with get_event_input() supporting manual URL/ID entry and scan mode - Update ANALYST_ORDER and ANALYST_MAPPING throughout for odds/event analysts - Update MessageBuffer FIXED_AGENTS: Bull/Bear Researcher → YES/NO/Timing Advocate - Update REPORT_SECTIONS: market_report/fundamentals_report → odds_report/event_report, trader_investment_plan → trader_plan, final_trade_decision → final_decision - Update get_user_selections(): event-based flow replacing ticker/date steps - Update run_analysis(): graph.propagate with event_id/event_question, chunk processing for yes/no/timing debate history - Update display_complete_report() and save_report_to_disk() with new field names and folder structure (5_risk_manager instead of 5_portfolio) - Update welcome message and progress display to reflect Polymarket workflow Co-Authored-By: Claude Opus 4.6 (1M context) --- cli/main.py | 259 +++++++++++++++++++++++--------------------------- cli/models.py | 6 +- cli/utils.py | 67 +++++++++---- 3 files changed, 172 insertions(+), 160 deletions(-) diff --git a/cli/main.py b/cli/main.py index adda48fc..7611558c 100644 --- a/cli/main.py +++ b/cli/main.py @@ -43,7 +43,7 @@ app = typer.Typer( class MessageBuffer: # Fixed teams that always run (not user-selectable) FIXED_AGENTS = { - "Research Team": ["Bull Researcher", "Bear Researcher", "Research Manager"], + "Research Team": ["YES Advocate", "NO Advocate", "Timing Advocate", "Research Manager"], "Trading Team": ["Trader"], "Risk Management": ["Aggressive Analyst", "Neutral Analyst", "Conservative Analyst"], "Portfolio Management": ["Portfolio Manager"], @@ -51,23 +51,23 @@ class MessageBuffer: # Analyst name mapping ANALYST_MAPPING = { - "market": "Market Analyst", + "odds": "Odds Analyst", "social": "Social Analyst", "news": "News Analyst", - "fundamentals": "Fundamentals Analyst", + "event": "Event Analyst", } # Report section mapping: section -> (analyst_key for filtering, finalizing_agent) # analyst_key: which analyst selection controls this section (None = always included) # finalizing_agent: which agent must be "completed" for this report to count as done REPORT_SECTIONS = { - "market_report": ("market", "Market Analyst"), + "odds_report": ("odds", "Odds Analyst"), "sentiment_report": ("social", "Social Analyst"), "news_report": ("news", "News Analyst"), - "fundamentals_report": ("fundamentals", "Fundamentals Analyst"), + "event_report": ("event", "Event Analyst"), "investment_plan": (None, "Research Manager"), - "trader_investment_plan": (None, "Trader"), - "final_trade_decision": (None, "Portfolio Manager"), + "trader_plan": (None, "Trader"), + "final_decision": (None, "Portfolio Manager"), } def __init__(self, max_length=100): @@ -169,13 +169,13 @@ class MessageBuffer: if latest_section and latest_content: # Format the current section for display section_titles = { - "market_report": "Market Analysis", + "odds_report": "Odds Analysis", "sentiment_report": "Social Sentiment", "news_report": "News Analysis", - "fundamentals_report": "Fundamentals Analysis", + "event_report": "Event Analysis", "investment_plan": "Research Team Decision", - "trader_investment_plan": "Trading Team Plan", - "final_trade_decision": "Portfolio Management Decision", + "trader_plan": "Trader Plan", + "final_decision": "Portfolio Management Decision", } self.current_report = ( f"### {section_titles[latest_section]}\n{latest_content}" @@ -188,12 +188,12 @@ class MessageBuffer: report_parts = [] # Analyst Team Reports - use .get() to handle missing sections - analyst_sections = ["market_report", "sentiment_report", "news_report", "fundamentals_report"] + analyst_sections = ["odds_report", "sentiment_report", "news_report", "event_report"] if any(self.report_sections.get(section) for section in analyst_sections): report_parts.append("## Analyst Team Reports") - if self.report_sections.get("market_report"): + if self.report_sections.get("odds_report"): report_parts.append( - f"### Market Analysis\n{self.report_sections['market_report']}" + f"### Odds Analysis\n{self.report_sections['odds_report']}" ) if self.report_sections.get("sentiment_report"): report_parts.append( @@ -203,9 +203,9 @@ class MessageBuffer: report_parts.append( f"### News Analysis\n{self.report_sections['news_report']}" ) - if self.report_sections.get("fundamentals_report"): + if self.report_sections.get("event_report"): report_parts.append( - f"### Fundamentals Analysis\n{self.report_sections['fundamentals_report']}" + f"### Event Analysis\n{self.report_sections['event_report']}" ) # Research Team Reports @@ -214,14 +214,14 @@ class MessageBuffer: report_parts.append(f"{self.report_sections['investment_plan']}") # Trading Team Reports - if self.report_sections.get("trader_investment_plan"): - report_parts.append("## Trading Team Plan") - report_parts.append(f"{self.report_sections['trader_investment_plan']}") + if self.report_sections.get("trader_plan"): + report_parts.append("## Trader Plan") + report_parts.append(f"{self.report_sections['trader_plan']}") # Portfolio Management Decision - if self.report_sections.get("final_trade_decision"): + if self.report_sections.get("final_decision"): report_parts.append("## Portfolio Management Decision") - report_parts.append(f"{self.report_sections['final_trade_decision']}") + report_parts.append(f"{self.report_sections['final_decision']}") self.final_report = "\n\n".join(report_parts) if report_parts else None @@ -282,12 +282,12 @@ def update_display(layout, spinner_text=None, stats_handler=None, start_time=Non # Group agents by team - filter to only include agents in agent_status all_teams = { "Analyst Team": [ - "Market Analyst", + "Odds Analyst", "Social Analyst", "News Analyst", - "Fundamentals Analyst", + "Event Analyst", ], - "Research Team": ["Bull Researcher", "Bear Researcher", "Research Manager"], + "Research Team": ["YES Advocate", "NO Advocate", "Timing Advocate", "Research Manager"], "Trading Team": ["Trader"], "Risk Management": ["Aggressive Analyst", "Neutral Analyst", "Conservative Analyst"], "Portfolio Management": ["Portfolio Manager"], @@ -467,9 +467,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 green]TradingAgents: Polymarket Prediction Market Analysis 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 += "I. Analyst Team → II. Research Team (YES/NO/Timing Debate) → III. Trader → IV. Risk Management → V. Portfolio Management\n\n" welcome_content += ( "[dim]Built by [Tauric Research](https://github.com/TauricResearch)[/dim]" ) @@ -498,29 +498,18 @@ def get_user_selections(): box_content += f"\n[dim]Default: {default}[/dim]" return Panel(box_content, border_style="blue", padding=(1, 2)) - # Step 1: Ticker symbol + # Step 1: Event input console.print( create_question_box( - "Step 1: Ticker Symbol", "Enter the ticker symbol to analyze", "SPY" + "Step 1: Polymarket Event", "Enter a Polymarket event ID or URL to analyze" ) ) - selected_ticker = get_ticker() + event_info = get_event_input() - # Step 2: Analysis date - default_date = datetime.datetime.now().strftime("%Y-%m-%d") + # Step 2: Select analysts console.print( create_question_box( - "Step 2: Analysis Date", - "Enter the analysis date (YYYY-MM-DD)", - default_date, - ) - ) - analysis_date = get_analysis_date() - - # Step 3: Select analysts - console.print( - create_question_box( - "Step 3: Analysts Team", "Select your LLM analyst agents for the analysis" + "Step 2: Analysts Team", "Select your LLM analyst agents for the analysis" ) ) selected_analysts = select_analysts() @@ -528,32 +517,32 @@ def get_user_selections(): f"[green]Selected analysts:[/green] {', '.join(analyst.value for analyst in selected_analysts)}" ) - # Step 4: Research depth + # Step 3: Research depth console.print( create_question_box( - "Step 4: Research Depth", "Select your research depth level" + "Step 3: Research Depth", "Select your research depth level" ) ) selected_research_depth = select_research_depth() - # Step 5: OpenAI backend + # Step 4: OpenAI backend console.print( create_question_box( - "Step 5: OpenAI backend", "Select which service to talk to" + "Step 4: LLM Provider", "Select which service to talk to" ) ) selected_llm_provider, backend_url = select_llm_provider() - - # Step 6: Thinking agents + + # Step 5: Thinking agents console.print( create_question_box( - "Step 6: Thinking Agents", "Select your thinking agents for analysis" + "Step 5: Thinking Agents", "Select your thinking agents for analysis" ) ) selected_shallow_thinker = select_shallow_thinking_agent(selected_llm_provider) selected_deep_thinker = select_deep_thinking_agent(selected_llm_provider) - # Step 7: Provider-specific thinking configuration + # Step 6: Provider-specific thinking configuration thinking_level = None reasoning_effort = None @@ -561,7 +550,7 @@ def get_user_selections(): if provider_lower == "google": console.print( create_question_box( - "Step 7: Thinking Mode", + "Step 6: Thinking Mode", "Configure Gemini thinking mode" ) ) @@ -569,15 +558,16 @@ def get_user_selections(): elif provider_lower == "openai": console.print( create_question_box( - "Step 7: Reasoning Effort", + "Step 6: Reasoning Effort", "Configure OpenAI reasoning effort level" ) ) reasoning_effort = ask_openai_reasoning_effort() return { - "ticker": selected_ticker, - "analysis_date": analysis_date, + "event_id": event_info["event_id"], + "event_question": event_info.get("event_question", event_info["event_id"]), + "analysis_date": datetime.datetime.now().strftime("%Y-%m-%d"), "analysts": selected_analysts, "research_depth": selected_research_depth, "llm_provider": selected_llm_provider.lower(), @@ -589,31 +579,8 @@ def get_user_selections(): } -def get_ticker(): - """Get ticker symbol from user input.""" - return typer.prompt("", default="SPY") - -def get_analysis_date(): - """Get the analysis date from user input.""" - while True: - date_str = typer.prompt( - "", default=datetime.datetime.now().strftime("%Y-%m-%d") - ) - try: - # Validate date format and ensure it's not in the future - analysis_date = datetime.datetime.strptime(date_str, "%Y-%m-%d") - if analysis_date.date() > datetime.datetime.now().date(): - console.print("[red]Error: Analysis date cannot be in the future[/red]") - continue - return date_str - except ValueError: - console.print( - "[red]Error: Invalid date format. Please use YYYY-MM-DD[/red]" - ) - - -def save_report_to_disk(final_state, ticker: str, save_path: Path): +def save_report_to_disk(final_state, event_id: str, save_path: Path): """Save complete analysis report to disk with organized subfolders.""" save_path.mkdir(parents=True, exist_ok=True) sections = [] @@ -621,10 +588,10 @@ def save_report_to_disk(final_state, ticker: str, save_path: Path): # 1. Analysts analysts_dir = save_path / "1_analysts" analyst_parts = [] - if final_state.get("market_report"): + if final_state.get("odds_report"): analysts_dir.mkdir(exist_ok=True) - (analysts_dir / "market.md").write_text(final_state["market_report"]) - analyst_parts.append(("Market Analyst", final_state["market_report"])) + (analysts_dir / "odds.md").write_text(final_state["odds_report"]) + analyst_parts.append(("Odds Analyst", final_state["odds_report"])) if final_state.get("sentiment_report"): analysts_dir.mkdir(exist_ok=True) (analysts_dir / "sentiment.md").write_text(final_state["sentiment_report"]) @@ -633,10 +600,10 @@ def save_report_to_disk(final_state, ticker: str, save_path: Path): analysts_dir.mkdir(exist_ok=True) (analysts_dir / "news.md").write_text(final_state["news_report"]) analyst_parts.append(("News Analyst", final_state["news_report"])) - if final_state.get("fundamentals_report"): + if final_state.get("event_report"): analysts_dir.mkdir(exist_ok=True) - (analysts_dir / "fundamentals.md").write_text(final_state["fundamentals_report"]) - analyst_parts.append(("Fundamentals Analyst", final_state["fundamentals_report"])) + (analysts_dir / "event.md").write_text(final_state["event_report"]) + analyst_parts.append(("Event Analyst", final_state["event_report"])) if analyst_parts: content = "\n\n".join(f"### {name}\n{text}" for name, text in analyst_parts) sections.append(f"## I. Analyst Team Reports\n\n{content}") @@ -646,14 +613,18 @@ def save_report_to_disk(final_state, ticker: str, save_path: Path): research_dir = save_path / "2_research" debate = final_state["investment_debate_state"] research_parts = [] - if debate.get("bull_history"): + if debate.get("yes_history"): research_dir.mkdir(exist_ok=True) - (research_dir / "bull.md").write_text(debate["bull_history"]) - research_parts.append(("Bull Researcher", debate["bull_history"])) - if debate.get("bear_history"): + (research_dir / "yes_advocate.md").write_text(debate["yes_history"]) + research_parts.append(("YES Advocate", debate["yes_history"])) + if debate.get("no_history"): research_dir.mkdir(exist_ok=True) - (research_dir / "bear.md").write_text(debate["bear_history"]) - research_parts.append(("Bear Researcher", debate["bear_history"])) + (research_dir / "no_advocate.md").write_text(debate["no_history"]) + research_parts.append(("NO Advocate", debate["no_history"])) + if debate.get("timing_history"): + research_dir.mkdir(exist_ok=True) + (research_dir / "timing_advocate.md").write_text(debate["timing_history"]) + research_parts.append(("Timing Advocate", debate["timing_history"])) if debate.get("judge_decision"): research_dir.mkdir(exist_ok=True) (research_dir / "manager.md").write_text(debate["judge_decision"]) @@ -663,11 +634,11 @@ def save_report_to_disk(final_state, ticker: str, save_path: Path): sections.append(f"## II. Research Team Decision\n\n{content}") # 3. Trading - if final_state.get("trader_investment_plan"): + if final_state.get("trader_plan"): trading_dir = save_path / "3_trading" trading_dir.mkdir(exist_ok=True) - (trading_dir / "trader.md").write_text(final_state["trader_investment_plan"]) - sections.append(f"## III. Trading Team Plan\n\n### Trader\n{final_state['trader_investment_plan']}") + (trading_dir / "trader.md").write_text(final_state["trader_plan"]) + sections.append(f"## III. Trader Plan\n\n### Trader\n{final_state['trader_plan']}") # 4. Risk Management if final_state.get("risk_debate_state"): @@ -690,15 +661,15 @@ def save_report_to_disk(final_state, ticker: str, save_path: Path): content = "\n\n".join(f"### {name}\n{text}" for name, text in risk_parts) sections.append(f"## IV. Risk Management Team Decision\n\n{content}") - # 5. Portfolio Manager + # 5. Risk Manager / Portfolio Manager if risk.get("judge_decision"): - portfolio_dir = save_path / "5_portfolio" - portfolio_dir.mkdir(exist_ok=True) - (portfolio_dir / "decision.md").write_text(risk["judge_decision"]) + risk_mgr_dir = save_path / "5_risk_manager" + risk_mgr_dir.mkdir(exist_ok=True) + (risk_mgr_dir / "decision.md").write_text(risk["judge_decision"]) sections.append(f"## V. Portfolio Manager Decision\n\n### Portfolio Manager\n{risk['judge_decision']}") # Write consolidated report - header = f"# Trading Analysis Report: {ticker}\n\nGenerated: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n" + header = f"# Polymarket Analysis Report: {event_id}\n\nGenerated: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n" (save_path / "complete_report.md").write_text(header + "\n\n".join(sections)) return save_path / "complete_report.md" @@ -710,14 +681,14 @@ def display_complete_report(final_state): # I. Analyst Team Reports analysts = [] - if final_state.get("market_report"): - analysts.append(("Market Analyst", final_state["market_report"])) + if final_state.get("odds_report"): + analysts.append(("Odds Analyst", final_state["odds_report"])) if final_state.get("sentiment_report"): analysts.append(("Social Analyst", final_state["sentiment_report"])) if final_state.get("news_report"): analysts.append(("News Analyst", final_state["news_report"])) - if final_state.get("fundamentals_report"): - analysts.append(("Fundamentals Analyst", final_state["fundamentals_report"])) + if final_state.get("event_report"): + analysts.append(("Event Analyst", final_state["event_report"])) if analysts: console.print(Panel("[bold]I. Analyst Team Reports[/bold]", border_style="cyan")) for title, content in analysts: @@ -727,10 +698,12 @@ def display_complete_report(final_state): if final_state.get("investment_debate_state"): debate = final_state["investment_debate_state"] research = [] - if debate.get("bull_history"): - research.append(("Bull Researcher", debate["bull_history"])) - if debate.get("bear_history"): - research.append(("Bear Researcher", debate["bear_history"])) + if debate.get("yes_history"): + research.append(("YES Advocate", debate["yes_history"])) + if debate.get("no_history"): + research.append(("NO Advocate", debate["no_history"])) + if debate.get("timing_history"): + research.append(("Timing Advocate", debate["timing_history"])) if debate.get("judge_decision"): research.append(("Research Manager", debate["judge_decision"])) if research: @@ -738,10 +711,10 @@ def display_complete_report(final_state): for title, content in research: console.print(Panel(Markdown(content), title=title, border_style="blue", padding=(1, 2))) - # III. Trading Team - if final_state.get("trader_investment_plan"): - console.print(Panel("[bold]III. Trading Team Plan[/bold]", border_style="yellow")) - console.print(Panel(Markdown(final_state["trader_investment_plan"]), title="Trader", border_style="blue", padding=(1, 2))) + # III. Trader Plan + if final_state.get("trader_plan"): + console.print(Panel("[bold]III. Trader Plan[/bold]", border_style="yellow")) + console.print(Panel(Markdown(final_state["trader_plan"]), title="Trader", border_style="blue", padding=(1, 2))) # IV. Risk Management Team if final_state.get("risk_debate_state"): @@ -766,24 +739,24 @@ def display_complete_report(final_state): def update_research_team_status(status): """Update status for research team members (not Trader).""" - research_team = ["Bull Researcher", "Bear Researcher", "Research Manager"] + research_team = ["YES Advocate", "NO Advocate", "Timing Advocate", "Research Manager"] for agent in research_team: message_buffer.update_agent_status(agent, status) # Ordered list of analysts for status transitions -ANALYST_ORDER = ["market", "social", "news", "fundamentals"] +ANALYST_ORDER = ["odds", "social", "news", "event"] ANALYST_AGENT_NAMES = { - "market": "Market Analyst", + "odds": "Odds Analyst", "social": "Social Analyst", "news": "News Analyst", - "fundamentals": "Fundamentals Analyst", + "event": "Event Analyst", } ANALYST_REPORT_MAP = { - "market": "market_report", + "odds": "odds_report", "social": "sentiment_report", "news": "news_report", - "fundamentals": "fundamentals_report", + "event": "event_report", } @@ -818,8 +791,8 @@ def update_analyst_statuses(message_buffer, chunk): # When all analysts complete, transition research team to in_progress if not found_active and selected: - if message_buffer.agent_status.get("Bull Researcher") == "pending": - message_buffer.update_agent_status("Bull Researcher", "in_progress") + if message_buffer.agent_status.get("YES Advocate") == "pending": + message_buffer.update_agent_status("YES Advocate", "in_progress") def extract_content_string(content): """Extract string content from various message formats. @@ -934,7 +907,7 @@ def run_analysis(): start_time = time.time() # Create result directory - results_dir = Path(config["results_dir"]) / selections["ticker"] / selections["analysis_date"] + results_dir = Path(config["results_dir"]) / selections["event_id"] / selections["analysis_date"] results_dir.mkdir(parents=True, exist_ok=True) report_dir = results_dir / "reports" report_dir.mkdir(parents=True, exist_ok=True) @@ -988,7 +961,7 @@ def run_analysis(): update_display(layout, stats_handler=stats_handler, start_time=start_time) # Add initial messages - message_buffer.add_message("System", f"Selected ticker: {selections['ticker']}") + message_buffer.add_message("System", f"Selected event: {selections['event_id']}") message_buffer.add_message( "System", f"Analysis date: {selections['analysis_date']}" ) @@ -999,19 +972,20 @@ def run_analysis(): update_display(layout, stats_handler=stats_handler, start_time=start_time) # Update agent status to in_progress for the first analyst - first_analyst = f"{selections['analysts'][0].value.capitalize()} Analyst" + first_analyst_key = selections['analysts'][0].value + first_analyst = ANALYST_AGENT_NAMES.get(first_analyst_key, f"{first_analyst_key.capitalize()} Analyst") message_buffer.update_agent_status(first_analyst, "in_progress") update_display(layout, stats_handler=stats_handler, start_time=start_time) # Create spinner text spinner_text = ( - f"Analyzing {selections['ticker']} on {selections['analysis_date']}..." + f"Analyzing event {selections['event_id']}..." ) update_display(layout, spinner_text, stats_handler=stats_handler, start_time=start_time) # Initialize state and get graph args with callbacks init_agent_state = graph.propagator.create_initial_state( - selections["ticker"], selections["analysis_date"] + selections["event_id"], selections.get("event_question", selections["event_id"]), selections["analysis_date"] ) # Pass callbacks to graph config for tool execution tracking # (LLM tracking is handled separately via LLM constructor) @@ -1049,20 +1023,25 @@ def run_analysis(): # Research Team - Handle Investment Debate State if chunk.get("investment_debate_state"): debate_state = chunk["investment_debate_state"] - bull_hist = debate_state.get("bull_history", "").strip() - bear_hist = debate_state.get("bear_history", "").strip() + yes_hist = debate_state.get("yes_history", "").strip() + no_hist = debate_state.get("no_history", "").strip() + timing_hist = debate_state.get("timing_history", "").strip() judge = debate_state.get("judge_decision", "").strip() # Only update status when there's actual content - if bull_hist or bear_hist: + if yes_hist or no_hist or timing_hist: update_research_team_status("in_progress") - if bull_hist: + if yes_hist: message_buffer.update_report_section( - "investment_plan", f"### Bull Researcher Analysis\n{bull_hist}" + "investment_plan", f"### YES Advocate Analysis\n{yes_hist}" ) - if bear_hist: + if no_hist: message_buffer.update_report_section( - "investment_plan", f"### Bear Researcher Analysis\n{bear_hist}" + "investment_plan", f"### NO Advocate Analysis\n{no_hist}" + ) + if timing_hist: + message_buffer.update_report_section( + "investment_plan", f"### Timing Advocate Analysis\n{timing_hist}" ) if judge: message_buffer.update_report_section( @@ -1072,9 +1051,9 @@ def run_analysis(): message_buffer.update_agent_status("Trader", "in_progress") # Trading Team - if chunk.get("trader_investment_plan"): + if chunk.get("trader_plan"): message_buffer.update_report_section( - "trader_investment_plan", chunk["trader_investment_plan"] + "trader_plan", chunk["trader_plan"] ) if message_buffer.agent_status.get("Trader") != "completed": message_buffer.update_agent_status("Trader", "completed") @@ -1092,25 +1071,25 @@ def run_analysis(): if message_buffer.agent_status.get("Aggressive Analyst") != "completed": message_buffer.update_agent_status("Aggressive Analyst", "in_progress") message_buffer.update_report_section( - "final_trade_decision", f"### Aggressive Analyst Analysis\n{agg_hist}" + "final_decision", f"### Aggressive Analyst Analysis\n{agg_hist}" ) if con_hist: if message_buffer.agent_status.get("Conservative Analyst") != "completed": message_buffer.update_agent_status("Conservative Analyst", "in_progress") message_buffer.update_report_section( - "final_trade_decision", f"### Conservative Analyst Analysis\n{con_hist}" + "final_decision", f"### Conservative Analyst Analysis\n{con_hist}" ) if neu_hist: if message_buffer.agent_status.get("Neutral Analyst") != "completed": message_buffer.update_agent_status("Neutral Analyst", "in_progress") message_buffer.update_report_section( - "final_trade_decision", f"### Neutral Analyst Analysis\n{neu_hist}" + "final_decision", f"### Neutral Analyst Analysis\n{neu_hist}" ) if judge: if message_buffer.agent_status.get("Portfolio Manager") != "completed": message_buffer.update_agent_status("Portfolio Manager", "in_progress") message_buffer.update_report_section( - "final_trade_decision", f"### Portfolio Manager Decision\n{judge}" + "final_decision", f"### Portfolio Manager Decision\n{judge}" ) message_buffer.update_agent_status("Aggressive Analyst", "completed") message_buffer.update_agent_status("Conservative Analyst", "completed") @@ -1124,14 +1103,14 @@ def run_analysis(): # Get final state and decision final_state = trace[-1] - decision = graph.process_signal(final_state["final_trade_decision"]) + decision = graph.process_signal(final_state["final_decision"]) # Update all agent statuses to completed for agent in message_buffer.agent_status: message_buffer.update_agent_status(agent, "completed") message_buffer.add_message( - "System", f"Completed analysis for {selections['analysis_date']}" + "System", f"Completed analysis for event {selections['event_id']}" ) # Update final report sections @@ -1148,14 +1127,14 @@ def run_analysis(): save_choice = typer.prompt("Save report?", default="Y").strip().upper() if save_choice in ("Y", "YES", ""): timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") - default_path = Path.cwd() / "reports" / f"{selections['ticker']}_{timestamp}" + default_path = Path.cwd() / "reports" / f"{selections['event_id']}_{timestamp}" save_path_str = typer.prompt( "Save path (press Enter for default)", default=str(default_path) ).strip() save_path = Path(save_path_str) try: - report_file = save_report_to_disk(final_state, selections["ticker"], save_path) + report_file = save_report_to_disk(final_state, selections["event_id"], save_path) console.print(f"\n[green]✓ Report saved to:[/green] {save_path.resolve()}") console.print(f" [dim]Complete report:[/dim] {report_file.name}") except Exception as e: diff --git a/cli/models.py b/cli/models.py index f68c3da1..0cee161d 100644 --- a/cli/models.py +++ b/cli/models.py @@ -1,10 +1,8 @@ from enum import Enum -from typing import List, Optional, Dict -from pydantic import BaseModel class AnalystType(str, Enum): - MARKET = "market" + ODDS = "odds" SOCIAL = "social" NEWS = "news" - FUNDAMENTALS = "fundamentals" + EVENT = "event" diff --git a/cli/utils.py b/cli/utils.py index 5a8ec16c..73b107c4 100644 --- a/cli/utils.py +++ b/cli/utils.py @@ -8,31 +8,66 @@ from cli.models import AnalystType console = Console() ANALYST_ORDER = [ - ("Market Analyst", AnalystType.MARKET), + ("Odds Analyst", AnalystType.ODDS), ("Social Media Analyst", AnalystType.SOCIAL), ("News Analyst", AnalystType.NEWS), - ("Fundamentals Analyst", AnalystType.FUNDAMENTALS), + ("Event Analyst", AnalystType.EVENT), ] -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.", - style=questionary.Style( - [ - ("text", "fg:green"), - ("highlighted", "noinherit"), - ] - ), +def get_event_input() -> dict: + """Get event selection from user - manual or scan mode.""" + mode = questionary.select( + "Select input mode:", + choices=[ + questionary.Choice("Manual - Enter event URL or ID", value="manual"), + questionary.Choice("Scan - Search active markets", value="scan"), + ], + style=questionary.Style([ + ("selected", "fg:green noinherit"), + ("highlighted", "noinherit"), + ("pointer", "noinherit"), + ]), ).ask() - if not ticker: - console.print("\n[red]No ticker symbol provided. Exiting...[/red]") + if mode is None: + console.print("\n[red]No mode selected. Exiting...[/red]") exit(1) - return ticker.strip().upper() + if mode == "manual": + event_input = questionary.text( + "Enter Polymarket event ID or URL:", + validate=lambda x: len(x.strip()) > 0 or "Please enter an event ID or URL.", + style=questionary.Style([ + ("text", "fg:green"), + ("highlighted", "noinherit"), + ]), + ).ask() + if not event_input: + console.print("\n[red]No event provided. Exiting...[/red]") + exit(1) + # Parse URL if needed + event_id = event_input.strip() + if "polymarket.com" in event_id: + # Extract slug from URL like polymarket.com/event/slug-here + parts = event_id.rstrip("/").split("/") + event_id = parts[-1] if parts else event_id + return {"event_id": event_id, "mode": "manual"} + else: + # Scan mode - show filter options then search + console.print("[dim]Searching active markets...[/dim]") + from tradingagents.agents.utils.polymarket_tools import search_markets + results = search_markets.invoke({"min_volume": 10000, "limit": 10}) + console.print(results) + + event_id = questionary.text( + "Enter event ID from the results above:", + style=questionary.Style([("text", "fg:green"), ("highlighted", "noinherit")]), + ).ask() + if not event_id: + console.print("\n[red]No event selected. Exiting...[/red]") + exit(1) + return {"event_id": event_id.strip(), "mode": "scan"} def get_analysis_date() -> str: