TauricResearch/TradingAgents · warning · NoMarketDataError

latest row is {latest.date()}, {stale_days} days before the

Error message

latest row is {latest.date()}, {stale_days} days before the requested {requested.date()} (stale) — refusing to use it

What it means

Raised by the staleness guard in tradingagents/dataflows/stockstats_utils.py when the newest OHLCV row (after normalization) is more than max_stale_days before the requested curr_date. It is a NoMarketDataError carrying (symbol, canonical, detail) — the router converts it into the explicit NO_DATA_AVAILABLE sentinel telling the agent not to fabricate values, rather than silently feeding year-old prices into indicators (#1021).

Source

Thrown at tradingagents/dataflows/stockstats_utils.py:123

    it like any other "no usable data from this vendor" — try the next vendor,
    then emit one clear unavailable signal. Empty frames are left to the
    caller's existing no-data handling; this guards only the dangerous case of
    present-but-stale rows (a vendor returning a year-old frame that would
    otherwise feed wrong prices to the agent, #1021).
    """
    if data is None or data.empty:
        return
    requested = pd.to_datetime(curr_date, errors="coerce")
    if pd.isna(requested):
        return
    requested = requested.normalize()
    dates = _coerce_ohlcv_dates(data)
    if dates.empty:
        return
    latest = dates.max().normalize()
    stale_days = (requested - latest).days
    if stale_days > max_stale_days:
        raise NoMarketDataError(
            symbol,
            canonical,
            f"latest row is {latest.date()}, {stale_days} days before the "
            f"requested {requested.date()} (stale) — refusing to use it",
        )


def _needs_same_day_refresh(data_file, curr_date_dt, today_date) -> bool:
    """Whether a cached frame must be refetched to reflect the requested day.

    The cache file is keyed per day, so without this a run started before the
    day's bar was final keeps serving that snapshot to every later run (#1150).
    Two distinct staleness cases exist for a current-day request: the bar may be
    missing entirely, or present but still in progress — Yahoo publishes a
    partial daily candle during market hours, whose ``Close`` is not the closing
    price. Row inspection cannot tell a partial bar from a final one, so the TTL
    governs every current-day cache. Historical requests always reuse the cache,
    since those rows are immutable.

View on GitHub (pinned to a33fd4c0f1)

Solutions

  1. Tune the tolerance: pass/raise max_stale_days appropriate to the market calendar (e.g. >= 4 days covers long weekends)
  2. Refresh the cache for the requested date: delete the symbol's cached CSV so the next call refetches up to curr_date
  3. Align curr_date to the last actual trading day (e.g. previous business day) instead of a weekend/holiday date
  4. Handle NoMarketDataError (or the returned NO_DATA_AVAILABLE sentinel string) by skipping or reporting unavailable instead of retrying — it is a data verdict, not a transient failure

Example fix

# before
get_stock_data_indicators_window_sma("HALTED", "2025-06-10", 10, 10)
# -> NoMarketDataError: ... latest row is 2025-05-20, 21 days before the requested 2025-06-10 (stale)

# after
# align to a real trading day and widen tolerance for weekends
get_stock_data_indicators_window_sma("SPY", "2025-06-10", 10, 10, max_stale_days=5)
from tradingagents.dataflows.errors import NoMarketDataError
try: ...
except NoMarketDataError as e: report_unavailable(e.symbol)
Defensive patterns

Strategy: validation

Validate before calling

import pandas as pd
from tradingagents.dataflows.stockstats_utils import load_ohlcv, _coerce_ohlcv_dates

def fresh_enough(symbol: str, curr_date: str, max_stale_days: int) -> bool:
    data = load_ohlcv(symbol, curr_date)
    if data is None or data.empty:
        return False
    dates = _coerce_ohlcv_dates(data)
    if dates.empty:
        return False
    latest = dates.max().normalize()
    return (pd.to_datetime(curr_date).normalize() - latest).days <= max_stale_days

Try / catch

from tradingagents.dataflows.errors import NoMarketDataError

try:
    out = get_stock_data_indicators_window_sma(sym, curr_date, 10, 10)
except NoMarketDataError as e:
    # stale data is a verdict, not a transient error: skip/report, don't retry blindly
    log_unavailable(e.symbol, e.detail)

Prevention

When it happens

Trigger: Requesting curr_date=today on a symbol whose cache ends weeks earlier (e.g. a halted/delisted stock); weekend/holiday requests beyond max_stale_days; a cache file written mid-run before the day's bar existed (#1150) being reused for a later date; non-US calendars where the last trading day is far back.

Common situations: Backtests crossing market holidays; symbols with sparse trading (some OTC/ETCs); stale per-day cache files; analysts asking for 'today' data on a long weekend.

Related errors


AI-assisted analysis of TauricResearch/TradingAgents@a33fd4c0f1 (2026-08-14). Data as JSON: /api/errors/1d83f2834030076d. Report an issue: GitHub.