mirror of
https://github.com/TauricResearch/TradingAgents.git
synced 2026-09-27 06:56:39 +03:00
refactor(graph): wire each analyst from one declaration of its tools
- an analyst's TOOLS tuple is both what it is offered and what its tool node runs - one routing function replaces four copies of should_continue_<analyst> - the sentiment analyst has no tools, so the unreachable social tool node is gone
This commit is contained in:
@@ -1,23 +0,0 @@
|
|||||||
"""The market analyst is bound (and prompt-instructed) to call
|
|
||||||
get_verified_market_snapshot; if the executor ToolNode doesn't register it, the
|
|
||||||
call fails and the model reports the tool "unavailable" and skips verification.
|
|
||||||
|
|
||||||
Regression guard for that wiring gap (snapshot bound to the LLM but missing from
|
|
||||||
the market ToolNode).
|
|
||||||
"""
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from tradingagents.graph.trading_graph import TradingAgentsGraph
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_market_toolnode_can_execute_verified_snapshot():
|
|
||||||
# _create_tool_nodes does not use self -> call unbound (avoids building LLMs).
|
|
||||||
nodes = TradingAgentsGraph._create_tool_nodes(None)
|
|
||||||
market_tools = set(nodes["market"].tools_by_name)
|
|
||||||
assert "get_verified_market_snapshot" in market_tools, (
|
|
||||||
"get_verified_market_snapshot is bound to the market analyst but not "
|
|
||||||
"registered in the market ToolNode, so the model's call fails."
|
|
||||||
)
|
|
||||||
# the other core market tools must remain too
|
|
||||||
assert {"get_stock_data", "get_indicators"} <= market_tools
|
|
||||||
@@ -10,20 +10,21 @@ from tradingagents.agents.utils.agent_utils import (
|
|||||||
get_language_instruction,
|
get_language_instruction,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# The tools this analyst is offered; its tool node is built from the same tuple.
|
||||||
|
TOOLS = (
|
||||||
|
get_fundamentals,
|
||||||
|
get_balance_sheet,
|
||||||
|
get_cashflow,
|
||||||
|
get_income_statement,
|
||||||
|
get_insider_transactions,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_fundamentals_analyst(llm):
|
def create_fundamentals_analyst(llm):
|
||||||
def fundamentals_analyst_node(state):
|
def fundamentals_analyst_node(state):
|
||||||
current_date = state["trade_date"]
|
current_date = state["trade_date"]
|
||||||
instrument_context = get_instrument_context_from_state(state)
|
instrument_context = get_instrument_context_from_state(state)
|
||||||
|
|
||||||
tools = [
|
|
||||||
get_fundamentals,
|
|
||||||
get_balance_sheet,
|
|
||||||
get_cashflow,
|
|
||||||
get_income_statement,
|
|
||||||
get_insider_transactions,
|
|
||||||
]
|
|
||||||
|
|
||||||
system_message = (
|
system_message = (
|
||||||
"You are a researcher tasked with analyzing fundamental information over the past week about a company. Please write a comprehensive report of the company's fundamental information such as financial documents, company profile, basic company financials, and company financial history to gain a full view of the company's fundamental information to inform traders. Make sure to include as much detail as possible. Provide specific, actionable insights with supporting evidence to help traders make informed decisions."
|
"You are a researcher tasked with analyzing fundamental information over the past week about a company. Please write a comprehensive report of the company's fundamental information such as financial documents, company profile, basic company financials, and company financial history to gain a full view of the company's fundamental information to inform traders. Make sure to include as much detail as possible. Provide specific, actionable insights with supporting evidence to help traders make informed decisions."
|
||||||
+ " Make sure to append a Markdown table at the end of the report to organize key points in the report, organized and easy to read."
|
+ " Make sure to append a Markdown table at the end of the report to organize key points in the report, organized and easy to read."
|
||||||
@@ -49,11 +50,11 @@ def create_fundamentals_analyst(llm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
prompt = prompt.partial(system_message=system_message)
|
prompt = prompt.partial(system_message=system_message)
|
||||||
prompt = prompt.partial(tool_names=", ".join([tool.name for tool in tools]))
|
prompt = prompt.partial(tool_names=", ".join([tool.name for tool in TOOLS]))
|
||||||
prompt = prompt.partial(current_date=current_date)
|
prompt = prompt.partial(current_date=current_date)
|
||||||
prompt = prompt.partial(instrument_context=instrument_context)
|
prompt = prompt.partial(instrument_context=instrument_context)
|
||||||
|
|
||||||
chain = prompt | llm.bind_tools(tools)
|
chain = prompt | llm.bind_tools(TOOLS)
|
||||||
|
|
||||||
result = chain.invoke(state["messages"])
|
result = chain.invoke(state["messages"])
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,13 @@ from tradingagents.agents.utils.agent_utils import (
|
|||||||
get_verified_market_snapshot,
|
get_verified_market_snapshot,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# The tools this analyst is offered; its tool node is built from the same tuple.
|
||||||
|
TOOLS = (
|
||||||
|
get_stock_data,
|
||||||
|
get_indicators,
|
||||||
|
get_verified_market_snapshot,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_market_analyst(llm):
|
def create_market_analyst(llm):
|
||||||
|
|
||||||
@@ -15,12 +22,6 @@ def create_market_analyst(llm):
|
|||||||
current_date = state["trade_date"]
|
current_date = state["trade_date"]
|
||||||
instrument_context = get_instrument_context_from_state(state)
|
instrument_context = get_instrument_context_from_state(state)
|
||||||
|
|
||||||
tools = [
|
|
||||||
get_stock_data,
|
|
||||||
get_indicators,
|
|
||||||
get_verified_market_snapshot,
|
|
||||||
]
|
|
||||||
|
|
||||||
system_message = (
|
system_message = (
|
||||||
"""You are a trading assistant tasked with analyzing financial markets. Your role is to select the **most relevant indicators** for a given market condition or trading strategy from the following list. The goal is to choose up to **8 indicators** that provide complementary insights without redundancy. Categories and each category's indicators are:
|
"""You are a trading assistant tasked with analyzing financial markets. Your role is to select the **most relevant indicators** for a given market condition or trading strategy from the following list. The goal is to choose up to **8 indicators** that provide complementary insights without redundancy. Categories and each category's indicators are:
|
||||||
|
|
||||||
@@ -73,11 +74,11 @@ Write a very detailed and nuanced report of the trends you observe. Provide spec
|
|||||||
)
|
)
|
||||||
|
|
||||||
prompt = prompt.partial(system_message=system_message)
|
prompt = prompt.partial(system_message=system_message)
|
||||||
prompt = prompt.partial(tool_names=", ".join([tool.name for tool in tools]))
|
prompt = prompt.partial(tool_names=", ".join([tool.name for tool in TOOLS]))
|
||||||
prompt = prompt.partial(current_date=current_date)
|
prompt = prompt.partial(current_date=current_date)
|
||||||
prompt = prompt.partial(instrument_context=instrument_context)
|
prompt = prompt.partial(instrument_context=instrument_context)
|
||||||
|
|
||||||
chain = prompt | llm.bind_tools(tools)
|
chain = prompt | llm.bind_tools(TOOLS)
|
||||||
|
|
||||||
result = chain.invoke(state["messages"])
|
result = chain.invoke(state["messages"])
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,14 @@ from tradingagents.agents.utils.agent_utils import (
|
|||||||
get_prediction_markets,
|
get_prediction_markets,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# The tools this analyst is offered; its tool node is built from the same tuple.
|
||||||
|
TOOLS = (
|
||||||
|
get_news,
|
||||||
|
get_global_news,
|
||||||
|
get_macro_indicators,
|
||||||
|
get_prediction_markets,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_news_analyst(llm):
|
def create_news_analyst(llm):
|
||||||
def news_analyst_node(state):
|
def news_analyst_node(state):
|
||||||
@@ -17,13 +25,6 @@ def create_news_analyst(llm):
|
|||||||
asset_label = "company" if asset_type == "stock" else "asset"
|
asset_label = "company" if asset_type == "stock" else "asset"
|
||||||
instrument_context = get_instrument_context_from_state(state)
|
instrument_context = get_instrument_context_from_state(state)
|
||||||
|
|
||||||
tools = [
|
|
||||||
get_news,
|
|
||||||
get_global_news,
|
|
||||||
get_macro_indicators,
|
|
||||||
get_prediction_markets,
|
|
||||||
]
|
|
||||||
|
|
||||||
system_message = (
|
system_message = (
|
||||||
f"You are a news researcher tasked with analyzing recent news and trends over the past week. Please write a comprehensive report of the current state of the world that is relevant for trading and macroeconomics. Use the available tools: get_news(ticker, start_date, end_date) for {asset_label}-specific news by ticker symbol, get_global_news(curr_date, look_back_days, limit) for broader macroeconomic news, get_macro_indicators(indicator, curr_date, look_back_days) to ground macro commentary in actual data from FRED (e.g. 'cpi', 'core_pce', 'unemployment', 'fed_funds_rate', '10y_treasury', 'yield_curve'), and get_prediction_markets(topic, limit) for live market-implied probabilities of forward-looking events (e.g. 'Fed rate cut', 'recession 2026', geopolitical or sector events). Provide specific, actionable insights with supporting evidence to help traders make informed decisions."
|
f"You are a news researcher tasked with analyzing recent news and trends over the past week. Please write a comprehensive report of the current state of the world that is relevant for trading and macroeconomics. Use the available tools: get_news(ticker, start_date, end_date) for {asset_label}-specific news by ticker symbol, get_global_news(curr_date, look_back_days, limit) for broader macroeconomic news, get_macro_indicators(indicator, curr_date, look_back_days) to ground macro commentary in actual data from FRED (e.g. 'cpi', 'core_pce', 'unemployment', 'fed_funds_rate', '10y_treasury', 'yield_curve'), and get_prediction_markets(topic, limit) for live market-implied probabilities of forward-looking events (e.g. 'Fed rate cut', 'recession 2026', geopolitical or sector events). Provide specific, actionable insights with supporting evidence to help traders make informed decisions."
|
||||||
+ """ Make sure to append a Markdown table at the end of the report to organize key points in the report, organized and easy to read."""
|
+ """ Make sure to append a Markdown table at the end of the report to organize key points in the report, organized and easy to read."""
|
||||||
@@ -48,11 +49,11 @@ def create_news_analyst(llm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
prompt = prompt.partial(system_message=system_message)
|
prompt = prompt.partial(system_message=system_message)
|
||||||
prompt = prompt.partial(tool_names=", ".join([tool.name for tool in tools]))
|
prompt = prompt.partial(tool_names=", ".join([tool.name for tool in TOOLS]))
|
||||||
prompt = prompt.partial(current_date=current_date)
|
prompt = prompt.partial(current_date=current_date)
|
||||||
prompt = prompt.partial(instrument_context=instrument_context)
|
prompt = prompt.partial(instrument_context=instrument_context)
|
||||||
|
|
||||||
chain = prompt | llm.bind_tools(tools)
|
chain = prompt | llm.bind_tools(TOOLS)
|
||||||
result = chain.invoke(state["messages"])
|
result = chain.invoke(state["messages"])
|
||||||
|
|
||||||
report = ""
|
report = ""
|
||||||
|
|||||||
@@ -2,14 +2,21 @@ from collections.abc import Iterable
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from time import monotonic
|
from time import monotonic
|
||||||
|
|
||||||
|
from tradingagents.agents.analysts import fundamentals_analyst, market_analyst, news_analyst
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class AnalystNodeSpec:
|
class AnalystNodeSpec:
|
||||||
key: str
|
key: str
|
||||||
agent_node: str
|
agent_node: str
|
||||||
clear_node: str
|
clear_node: str
|
||||||
tool_node: str
|
|
||||||
report_key: str
|
report_key: str
|
||||||
|
tools: tuple = ()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tool_node(self) -> str | None:
|
||||||
|
"""The node that runs this analyst's tool calls; None when it has no tools."""
|
||||||
|
return f"tools_{self.key}" if self.tools else None
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@@ -22,33 +29,30 @@ ANALYST_NODE_SPECS: dict[str, AnalystNodeSpec] = {
|
|||||||
key="market",
|
key="market",
|
||||||
agent_node="Market Analyst",
|
agent_node="Market Analyst",
|
||||||
clear_node="Msg Clear Market",
|
clear_node="Msg Clear Market",
|
||||||
tool_node="tools_market",
|
|
||||||
report_key="market_report",
|
report_key="market_report",
|
||||||
|
tools=market_analyst.TOOLS,
|
||||||
),
|
),
|
||||||
"social": AnalystNodeSpec(
|
"social": AnalystNodeSpec(
|
||||||
# Wire key stays "social" for saved-config back-compat; the
|
# Saved configs select this analyst as "social". It fetches its
|
||||||
# user-facing label is "Sentiment Analyst" to match the rename
|
# sources before calling the model, so it has no tools.
|
||||||
# that landed in v0.2.5 (sentiment_analyst now ingests news +
|
|
||||||
# StockTwits + Reddit, not just social media).
|
|
||||||
key="social",
|
key="social",
|
||||||
agent_node="Sentiment Analyst",
|
agent_node="Sentiment Analyst",
|
||||||
clear_node="Msg Clear Sentiment",
|
clear_node="Msg Clear Sentiment",
|
||||||
tool_node="tools_social",
|
|
||||||
report_key="sentiment_report",
|
report_key="sentiment_report",
|
||||||
),
|
),
|
||||||
"news": AnalystNodeSpec(
|
"news": AnalystNodeSpec(
|
||||||
key="news",
|
key="news",
|
||||||
agent_node="News Analyst",
|
agent_node="News Analyst",
|
||||||
clear_node="Msg Clear News",
|
clear_node="Msg Clear News",
|
||||||
tool_node="tools_news",
|
|
||||||
report_key="news_report",
|
report_key="news_report",
|
||||||
|
tools=news_analyst.TOOLS,
|
||||||
),
|
),
|
||||||
"fundamentals": AnalystNodeSpec(
|
"fundamentals": AnalystNodeSpec(
|
||||||
key="fundamentals",
|
key="fundamentals",
|
||||||
agent_node="Fundamentals Analyst",
|
agent_node="Fundamentals Analyst",
|
||||||
clear_node="Msg Clear Fundamentals",
|
clear_node="Msg Clear Fundamentals",
|
||||||
tool_node="tools_fundamentals",
|
|
||||||
report_key="fundamentals_report",
|
report_key="fundamentals_report",
|
||||||
|
tools=fundamentals_analyst.TOOLS,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,44 +11,6 @@ class ConditionalLogic:
|
|||||||
self.max_debate_rounds = max_debate_rounds
|
self.max_debate_rounds = max_debate_rounds
|
||||||
self.max_risk_discuss_rounds = max_risk_discuss_rounds
|
self.max_risk_discuss_rounds = max_risk_discuss_rounds
|
||||||
|
|
||||||
def should_continue_market(self, state: AgentState):
|
|
||||||
"""Determine if market analysis should continue."""
|
|
||||||
messages = state["messages"]
|
|
||||||
last_message = messages[-1]
|
|
||||||
if last_message.tool_calls:
|
|
||||||
return "tools_market"
|
|
||||||
return "Msg Clear Market"
|
|
||||||
|
|
||||||
def should_continue_social(self, state: AgentState):
|
|
||||||
"""Determine if sentiment-analyst tool round should continue.
|
|
||||||
|
|
||||||
Method name keeps the legacy ``social`` suffix to match the
|
|
||||||
``AnalystType.SOCIAL = "social"`` wire value (saved-config
|
|
||||||
back-compat); the returned ``clear_node`` label uses the v0.2.5
|
|
||||||
rename so it matches the node registered by the execution plan.
|
|
||||||
"""
|
|
||||||
messages = state["messages"]
|
|
||||||
last_message = messages[-1]
|
|
||||||
if last_message.tool_calls:
|
|
||||||
return "tools_social"
|
|
||||||
return "Msg Clear Sentiment"
|
|
||||||
|
|
||||||
def should_continue_news(self, state: AgentState):
|
|
||||||
"""Determine if news analysis should continue."""
|
|
||||||
messages = state["messages"]
|
|
||||||
last_message = messages[-1]
|
|
||||||
if last_message.tool_calls:
|
|
||||||
return "tools_news"
|
|
||||||
return "Msg Clear News"
|
|
||||||
|
|
||||||
def should_continue_fundamentals(self, state: AgentState):
|
|
||||||
"""Determine if fundamentals analysis should continue."""
|
|
||||||
messages = state["messages"]
|
|
||||||
last_message = messages[-1]
|
|
||||||
if last_message.tool_calls:
|
|
||||||
return "tools_fundamentals"
|
|
||||||
return "Msg Clear Fundamentals"
|
|
||||||
|
|
||||||
def should_continue_debate(self, state: AgentState) -> str:
|
def should_continue_debate(self, state: AgentState) -> str:
|
||||||
"""Determine if debate should continue."""
|
"""Determine if debate should continue."""
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,13 @@ RISK_ANALYSIS_PATH_MAP = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _tools_or_clear(spec):
|
||||||
|
"""Route an analyst's turn: run its tool calls, or finish its report."""
|
||||||
|
def route(state) -> str:
|
||||||
|
return spec.tool_node if state["messages"][-1].tool_calls else spec.clear_node
|
||||||
|
return route
|
||||||
|
|
||||||
|
|
||||||
class GraphSetup:
|
class GraphSetup:
|
||||||
"""Handles the setup and configuration of the agent graph."""
|
"""Handles the setup and configuration of the agent graph."""
|
||||||
|
|
||||||
@@ -49,13 +56,11 @@ class GraphSetup:
|
|||||||
self,
|
self,
|
||||||
quick_thinking_llm: Any,
|
quick_thinking_llm: Any,
|
||||||
deep_thinking_llm: Any,
|
deep_thinking_llm: Any,
|
||||||
tool_nodes: dict[str, ToolNode],
|
|
||||||
conditional_logic: ConditionalLogic,
|
conditional_logic: ConditionalLogic,
|
||||||
):
|
):
|
||||||
"""Initialize with required components."""
|
"""Initialize with required components."""
|
||||||
self.quick_thinking_llm = quick_thinking_llm
|
self.quick_thinking_llm = quick_thinking_llm
|
||||||
self.deep_thinking_llm = deep_thinking_llm
|
self.deep_thinking_llm = deep_thinking_llm
|
||||||
self.tool_nodes = tool_nodes
|
|
||||||
self.conditional_logic = conditional_logic
|
self.conditional_logic = conditional_logic
|
||||||
|
|
||||||
def setup_graph(
|
def setup_graph(
|
||||||
@@ -98,7 +103,8 @@ class GraphSetup:
|
|||||||
for spec in plan.specs:
|
for spec in plan.specs:
|
||||||
workflow.add_node(spec.agent_node, analyst_factories[spec.key]())
|
workflow.add_node(spec.agent_node, analyst_factories[spec.key]())
|
||||||
workflow.add_node(spec.clear_node, create_msg_delete())
|
workflow.add_node(spec.clear_node, create_msg_delete())
|
||||||
workflow.add_node(spec.tool_node, self.tool_nodes[spec.key])
|
if spec.tools:
|
||||||
|
workflow.add_node(spec.tool_node, ToolNode(list(spec.tools)))
|
||||||
|
|
||||||
# Add other nodes
|
# Add other nodes
|
||||||
workflow.add_node("Bull Researcher", bull_researcher_node)
|
workflow.add_node("Bull Researcher", bull_researcher_node)
|
||||||
@@ -116,23 +122,17 @@ class GraphSetup:
|
|||||||
|
|
||||||
# Connect analysts in sequence
|
# Connect analysts in sequence
|
||||||
for i, spec in enumerate(plan.specs):
|
for i, spec in enumerate(plan.specs):
|
||||||
current_analyst = spec.agent_node
|
if spec.tools:
|
||||||
current_tools = spec.tool_node
|
workflow.add_conditional_edges(
|
||||||
current_clear = spec.clear_node
|
spec.agent_node, _tools_or_clear(spec), [spec.tool_node, spec.clear_node]
|
||||||
|
)
|
||||||
# Add conditional edges for current analyst
|
workflow.add_edge(spec.tool_node, spec.agent_node)
|
||||||
workflow.add_conditional_edges(
|
|
||||||
current_analyst,
|
|
||||||
getattr(self.conditional_logic, f"should_continue_{spec.key}"),
|
|
||||||
[current_tools, current_clear],
|
|
||||||
)
|
|
||||||
workflow.add_edge(current_tools, current_analyst)
|
|
||||||
|
|
||||||
# Connect to next analyst or to Bull Researcher if this is the last analyst
|
|
||||||
if i < len(plan.specs) - 1:
|
|
||||||
workflow.add_edge(current_clear, plan.specs[i + 1].agent_node)
|
|
||||||
else:
|
else:
|
||||||
workflow.add_edge(current_clear, "Bull Researcher")
|
workflow.add_edge(spec.agent_node, spec.clear_node)
|
||||||
|
|
||||||
|
# The last analyst hands over to the research debate.
|
||||||
|
following = plan.specs[i + 1].agent_node if i < len(plan.specs) - 1 else "Bull Researcher"
|
||||||
|
workflow.add_edge(spec.clear_node, following)
|
||||||
|
|
||||||
# Both research-debate edges share the complete DEBATE_PATH_MAP (#1088).
|
# Both research-debate edges share the complete DEBATE_PATH_MAP (#1088).
|
||||||
for debate_node in ("Bull Researcher", "Bear Researcher"):
|
for debate_node in ("Bull Researcher", "Bear Researcher"):
|
||||||
|
|||||||
@@ -8,23 +8,8 @@ from datetime import datetime, timedelta
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from langgraph.prebuilt import ToolNode
|
|
||||||
|
|
||||||
# Import the abstract tool methods from agent_utils
|
|
||||||
from tradingagents.agents.utils.agent_utils import (
|
from tradingagents.agents.utils.agent_utils import (
|
||||||
build_instrument_context,
|
build_instrument_context,
|
||||||
get_balance_sheet,
|
|
||||||
get_cashflow,
|
|
||||||
get_fundamentals,
|
|
||||||
get_global_news,
|
|
||||||
get_income_statement,
|
|
||||||
get_indicators,
|
|
||||||
get_insider_transactions,
|
|
||||||
get_macro_indicators,
|
|
||||||
get_news,
|
|
||||||
get_prediction_markets,
|
|
||||||
get_stock_data,
|
|
||||||
get_verified_market_snapshot,
|
|
||||||
resolve_instrument_identity,
|
resolve_instrument_identity,
|
||||||
)
|
)
|
||||||
from tradingagents.agents.utils.memory import TradingMemoryLog
|
from tradingagents.agents.utils.memory import TradingMemoryLog
|
||||||
@@ -144,9 +129,6 @@ class TradingAgentsGraph:
|
|||||||
|
|
||||||
self.memory_log = TradingMemoryLog(self.config)
|
self.memory_log = TradingMemoryLog(self.config)
|
||||||
|
|
||||||
# Create tool nodes
|
|
||||||
self.tool_nodes = self._create_tool_nodes()
|
|
||||||
|
|
||||||
# Initialize components
|
# Initialize components
|
||||||
self.conditional_logic = ConditionalLogic(
|
self.conditional_logic = ConditionalLogic(
|
||||||
max_debate_rounds=self.config["max_debate_rounds"],
|
max_debate_rounds=self.config["max_debate_rounds"],
|
||||||
@@ -155,7 +137,6 @@ class TradingAgentsGraph:
|
|||||||
self.graph_setup = GraphSetup(
|
self.graph_setup = GraphSetup(
|
||||||
self.quick_thinking_llm,
|
self.quick_thinking_llm,
|
||||||
self.deep_thinking_llm,
|
self.deep_thinking_llm,
|
||||||
self.tool_nodes,
|
|
||||||
self.conditional_logic,
|
self.conditional_logic,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -218,47 +199,6 @@ class TradingAgentsGraph:
|
|||||||
|
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def _create_tool_nodes(self) -> dict[str, ToolNode]:
|
|
||||||
"""Create tool nodes for different data sources using abstract methods."""
|
|
||||||
return {
|
|
||||||
"market": ToolNode(
|
|
||||||
[
|
|
||||||
# Core stock data tools
|
|
||||||
get_stock_data,
|
|
||||||
# Technical indicators
|
|
||||||
get_indicators,
|
|
||||||
# Deterministic verification snapshot (bound to the analyst
|
|
||||||
# LLM and required by its prompt; must be executable here or
|
|
||||||
# the call fails and the model reports it "unavailable").
|
|
||||||
get_verified_market_snapshot,
|
|
||||||
]
|
|
||||||
),
|
|
||||||
"social": ToolNode(
|
|
||||||
[
|
|
||||||
# News tools for social media analysis
|
|
||||||
get_news,
|
|
||||||
]
|
|
||||||
),
|
|
||||||
"news": ToolNode(
|
|
||||||
[
|
|
||||||
get_news,
|
|
||||||
get_global_news,
|
|
||||||
get_macro_indicators,
|
|
||||||
get_prediction_markets,
|
|
||||||
]
|
|
||||||
),
|
|
||||||
"fundamentals": ToolNode(
|
|
||||||
[
|
|
||||||
# Fundamental analysis tools
|
|
||||||
get_fundamentals,
|
|
||||||
get_balance_sheet,
|
|
||||||
get_cashflow,
|
|
||||||
get_income_statement,
|
|
||||||
get_insider_transactions,
|
|
||||||
]
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
def _resolve_benchmark(self, ticker: str) -> str:
|
def _resolve_benchmark(self, ticker: str) -> str:
|
||||||
"""Pick the benchmark ticker for alpha calculation against ``ticker``.
|
"""Pick the benchmark ticker for alpha calculation against ``ticker``.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user