diff --git a/tests/test_alpha_vantage_hardening.py b/tests/test_alpha_vantage_hardening.py index 0d15bf12b..d82989875 100644 --- a/tests/test_alpha_vantage_hardening.py +++ b/tests/test_alpha_vantage_hardening.py @@ -82,7 +82,7 @@ def test_fundamentals_look_ahead_filter_runs_on_json_string(monkeypatch): # #1115: the payload arrives as a JSON *string*; the old dict-only guard let # future-dated fiscal periods leak into historical runs. monkeypatch.setattr(avf, "_make_api_request", lambda fn, params: _FUNDAMENTALS_JSON) - out = avf.get_balance_sheet("AAPL", curr_date="2024-01-01") + out = avf.get_balance_sheet("AAPL", as_of_date="2024-01-01") assert isinstance(out, str) # callers still receive a str parsed = json.loads(out) assert [r["fiscalDateEnding"] for r in parsed["annualReports"]] == ["2023-12-31"] @@ -98,7 +98,7 @@ def test_fundamentals_no_curr_date_passes_through(monkeypatch): @pytest.mark.unit def test_fundamentals_non_json_body_unchanged(monkeypatch): monkeypatch.setattr(avf, "_make_api_request", lambda fn, params: "not-json") - assert avf.get_cashflow("AAPL", curr_date="2024-01-01") == "not-json" + assert avf.get_cashflow("AAPL", as_of_date="2024-01-01") == "not-json" # --------------------------------------------------------------------------- diff --git a/tests/test_fred.py b/tests/test_fred.py index c28c29891..b27ba680d 100644 --- a/tests/test_fred.py +++ b/tests/test_fred.py @@ -141,7 +141,7 @@ class FredFormattingTests(unittest.TestCase): self.assertEqual(len(body_rows), fred.MAX_ROWS) def test_window_is_lookahead_safe(self): - # observation_end must equal curr_date so a past date never pulls future data. + # observation_end must equal as_of_date so a past date never pulls future data. captured = {} def _capture(path, params): @@ -156,9 +156,9 @@ class FredFormattingTests(unittest.TestCase): def test_requests_pin_the_data_vintage(self): # #1275: both the metadata and observations requests must pin the vintage - # to curr_date (clamped to FRED's today), or FRED serves the latest + # to as_of_date (clamped to FRED's today), or FRED serves the latest # revision and revision-prone series leak future information. A past - # curr_date sits below FRED's today, so it pins through unchanged. + # as_of_date sits below FRED's today, so it pins through unchanged. captured = {} def _capture(path, params): @@ -174,11 +174,11 @@ class FredFormattingTests(unittest.TestCase): self.assertEqual(captured[path]["realtime_end"], "2025-09-30", path) def test_future_curr_date_clamps_vintage_to_fred_today(self): - # #1275 regression: on a live run curr_date is the caller's LOCAL date, + # #1275 regression: on a live run as_of_date is the caller's LOCAL date, # which can be a day ahead of FRED's US-Central clock. Pinning the vintage # to that future date 400s, and the routing layer then drops macro data # silently. The pin must clamp to FRED's today; the observation window - # (future bars can't exist yet) stays at curr_date. + # (future bars can't exist yet) stays at as_of_date. captured = {} def _capture(path, params): @@ -192,7 +192,7 @@ class FredFormattingTests(unittest.TestCase): for path in ("series", "series/observations"): self.assertEqual(captured[path]["realtime_start"], "2026-08-31", path) self.assertEqual(captured[path]["realtime_end"], "2026-08-31", path) - # the observation window still tracks curr_date, not the clamped vintage + # the observation window still tracks as_of_date, not the clamped vintage self.assertEqual(captured["series/observations"]["observation_end"], "2026-09-01") diff --git a/tests/test_fundamentals_lookahead.py b/tests/test_fundamentals_lookahead.py index d604c330e..d91d4390c 100644 --- a/tests/test_fundamentals_lookahead.py +++ b/tests/test_fundamentals_lookahead.py @@ -10,7 +10,7 @@ FRED (#1275), social (#1220) and memory (#1251) leaks. Both vendors withhold on one shared rule (``date_window.withhold_live_profile``) so switching ``fundamental_data`` between them cannot reintroduce the leak. The -statement tools stay point-in-time by filtering on ``curr_date``, and a live run +statement tools stay point-in-time by filtering on ``as_of_date``, and a live run is unchanged. All API access is mocked. """ from __future__ import annotations @@ -45,19 +45,19 @@ _LEAKY = ("3500000000000", "34.2", "260.1", "391000000000", "Apple Inc.", "Technology", "Consumer Electronics") -def _yf(curr_date, info=_INFO, today=_TODAY): +def _yf(as_of_date, info=_INFO, today=_TODAY): with mock.patch.object(date_window, "get_current_date", return_value=today), \ mock.patch.object(yahoo_fundamentals, "yf_retry", lambda fn: info), \ mock.patch.object(yahoo_market.yf, "Ticker"): - return yahoo_fundamentals.get_fundamentals("AAPL", curr_date) + return yahoo_fundamentals.get_fundamentals("AAPL", as_of_date) -def _av(curr_date, today=_TODAY): +def _av(as_of_date, today=_TODAY): """Alpha Vantage path; the API call is mocked so a leak would be visible.""" with mock.patch.object(date_window, "get_current_date", return_value=today), \ mock.patch.object(av, "_make_api_request", return_value="MarketCapitalization: 3500000000000") as req: - return av.get_fundamentals("AAPL", curr_date), req + return av.get_fundamentals("AAPL", as_of_date), req @pytest.mark.unit diff --git a/tests/test_no_data_handling.py b/tests/test_no_data_handling.py index f1eadd439..355bc76bf 100644 --- a/tests/test_no_data_handling.py +++ b/tests/test_no_data_handling.py @@ -102,4 +102,4 @@ def test_an_unreachable_yahoo_is_not_reported_as_a_symbol_without_insider_data() with mock.patch.object(fundamentals.yf, "Ticker", return_value=ticker), \ mock.patch.object(fundamentals, "vendor_reachable", return_value=False), \ pytest.raises(VendorRateLimitError): - fundamentals.get_insider_transactions("AAPL", curr_date="2026-09-21") + fundamentals.get_insider_transactions("AAPL", as_of_date="2026-09-21") diff --git a/tests/test_undated_tools_as_of.py b/tests/test_undated_tools_as_of.py index 2ece8366d..ea3450083 100644 --- a/tests/test_undated_tools_as_of.py +++ b/tests/test_undated_tools_as_of.py @@ -70,14 +70,14 @@ def test_alpha_vantage_insider_filings_after_the_date_are_dropped(): @pytest.mark.unit def test_polymarket_withholds_live_odds_from_a_historical_run(): with mock.patch.object(polymarket, "_request", side_effect=AssertionError("must not fetch")): - out = polymarket.get_prediction_markets("Fed rate cut", curr_date="2025-06-01") + out = polymarket.get_prediction_markets("Fed rate cut", as_of_date="2025-06-01") assert "withheld" in out @pytest.mark.unit def test_polymarket_serves_a_current_run(): with mock.patch.object(polymarket, "_request", return_value={"events": []}) as req: - polymarket.get_prediction_markets("Fed rate cut", curr_date=polymarket.get_current_date()) + polymarket.get_prediction_markets("Fed rate cut", as_of_date=polymarket.get_current_date()) req.assert_called_once() @@ -103,7 +103,7 @@ def test_a_historical_run_is_told_the_identity_is_current(monkeypatch): identity = {"company_name": "Example Corp", "sector": "Technology", "industry": "Software", "exchange": "NMS"} - historical = build_instrument_context("EXMP", "stock", identity, curr_date="2024-03-14") + historical = build_instrument_context("EXMP", "stock", identity, trade_date="2024-03-14") assert "Example Corp" in historical assert "2024-03-14" in historical and "today" in historical.lower() @@ -114,7 +114,7 @@ def test_a_current_run_is_not_cluttered_with_a_vintage_note(monkeypatch): from tradingagents.dataflows.date_window import get_current_date today = build_instrument_context("EXMP", "stock", {"company_name": "Example Corp"}, - curr_date=get_current_date()) + trade_date=get_current_date()) assert "Example Corp" in today assert "resolved today" not in today.lower() @@ -297,7 +297,7 @@ def test_an_unavailable_notice_names_no_date_after_the_run(): coverage_gap([pd.Timestamp(today, tz="UTC")], "2025-01-01", "2025-01-07", "Feed", "news"), withhold_live_profile("2025-01-07", "AAPL"), _yf_insider(_insider_frame(today), "2025-01-07"), - build_instrument_context("EXMP", "stock", {"company_name": "Example"}, curr_date="2025-01-07"), + build_instrument_context("EXMP", "stock", {"company_name": "Example"}, trade_date="2025-01-07"), ] for notice in notices: assert _dates_after(notice, "2025-01-07") == [], notice diff --git a/tradingagents/agents/context.py b/tradingagents/agents/context.py index 69276fd61..9842f2d5b 100644 --- a/tradingagents/agents/context.py +++ b/tradingagents/agents/context.py @@ -100,7 +100,7 @@ def build_instrument_context( ticker: str, asset_type: str = "stock", identity: Mapping[str, str] | None = None, - curr_date: str | None = None, + trade_date: str | None = None, ) -> str: """Describe the exact instrument so agents preserve identity and ticker. @@ -144,10 +144,10 @@ def build_instrument_context( "result explicitly disproves this resolved identity." ) today = get_current_date() - if curr_date and str(curr_date) < today: + if trade_date and str(trade_date) < today: context += ( f" This identity is how the vendor describes the instrument today, " - f"not necessarily on {curr_date}: a name or classification changed " + f"not necessarily on {trade_date}: a name or classification changed " f"since then would read as the current one." ) diff --git a/tradingagents/dataflows/date_window.py b/tradingagents/dataflows/date_window.py index d84e43887..aeca0f121 100644 --- a/tradingagents/dataflows/date_window.py +++ b/tradingagents/dataflows/date_window.py @@ -95,7 +95,7 @@ def as_of_window(start_date: str, end_date: str, trade_date: str) -> tuple[str, return f"{_parse(end) - span:%Y-%m-%d}", end -def withhold_live_profile(curr_date: str | None, label: str) -> str | None: +def withhold_live_profile(as_of_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 @@ -105,20 +105,20 @@ def withhold_live_profile(curr_date: str | None, label: str) -> str | None: Every fundamentals vendor withholds on this rule, so switching between them cannot reintroduce the leak. """ - if not curr_date: + if not as_of_date: return None today = get_current_date() - if curr_date >= today: + if as_of_date >= today: return None return ( f"# Company Fundamentals for {label}\n" - f"# Point-in-time as of: {curr_date}\n\n" + f"# Point-in-time as of: {as_of_date}\n\n" f"Profile fundamentals are withheld for this date. This vendor serves " f"only present-day values with no historical vintage: market " f"cap, valuation multiples, the 52-week range and TTM income move with " f"today's quote, and even the name, sector and industry reflect today " - f"rather than {curr_date} (companies rename and get reclassified). " - f"Serving them would put post-decision information into a {curr_date} " - f"analysis. Point-in-time fundamentals for {curr_date} are available " + f"rather than {as_of_date} (companies rename and get reclassified). " + f"Serving them would put post-decision information into a {as_of_date} " + f"analysis. Point-in-time fundamentals for {as_of_date} are available " f"from the balance sheet, income statement, and cash flow tools." ) diff --git a/tradingagents/dataflows/vendors/alpha_vantage/fundamentals.py b/tradingagents/dataflows/vendors/alpha_vantage/fundamentals.py index a53698190..17a304ed5 100644 --- a/tradingagents/dataflows/vendors/alpha_vantage/fundamentals.py +++ b/tradingagents/dataflows/vendors/alpha_vantage/fundamentals.py @@ -4,14 +4,14 @@ from tradingagents.dataflows.date_window import withhold_live_profile from tradingagents.dataflows.vendors.alpha_vantage.common import _make_api_request -def _filter_reports_by_date(result, curr_date: str): - """Drop annual/quarterly reports dated after curr_date to prevent look-ahead. +def _filter_reports_by_date(result, as_of_date: str): + """Drop annual/quarterly reports dated after as_of_date to prevent look-ahead. ``_make_api_request`` returns the fundamentals payload as a JSON string, so - parse, filter, and re-serialize. A non-JSON body or an unset ``curr_date`` is + parse, filter, and re-serialize. A non-JSON body or an unset ``as_of_date`` is returned unchanged. """ - if not curr_date or not isinstance(result, str): + if not as_of_date or not isinstance(result, str): return result try: payload = json.loads(result) @@ -23,28 +23,28 @@ def _filter_reports_by_date(result, curr_date: str): if isinstance(payload.get(key), list): payload[key] = [ r for r in payload[key] - if r.get("fiscalDateEnding", "") <= curr_date + if r.get("fiscalDateEnding", "") <= as_of_date ] return json.dumps(payload) -def get_fundamentals(ticker: str, curr_date: str = None) -> str: +def get_fundamentals(ticker: str, as_of_date: str = None) -> str: """ Retrieve comprehensive fundamental data for a given ticker symbol using Alpha Vantage. OVERVIEW serves only present-day values and carries no historical vintage, so - a past ``curr_date`` withholds it rather than leaking post-decision figures + a past ``as_of_date`` withholds it rather than leaking post-decision figures into a backtest (#1300); the statement endpoints below stay point-in-time via ``_filter_reports_by_date``. Args: ticker (str): Ticker symbol of the company - curr_date (str): Analysis date, yyyy-mm-dd + as_of_date (str): Analysis date, yyyy-mm-dd Returns: str: Company overview data including financial ratios and key metrics """ - withheld = withhold_live_profile(curr_date, ticker) + withheld = withhold_live_profile(as_of_date, ticker) if withheld: return withheld @@ -55,20 +55,20 @@ def get_fundamentals(ticker: str, curr_date: str = None) -> str: return _make_api_request("OVERVIEW", params) -def get_balance_sheet(ticker: str, freq: str = "quarterly", curr_date: str = None): +def get_balance_sheet(ticker: str, freq: str = "quarterly", as_of_date: str = None): """Retrieve balance sheet data for a given ticker symbol using Alpha Vantage.""" result = _make_api_request("BALANCE_SHEET", {"symbol": ticker}) - return _filter_reports_by_date(result, curr_date) + return _filter_reports_by_date(result, as_of_date) -def get_cashflow(ticker: str, freq: str = "quarterly", curr_date: str = None): +def get_cashflow(ticker: str, freq: str = "quarterly", as_of_date: str = None): """Retrieve cash flow statement data for a given ticker symbol using Alpha Vantage.""" result = _make_api_request("CASH_FLOW", {"symbol": ticker}) - return _filter_reports_by_date(result, curr_date) + return _filter_reports_by_date(result, as_of_date) -def get_income_statement(ticker: str, freq: str = "quarterly", curr_date: str = None): +def get_income_statement(ticker: str, freq: str = "quarterly", as_of_date: str = None): """Retrieve income statement data for a given ticker symbol using Alpha Vantage.""" result = _make_api_request("INCOME_STATEMENT", {"symbol": ticker}) - return _filter_reports_by_date(result, curr_date) + return _filter_reports_by_date(result, as_of_date) diff --git a/tradingagents/dataflows/vendors/alpha_vantage/indicator.py b/tradingagents/dataflows/vendors/alpha_vantage/indicator.py index 20ab45e9b..2408be428 100644 --- a/tradingagents/dataflows/vendors/alpha_vantage/indicator.py +++ b/tradingagents/dataflows/vendors/alpha_vantage/indicator.py @@ -9,7 +9,7 @@ logger = logging.getLogger(__name__) def get_indicator( symbol: str, indicator: str, - curr_date: str, + as_of_date: str, look_back_days: int, interval: str = "daily", time_period: int = 14, @@ -21,7 +21,7 @@ def get_indicator( Args: symbol: ticker symbol of the company indicator: technical indicator to get the analysis and report of - curr_date: The current trading date you are trading on, YYYY-mm-dd + as_of_date: The current trading date you are trading on, YYYY-mm-dd look_back_days: how many days to look back interval: Time interval (daily, weekly, monthly) time_period: Number of data points for calculation @@ -72,8 +72,8 @@ def get_indicator( f"Alpha Vantage does not serve {indicator}; it serves {list(supported_indicators)}" ) - curr_date_dt = datetime.strptime(curr_date, "%Y-%m-%d") - before = curr_date_dt - relativedelta(days=look_back_days) + as_of_dt = datetime.strptime(as_of_date, "%Y-%m-%d") + before = as_of_dt - relativedelta(days=look_back_days) # Get the full data for the period instead of making individual calls _, required_series_type = supported_indicators[indicator] @@ -184,7 +184,7 @@ def get_indicator( date_str = values[date_col_idx].strip() date_dt = datetime.strptime(date_str, "%Y-%m-%d") - if before <= date_dt <= curr_date_dt: + if before <= date_dt <= as_of_dt: value = values[value_col_idx].strip() result_data.append((date_dt, value)) except (ValueError, IndexError): @@ -201,7 +201,7 @@ def get_indicator( ind_string = "No data available for the specified date range.\n" result_str = ( - f"## {indicator.upper()} values from {before.strftime('%Y-%m-%d')} to {curr_date}:\n\n" + f"## {indicator.upper()} values from {before.strftime('%Y-%m-%d')} to {as_of_date}:\n\n" + ind_string + "\n\n" + indicator_descriptions.get(indicator, "No description available.") diff --git a/tradingagents/dataflows/vendors/alpha_vantage/news.py b/tradingagents/dataflows/vendors/alpha_vantage/news.py index 299264da7..164768f47 100644 --- a/tradingagents/dataflows/vendors/alpha_vantage/news.py +++ b/tradingagents/dataflows/vendors/alpha_vantage/news.py @@ -33,13 +33,13 @@ def get_news(ticker, start_date, end_date) -> dict[str, str] | str: return _make_api_request("NEWS_SENTIMENT", params) -def get_global_news(curr_date, look_back_days: int | None = None, limit: int | None = None) -> dict[str, str] | str: +def get_global_news(as_of_date, look_back_days: int | None = None, limit: int | None = None) -> dict[str, str] | str: """Returns global market news & sentiment data without ticker-specific filtering. Covers broad market topics like financial markets, economy, and more. Args: - curr_date: Current date in yyyy-mm-dd format. + as_of_date: Current date in yyyy-mm-dd format. look_back_days: Number of days to look back; ``None`` uses ``global_news_lookback_days`` from the active config. limit: Maximum number of articles; ``None`` uses @@ -56,28 +56,28 @@ def get_global_news(curr_date, look_back_days: int | None = None, limit: int | N if limit is None: limit = config["global_news_article_limit"] - curr_dt = datetime.strptime(curr_date, "%Y-%m-%d") + curr_dt = datetime.strptime(as_of_date, "%Y-%m-%d") start_dt = curr_dt - timedelta(days=look_back_days) start_date = start_dt.strftime("%Y-%m-%d") params = { "topics": "financial_markets,economy_macro,economy_monetary", "time_from": format_datetime_for_api(start_date), - "time_to": format_datetime_for_api(curr_date, end_of_day=True), + "time_to": format_datetime_for_api(as_of_date, end_of_day=True), "limit": str(limit), } return _make_api_request("NEWS_SENTIMENT", params) -def get_insider_transactions(symbol: str, curr_date: str | None = None) -> dict[str, str] | str: +def get_insider_transactions(symbol: str, as_of_date: str | None = None) -> dict[str, str] | str: """Returns latest and historical insider transactions by key stakeholders. Covers transactions by founders, executives, board members, etc. Args: symbol: Ticker symbol. Example: "IBM". - curr_date: When given, only transactions on or before it (yyyy-mm-dd). + as_of_date: When given, only transactions on or before it (yyyy-mm-dd). Returns: Dictionary containing insider transaction data or JSON string. @@ -88,8 +88,8 @@ def get_insider_transactions(symbol: str, curr_date: str | None = None) -> dict[ } response = _make_api_request("INSIDER_TRANSACTIONS", params) - if not curr_date: + if not as_of_date: return response payload = json.loads(response) - payload["data"] = [t for t in payload["data"] if t["transaction_date"] <= curr_date] + payload["data"] = [t for t in payload["data"] if t["transaction_date"] <= as_of_date] return json.dumps(payload) diff --git a/tradingagents/dataflows/vendors/fred.py b/tradingagents/dataflows/vendors/fred.py index e5a130664..86fbea43b 100644 --- a/tradingagents/dataflows/vendors/fred.py +++ b/tradingagents/dataflows/vendors/fred.py @@ -126,7 +126,7 @@ def _fred_today() -> str: """FRED's current calendar date (US Central) as ``yyyy-mm-dd``. The vintage pin is clamped to this: FRED rejects a ``realtime_start`` after - its own today with a 400, and ``curr_date`` on a live run comes from the + its own today with a 400, and ``as_of_date`` on a live run comes from the caller's local clock, which can already be tomorrow in Chicago. """ return datetime.now(FRED_TZ).strftime("%Y-%m-%d") @@ -155,7 +155,7 @@ def _request(path: str, params: dict) -> dict: def get_macro_data( indicator: str, - curr_date: str, + as_of_date: str, look_back_days: int | None = None, ) -> str: """Fetch a FRED macroeconomic series as a formatted markdown report. @@ -163,9 +163,9 @@ def get_macro_data( Args: indicator: A friendly alias (e.g. "cpi", "unemployment", "10y_treasury") or a raw FRED series ID (e.g. "CPIAUCSL", "DGS10"). - curr_date: The as-of date (yyyy-mm-dd). It bounds the observation window + as_of_date: The as-of date (yyyy-mm-dd). It bounds the observation window AND pins the data vintage: FRED is queried with the realtime bounds - set to ``curr_date`` (clamped to FRED's own today) so a historical + set to ``as_of_date`` (clamped to FRED's own today) so a historical run sees the values that were actually published by that date, not later revisions. Without this, revision-prone series (CPI, GDP, ...) would leak future information into a backtest (#1275). @@ -178,18 +178,18 @@ def get_macro_data( if look_back_days is None: look_back_days = DEFAULT_LOOKBACK_DAYS - end_dt = datetime.strptime(curr_date, "%Y-%m-%d") + end_dt = datetime.strptime(as_of_date, "%Y-%m-%d") start_date = (end_dt - timedelta(days=look_back_days)).strftime("%Y-%m-%d") # Pin the data vintage. FRED defaults both realtime bounds to today, serving # the LATEST revision of every observation; a single-day realtime interval # asks for the values known as of the pin instead, on both the metadata and # observations requests (#1275). Clamp to FRED's today: on a live run - # curr_date is the caller's local date, which can be a day ahead of Chicago, + # as_of_date is the caller's local date, which can be a day ahead of Chicago, # and a realtime date in FRED's future 400s -> the routing layer would then - # drop macro data silently. A past curr_date is unaffected, so historical + # drop macro data silently. A past as_of_date is unaffected, so historical # point-in-time behaviour is preserved. - pit = min(curr_date, _fred_today()) + pit = min(as_of_date, _fred_today()) realtime = {"realtime_start": pit, "realtime_end": pit} # Invalid LLM-supplied indicator: return guidance rather than raising, so a @@ -217,7 +217,7 @@ def get_macro_data( { "series_id": series_id, "observation_start": start_date, - "observation_end": curr_date, + "observation_end": as_of_date, "sort_order": "asc", **realtime, }, @@ -235,7 +235,7 @@ def get_macro_data( f"- Units: {units}\n" f"- Frequency: {frequency}" f"{f' ({seasonal})' if seasonal else ''}\n" - f"- Window: {start_date} to {curr_date}\n" + f"- Window: {start_date} to {as_of_date}\n" ) if not points: diff --git a/tradingagents/dataflows/vendors/polymarket.py b/tradingagents/dataflows/vendors/polymarket.py index 96a352b0b..0e5303dd1 100644 --- a/tradingagents/dataflows/vendors/polymarket.py +++ b/tradingagents/dataflows/vendors/polymarket.py @@ -67,7 +67,7 @@ def _is_forward_looking(market: dict, now: datetime) -> bool: ) -def get_prediction_markets(topic: str, limit: int | None = None, curr_date: str | None = None) -> str: +def get_prediction_markets(topic: str, limit: int | None = None, as_of_date: str | None = None) -> str: """Return live prediction-market probabilities for an event topic. Args: @@ -75,7 +75,7 @@ def get_prediction_markets(topic: str, limit: int | None = None, curr_date: str "US election", or a sector/company event. limit: Max markets to return (ranked by traded volume); ``None`` uses DEFAULT_LIMIT. - curr_date: The analysis date. Polymarket serves only live odds, so a + as_of_date: The analysis date. Polymarket serves only live odds, so a date before today withholds them. Returns: @@ -83,11 +83,11 @@ def get_prediction_markets(topic: str, limit: int | None = None, curr_date: str each with its implied probability, traded volume, resolution date, and recent (1-week) move. """ - if curr_date and curr_date < get_current_date(): + if as_of_date and as_of_date < get_current_date(): return ( - f"Prediction-market odds are withheld for {curr_date}. Polymarket serves " + f"Prediction-market odds are withheld for {as_of_date}. Polymarket serves " f"only live odds on open markets, with no historical vintage, so serving " - f"them would put post-decision information into a {curr_date} analysis." + f"them would put post-decision information into a {as_of_date} analysis." ) if limit is None: limit = DEFAULT_LIMIT diff --git a/tradingagents/dataflows/vendors/sec_edgar.py b/tradingagents/dataflows/vendors/sec_edgar.py index e096748d4..9b7315ecc 100644 --- a/tradingagents/dataflows/vendors/sec_edgar.py +++ b/tradingagents/dataflows/vendors/sec_edgar.py @@ -5,7 +5,7 @@ statement at the fiscal period end. That is two claims a run should not make: a period that has ended is not public until the company files, weeks later, and a figure that was later restated is not what investors saw at the time. -EDGAR reports every fact with the date it was filed, so a run dated ``curr_date`` +EDGAR reports every fact with the date it was filed, so a run dated ``as_of_date`` serves exactly what was on file by then, restatements included at the vintage that was current: Apple's 2008 total assets read 39.6B until the 2010 amendment restated them to 36.2B. @@ -144,7 +144,7 @@ def cik_for(ticker: str) -> str | None: return None -def _as_of(facts: dict, tags: tuple[str, ...], curr_date: str, span: tuple[int, int], +def _as_of(facts: dict, tags: tuple[str, ...], as_of_date: str, span: tuple[int, int], forms: tuple[str, ...] = ()) -> tuple[dict, str]: """({period end: value}, unit) for the first tag the filer reports, as known then. @@ -164,7 +164,7 @@ def _as_of(facts: dict, tags: tuple[str, ...], curr_date: str, span: tuple[int, latest: dict[str, dict] = {} covered: set[str] = set() # period ends a filing of ``forms`` reports for fact in unit_values: - if fact["filed"] > curr_date or fact["end"] in values: + if fact["filed"] > as_of_date or fact["end"] in values: continue # A duration fact (revenue, cash flow) must cover the span asked # for. An instant fact (a balance) has no span and serves both. @@ -184,8 +184,8 @@ def _as_of(facts: dict, tags: tuple[str, ...], curr_date: str, span: tuple[int, return dict(sorted(values.items())), chosen_unit -def _statement(kind: str, ticker: str, freq: str, curr_date: str, title: str) -> str: - curr_date = curr_date or datetime.now().strftime("%Y-%m-%d") +def _statement(kind: str, ticker: str, freq: str, as_of_date: str, title: str) -> str: + as_of_date = as_of_date or datetime.now().strftime("%Y-%m-%d") cik = cik_for(ticker) if cik is None: raise NoMarketDataError(ticker, ticker, "not a US SEC filer") @@ -198,14 +198,14 @@ def _statement(kind: str, ticker: str, freq: str, curr_date: str, title: str) -> quarterly = freq.lower() == "quarterly" span = _SPANS["quarterly" if quarterly else "annual"] forms = () if quarterly else _ANNUAL_FORMS - lines = {label: _as_of(us_gaap, tags, curr_date, span, forms) for label, tags in _STATEMENTS[kind]} + lines = {label: _as_of(us_gaap, tags, as_of_date, span, forms) for label, tags in _STATEMENTS[kind]} periods = sorted({end for values, _ in lines.values() for end in values}) if not periods: - raise NoMarketDataError(ticker, ticker, f"no {freq} {title.lower()} filed by {curr_date}") + raise NoMarketDataError(ticker, ticker, f"no {freq} {title.lower()} filed by {as_of_date}") header = ( f"# {title} for {ticker.upper()} ({freq}), USD in millions unless the row says otherwise\n" - f"# SEC EDGAR facts filed on or before {curr_date}, at the values filed then\n\n" + f"# SEC EDGAR facts filed on or before {as_of_date}, at the values filed then\n\n" ) rows = [",".join([""] + periods)] for label, (values, unit) in lines.items(): @@ -223,21 +223,21 @@ def _statement(kind: str, ticker: str, freq: str, curr_date: str, title: str) -> return header + "\n".join(rows) + "\n" -def get_balance_sheet(ticker: str, freq: str = "quarterly", curr_date: str | None = None) -> str: - """Balance sheet as filed on or before ``curr_date``.""" - return _statement("balance_sheet", ticker, freq, curr_date, "Balance Sheet") +def get_balance_sheet(ticker: str, freq: str = "quarterly", as_of_date: str | None = None) -> str: + """Balance sheet as filed on or before ``as_of_date``.""" + return _statement("balance_sheet", ticker, freq, as_of_date, "Balance Sheet") -def get_income_statement(ticker: str, freq: str = "quarterly", curr_date: str | None = None) -> str: - """Income statement as filed on or before ``curr_date``. +def get_income_statement(ticker: str, freq: str = "quarterly", as_of_date: str | None = None) -> str: + """Income statement as filed on or before ``as_of_date``. A fourth quarter is never derived: filers report it only inside the annual figure, and subtracting three separately filed quarters would invent a number with no filing date behind it. """ - return _statement("income_statement", ticker, freq, curr_date, "Income Statement") + return _statement("income_statement", ticker, freq, as_of_date, "Income Statement") -def get_cashflow(ticker: str, freq: str = "quarterly", curr_date: str | None = None) -> str: - """Cash flow statement as filed on or before ``curr_date``.""" - return _statement("cashflow", ticker, freq, curr_date, "Cash Flow Statement") +def get_cashflow(ticker: str, freq: str = "quarterly", as_of_date: str | None = None) -> str: + """Cash flow statement as filed on or before ``as_of_date``.""" + return _statement("cashflow", ticker, freq, as_of_date, "Cash Flow Statement") diff --git a/tradingagents/dataflows/vendors/yahoo/fundamentals.py b/tradingagents/dataflows/vendors/yahoo/fundamentals.py index 8c8e108b9..84d0e55c4 100644 --- a/tradingagents/dataflows/vendors/yahoo/fundamentals.py +++ b/tradingagents/dataflows/vendors/yahoo/fundamentals.py @@ -16,19 +16,19 @@ from tradingagents.dataflows.vendors.yahoo.ohlcv import ( def get_fundamentals( ticker: Annotated[str, "ticker symbol of the company"], - curr_date: Annotated[str, "analysis date in YYYY-MM-DD format"] = None + as_of_date: Annotated[str, "analysis date in YYYY-MM-DD format"] = None ): """Get company fundamentals overview from yfinance. ``Ticker.info`` is a present-day snapshot with no historical vintage, so a - past ``curr_date`` withholds it through the shared point-in-time guard + past ``as_of_date`` withholds it through the shared point-in-time guard (``date_window.withhold_live_profile``, #1300). """ canonical = normalize_symbol(ticker) # Guard before the request: the response would only be discarded, and the # answer does not depend on it. - withheld = withhold_live_profile(curr_date, canonical) + withheld = withhold_live_profile(as_of_date, canonical) if withheld: return withheld @@ -99,14 +99,14 @@ _PERIOD_END_VINTAGE = ( ) -def _statement(ticker, freq, curr_date, title, quarterly_attr, annual_attr) -> str: - """One financial statement as CSV, cut at ``curr_date`` by period end.""" +def _statement(ticker, freq, as_of_date, title, quarterly_attr, annual_attr) -> str: + """One financial statement as CSV, cut at ``as_of_date`` by period end.""" canonical = normalize_symbol(ticker) what = title.lower() try: ticker_obj = yf.Ticker(canonical) attr = quarterly_attr if freq.lower() == "quarterly" else annual_attr - data = filter_financials_by_date(yf_retry(lambda: getattr(ticker_obj, attr)), curr_date) + data = filter_financials_by_date(yf_retry(lambda: getattr(ticker_obj, attr)), as_of_date) if data.empty: raise_for_empty(ticker, canonical, f"{what} data") return f"# {title} data for {canonical} ({freq})\n" + _PERIOD_END_VINTAGE + data.to_csv() @@ -119,28 +119,28 @@ def _statement(ticker, freq, curr_date, title, quarterly_attr, annual_attr) -> s def get_balance_sheet( ticker: Annotated[str, "ticker symbol of the company"], freq: Annotated[str, "frequency of data: 'annual' or 'quarterly'"] = "quarterly", - curr_date: Annotated[str, "current date in YYYY-MM-DD format"] = None + as_of_date: Annotated[str, "current date in YYYY-MM-DD format"] = None ): """Get balance sheet data from yfinance.""" - return _statement(ticker, freq, curr_date, "Balance Sheet", "quarterly_balance_sheet", "balance_sheet") + return _statement(ticker, freq, as_of_date, "Balance Sheet", "quarterly_balance_sheet", "balance_sheet") def get_cashflow( ticker: Annotated[str, "ticker symbol of the company"], freq: Annotated[str, "frequency of data: 'annual' or 'quarterly'"] = "quarterly", - curr_date: Annotated[str, "current date in YYYY-MM-DD format"] = None + as_of_date: Annotated[str, "current date in YYYY-MM-DD format"] = None ): """Get cash flow data from yfinance.""" - return _statement(ticker, freq, curr_date, "Cash Flow", "quarterly_cashflow", "cashflow") + return _statement(ticker, freq, as_of_date, "Cash Flow", "quarterly_cashflow", "cashflow") def get_income_statement( ticker: Annotated[str, "ticker symbol of the company"], freq: Annotated[str, "frequency of data: 'annual' or 'quarterly'"] = "quarterly", - curr_date: Annotated[str, "current date in YYYY-MM-DD format"] = None + as_of_date: Annotated[str, "current date in YYYY-MM-DD format"] = None ): """Get income statement data from yfinance.""" - return _statement(ticker, freq, curr_date, "Income Statement", "quarterly_income_stmt", "income_stmt") + return _statement(ticker, freq, as_of_date, "Income Statement", "quarterly_income_stmt", "income_stmt") # Rows are dated by the transaction, which is when the insider traded, not when @@ -156,7 +156,7 @@ _TRANSACTION_DATE_VINTAGE = ( def get_insider_transactions( ticker: Annotated[str, "ticker symbol of the company"], - curr_date: Annotated[str | None, "only transactions on or before this date, yyyy-mm-dd"] = None, + as_of_date: Annotated[str | None, "only transactions on or before this date, yyyy-mm-dd"] = None, ): """Get insider transactions data from yfinance.""" canonical = normalize_symbol(ticker) @@ -171,12 +171,12 @@ def get_insider_transactions( raise VendorRateLimitError("Yahoo Finance is unreachable; insider filings were not retrieved") return f"No insider transactions reported for symbol '{canonical}'" - if curr_date: + if as_of_date: traded = data["Start Date"] - kept = data[traded <= pd.Timestamp(curr_date)] + kept = data[traded <= pd.Timestamp(as_of_date)] if kept.empty: return ( - f"" ) data = kept @@ -198,15 +198,15 @@ def get_company_profile(ticker: str) -> dict: raise NoMarketDataError(ticker, canonical, f"profile unavailable: {e}") from e -def filter_financials_by_date(data: pd.DataFrame, curr_date: str) -> pd.DataFrame: - """Drop financial statement columns (fiscal period timestamps) after curr_date. +def filter_financials_by_date(data: pd.DataFrame, as_of_date: str) -> pd.DataFrame: + """Drop financial statement columns (fiscal period timestamps) after as_of_date. yfinance financial statements use fiscal period end dates as columns. - Columns after curr_date represent future data and are removed to + Columns after as_of_date represent future data and are removed to prevent look-ahead bias. """ - if not curr_date or data.empty: + if not as_of_date or data.empty: return data - cutoff = pd.Timestamp(curr_date) + cutoff = pd.Timestamp(as_of_date) mask = pd.to_datetime(data.columns, errors="coerce") <= cutoff return data.loc[:, mask] diff --git a/tradingagents/dataflows/vendors/yahoo/market.py b/tradingagents/dataflows/vendors/yahoo/market.py index 5e4d5a9c2..6965e54d2 100644 --- a/tradingagents/dataflows/vendors/yahoo/market.py +++ b/tradingagents/dataflows/vendors/yahoo/market.py @@ -73,7 +73,7 @@ def get_YFin_data_online( def get_stock_stats_indicators_window( symbol: Annotated[str, "ticker symbol of the company"], indicator: Annotated[str, "technical indicator to get the analysis and report of"], - curr_date: Annotated[ + as_of_date: Annotated[ str, "The current trading date you are trading on, YYYY-mm-dd" ], look_back_days: Annotated[int, "how many days to look back"], @@ -157,16 +157,16 @@ def get_stock_stats_indicators_window( f"Indicator {indicator} is not supported. Please choose from: {list(best_ind_params.keys())}" ) - end_date = curr_date - curr_date_dt = datetime.strptime(curr_date, "%Y-%m-%d") - before = curr_date_dt - relativedelta(days=look_back_days) + end_date = as_of_date + as_of_dt = datetime.strptime(as_of_date, "%Y-%m-%d") + before = as_of_dt - relativedelta(days=look_back_days) # Optimized: Get stock data once and calculate indicators for all dates try: - indicator_data = _get_stock_stats_bulk(symbol, indicator, curr_date) + indicator_data = _get_stock_stats_bulk(symbol, indicator, as_of_date) # Generate the date range we need - current_dt = curr_date_dt + current_dt = as_of_dt date_values = [] while current_dt >= before: @@ -191,13 +191,13 @@ def get_stock_stats_indicators_window( logger.warning("Bulk stockstats fetch failed, falling back per-day: %s", e) # Fallback to original implementation if bulk method fails ind_string = "" - curr_date_dt = datetime.strptime(curr_date, "%Y-%m-%d") - while curr_date_dt >= before: + as_of_dt = datetime.strptime(as_of_date, "%Y-%m-%d") + while as_of_dt >= before: indicator_value = get_stockstats_indicator( - symbol, indicator, curr_date_dt.strftime("%Y-%m-%d") + symbol, indicator, as_of_dt.strftime("%Y-%m-%d") ) - ind_string += f"{curr_date_dt.strftime('%Y-%m-%d')}: {indicator_value}\n" - curr_date_dt = curr_date_dt - relativedelta(days=1) + ind_string += f"{as_of_dt.strftime('%Y-%m-%d')}: {indicator_value}\n" + as_of_dt = as_of_dt - relativedelta(days=1) result_str = ( f"## {indicator} values from {before.strftime('%Y-%m-%d')} to {end_date}:\n\n" @@ -212,7 +212,7 @@ def get_stock_stats_indicators_window( def _get_stock_stats_bulk( symbol: Annotated[str, "ticker symbol of the company"], indicator: Annotated[str, "technical indicator to calculate"], - curr_date: Annotated[str, "current date for reference"] + as_of_date: Annotated[str, "current date for reference"] ) -> dict: """ Optimized bulk calculation of stock stats indicators. @@ -221,7 +221,7 @@ def _get_stock_stats_bulk( """ from stockstats import wrap - data = load_ohlcv(symbol, curr_date) + data = load_ohlcv(symbol, as_of_date) df = wrap(data) df["Date"] = df["Date"].dt.strftime("%Y-%m-%d") @@ -243,19 +243,19 @@ def _get_stock_stats_bulk( def get_stockstats_indicator( symbol: Annotated[str, "ticker symbol of the company"], indicator: Annotated[str, "technical indicator to get the analysis and report of"], - curr_date: Annotated[ + as_of_date: Annotated[ str, "The current trading date you are trading on, YYYY-mm-dd" ], ) -> str: - curr_date_dt = datetime.strptime(curr_date, "%Y-%m-%d") - curr_date = curr_date_dt.strftime("%Y-%m-%d") + as_of_dt = datetime.strptime(as_of_date, "%Y-%m-%d") + as_of_date = as_of_dt.strftime("%Y-%m-%d") try: indicator_value = get_stock_stats( symbol, indicator, - curr_date, + as_of_date, ) except VendorError: raise # Unknown/delisted symbol — let the router emit the sentinel @@ -264,7 +264,7 @@ def get_stockstats_indicator( # reads as no value that day rather than a read that failed. Raise so the # router can try the next vendor or report the series unavailable. raise NoMarketDataError( - symbol, symbol, f"{indicator} could not be read for {curr_date}: {e}" + symbol, symbol, f"{indicator} could not be read for {as_of_date}: {e}" ) from e return str(indicator_value) @@ -285,17 +285,17 @@ def get_stock_stats( indicator: Annotated[ str, "quantitative indicators based off of the stock data for the company" ], - curr_date: Annotated[ + as_of_date: Annotated[ str, "curr date for retrieving stock price data, YYYY-mm-dd" ], ): - data = load_ohlcv(symbol, curr_date) + data = load_ohlcv(symbol, as_of_date) df = wrap(data) df["Date"] = df["Date"].dt.strftime("%Y-%m-%d") - curr_date_str = pd.to_datetime(curr_date).strftime("%Y-%m-%d") + as_of_str = pd.to_datetime(as_of_date).strftime("%Y-%m-%d") df[indicator] # trigger stockstats to calculate the indicator - matching_rows = df[df["Date"].str.startswith(curr_date_str)] + matching_rows = df[df["Date"].str.startswith(as_of_str)] if not matching_rows.empty: indicator_value = matching_rows[indicator].values[0] diff --git a/tradingagents/dataflows/vendors/yahoo/news.py b/tradingagents/dataflows/vendors/yahoo/news.py index 9561e9fad..04fe18f8a 100644 --- a/tradingagents/dataflows/vendors/yahoo/news.py +++ b/tradingagents/dataflows/vendors/yahoo/news.py @@ -121,7 +121,7 @@ def get_news_yfinance( def get_global_news_yfinance( - curr_date: str, + as_of_date: str, look_back_days: int | None = None, limit: int | None = None, ) -> str: @@ -129,7 +129,7 @@ def get_global_news_yfinance( Retrieve global/macro economic news using yfinance Search. Args: - curr_date: Current date in yyyy-mm-dd format + as_of_date: Current date in yyyy-mm-dd format look_back_days: Number of days to look back. ``None`` falls back to ``global_news_lookback_days`` from the active config. limit: Maximum number of articles to return. ``None`` falls back to @@ -145,7 +145,7 @@ def get_global_news_yfinance( limit = config["global_news_article_limit"] search_queries = config["global_news_queries"] - curr_dt = datetime.strptime(curr_date, "%Y-%m-%d") + curr_dt = datetime.strptime(as_of_date, "%Y-%m-%d") start_dt = curr_dt - relativedelta(days=look_back_days) start_date = start_dt.strftime("%Y-%m-%d") @@ -189,10 +189,10 @@ def get_global_news_yfinance( if not news_str: # Results merge several fuzzy searches, so their timestamps prove no # continuous coverage; judge the window against the present only. - gap = coverage_gap((), start_date, curr_date, "Yahoo Finance global news", "market news") - return gap or f"No global news found between {start_date} and {curr_date}" + gap = coverage_gap((), start_date, as_of_date, "Yahoo Finance global news", "market news") + return gap or f"No global news found between {start_date} and {as_of_date}" - return f"## Global Market News, from {start_date} to {curr_date}:\n\n{news_str}" + return f"## Global Market News, from {start_date} to {as_of_date}:\n\n{news_str}" except VendorError: raise diff --git a/tradingagents/dataflows/vendors/yahoo/ohlcv.py b/tradingagents/dataflows/vendors/yahoo/ohlcv.py index e5001aeb1..87029e777 100644 --- a/tradingagents/dataflows/vendors/yahoo/ohlcv.py +++ b/tradingagents/dataflows/vendors/yahoo/ohlcv.py @@ -93,7 +93,7 @@ def _local_midnight(value) -> pd.Timestamp: def _normalize_dates(dates) -> pd.Series: """Parse to naive, midnight-normalized dates so tz-aware or intraday - timestamps compare correctly against the naive ``curr_date`` cutoff (#1201). + timestamps compare correctly against the naive ``as_of_date`` cutoff (#1201). Normalized per element: 5 years of yfinance bars span daylight-saving changes (and cache CSVs round-trip the offsets as strings), so the series can @@ -147,13 +147,13 @@ def _coerce_ohlcv_dates(data: pd.DataFrame) -> pd.Series: def _assert_ohlcv_not_stale( data: pd.DataFrame, - curr_date: str, + as_of_date: str, symbol: str, canonical: str | None = None, *, max_stale_days: int = MAX_OHLCV_STALE_DAYS, ) -> None: - """Reject OHLCV whose latest row is far older than curr_date. + """Reject OHLCV whose latest row is far older than as_of_date. Raises NoMarketDataError (with a stale-specific detail) so the router treats it like any other "no usable data from this vendor" — try the next vendor, @@ -164,7 +164,7 @@ def _assert_ohlcv_not_stale( """ if data is None or data.empty: return - requested = pd.to_datetime(curr_date, errors="coerce") + requested = pd.to_datetime(as_of_date, errors="coerce") if pd.isna(requested): return requested = requested.normalize() @@ -182,7 +182,7 @@ def _assert_ohlcv_not_stale( ) -def _cache_is_fresh(data_file, curr_date_dt, now) -> bool: +def _cache_is_fresh(data_file, as_of_dt, now) -> bool: """Whether the symbol's cached download can serve this request. The file holds the download made on the day it was written, so it serves @@ -194,14 +194,14 @@ def _cache_is_fresh(data_file, curr_date_dt, now) -> bool: written = pd.Timestamp.fromtimestamp(os.path.getmtime(data_file)) if written.date() != now.date(): return False - return curr_date_dt.date() < now.date() or (now - written).total_seconds() <= OHLCV_CACHE_TTL_SECONDS + return as_of_dt.date() < now.date() or (now - written).total_seconds() <= OHLCV_CACHE_TTL_SECONDS -def load_ohlcv(symbol: str, curr_date: str, fill_gaps: bool = True) -> pd.DataFrame: +def load_ohlcv(symbol: str, as_of_date: str, fill_gaps: bool = True) -> pd.DataFrame: """Fetch OHLCV data with caching, filtered to prevent look-ahead bias. Downloads 5 years of data up to today and caches per symbol. On - subsequent calls the cache is reused. Rows after curr_date are + subsequent calls the cache is reused. Rows after as_of_date are filtered out so backtests never see future prices. ``fill_gaps`` carries prices forward over gaps so indicators compute on a @@ -215,15 +215,15 @@ def load_ohlcv(symbol: str, curr_date: str, fill_gaps: bool = True) -> pd.DataFr safe_symbol = safe_ticker_component(canonical) config = get_config() - curr_date_dt = pd.to_datetime(curr_date).normalize() + as_of_dt = pd.to_datetime(as_of_date).normalize() # One cache file per symbol, holding the latest 5y-to-today download. now = pd.Timestamp.today() start_date = now - pd.DateOffset(years=5) start_str = start_date.strftime("%Y-%m-%d") # yfinance ``end`` is EXCLUSIVE; request tomorrow so today's row is included - # when curr_date is the current day (#986). Look-ahead is still prevented by - # the curr_date filter below. + # when as_of_date is the current day (#986). Look-ahead is still prevented by + # the as_of_date filter below. end_str = (now + pd.Timedelta(days=1)).strftime("%Y-%m-%d") os.makedirs(config["data_cache_dir"], exist_ok=True) @@ -241,7 +241,7 @@ def load_ohlcv(symbol: str, curr_date: str, fill_gaps: bool = True) -> pd.DataFr if ( not cached.empty and "Close" in cached.columns - and _cache_is_fresh(data_file, curr_date_dt, now) + and _cache_is_fresh(data_file, as_of_dt, now) ): data = cached @@ -271,8 +271,8 @@ def load_ohlcv(symbol: str, curr_date: str, fill_gaps: bool = True) -> pd.DataFr data = _clean_dataframe(data) - # Filter to curr_date to prevent look-ahead bias in backtesting. - data = data[data["Date"] <= curr_date_dt] + # Filter to as_of_date to prevent look-ahead bias in backtesting. + data = data[data["Date"] <= as_of_dt] # 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 @@ -295,9 +295,9 @@ def load_ohlcv(symbol: str, curr_date: str, fill_gaps: bool = True) -> pd.DataFr # a filled cell is the previous session's price under this session's date. data = _fill_price_gaps(data) if fill_gaps else data.dropna(subset=["Close"]).copy() - # Reject a stale frame (latest row far older than curr_date) rather than + # Reject a stale frame (latest row far older than as_of_date) rather than # feeding year-old prices into indicators (#1021). - _assert_ohlcv_not_stale(data, curr_date, symbol, canonical) + _assert_ohlcv_not_stale(data, as_of_date, symbol, canonical) return data diff --git a/tradingagents/dataflows/vendors/yahoo/snapshot.py b/tradingagents/dataflows/vendors/yahoo/snapshot.py index 6c490ff71..06c16bd3c 100644 --- a/tradingagents/dataflows/vendors/yahoo/snapshot.py +++ b/tradingagents/dataflows/vendors/yahoo/snapshot.py @@ -25,8 +25,8 @@ DEFAULT_SNAPSHOT_INDICATORS: tuple[str, ...] = ( ) -def _verified_rows(symbol: str, curr_date: str) -> pd.DataFrame: - """OHLCV on or before curr_date, date-sorted. Raises if nothing usable. +def _verified_rows(symbol: str, as_of_date: str) -> pd.DataFrame: + """OHLCV on or before as_of_date, date-sorted. Raises if nothing usable. ``load_ohlcv`` already normalizes the Date column and filters out look-ahead rows, but we re-apply the cutoff defensively — this is a @@ -34,16 +34,16 @@ def _verified_rows(symbol: str, curr_date: str) -> pd.DataFrame: """ # As reported: this snapshot is quoted by the agents as exact prices, so a # gap-filled cell would put the previous session's number under this date. - data = load_ohlcv(symbol, curr_date, fill_gaps=False) + data = load_ohlcv(symbol, as_of_date, fill_gaps=False) if data is None or data.empty: raise ValueError(f"No OHLCV data available for {symbol}.") df = data.copy() df["Date"] = pd.to_datetime(df["Date"], errors="coerce") df = df.dropna(subset=["Date"]) - df = df[df["Date"] <= pd.to_datetime(curr_date)].sort_values("Date") + df = df[df["Date"] <= pd.to_datetime(as_of_date)].sort_values("Date") if df.empty: - raise ValueError(f"No OHLCV rows on or before {curr_date} for {symbol}.") + raise ValueError(f"No OHLCV rows on or before {as_of_date} for {symbol}.") return df @@ -63,7 +63,7 @@ def _fmt(value) -> str: def build_verified_market_snapshot( symbol: str, - curr_date: str, + as_of_date: str, look_back_days: int = 30, indicators: Iterable[str] | None = None, ) -> str: @@ -71,7 +71,7 @@ def build_verified_market_snapshot( # `df` keeps the original capitalized OHLCV columns (Open/High/Low/Close/ # Volume); stockstats `wrap()` lowercases columns and adds indicator # columns, so read raw prices from `df` and indicators from `stock_df`. - df = _verified_rows(symbol, curr_date) + df = _verified_rows(symbol, as_of_date) stock_df = wrap(df.copy()) selected = tuple(indicators or DEFAULT_SNAPSHOT_INDICATORS) @@ -91,7 +91,7 @@ def build_verified_market_snapshot( lines = [ f"## Verified market data snapshot for {symbol.upper()}", "", - f"- Requested analysis date: {curr_date}", + f"- Requested analysis date: {as_of_date}", f"- Latest trading row used: {latest_date}", "- Rows after the requested analysis date are excluded before verification.", "", diff --git a/tradingagents/graph/trading_graph.py b/tradingagents/graph/trading_graph.py index cd0296429..c3b7387ea 100644 --- a/tradingagents/graph/trading_graph.py +++ b/tradingagents/graph/trading_graph.py @@ -114,7 +114,7 @@ class TradingAgentsGraph: self._resuming = False def resolve_instrument_context(self, ticker: str, asset_type: str = "stock", - curr_date: str | None = None) -> str: + trade_date: str | None = None) -> str: """Resolve ticker identity once and return the full instrument context. Deterministic yfinance lookup (cached, fail-open) injected into a @@ -124,7 +124,7 @@ class TradingAgentsGraph: graph regardless of entry point. """ identity = resolve_instrument_identity(ticker) - return build_instrument_context(ticker, asset_type, identity, curr_date) + return build_instrument_context(ticker, asset_type, identity, trade_date) def _memory_as_of(self, trade_date) -> str | None: """Point-in-time cutoff for past-context lessons (#1251).