fix(graph): stop keeping every run's full state in the graph

- a graph reused across a backtest grid held each run's complete state for its whole life
- the state log is written from the run's own state; the ticker attribute it read is removed
This commit is contained in:
Yijia-Xiao
2026-09-24 04:31:04 +00:00
parent f197e09dcc
commit 2938e1c7e9
4 changed files with 37 additions and 24 deletions
+19 -14
View File
@@ -35,21 +35,12 @@ def test_the_other_shapes_are_unchanged(value, expected):
assert extract_content_string(value) == expected
@pytest.mark.unit
def test_the_state_log_keeps_non_ascii_readable(tmp_path):
"""Reports can be in any language; the log is read by a person."""
from tradingagents.graph.trading_graph import TradingAgentsGraph
graph = object.__new__(TradingAgentsGraph)
graph.config = {"results_dir": str(tmp_path)}
graph.ticker = "600519.SS"
graph.log_states_dict = {}
graph._log_state("2026-09-01", {
"company_of_interest": "600519.SS", "trade_date": "2026-09-01",
def _state(ticker, final="评级: 买入"):
return {
"company_of_interest": ticker, "trade_date": "2026-09-01",
"market_report": "市场", "sentiment_report": "情绪", "news_report": "新闻",
"fundamentals_report": "基本面", "investment_plan": "计划",
"trader_investment_plan": "交易计划", "final_trade_decision": "评级: 买入",
"trader_investment_plan": "交易计划", "final_trade_decision": final,
"investment_debate_state": {"bull_history": "", "bear_history": "", "history": "",
"current_response": "", "judge_decision": "", "count": 0},
"risk_debate_state": {"aggressive_history": "", "conservative_history": "",
@@ -57,7 +48,21 @@ def test_the_state_log_keeps_non_ascii_readable(tmp_path):
"latest_speaker": "", "current_aggressive_response": "",
"current_conservative_response": "", "current_neutral_response": "",
"count": 0},
})
}
def _bare_graph(tmp_path):
from tradingagents.graph.trading_graph import TradingAgentsGraph
graph = object.__new__(TradingAgentsGraph)
graph.config = {"results_dir": str(tmp_path)}
return graph
@pytest.mark.unit
def test_the_state_log_keeps_non_ascii_readable(tmp_path):
"""Reports can be in any language; the log is read by a person."""
_bare_graph(tmp_path)._log_state("2026-09-01", _state("600519.SS"))
written = next(tmp_path.rglob("full_states_log*.json")).read_text(encoding="utf-8")
assert "买入" in written
+13
View File
@@ -153,3 +153,16 @@ def test_an_interrupted_run_resumes_from_its_checkpoint(tmp_path, monkeypatch, o
_graph(tmp_path / "fresh", monkeypatch, full_run).propagate("NVDA", TRADE_DATE)
# The resumed run makes only the calls the interrupted one had not completed.
assert resumed_calls == len(full_run.calls) - (model.fail_at - 1)
@pytest.mark.unit
def test_a_graph_reused_across_runs_keeps_no_run_state(tmp_path, monkeypatch, offline):
"""A backtest reuses one graph over its whole grid; holding every run's full
state would grow without bound."""
graph = _graph(tmp_path, monkeypatch, ScriptedModel())
for trade_date in ("2026-01-08", TRADE_DATE):
graph.propagate("NVDA", trade_date)
held = [v for v in vars(graph).values() if isinstance(v, dict) and TRADE_DATE in v]
assert held == []
assert len(list(tmp_path.glob("results/NVDA/TradingAgentsStrategy_logs/*.json"))) == 2
-1
View File
@@ -932,7 +932,6 @@ class TestLegacyRemoval:
}
mock_graph = MagicMock()
mock_graph.memory_log = TradingMemoryLog({"memory_log_path": str(tmp_path / "mem.md")})
mock_graph.log_states_dict = {}
mock_graph.debug = False
mock_graph.config = {"results_dir": str(tmp_path)}
mock_graph.graph.invoke.return_value = fake_state