docs: tighten the comments on the point-in-time guards

- keep what the code cannot state itself: which session a closeless bar is,
  where the drop actually happens, and why the trim must stay unguarded
- drop the field-by-field enumeration, the account of what the previous
  behaviour got wrong, and the restatements of adjacent calls
This commit is contained in:
Yijia-Xiao
2026-09-07 22:21:20 +00:00
parent d6ca23aee5
commit 821848bb82
3 changed files with 10 additions and 19 deletions

View File

@@ -130,8 +130,8 @@ def _filter_csv_by_date_range(csv_data: str, start_date: str, end_date: str) ->
# Deliberately unguarded: TIME_SERIES_DAILY_ADJUSTED returns the full series
# up to today, so this trim is the only thing keeping bars after end_date out
# of a historical run. Returning the untrimmed body on failure would leak
# future prices, so a parse failure propagates and the caller fails closed.
# of a historical run. Swallowing a parse failure would serve the untrimmed
# body, and with it future prices.
df = pd.read_csv(StringIO(csv_data))
# Assume the first column is the date column (timestamp)

View File

@@ -36,16 +36,11 @@ def withhold_live_profile(curr_date: str | None, label: str) -> str | None:
"""Notice to serve instead of a live-only company profile, or None to serve it.
Vendor "company overview" endpoints (yfinance ``Ticker.info``, Alpha Vantage
``OVERVIEW``) return only present-day values: market cap, valuation
multiples, the 52-week range and TTM income all move with today's quote, and
even name, sector and industry shift when a company renames or is
reclassified. None of it carries a historical vintage, so serving it into a
run dated in the past puts post-decision information into the analyst's
context (#1300).
Centralized so every fundamentals vendor withholds on the same rule and says
the same thing; point-in-time statements come from the balance sheet, income
statement and cash flow tools, which filter on ``curr_date``.
``OVERVIEW``) carry no historical vintage — not even name, sector and
industry, which move when a company renames or is reclassified — so serving
one into a run dated in the past leaks post-decision information (#1300).
Every fundamentals vendor withholds on this rule, so switching between them
cannot reintroduce the leak.
"""
if not curr_date:
return None

View File

@@ -250,14 +250,10 @@ def load_ohlcv(symbol: str, curr_date: str) -> pd.DataFrame:
# Filter to curr_date to prevent look-ahead bias in backtesting.
data = data[data["Date"] <= curr_date_dt]
# A newest bar with no close is usually an unsettled session — mid-session,
# a holiday, or a thinly traded instrument — not a symbol without data.
# A closeless newest bar is an unsettled session, not a symbol without data.
# _fill_price_gaps below drops it, here and mid-series alike, so the frame
# ends at the last settled bar rather than carrying a fabricated close
# (#1201). Refusing the whole frame instead reported a tradable symbol as
# invalid or delisted (#1289), so only a range with no close anywhere is
# treated as no data; the staleness check decides whether what remains is
# recent enough for curr_date.
# ends at the last settled bar; only a range with no close anywhere is no
# data (#1201, #1289).
if not data.empty and pd.isna(data["Close"].iloc[-1]):
settled = data["Close"].notna().to_numpy().nonzero()[0]
if settled.size == 0: