fix(memory): don't settle a decision before its holding window trades

- _fetch_returns settled on min(holding_days, available), so a rerun a day or
  two after a decision reflected on a 1-2 day partial return as if final
- require the full holding window in both the stock and benchmark series before
  resolving; otherwise leave the entry pending to retry next run
- this also makes the #1251 resolution date the full-window date, not a partial
  bar's #1169
This commit is contained in:
Yijia-Xiao
2026-08-31 01:55:31 +00:00
parent a2f51da917
commit 30d42abd5d
2 changed files with 51 additions and 17 deletions
+13 -10
View File
@@ -278,10 +278,11 @@ class TradingAgentsGraph:
``benchmark`` is the index used as the alpha baseline (resolved by the
caller via ``_resolve_benchmark``). Returns ``(raw_return, alpha_return,
actual_holding_days, resolution_date)`` — where ``resolution_date`` is
the date of the last price bar used, i.e. when the outcome became known
(#1251) — or ``(None, None, None, None)`` if price data is unavailable
(too recent, delisted, or network error).
holding_days, resolution_date)`` — where ``resolution_date`` is the date
of the last price bar used, i.e. when the outcome became known (#1251) —
or ``(None, None, None, None)`` when the outcome cannot be settled yet:
the full holding window has not traded (#1169), or the symbol is delisted
or unreachable.
"""
from tradingagents.dataflows.symbol_utils import normalize_symbol
@@ -296,23 +297,25 @@ class TradingAgentsGraph:
stock = yf.Ticker(normalize_symbol(ticker)).history(start=trade_date, end=end_str)
bench = yf.Ticker(benchmark).history(start=trade_date, end=end_str)
if len(stock) < 2 or len(bench) < 2:
# Require the full holding window in both series. A rerun before it
# has traded leaves the entry pending to retry next run, rather than
# settling on a premature partial return (#1169).
if len(stock) <= holding_days or len(bench) <= holding_days:
return None, None, None, None
actual_days = min(holding_days, len(stock) - 1, len(bench) - 1)
raw = float(
(stock["Close"].iloc[actual_days] - stock["Close"].iloc[0])
(stock["Close"].iloc[holding_days] - stock["Close"].iloc[0])
/ stock["Close"].iloc[0]
)
bench_ret = float(
(bench["Close"].iloc[actual_days] - bench["Close"].iloc[0])
(bench["Close"].iloc[holding_days] - bench["Close"].iloc[0])
/ bench["Close"].iloc[0]
)
alpha = raw - bench_ret
# The date of the last price bar used is when this outcome became
# known — the point-in-time cutoff for injecting the lesson (#1251).
resolution_date = stock.index[actual_days].strftime("%Y-%m-%d")
return raw, alpha, actual_days, resolution_date
resolution_date = stock.index[holding_days].strftime("%Y-%m-%d")
return raw, alpha, holding_days, resolution_date
except Exception as e:
logger.warning(
"Could not resolve outcome for %s on %s vs %s (will retry next run): %s",