mirror of
https://github.com/TauricResearch/TradingAgents.git
synced 2026-11-04 00:22:43 +02:00
fix(backtest): report the settlement failures a sweep's last pass records (#1557)
- run_backtest adds settle_pending's failed entries to settlement_failures, each reason prefixed with its date
This commit is contained in:
@@ -12,6 +12,7 @@ import pytest
|
|||||||
|
|
||||||
from tradingagents.backtest import iter_grid, run_backtest, summarize
|
from tradingagents.backtest import iter_grid, run_backtest, summarize
|
||||||
from tradingagents.memory import TradingMemoryLog
|
from tradingagents.memory import TradingMemoryLog
|
||||||
|
from tradingagents.memory.settlement import Settlement
|
||||||
|
|
||||||
DECISION = "Rating: Buy\n\nbuy it"
|
DECISION = "Rating: Buy\n\nbuy it"
|
||||||
|
|
||||||
@@ -58,6 +59,7 @@ class _FakeGraph:
|
|||||||
|
|
||||||
def settle_pending(self, ticker):
|
def settle_pending(self, ticker):
|
||||||
self.settled.append(ticker)
|
self.settled.append(ticker)
|
||||||
|
return Settlement()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
@@ -172,6 +174,7 @@ def test_a_failed_settlement_does_not_lose_the_remaining_tickers(tmp_path, monke
|
|||||||
if ticker == "NVDA":
|
if ticker == "NVDA":
|
||||||
raise RuntimeError("reflector timed out")
|
raise RuntimeError("reflector timed out")
|
||||||
settled.append(ticker)
|
settled.append(ticker)
|
||||||
|
return Settlement()
|
||||||
|
|
||||||
monkeypatch.setattr(_FakeGraph, "settle_pending", _settle, raising=False)
|
monkeypatch.setattr(_FakeGraph, "settle_pending", _settle, raising=False)
|
||||||
result = run_backtest(["NVDA", "AAPL"], ["2026-01-05"], _config(tmp_path))
|
result = run_backtest(["NVDA", "AAPL"], ["2026-01-05"], _config(tmp_path))
|
||||||
@@ -181,6 +184,46 @@ def test_a_failed_settlement_does_not_lose_the_remaining_tickers(tmp_path, monke
|
|||||||
assert result.settlement_failures == [("NVDA", "reflector timed out")]
|
assert result.settlement_failures == [("NVDA", "reflector timed out")]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@pytest.mark.parametrize("failure", ["prices", "reflection"])
|
||||||
|
def test_handled_settlement_failures_reach_the_backtest_result(tmp_path, monkeypatch, failure):
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
from tradingagents.memory import settlement
|
||||||
|
|
||||||
|
def returns(ticker, *args, **kwargs):
|
||||||
|
if ticker == "AAPL":
|
||||||
|
return None, None, None, None # an incomplete window is not a failure
|
||||||
|
if failure == "prices":
|
||||||
|
raise RuntimeError("vendor unavailable")
|
||||||
|
return 0.1, 0.02, 5, "2026-02-01"
|
||||||
|
|
||||||
|
def reflect(**kwargs):
|
||||||
|
raise RuntimeError("reflector timed out")
|
||||||
|
|
||||||
|
def settle(self, ticker):
|
||||||
|
self.settled.append(ticker)
|
||||||
|
return settlement.settle_pending(
|
||||||
|
ticker, self.memory_log, SimpleNamespace(reflect_on_final_decision=reflect), self.config,
|
||||||
|
)
|
||||||
|
|
||||||
|
monkeypatch.setattr(settlement, "fetch_returns", returns)
|
||||||
|
monkeypatch.setattr(_FakeGraph, "settle_pending", settle)
|
||||||
|
result = run_backtest(
|
||||||
|
["NVDA", "AAPL"], ["2026-01-05", "2026-01-12"], _config(tmp_path),
|
||||||
|
)
|
||||||
|
|
||||||
|
reason = ("prices unavailable: vendor unavailable" if failure == "prices"
|
||||||
|
else "reflection failed: reflector timed out")
|
||||||
|
assert result.settlement_failures == [
|
||||||
|
("NVDA", f"2026-01-05: {reason}"),
|
||||||
|
("NVDA", f"2026-01-12: {reason}"),
|
||||||
|
]
|
||||||
|
assert _FakeGraph.instances[-1].settled == ["NVDA", "AAPL"]
|
||||||
|
assert result.cells_run == 4 and not result.failures
|
||||||
|
assert len(_FakeGraph.instances[-1].memory_log.get_pending_entries()) == 4
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_pending_note_appears_only_when_something_is_pending(tmp_path):
|
def test_pending_note_appears_only_when_something_is_pending(tmp_path):
|
||||||
settled = [("NVDA", "2026-01-05", DECISION, (0.1, 0.05))]
|
settled = [("NVDA", "2026-01-05", DECISION, (0.1, 0.05))]
|
||||||
|
|||||||
@@ -174,7 +174,11 @@ def run_backtest(
|
|||||||
# last cell would stay pending without this pass.
|
# last cell would stay pending without this pass.
|
||||||
for ticker in tickers:
|
for ticker in tickers:
|
||||||
try:
|
try:
|
||||||
graph.settle_pending(ticker)
|
settlement = graph.settle_pending(ticker)
|
||||||
|
result.settlement_failures.extend(
|
||||||
|
(failed_ticker, f"{date}: {reason}")
|
||||||
|
for failed_ticker, date, reason in settlement.failed
|
||||||
|
)
|
||||||
except Exception as exc: # reflection calls an LLM; one failure is not the sweep's
|
except Exception as exc: # reflection calls an LLM; one failure is not the sweep's
|
||||||
logger.warning("Settling %s failed: %s", ticker, exc)
|
logger.warning("Settling %s failed: %s", ticker, exc)
|
||||||
result.settlement_failures.append((ticker, str(exc)))
|
result.settlement_failures.append((ticker, str(exc)))
|
||||||
|
|||||||
Reference in New Issue
Block a user