fix(cli): take the backtest's analysts as an analysis does

- backtest --analysts accepts sentiment and is checked against --asset-type before the sweep
- without --analysts, a crypto sweep runs the analysts crypto allows, not fundamentals
This commit is contained in:
Yijia-Xiao
2026-09-29 05:57:18 +00:00
parent e973dd0e25
commit d201105a4d
2 changed files with 39 additions and 4 deletions
+10 -4
View File
@@ -3,6 +3,8 @@ import sys
import typer
from cli.display import console
from cli.models import AnalystType, AssetType
from cli.prompts import filter_analysts_for_asset_type, parse_analysts
from cli.run import run_analysis
from tradingagents.backtest import iter_grid, run_backtest, summarize
from tradingagents.default_config import DEFAULT_CONFIG
@@ -101,7 +103,7 @@ def backtest(
end: str = typer.Option(..., "--end", help="Last analysis date, YYYY-MM-DD"),
every: int = typer.Option(7, "--every", help="Days between analysis dates"),
analysts: str = typer.Option(
None, "--analysts", help="Comma-separated analysts to run; omit for all four"
None, "--analysts", help="Comma-separated analysts to run: market, sentiment, news, fundamentals; omit for all the asset type allows"
),
asset_type: str = typer.Option("stock", "--asset-type", help="stock or crypto"),
portfolio: str = typer.Option(
@@ -116,6 +118,11 @@ def backtest(
try:
dates = iter_grid(start, end, every)
book = load_portfolio(portfolio) if portfolio else None
kind = AssetType(asset_type.strip().lower())
# The analysts are named and checked as for an analysis; without a
# choice, every analyst the asset type allows runs.
chosen = (parse_analysts(analysts, kind) if analysts
else filter_analysts_for_asset_type(list(AnalystType), kind))
except ValueError as exc:
console.print(f"[red]{exc}[/red]")
raise typer.Exit(code=1) from None
@@ -128,9 +135,8 @@ def backtest(
def show_progress(done, total, ticker, date):
console.print(f"[dim][{done}/{total}] {ticker} {date}[/dim]")
kwargs = {"asset_type": asset_type, "portfolio": book, "run_id": run_id, "progress": show_progress}
if analysts:
kwargs["selected_analysts"] = [a.strip().lower() for a in analysts.split(",") if a.strip()]
kwargs = {"asset_type": kind.value, "portfolio": book, "run_id": run_id, "progress": show_progress,
"selected_analysts": [a.value for a in chosen]}
try:
result = run_backtest(names, dates, DEFAULT_CONFIG, **kwargs)
+29
View File
@@ -151,3 +151,32 @@ def test_backtest_shows_each_cell_and_how_to_continue(runner, monkeypatch, tmp_p
assert "[1/2] NVDA 2026-06-01" in result.output and "[2/2] NVDA 2026-06-08" in result.output
assert f"--run-id {_Result(tmp_path).run_id}" in result.output
@pytest.mark.unit
@pytest.mark.parametrize("args, analysts", [
pytest.param(["--analysts", "sentiment,market"], ["market", "social"], id="named_as_in_analyze"),
pytest.param(["--asset-type", "crypto"], ["market", "social", "news"], id="crypto_default"),
])
def test_backtest_takes_its_analysts_as_an_analysis_does(runner, monkeypatch, tmp_path, args, analysts):
swept = []
monkeypatch.setattr(m, "run_backtest", lambda *a, **kw: swept.append(kw) or _Result(tmp_path))
monkeypatch.setattr(m, "summarize", lambda log: _Summary())
result = runner.invoke(m.app, ["backtest", "NVDA", "--start", "2026-06-01", "--end", "2026-06-01", *args])
assert result.exit_code == 0, result.output
assert swept[0]["selected_analysts"] == analysts
@pytest.mark.unit
@pytest.mark.parametrize("args", [
pytest.param(["--analysts", "fundamentals", "--asset-type", "crypto"], id="unavailable_for_crypto"),
pytest.param(["--asset-type", "bond"], id="unknown_asset_type"),
])
def test_backtest_refuses_a_selection_before_the_sweep(runner, monkeypatch, args):
monkeypatch.setattr(m, "run_backtest", lambda *a, **kw: pytest.fail("the sweep started"))
result = runner.invoke(m.app, ["backtest", "NVDA", "--start", "2026-06-01", "--end", "2026-06-01", *args])
assert result.exit_code == 1