virattt/ai-hedge-fund · critical · ValueError
no {spec.benchmark} bars in [{start}, {end}] — cannot build
Error message
no {spec.benchmark} bars in [{start}, {end}] — cannot build the trading grid What it means
Raised in the TUI's off-thread backtest worker (_run in hedge_fund/tui/app.py:1778) under the same policy as error [0]: the benchmark returned no bars within [start, end], so no trading grid can be built. The TUI fetches benchmark bars first (via CachedDataClient wrapping FDClient) to size its progress UI before warming agents and replaying cycles — this check is the earliest failure point of a TUI-launched run.
Source
Thrown at hedge_fund/tui/app.py:1778
def _after_done(self, event: OptionList.OptionSelected) -> None:
# "Back to home" means home: a ctrl+b backtest sits on top of the run
# screen, so popping once would land on its stale ticker input.
while not isinstance(self.app.screen, HomeScreen):
self.app.pop_screen()
# ---- the worker (everything below the UI runs off-thread) -------------
@work(thread=True, exclusive=True)
def _run(self, spec: FundSpec, start: str, end: str,
universe: list[str]) -> None:
app = self.app
try:
with FDClient() as raw:
bars = CachedDataClient(raw).get_prices(spec.benchmark, start, end)
closes = {b.time[:10]: b.close for b in bars
if start <= b.time[:10] <= end}
if not closes:
raise ValueError(
f"no {spec.benchmark} bars in [{start}, {end}] — "
"cannot build the trading grid"
)
grid = rebalance_grid(sorted(closes), spec.rebalance)
app.call_from_thread(self._begin_warm, spec, universe, len(grid))
self._warm_market(spec, universe, grid)
app.call_from_thread(self._begin_agents, spec)
self._warm_agents(spec, universe, grid)
app.call_from_thread(self._begin_replay, spec, closes, len(grid))
fund = Fund(spec)
def tick(i: int, n: int, record: CycleRecord) -> None:
started = time.time()
app.call_from_thread(self._board_tick, record)
dwell = _CYCLE_DWELL - (time.time() - started)
if dwell > 0:View on GitHub (pinned to eff8a7320f)
Solutions
- Verify the benchmark bars before launching the run: a quick shell call to get_prices(spec.benchmark, start, end) tells you whether the ticker/window is the problem.
- Correct the benchmark ticker in the mandate YAML to a symbol the provider actually returns (check casing/format).
- Adjust the date range so it contains at least one benchmark trading day and lies within your cached data coverage.
- If the cache is stale/truncated for the benchmark, clear or refresh it, then retry from the TUI.
Example fix
# before # TUI run with benchmark: 'spx' (unrecognized symbol) -> ValueError: no spx bars in [...] # after # mandate.yaml benchmark: SPY # a symbol the provider returns bars for
Defensive patterns
Strategy: validation
Validate before calling
def tui_run_is_launchable(client, spec, start: str, end: str) -> str | None:
"""None if ok, else a user-facing reason — call before starting the worker."""
bars = client.get_prices(spec.benchmark, start, end)
closes = [b.time[:10] for b in bars if start <= b.time[:10] <= end]
if not closes:
return (f"benchmark {spec.benchmark} has no bars in [{start}, {end}]; "
"check the symbol and pick a range with trading days")
return None Try / catch
try:
... # inside the @work(thread=True) worker
except ValueError as e:
if "cannot build the trading grid" in str(e):
app.call_from_thread(self.notify, f"Cannot start: {e}", severity="error")
return
raise Prevention
- Validate the benchmark bars on the UI thread before spawning the worker, so the user gets an inline message instead of a crashed run.
- Offer benchmark selection from known-good symbols rather than free text.
- Keep the price cache warm for the benchmark across the default date ranges the TUI offers.
- Reuse the same date-normalization helper the engine uses so string compares can't diverge.
When it happens
Trigger: Starting a backtest in the TUI with: a benchmark ticker that doesn't resolve (typo, wrong symbol format for the provider); a date range entirely on non-trading days; end date before the cached data begins or after it ends; the same string-compare pitfall where start/end aren't plain YYYY-MM-DD. Because it runs inside a @work(thread=True) worker, the ValueError surfaces through the TUI's error display rather than a console traceback.
Common situations: User types the benchmark symbol in a format the provider rejects; picks a holiday-week range in the date pickers; local cache was built for a different window so get_prices returns nothing; typo in the mandate's benchmark field surfaced only when the run starts.
Related errors
- {spec.name}: no {spec.benchmark} bars in [{start}, {end}] —
- unknown rebalance cadence {cadence!r}
- {ticker} as of {as_of}: only {len(metrics)} filed periods (n
- {spec.name}: equity is {equity_before:.2f} as of {as_of} — c
AI-assisted analysis of virattt/ai-hedge-fund@eff8a7320f (2026-08-15).
Data as JSON: /api/errors/ab85d95e55b29386.
Report an issue: GitHub.