{"record":{"id":"ab85d95e55b29386","repo":"virattt/ai-hedge-fund","slug":"no-spec-benchmark-bars-in-start-end-can","errorCode":null,"errorMessage":"no {spec.benchmark} bars in [{start}, {end}] — cannot build the trading grid","messagePattern":"no (.+?) bars in \\[(.+?), (.+?)\\] — cannot build the trading grid","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"critical","filePath":"hedge_fund/tui/app.py","lineNumber":1778,"sourceCode":"    def _after_done(self, event: OptionList.OptionSelected) -> None:\n        # \"Back to home\" means home: a ctrl+b backtest sits on top of the run\n        # screen, so popping once would land on its stale ticker input.\n        while not isinstance(self.app.screen, HomeScreen):\n            self.app.pop_screen()\n\n    # ---- the worker (everything below the UI runs off-thread) -------------\n\n    @work(thread=True, exclusive=True)\n    def _run(self, spec: FundSpec, start: str, end: str,\n             universe: list[str]) -> None:\n        app = self.app\n        try:\n            with FDClient() as raw:\n                bars = CachedDataClient(raw).get_prices(spec.benchmark, start, end)\n            closes = {b.time[:10]: b.close for b in bars\n                      if start <= b.time[:10] <= end}\n            if not closes:\n                raise ValueError(\n                    f\"no {spec.benchmark} bars in [{start}, {end}] — \"\n                    \"cannot build the trading grid\"\n                )\n            grid = rebalance_grid(sorted(closes), spec.rebalance)\n\n            app.call_from_thread(self._begin_warm, spec, universe, len(grid))\n            self._warm_market(spec, universe, grid)\n            app.call_from_thread(self._begin_agents, spec)\n            self._warm_agents(spec, universe, grid)\n            app.call_from_thread(self._begin_replay, spec, closes, len(grid))\n\n            fund = Fund(spec)\n\n            def tick(i: int, n: int, record: CycleRecord) -> None:\n                started = time.time()\n                app.call_from_thread(self._board_tick, record)\n                dwell = _CYCLE_DWELL - (time.time() - started)\n                if dwell > 0:","sourceCodeStart":1760,"sourceCodeEnd":1796,"githubUrl":"https://github.com/virattt/ai-hedge-fund/blob/eff8a7320fcf0b473b135690fa1a5b0d9b022a83/hedge_fund/tui/app.py#L1760-L1796","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\n# TUI run with benchmark: 'spx' (unrecognized symbol) -> ValueError: no spx bars in [...]\n\n# after\n# mandate.yaml\nbenchmark: SPY   # a symbol the provider returns bars for","handlingStrategy":"validation","validationCode":"def tui_run_is_launchable(client, spec, start: str, end: str) -> str | None:\n    \"\"\"None if ok, else a user-facing reason — call before starting the worker.\"\"\"\n    bars = client.get_prices(spec.benchmark, start, end)\n    closes = [b.time[:10] for b in bars if start <= b.time[:10] <= end]\n    if not closes:\n        return (f\"benchmark {spec.benchmark} has no bars in [{start}, {end}]; \"\n                \"check the symbol and pick a range with trading days\")\n    return None","typeGuard":null,"tryCatchPattern":"try:\n    ...  # inside the @work(thread=True) worker\nexcept ValueError as e:\n    if \"cannot build the trading grid\" in str(e):\n        app.call_from_thread(self.notify, f\"Cannot start: {e}\", severity=\"error\")\n        return\n    raise","preventionTips":["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."],"tags":["tui","backtesting","benchmark","data-validation"],"backgroundTag":null,"analyzedSha":"eff8a7320fcf0b473b135690fa1a5b0d9b022a83","analyzedAt":"2026-08-15T00:22:46.567Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}