feat: update graph workflow for Polymarket 3-way debate

This commit is contained in:
test 2026-03-21 21:21:19 +09:00
parent 26657acab5
commit 7e45020dbb
5 changed files with 239 additions and 235 deletions

View File

@ -1,6 +1,5 @@
# TradingAgents/graph/propagation.py
from typing import Dict, Any, List, Optional from typing import Dict, Any, List, Optional
from tradingagents.agents.utils.agent_states import ( from tradingagents.agents.utils.agent_states import (
AgentState, AgentState,
InvestDebateState, InvestDebateState,
@ -9,32 +8,38 @@ from tradingagents.agents.utils.agent_states import (
class Propagator: class Propagator:
"""Handles state initialization and propagation through the graph.""" """Handles state initialization and graph argument configuration."""
def __init__(self, max_recur_limit=100): def __init__(self, max_recur_limit=100):
"""Initialize with configuration parameters."""
self.max_recur_limit = max_recur_limit self.max_recur_limit = max_recur_limit
def create_initial_state( def create_initial_state(self, event_id, event_question, trade_date):
self, company_name: str, trade_date: str """Create the initial agent state for a Polymarket event analysis."""
) -> Dict[str, Any]:
"""Create the initial state for the agent graph."""
return { return {
"messages": [("human", company_name)], "messages": [("human", event_question)],
"company_of_interest": company_name, "event_id": event_id,
"event_question": event_question,
"trade_date": str(trade_date), "trade_date": str(trade_date),
"investment_debate_state": InvestDebateState( "sender": "",
{ "odds_report": "",
"bull_history": "", "sentiment_report": "",
"bear_history": "", "news_report": "",
"event_report": "",
"investment_debate_state": {
"yes_history": "",
"no_history": "",
"timing_history": "",
"history": "", "history": "",
"current_response": "", "current_yes_response": "",
"current_no_response": "",
"current_timing_response": "",
"latest_speaker": "",
"judge_decision": "", "judge_decision": "",
"count": 0, "count": 0,
} },
), "investment_plan": "",
"risk_debate_state": RiskDebateState( "trader_plan": "",
{ "risk_debate_state": {
"aggressive_history": "", "aggressive_history": "",
"conservative_history": "", "conservative_history": "",
"neutral_history": "", "neutral_history": "",
@ -45,21 +50,12 @@ class Propagator:
"current_neutral_response": "", "current_neutral_response": "",
"judge_decision": "", "judge_decision": "",
"count": 0, "count": 0,
} },
), "final_decision": "",
"market_report": "",
"fundamentals_report": "",
"sentiment_report": "",
"news_report": "",
} }
def get_graph_args(self, callbacks: Optional[List] = None) -> Dict[str, Any]: def get_graph_args(self, callbacks=None):
"""Get arguments for the graph invocation. """Get the arguments for graph invocation."""
Args:
callbacks: Optional list of callback handlers for tool execution tracking.
Note: LLM callbacks are handled separately via LLM constructor.
"""
config = {"recursion_limit": self.max_recur_limit} config = {"recursion_limit": self.max_recur_limit}
if callbacks: if callbacks:
config["callbacks"] = callbacks config["callbacks"] = callbacks

View File

@ -15,45 +15,42 @@ class Reflector:
def _get_reflection_prompt(self) -> str: def _get_reflection_prompt(self) -> str:
"""Get the system prompt for reflection.""" """Get the system prompt for reflection."""
return """ return """
You are an expert financial analyst tasked with reviewing trading decisions/analysis and providing a comprehensive, step-by-step analysis. You are an expert prediction market analyst tasked with reviewing trading decisions/analysis and providing a comprehensive, step-by-step analysis.
Your goal is to deliver detailed insights into investment decisions and highlight opportunities for improvement, adhering strictly to the following guidelines: Your goal is to deliver detailed insights into prediction market decisions and highlight opportunities for improvement, adhering strictly to the following guidelines:
1. Reasoning: 1. Reasoning:
- For each trading decision, determine whether it was correct or incorrect. A correct decision results in an increase in returns, while an incorrect decision does the opposite. - For each prediction decision, determine whether it was correct or incorrect. A correct decision results in a positive return, while an incorrect decision does the opposite.
- Analyze the contributing factors to each success or mistake. Consider: - Analyze the contributing factors to each success or mistake. Consider:
- Market intelligence. - Market odds and price movements.
- Technical indicators. - Order book depth and whale activity.
- Technical signals. - News and event analysis.
- Price movement analysis.
- Overall market data analysis
- News analysis.
- Social media and sentiment analysis. - Social media and sentiment analysis.
- Fundamental data analysis. - Timing and event resolution timeline.
- Weight the importance of each factor in the decision-making process. - Weight the importance of each factor in the decision-making process.
2. Improvement: 2. Improvement:
- For any incorrect decisions, propose revisions to maximize returns. - For any incorrect decisions, propose revisions to maximize returns.
- Provide a detailed list of corrective actions or improvements, including specific recommendations (e.g., changing a decision from HOLD to BUY on a particular date). - Provide a detailed list of corrective actions or improvements, including specific recommendations (e.g., changing a decision from SKIP to YES on a particular event).
3. Summary: 3. Summary:
- Summarize the lessons learned from the successes and mistakes. - Summarize the lessons learned from the successes and mistakes.
- Highlight how these lessons can be adapted for future trading scenarios and draw connections between similar situations to apply the knowledge gained. - Highlight how these lessons can be adapted for future prediction market scenarios and draw connections between similar situations to apply the knowledge gained.
4. Query: 4. Query:
- Extract key insights from the summary into a concise sentence of no more than 1000 tokens. - Extract key insights from the summary into a concise sentence of no more than 1000 tokens.
- Ensure the condensed sentence captures the essence of the lessons and reasoning for easy reference. - Ensure the condensed sentence captures the essence of the lessons and reasoning for easy reference.
Adhere strictly to these instructions, and ensure your output is detailed, accurate, and actionable. You will also be given objective descriptions of the market from a price movements, technical indicator, news, and sentiment perspective to provide more context for your analysis. Adhere strictly to these instructions, and ensure your output is detailed, accurate, and actionable. You will also be given objective descriptions of the market from an odds, news, and sentiment perspective to provide more context for your analysis.
""" """
def _extract_current_situation(self, current_state: Dict[str, Any]) -> str: def _extract_current_situation(self, current_state: Dict[str, Any]) -> str:
"""Extract the current market situation from the state.""" """Extract the current market situation from the state."""
curr_market_report = current_state["market_report"] curr_odds_report = current_state["odds_report"]
curr_sentiment_report = current_state["sentiment_report"] curr_sentiment_report = current_state["sentiment_report"]
curr_news_report = current_state["news_report"] curr_news_report = current_state["news_report"]
curr_fundamentals_report = current_state["fundamentals_report"] curr_event_report = current_state["event_report"]
return f"{curr_market_report}\n\n{curr_sentiment_report}\n\n{curr_news_report}\n\n{curr_fundamentals_report}" return f"{curr_odds_report}\n\n{curr_sentiment_report}\n\n{curr_news_report}\n\n{curr_event_report}"
def _reflect_on_component( def _reflect_on_component(
self, component_type: str, report: str, situation: str, returns_losses self, component_type: str, report: str, situation: str, returns_losses
@ -70,30 +67,40 @@ Adhere strictly to these instructions, and ensure your output is detailed, accur
result = self.quick_thinking_llm.invoke(messages).content result = self.quick_thinking_llm.invoke(messages).content
return result return result
def reflect_bull_researcher(self, current_state, returns_losses, bull_memory): def reflect_yes_advocate(self, current_state, returns_losses, yes_memory):
"""Reflect on bull researcher's analysis and update memory.""" """Reflect on YES advocate's analysis and update memory."""
situation = self._extract_current_situation(current_state) situation = self._extract_current_situation(current_state)
bull_debate_history = current_state["investment_debate_state"]["bull_history"] yes_debate_history = current_state["investment_debate_state"]["yes_history"]
result = self._reflect_on_component( result = self._reflect_on_component(
"BULL", bull_debate_history, situation, returns_losses "YES", yes_debate_history, situation, returns_losses
) )
bull_memory.add_situations([(situation, result)]) yes_memory.add_situations([(situation, result)])
def reflect_bear_researcher(self, current_state, returns_losses, bear_memory): def reflect_no_advocate(self, current_state, returns_losses, no_memory):
"""Reflect on bear researcher's analysis and update memory.""" """Reflect on NO advocate's analysis and update memory."""
situation = self._extract_current_situation(current_state) situation = self._extract_current_situation(current_state)
bear_debate_history = current_state["investment_debate_state"]["bear_history"] no_debate_history = current_state["investment_debate_state"]["no_history"]
result = self._reflect_on_component( result = self._reflect_on_component(
"BEAR", bear_debate_history, situation, returns_losses "NO", no_debate_history, situation, returns_losses
) )
bear_memory.add_situations([(situation, result)]) no_memory.add_situations([(situation, result)])
def reflect_timing_advocate(self, current_state, returns_losses, timing_memory):
"""Reflect on timing advocate's analysis and update memory."""
situation = self._extract_current_situation(current_state)
timing_debate_history = current_state["investment_debate_state"]["timing_history"]
result = self._reflect_on_component(
"TIMING", timing_debate_history, situation, returns_losses
)
timing_memory.add_situations([(situation, result)])
def reflect_trader(self, current_state, returns_losses, trader_memory): def reflect_trader(self, current_state, returns_losses, trader_memory):
"""Reflect on trader's decision and update memory.""" """Reflect on trader's decision and update memory."""
situation = self._extract_current_situation(current_state) situation = self._extract_current_situation(current_state)
trader_decision = current_state["trader_investment_plan"] trader_decision = current_state["trader_plan"]
result = self._reflect_on_component( result = self._reflect_on_component(
"TRADER", trader_decision, situation, returns_losses "TRADER", trader_decision, situation, returns_losses

View File

@ -10,6 +10,14 @@ from tradingagents.agents.utils.agent_states import AgentState
from .conditional_logic import ConditionalLogic from .conditional_logic import ConditionalLogic
# Map analyst key to (display name, factory function, clear name)
_ANALYST_MAP = {
"odds": ("Odds Analyst", create_odds_analyst, "Msg Clear Odds"),
"social": ("Social Analyst", create_social_media_analyst, "Msg Clear Social"),
"news": ("News Analyst", create_news_analyst, "Msg Clear News"),
"event": ("Event Analyst", create_event_analyst, "Msg Clear Event"),
}
class GraphSetup: class GraphSetup:
"""Handles the setup and configuration of the agent graph.""" """Handles the setup and configuration of the agent graph."""
@ -21,6 +29,7 @@ class GraphSetup:
tool_nodes: Dict[str, ToolNode], tool_nodes: Dict[str, ToolNode],
bull_memory, bull_memory,
bear_memory, bear_memory,
timing_memory,
trader_memory, trader_memory,
invest_judge_memory, invest_judge_memory,
risk_manager_memory, risk_manager_memory,
@ -32,66 +41,55 @@ class GraphSetup:
self.tool_nodes = tool_nodes self.tool_nodes = tool_nodes
self.bull_memory = bull_memory self.bull_memory = bull_memory
self.bear_memory = bear_memory self.bear_memory = bear_memory
self.timing_memory = timing_memory
self.trader_memory = trader_memory self.trader_memory = trader_memory
self.invest_judge_memory = invest_judge_memory self.invest_judge_memory = invest_judge_memory
self.risk_manager_memory = risk_manager_memory self.risk_manager_memory = risk_manager_memory
self.conditional_logic = conditional_logic self.conditional_logic = conditional_logic
def setup_graph( def setup_graph(
self, selected_analysts=["market", "social", "news", "fundamentals"] self, selected_analysts=["odds", "social", "news", "event"]
): ):
"""Set up and compile the agent workflow graph. """Set up and compile the agent workflow graph.
Args: Args:
selected_analysts (list): List of analyst types to include. Options are: selected_analysts (list): List of analyst types to include. Options are:
- "market": Market analyst - "odds": Odds analyst
- "social": Social media analyst - "social": Social media analyst
- "news": News analyst - "news": News analyst
- "fundamentals": Fundamentals analyst - "event": Event analyst
""" """
if len(selected_analysts) == 0: if len(selected_analysts) == 0:
raise ValueError("Trading Agents Graph Setup Error: no analysts selected!") raise ValueError("Trading Agents Graph Setup Error: no analysts selected!")
# Validate analyst keys
for key in selected_analysts:
if key not in _ANALYST_MAP:
raise ValueError(
f"Unknown analyst type '{key}'. Valid options: {list(_ANALYST_MAP.keys())}"
)
# Create analyst nodes # Create analyst nodes
analyst_nodes = {} analyst_nodes = {}
delete_nodes = {} delete_nodes = {}
tool_nodes = {} tool_nodes = {}
if "market" in selected_analysts: for key in selected_analysts:
analyst_nodes["market"] = create_market_analyst( display_name, factory, clear_name = _ANALYST_MAP[key]
self.quick_thinking_llm analyst_nodes[key] = factory(self.quick_thinking_llm)
) delete_nodes[key] = create_msg_delete()
delete_nodes["market"] = create_msg_delete() tool_nodes[key] = self.tool_nodes[key]
tool_nodes["market"] = self.tool_nodes["market"]
if "social" in selected_analysts:
analyst_nodes["social"] = create_social_media_analyst(
self.quick_thinking_llm
)
delete_nodes["social"] = create_msg_delete()
tool_nodes["social"] = self.tool_nodes["social"]
if "news" in selected_analysts:
analyst_nodes["news"] = create_news_analyst(
self.quick_thinking_llm
)
delete_nodes["news"] = create_msg_delete()
tool_nodes["news"] = self.tool_nodes["news"]
if "fundamentals" in selected_analysts:
analyst_nodes["fundamentals"] = create_fundamentals_analyst(
self.quick_thinking_llm
)
delete_nodes["fundamentals"] = create_msg_delete()
tool_nodes["fundamentals"] = self.tool_nodes["fundamentals"]
# Create researcher and manager nodes # Create researcher and manager nodes
bull_researcher_node = create_bull_researcher( yes_advocate_node = create_yes_advocate(
self.quick_thinking_llm, self.bull_memory self.quick_thinking_llm, self.bull_memory
) )
bear_researcher_node = create_bear_researcher( no_advocate_node = create_no_advocate(
self.quick_thinking_llm, self.bear_memory self.quick_thinking_llm, self.bear_memory
) )
timing_advocate_node = create_timing_advocate(
self.quick_thinking_llm, self.timing_memory
)
research_manager_node = create_research_manager( research_manager_node = create_research_manager(
self.deep_thinking_llm, self.invest_judge_memory self.deep_thinking_llm, self.invest_judge_memory
) )
@ -109,16 +107,16 @@ class GraphSetup:
workflow = StateGraph(AgentState) workflow = StateGraph(AgentState)
# Add analyst nodes to the graph # Add analyst nodes to the graph
for analyst_type, node in analyst_nodes.items(): for key in selected_analysts:
workflow.add_node(f"{analyst_type.capitalize()} Analyst", node) display_name, _, clear_name = _ANALYST_MAP[key]
workflow.add_node( workflow.add_node(display_name, analyst_nodes[key])
f"Msg Clear {analyst_type.capitalize()}", delete_nodes[analyst_type] workflow.add_node(clear_name, delete_nodes[key])
) workflow.add_node(f"tools_{key}", tool_nodes[key])
workflow.add_node(f"tools_{analyst_type}", tool_nodes[analyst_type])
# Add other nodes # Add researcher, manager, trader, and risk nodes
workflow.add_node("Bull Researcher", bull_researcher_node) workflow.add_node("YES Advocate", yes_advocate_node)
workflow.add_node("Bear Researcher", bear_researcher_node) workflow.add_node("NO Advocate", no_advocate_node)
workflow.add_node("Timing Advocate", timing_advocate_node)
workflow.add_node("Research Manager", research_manager_node) workflow.add_node("Research Manager", research_manager_node)
workflow.add_node("Trader", trader_node) workflow.add_node("Trader", trader_node)
workflow.add_node("Aggressive Analyst", aggressive_analyst) workflow.add_node("Aggressive Analyst", aggressive_analyst)
@ -128,49 +126,60 @@ class GraphSetup:
# Define edges # Define edges
# Start with the first analyst # Start with the first analyst
first_analyst = selected_analysts[0] first_key = selected_analysts[0]
workflow.add_edge(START, f"{first_analyst.capitalize()} Analyst") first_display_name = _ANALYST_MAP[first_key][0]
workflow.add_edge(START, first_display_name)
# Connect analysts in sequence # Connect analysts in sequence using conditional logic
for i, analyst_type in enumerate(selected_analysts): for i, key in enumerate(selected_analysts):
current_analyst = f"{analyst_type.capitalize()} Analyst" display_name, _, clear_name = _ANALYST_MAP[key]
current_tools = f"tools_{analyst_type}" tools_node = f"tools_{key}"
current_clear = f"Msg Clear {analyst_type.capitalize()}" cond_fn = getattr(self.conditional_logic, f"should_continue_{key}")
# Add conditional edges for current analyst
workflow.add_conditional_edges( workflow.add_conditional_edges(
current_analyst, display_name,
getattr(self.conditional_logic, f"should_continue_{analyst_type}"), cond_fn,
[current_tools, current_clear], [tools_node, clear_name],
) )
workflow.add_edge(current_tools, current_analyst) workflow.add_edge(tools_node, display_name)
# Connect to next analyst or to Bull Researcher if this is the last analyst # Connect clear node to next analyst or to YES Advocate
if i < len(selected_analysts) - 1: if i < len(selected_analysts) - 1:
next_analyst = f"{selected_analysts[i+1].capitalize()} Analyst" next_display_name = _ANALYST_MAP[selected_analysts[i + 1]][0]
workflow.add_edge(current_clear, next_analyst) workflow.add_edge(clear_name, next_display_name)
else: else:
workflow.add_edge(current_clear, "Bull Researcher") workflow.add_edge(clear_name, "YES Advocate")
# Add remaining edges # 3-way investment debate: YES → NO → Timing → YES (cycle until done)
workflow.add_conditional_edges( workflow.add_conditional_edges(
"Bull Researcher", "YES Advocate",
self.conditional_logic.should_continue_debate, self.conditional_logic.should_continue_debate,
{ {
"Bear Researcher": "Bear Researcher", "NO Advocate": "NO Advocate",
"Research Manager": "Research Manager", "Research Manager": "Research Manager",
}, },
) )
workflow.add_conditional_edges( workflow.add_conditional_edges(
"Bear Researcher", "NO Advocate",
self.conditional_logic.should_continue_debate, self.conditional_logic.should_continue_debate,
{ {
"Bull Researcher": "Bull Researcher", "Timing Advocate": "Timing Advocate",
"Research Manager": "Research Manager", "Research Manager": "Research Manager",
}, },
) )
workflow.add_conditional_edges(
"Timing Advocate",
self.conditional_logic.should_continue_debate,
{
"YES Advocate": "YES Advocate",
"Research Manager": "Research Manager",
},
)
workflow.add_edge("Research Manager", "Trader") workflow.add_edge("Research Manager", "Trader")
workflow.add_edge("Trader", "Aggressive Analyst") workflow.add_edge("Trader", "Aggressive Analyst")
# 3-way risk debate
workflow.add_conditional_edges( workflow.add_conditional_edges(
"Aggressive Analyst", "Aggressive Analyst",
self.conditional_logic.should_continue_risk_analysis, self.conditional_logic.should_continue_risk_analysis,

View File

@ -1,31 +1,59 @@
# TradingAgents/graph/signal_processing.py """Signal processing for extracting structured prediction decisions."""
from langchain_openai import ChatOpenAI import json
import re
class SignalProcessor: class SignalProcessor:
"""Processes trading signals to extract actionable decisions.""" """Processes raw LLM output into structured prediction decisions."""
def __init__(self, quick_thinking_llm: ChatOpenAI): def __init__(self, quick_thinking_llm):
"""Initialize with an LLM for processing.""" self.llm = quick_thinking_llm
self.quick_thinking_llm = quick_thinking_llm
def process_signal(self, full_signal: str) -> str: def process_signal(self, full_signal: str) -> str:
""" """Extract structured JSON decision from the final decision text."""
Process a full trading signal to extract the core decision. prompt = f"""Extract the final prediction decision from the following analysis.
Return ONLY a valid JSON object with these exact fields:
- "action": one of "YES", "NO", or "SKIP"
- "confidence": a float between 0.0 and 1.0
- "edge": estimated probability minus market price (float, can be negative)
- "position_size": recommended bet size as fraction of bankroll (float 0.0-1.0)
- "reasoning": one sentence summary
- "time_horizon": time until event resolution
Args: Analysis:
full_signal: Complete trading signal text {full_signal}
Returns: Return ONLY the JSON object, no other text."""
Extracted decision (BUY, SELL, or HOLD)
"""
messages = [
(
"system",
"You are an efficient assistant designed to analyze paragraphs or financial reports provided by a group of analysts. Your task is to extract the investment decision: SELL, BUY, or HOLD. Provide only the extracted decision (SELL, BUY, or HOLD) as your output, without adding any additional text or information.",
),
("human", full_signal),
]
return self.quick_thinking_llm.invoke(messages).content response = self.llm.invoke(prompt)
content = response.content if hasattr(response, "content") else str(response)
try:
json_match = re.search(r'\{[^\{\}]*\}', content, re.DOTALL)
if json_match:
parsed = json.loads(json_match.group())
required = ["action", "confidence", "edge", "position_size", "reasoning", "time_horizon"]
if all(k in parsed for k in required):
parsed["action"] = parsed["action"].upper().strip()
if parsed["action"] not in ("YES", "NO", "SKIP"):
parsed["action"] = "SKIP"
return json.dumps(parsed)
except (json.JSONDecodeError, AttributeError):
pass
action = "SKIP"
text_upper = content.upper()
if "YES" in text_upper and "NO" not in text_upper:
action = "YES"
elif "NO" in text_upper and "YES" not in text_upper:
action = "NO"
return json.dumps({
"action": action,
"confidence": 0.5,
"edge": 0.0,
"position_size": 0.0,
"reasoning": "Could not parse structured output from LLM response.",
"time_horizon": "unknown",
})

View File

@ -20,17 +20,18 @@ from tradingagents.agents.utils.agent_states import (
) )
from tradingagents.dataflows.config import set_config from tradingagents.dataflows.config import set_config
# Import the new abstract tool methods from agent_utils # Import Polymarket tools from agent_utils
from tradingagents.agents.utils.agent_utils import ( from tradingagents.agents.utils.agent_utils import (
get_stock_data, get_market_data,
get_indicators, get_price_history,
get_fundamentals, get_event_news,
get_balance_sheet, get_global_news,
get_cashflow, get_whale_activity,
get_income_statement, get_event_details,
get_news, get_orderbook,
get_insider_transactions, get_market_stats,
get_global_news get_leaderboard_signals,
get_social_sentiment,
) )
from .conditional_logic import ConditionalLogic from .conditional_logic import ConditionalLogic
@ -45,7 +46,7 @@ class TradingAgentsGraph:
def __init__( def __init__(
self, self,
selected_analysts=["market", "social", "news", "fundamentals"], selected_analysts=["odds", "social", "news", "event"],
debug=False, debug=False,
config: Dict[str, Any] = None, config: Dict[str, Any] = None,
callbacks: Optional[List] = None, callbacks: Optional[List] = None,
@ -97,6 +98,7 @@ class TradingAgentsGraph:
# Initialize memories # Initialize memories
self.bull_memory = FinancialSituationMemory("bull_memory", self.config) self.bull_memory = FinancialSituationMemory("bull_memory", self.config)
self.bear_memory = FinancialSituationMemory("bear_memory", self.config) self.bear_memory = FinancialSituationMemory("bear_memory", self.config)
self.timing_memory = FinancialSituationMemory("timing_memory", self.config)
self.trader_memory = FinancialSituationMemory("trader_memory", self.config) self.trader_memory = FinancialSituationMemory("trader_memory", self.config)
self.invest_judge_memory = FinancialSituationMemory("invest_judge_memory", self.config) self.invest_judge_memory = FinancialSituationMemory("invest_judge_memory", self.config)
self.risk_manager_memory = FinancialSituationMemory("risk_manager_memory", self.config) self.risk_manager_memory = FinancialSituationMemory("risk_manager_memory", self.config)
@ -115,6 +117,7 @@ class TradingAgentsGraph:
self.tool_nodes, self.tool_nodes,
self.bull_memory, self.bull_memory,
self.bear_memory, self.bear_memory,
self.timing_memory,
self.trader_memory, self.trader_memory,
self.invest_judge_memory, self.invest_judge_memory,
self.risk_manager_memory, self.risk_manager_memory,
@ -127,7 +130,7 @@ class TradingAgentsGraph:
# State tracking # State tracking
self.curr_state = None self.curr_state = None
self.ticker = None self.event_id = None
self.log_states_dict = {} # date to full state dict self.log_states_dict = {} # date to full state dict
# Set up the graph # Set up the graph
@ -151,49 +154,22 @@ class TradingAgentsGraph:
return kwargs return kwargs
def _create_tool_nodes(self) -> Dict[str, ToolNode]: def _create_tool_nodes(self) -> Dict[str, ToolNode]:
"""Create tool nodes for different data sources using abstract methods.""" """Create tool nodes for different data sources using Polymarket tools."""
return { return {
"market": ToolNode( "odds": ToolNode([get_market_data, get_price_history, get_orderbook]),
[ "social": ToolNode([get_social_sentiment, get_whale_activity]),
# Core stock data tools "news": ToolNode([get_event_news, get_global_news]),
get_stock_data, "event": ToolNode([get_event_details, get_market_stats, get_leaderboard_signals]),
# Technical indicators
get_indicators,
]
),
"social": ToolNode(
[
# News tools for social media analysis
get_news,
]
),
"news": ToolNode(
[
# News and insider information
get_news,
get_global_news,
get_insider_transactions,
]
),
"fundamentals": ToolNode(
[
# Fundamental analysis tools
get_fundamentals,
get_balance_sheet,
get_cashflow,
get_income_statement,
]
),
} }
def propagate(self, company_name, trade_date): def propagate(self, event_id, event_question, trade_date):
"""Run the trading agents graph for a company on a specific date.""" """Run the trading agents graph for a Polymarket event on a specific date."""
self.ticker = company_name self.event_id = event_id
# Initialize state # Initialize state
init_agent_state = self.propagator.create_initial_state( init_agent_state = self.propagator.create_initial_state(
company_name, trade_date event_id, event_question, trade_date
) )
args = self.propagator.get_graph_args() args = self.propagator.get_graph_args()
@ -219,29 +195,27 @@ class TradingAgentsGraph:
self._log_state(trade_date, final_state) self._log_state(trade_date, final_state)
# Return decision and processed signal # Return decision and processed signal
return final_state, self.process_signal(final_state["final_trade_decision"]) return final_state, self.process_signal(final_state["final_decision"])
def _log_state(self, trade_date, final_state): def _log_state(self, trade_date, final_state):
"""Log the final state to a JSON file.""" """Log the final state to a JSON file."""
self.log_states_dict[str(trade_date)] = { self.log_states_dict[str(trade_date)] = {
"company_of_interest": final_state["company_of_interest"], "event_id": final_state["event_id"],
"event_question": final_state["event_question"],
"trade_date": final_state["trade_date"], "trade_date": final_state["trade_date"],
"market_report": final_state["market_report"], "odds_report": final_state["odds_report"],
"sentiment_report": final_state["sentiment_report"], "sentiment_report": final_state["sentiment_report"],
"news_report": final_state["news_report"], "news_report": final_state["news_report"],
"fundamentals_report": final_state["fundamentals_report"], "event_report": final_state["event_report"],
"investment_debate_state": { "investment_debate_state": {
"bull_history": final_state["investment_debate_state"]["bull_history"], "yes_history": final_state["investment_debate_state"]["yes_history"],
"bear_history": final_state["investment_debate_state"]["bear_history"], "no_history": final_state["investment_debate_state"]["no_history"],
"timing_history": final_state["investment_debate_state"]["timing_history"],
"history": final_state["investment_debate_state"]["history"], "history": final_state["investment_debate_state"]["history"],
"current_response": final_state["investment_debate_state"][ "judge_decision": final_state["investment_debate_state"]["judge_decision"],
"current_response"
],
"judge_decision": final_state["investment_debate_state"][
"judge_decision"
],
}, },
"trader_investment_decision": final_state["trader_investment_plan"], "investment_plan": final_state["investment_plan"],
"trader_plan": final_state["trader_plan"],
"risk_debate_state": { "risk_debate_state": {
"aggressive_history": final_state["risk_debate_state"]["aggressive_history"], "aggressive_history": final_state["risk_debate_state"]["aggressive_history"],
"conservative_history": final_state["risk_debate_state"]["conservative_history"], "conservative_history": final_state["risk_debate_state"]["conservative_history"],
@ -249,38 +223,28 @@ class TradingAgentsGraph:
"history": final_state["risk_debate_state"]["history"], "history": final_state["risk_debate_state"]["history"],
"judge_decision": final_state["risk_debate_state"]["judge_decision"], "judge_decision": final_state["risk_debate_state"]["judge_decision"],
}, },
"investment_plan": final_state["investment_plan"], "final_decision": final_state["final_decision"],
"final_trade_decision": final_state["final_trade_decision"],
} }
# Save to file # Save to file
directory = Path(f"eval_results/{self.ticker}/TradingAgentsStrategy_logs/") safe_id = str(self.event_id).replace("/", "_")
directory = Path(f"eval_results/{safe_id}/TradingAgentsStrategy_logs/")
directory.mkdir(parents=True, exist_ok=True) directory.mkdir(parents=True, exist_ok=True)
with open( with open(
f"eval_results/{self.ticker}/TradingAgentsStrategy_logs/full_states_log_{trade_date}.json", f"eval_results/{safe_id}/TradingAgentsStrategy_logs/full_states_log_{trade_date}.json",
"w", "w",
encoding="utf-8", encoding="utf-8",
) as f: ) as f:
json.dump(self.log_states_dict, f, indent=4) json.dump(self.log_states_dict, f, indent=4)
def reflect_and_remember(self, returns_losses): def reflect_and_remember(self, returns_losses):
"""Reflect on decisions and update memory based on returns.""" """Reflect on decisions and update memory based on outcomes.
self.reflector.reflect_bull_researcher(
self.curr_state, returns_losses, self.bull_memory Phase 1 no-op: reflection is deferred until Phase 2 when outcome data
) is available from resolved Polymarket events.
self.reflector.reflect_bear_researcher( """
self.curr_state, returns_losses, self.bear_memory pass
)
self.reflector.reflect_trader(
self.curr_state, returns_losses, self.trader_memory
)
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
)
def process_signal(self, full_signal): def process_signal(self, full_signal):
"""Process a signal to extract the core decision.""" """Process a signal to extract the core decision."""