mirror of
https://github.com/TauricResearch/TradingAgents.git
synced 2026-09-25 22:12:27 +03:00
chore: remove code nothing uses
- is_yahoo_safe, has_checkpoint, get_wall_times, the graph's curr_state and the project_dir config key - the CLI's final_report and current_agent state, its duplicate get_analysis_date and the save_report_to_disk wrapper - the dotenv import guard (a hard dependency) and a warning filter for langgraph-checkpoint 4.0.3
This commit is contained in:
+1
-55
@@ -112,9 +112,7 @@ class MessageBuffer:
|
||||
self.messages = deque(maxlen=max_length)
|
||||
self.tool_calls = deque(maxlen=max_length)
|
||||
self.current_report = None
|
||||
self.final_report = None # Store the complete final report
|
||||
self.agent_status = {}
|
||||
self.current_agent = None
|
||||
self.report_sections = {}
|
||||
self.selected_analysts = []
|
||||
self._processed_message_ids = set()
|
||||
@@ -148,8 +146,6 @@ class MessageBuffer:
|
||||
|
||||
# Reset other state
|
||||
self.current_report = None
|
||||
self.final_report = None
|
||||
self.current_agent = None
|
||||
self.messages.clear()
|
||||
self.tool_calls.clear()
|
||||
self._processed_message_ids.clear()
|
||||
@@ -186,7 +182,6 @@ class MessageBuffer:
|
||||
def update_agent_status(self, agent, status):
|
||||
if agent in self.agent_status:
|
||||
self.agent_status[agent] = status
|
||||
self.current_agent = agent
|
||||
|
||||
def update_report_section(self, section_name, content):
|
||||
if section_name in self.report_sections:
|
||||
@@ -219,50 +214,6 @@ class MessageBuffer:
|
||||
f"### {section_titles[latest_section]}\n{latest_content}"
|
||||
)
|
||||
|
||||
# Update the final complete report
|
||||
self._update_final_report()
|
||||
|
||||
def _update_final_report(self):
|
||||
report_parts = []
|
||||
|
||||
# Analyst Team Reports - use .get() to handle missing sections
|
||||
analyst_sections = ["market_report", "sentiment_report", "news_report", "fundamentals_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"):
|
||||
report_parts.append(
|
||||
f"### Market Analysis\n{self.report_sections['market_report']}"
|
||||
)
|
||||
if self.report_sections.get("sentiment_report"):
|
||||
report_parts.append(
|
||||
f"### Social Sentiment\n{self.report_sections['sentiment_report']}"
|
||||
)
|
||||
if self.report_sections.get("news_report"):
|
||||
report_parts.append(
|
||||
f"### News Analysis\n{self.report_sections['news_report']}"
|
||||
)
|
||||
if self.report_sections.get("fundamentals_report"):
|
||||
report_parts.append(
|
||||
f"### Fundamentals Analysis\n{self.report_sections['fundamentals_report']}"
|
||||
)
|
||||
|
||||
# Research Team Reports
|
||||
if self.report_sections.get("investment_plan"):
|
||||
report_parts.append("## Research Team Decision")
|
||||
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']}")
|
||||
|
||||
# Portfolio Management Decision
|
||||
if self.report_sections.get("final_trade_decision"):
|
||||
report_parts.append("## Portfolio Management Decision")
|
||||
report_parts.append(f"{self.report_sections['final_trade_decision']}")
|
||||
|
||||
self.final_report = "\n\n".join(report_parts) if report_parts else None
|
||||
|
||||
|
||||
message_buffer = MessageBuffer()
|
||||
|
||||
@@ -780,11 +731,6 @@ def get_analysis_date():
|
||||
)
|
||||
|
||||
|
||||
def save_report_to_disk(final_state, ticker: str, save_path: Path):
|
||||
"""Save the complete analysis report to disk (shared CLI/API writer)."""
|
||||
return write_report_tree(final_state, ticker, save_path)
|
||||
|
||||
|
||||
def display_complete_report(final_state):
|
||||
"""Display the complete analysis report sequentially (avoids truncation)."""
|
||||
console.print()
|
||||
@@ -1344,7 +1290,7 @@ def run_analysis(checkpoint: bool | None = None, portfolio=None):
|
||||
).strip()
|
||||
save_path = Path(save_path_str)
|
||||
try:
|
||||
report_file = save_report_to_disk(final_state, selections["ticker"], save_path)
|
||||
report_file = write_report_tree(final_state, selections["ticker"], 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:
|
||||
|
||||
@@ -99,39 +99,6 @@ def filter_analysts_for_asset_type(
|
||||
]
|
||||
|
||||
|
||||
def get_analysis_date() -> str:
|
||||
"""Prompt the user to enter a date in YYYY-MM-DD format."""
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
def validate_date(date_str: str) -> bool:
|
||||
if not re.match(r"^\d{4}-\d{2}-\d{2}$", date_str):
|
||||
return False
|
||||
try:
|
||||
datetime.strptime(date_str, "%Y-%m-%d")
|
||||
return True
|
||||
except ValueError:
|
||||
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.",
|
||||
style=questionary.Style(
|
||||
[
|
||||
("text", "fg:green"),
|
||||
("highlighted", "noinherit"),
|
||||
]
|
||||
),
|
||||
).ask()
|
||||
|
||||
if not date:
|
||||
console.print("\n[red]No date provided. Exiting...[/red]")
|
||||
exit(1)
|
||||
|
||||
return date.strip()
|
||||
|
||||
|
||||
def _matching_choice(options, default):
|
||||
"""The option value equal to ``default``, or None to leave the menu as is."""
|
||||
return next((value for _, value in options if value == default), None)
|
||||
|
||||
@@ -49,7 +49,7 @@ class AnalystWallTimeTrackerTests(unittest.TestCase):
|
||||
tracker.mark_started("market", started_at=10.0)
|
||||
tracker.mark_completed("market", completed_at=13.5)
|
||||
|
||||
self.assertEqual(tracker.get_wall_times(), {"market": 3.5})
|
||||
self.assertEqual(tracker.format_summary(), "Analyst wall time: Market 3.50s")
|
||||
|
||||
def test_formats_summary_in_plan_order(self):
|
||||
plan = build_analyst_execution_plan(["news", "market"])
|
||||
@@ -70,21 +70,18 @@ class AnalystWallTimeTrackerTests(unittest.TestCase):
|
||||
tracker = AnalystWallTimeTracker(plan)
|
||||
|
||||
sync_analyst_tracker_from_chunk(tracker, {}, now=10.0)
|
||||
self.assertEqual(tracker.get_wall_times(), {})
|
||||
self.assertEqual(tracker.format_summary(), "Analyst wall time: pending")
|
||||
|
||||
sync_analyst_tracker_from_chunk(
|
||||
tracker,
|
||||
{"market_report": "done"},
|
||||
now=13.0,
|
||||
)
|
||||
self.assertEqual(tracker.get_wall_times(), {"market": 3.0})
|
||||
self.assertEqual(tracker.format_summary(), "Analyst wall time: Market 3.00s")
|
||||
|
||||
sync_analyst_tracker_from_chunk(
|
||||
tracker,
|
||||
{"market_report": "done", "news_report": "done"},
|
||||
now=18.0,
|
||||
)
|
||||
self.assertEqual(
|
||||
tracker.get_wall_times(),
|
||||
{"market": 3.0, "news": 5.0},
|
||||
)
|
||||
self.assertEqual(tracker.format_summary(), "Analyst wall time: Market 3.00s | News 5.00s")
|
||||
|
||||
@@ -10,7 +10,6 @@ from tradingagents.graph.checkpointer import (
|
||||
checkpoint_step,
|
||||
clear_checkpoint,
|
||||
get_checkpointer,
|
||||
has_checkpoint,
|
||||
thread_id,
|
||||
)
|
||||
|
||||
@@ -63,7 +62,7 @@ class TestCheckpointResume(unittest.TestCase):
|
||||
graph.invoke({"count": 0}, config=cfg)
|
||||
|
||||
# Checkpoint should exist at step 1 (analyst completed)
|
||||
self.assertTrue(has_checkpoint(self.tmpdir, self.ticker, self.date))
|
||||
self.assertIsNotNone(checkpoint_step(self.tmpdir, self.ticker, self.date))
|
||||
step = checkpoint_step(self.tmpdir, self.ticker, self.date)
|
||||
self.assertEqual(step, 1)
|
||||
|
||||
@@ -90,11 +89,11 @@ class TestCheckpointResume(unittest.TestCase):
|
||||
with self.assertRaises(RuntimeError):
|
||||
graph.invoke({"count": 0}, config=cfg)
|
||||
|
||||
self.assertTrue(has_checkpoint(self.tmpdir, self.ticker, self.date))
|
||||
self.assertIsNotNone(checkpoint_step(self.tmpdir, self.ticker, self.date))
|
||||
|
||||
# Clear it
|
||||
clear_checkpoint(self.tmpdir, self.ticker, self.date)
|
||||
self.assertFalse(has_checkpoint(self.tmpdir, self.ticker, self.date))
|
||||
self.assertIsNone(checkpoint_step(self.tmpdir, self.ticker, self.date))
|
||||
|
||||
# Fresh run succeeds from scratch
|
||||
_should_crash = False
|
||||
@@ -119,10 +118,10 @@ class TestCheckpointResume(unittest.TestCase):
|
||||
with self.assertRaises(RuntimeError):
|
||||
graph.invoke({"count": 0}, config={"configurable": {"thread_id": tid1}})
|
||||
|
||||
self.assertTrue(has_checkpoint(self.tmpdir, self.ticker, self.date))
|
||||
self.assertIsNotNone(checkpoint_step(self.tmpdir, self.ticker, self.date))
|
||||
|
||||
# date2 should have no checkpoint
|
||||
self.assertFalse(has_checkpoint(self.tmpdir, self.ticker, date2))
|
||||
self.assertIsNone(checkpoint_step(self.tmpdir, self.ticker, date2))
|
||||
|
||||
# Run with date2 — should start fresh and succeed
|
||||
_should_crash = False
|
||||
@@ -137,7 +136,7 @@ class TestCheckpointResume(unittest.TestCase):
|
||||
self.assertEqual(result["count"], 11)
|
||||
|
||||
# Original date checkpoint still exists (untouched)
|
||||
self.assertTrue(has_checkpoint(self.tmpdir, self.ticker, self.date))
|
||||
self.assertIsNotNone(checkpoint_step(self.tmpdir, self.ticker, self.date))
|
||||
|
||||
|
||||
class TestCheckpointSignature(unittest.TestCase):
|
||||
@@ -178,9 +177,9 @@ class TestCheckpointSignature(unittest.TestCase):
|
||||
with self.assertRaises(RuntimeError):
|
||||
graph.invoke({"count": 0}, config={"configurable": {"thread_id": tid1}})
|
||||
|
||||
self.assertTrue(has_checkpoint(self.tmpdir, self.ticker, self.date, sig1))
|
||||
self.assertIsNotNone(checkpoint_step(self.tmpdir, self.ticker, self.date, sig1))
|
||||
# A different graph shape has no checkpoint to resume from.
|
||||
self.assertFalse(has_checkpoint(self.tmpdir, self.ticker, self.date, sig2))
|
||||
self.assertIsNone(checkpoint_step(self.tmpdir, self.ticker, self.date, sig2))
|
||||
|
||||
_should_crash = False
|
||||
tid2 = thread_id(self.ticker, self.date, sig2)
|
||||
@@ -190,7 +189,7 @@ class TestCheckpointSignature(unittest.TestCase):
|
||||
result = graph.invoke({"count": 0}, config={"configurable": {"thread_id": tid2}})
|
||||
self.assertEqual(result["count"], 11)
|
||||
# sig1's checkpoint remains untouched.
|
||||
self.assertTrue(has_checkpoint(self.tmpdir, self.ticker, self.date, sig1))
|
||||
self.assertIsNotNone(checkpoint_step(self.tmpdir, self.ticker, self.date, sig1))
|
||||
|
||||
def test_run_signature_captures_graph_shape(self):
|
||||
from tradingagents.graph.trading_graph import TradingAgentsGraph
|
||||
|
||||
@@ -7,7 +7,6 @@ import pytest
|
||||
from tradingagents.dataflows.symbol_utils import (
|
||||
NoMarketDataError,
|
||||
crypto_base,
|
||||
is_yahoo_safe,
|
||||
normalize_symbol,
|
||||
)
|
||||
|
||||
@@ -88,17 +87,6 @@ class TestNoMarketDataError(unittest.TestCase):
|
||||
self.assertEqual(err.canonical, "FOOBAR")
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestIsYahooSafe(unittest.TestCase):
|
||||
def test_accepts_structural_chars(self):
|
||||
for sym in ("AAPL", "GC=F", "^GSPC", "BRK.B", "BTC-USD"):
|
||||
self.assertTrue(is_yahoo_safe(sym))
|
||||
|
||||
def test_rejects_slash_and_space(self):
|
||||
for sym in ("a/b", "AA PL", ""):
|
||||
self.assertFalse(is_yahoo_safe(sym))
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestCryptoBase(unittest.TestCase):
|
||||
def test_resolves_known_crypto_forms(self):
|
||||
|
||||
@@ -1,37 +1,9 @@
|
||||
import contextlib
|
||||
import warnings
|
||||
from dotenv import find_dotenv, load_dotenv
|
||||
|
||||
# Load .env files at package import so DEFAULT_CONFIG's env-var overlay
|
||||
# (and every llm_clients consumer) sees the user's keys regardless of
|
||||
# which entry point started the process. find_dotenv(usecwd=True) walks
|
||||
# from the CWD, so the installed `tradingagents` console script picks up
|
||||
# the project's .env instead of stepping up from site-packages.
|
||||
# load_dotenv defaults to override=False, so it never clobbers values
|
||||
# the caller has already exported.
|
||||
try:
|
||||
from dotenv import find_dotenv, load_dotenv
|
||||
|
||||
load_dotenv(find_dotenv(usecwd=True))
|
||||
load_dotenv(find_dotenv(".env.enterprise", usecwd=True), override=False)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
# langchain-core 1.3.3 calls surface_langchain_deprecation_warnings() in
|
||||
# its own __init__, which prepends default-action filters for its
|
||||
# subclassed warning categories. To suppress a specific warning we must
|
||||
# install our filter AFTER langchain-core has installed its own, so import
|
||||
# it first. The package is a guaranteed transitive dep via langgraph.
|
||||
with contextlib.suppress(ImportError):
|
||||
import langchain_core # noqa: F401
|
||||
|
||||
# langgraph-checkpoint 4.0.3 calls Reviver() at module load without an
|
||||
# explicit allowed_objects, which triggers a noisy pending-deprecation
|
||||
# warning from langchain-core 1.3.3 on every interpreter start. The fix
|
||||
# is already merged upstream (langchain-ai/langgraph#7743, 2026-05-08)
|
||||
# and will arrive in the next langgraph-checkpoint release. Remove this
|
||||
# block (and the langchain_core preload above) when we bump past it.
|
||||
warnings.filterwarnings(
|
||||
"ignore",
|
||||
message=r"The default value of `allowed_objects`.*",
|
||||
category=PendingDeprecationWarning,
|
||||
)
|
||||
# Load .env at package import so DEFAULT_CONFIG's env-var overlay and every LLM
|
||||
# client see the user's keys whichever entry point started the process.
|
||||
# usecwd=True walks from the working directory, so the installed console script
|
||||
# finds the project's .env rather than looking beside site-packages. Values the
|
||||
# caller has already exported are never overridden.
|
||||
load_dotenv(find_dotenv(usecwd=True))
|
||||
load_dotenv(find_dotenv(".env.enterprise", usecwd=True), override=False)
|
||||
|
||||
@@ -71,9 +71,6 @@ _ALIASES = {
|
||||
"FRA40": "^FCHI", "EU50": "^STOXX50E", "HK50": "^HSI",
|
||||
}
|
||||
|
||||
# Yahoo symbols may contain letters, digits, and these structural characters.
|
||||
_YAHOO_SAFE = re.compile(r"^[A-Za-z0-9._\-\^=]+$")
|
||||
|
||||
# HKEX codes as Yahoo spells them: the number zero-padded to 4 digits (#957).
|
||||
_HK_CODE = re.compile(r"^(\d{1,5})\.HK$")
|
||||
_SHANGHAI_SH = re.compile(r"^(\d{6})\.SH$")
|
||||
@@ -150,7 +147,3 @@ def normalize_symbol(raw: str) -> str:
|
||||
logger.info("Resolved symbol %r to Yahoo symbol %r", raw, canonical)
|
||||
return canonical
|
||||
|
||||
|
||||
def is_yahoo_safe(symbol: str) -> bool:
|
||||
"""True when ``symbol`` only contains characters Yahoo symbols use."""
|
||||
return bool(symbol) and _YAHOO_SAFE.fullmatch(symbol) is not None
|
||||
|
||||
@@ -70,7 +70,6 @@ def _apply_env_overrides(config: dict) -> dict:
|
||||
|
||||
|
||||
DEFAULT_CONFIG = _apply_env_overrides({
|
||||
"project_dir": os.path.abspath(os.path.join(os.path.dirname(__file__), ".")),
|
||||
"results_dir": os.getenv("TRADINGAGENTS_RESULTS_DIR") or os.path.join(_TRADINGAGENTS_HOME, "logs"),
|
||||
"data_cache_dir": os.getenv("TRADINGAGENTS_CACHE_DIR") or os.path.join(_TRADINGAGENTS_HOME, "cache"),
|
||||
"memory_log_path": os.getenv("TRADINGAGENTS_MEMORY_LOG_PATH") or os.path.join(_TRADINGAGENTS_HOME, "memory", "trading_memory.md"),
|
||||
|
||||
@@ -103,9 +103,6 @@ class AnalystWallTimeTracker:
|
||||
finished_at = monotonic() if completed_at is None else completed_at
|
||||
self._wall_times[analyst_key] = max(0.0, finished_at - started_at)
|
||||
|
||||
def get_wall_times(self) -> dict[str, float]:
|
||||
return dict(self._wall_times)
|
||||
|
||||
def format_summary(self) -> str:
|
||||
parts = []
|
||||
for spec in self.plan.specs:
|
||||
|
||||
@@ -51,11 +51,6 @@ def get_checkpointer(data_dir: str | Path, ticker: str) -> Generator[SqliteSaver
|
||||
conn.close()
|
||||
|
||||
|
||||
def has_checkpoint(data_dir: str | Path, ticker: str, date: str, signature: str = "") -> bool:
|
||||
"""Check whether a resumable checkpoint exists for ticker+date."""
|
||||
return checkpoint_step(data_dir, ticker, date, signature) is not None
|
||||
|
||||
|
||||
def checkpoint_step(data_dir: str | Path, ticker: str, date: str, signature: str = "") -> int | None:
|
||||
"""Return the step number of the latest checkpoint, or None if none exists."""
|
||||
db = _db_path(data_dir, ticker)
|
||||
|
||||
@@ -145,9 +145,6 @@ class TradingAgentsGraph:
|
||||
)
|
||||
self.reflector = Reflector(self.quick_thinking_llm)
|
||||
|
||||
# State tracking
|
||||
self.curr_state = None
|
||||
|
||||
# Graph-shape-affecting run choices, kept for the checkpoint signature.
|
||||
self.selected_analysts = tuple(selected_analysts)
|
||||
|
||||
@@ -538,9 +535,6 @@ class TradingAgentsGraph:
|
||||
else:
|
||||
final_state = self.graph.invoke(graph_input, **args)
|
||||
|
||||
# Store current state for reflection.
|
||||
self.curr_state = final_state
|
||||
|
||||
# Log state to disk.
|
||||
self._log_state(trade_date, final_state)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user