mirror of
https://github.com/TauricResearch/TradingAgents.git
synced 2026-09-19 19:25:24 +03:00
fix(dataflows): pin the FRED data vintage to the as-of date
- FRED defaults both realtime bounds to today, so historical macro requests served the latest revision and leaked future information into backtests - set realtime_start=realtime_end=curr_date on both the metadata and observations requests #1275
This commit is contained in:
@@ -150,6 +150,23 @@ class FredFormattingTests(unittest.TestCase):
|
|||||||
self.assertEqual(obs_params["observation_end"], "2025-09-30")
|
self.assertEqual(obs_params["observation_end"], "2025-09-30")
|
||||||
self.assertEqual(obs_params["observation_start"], "2025-07-02") # 90d back
|
self.assertEqual(obs_params["observation_start"], "2025-07-02") # 90d back
|
||||||
|
|
||||||
|
def test_requests_pin_the_data_vintage(self):
|
||||||
|
# #1275: both the metadata and observations requests must set
|
||||||
|
# realtime_start=realtime_end=curr_date, or FRED serves the latest
|
||||||
|
# revision and revision-prone series leak future information.
|
||||||
|
captured = {}
|
||||||
|
|
||||||
|
def _capture(path, params):
|
||||||
|
captured[path] = params
|
||||||
|
return _META if path == "series" else _OBS
|
||||||
|
|
||||||
|
with mock.patch.object(fred, "_request", side_effect=_capture):
|
||||||
|
fred.get_macro_data("cpi", "2025-09-30", 90)
|
||||||
|
|
||||||
|
for path in ("series", "series/observations"):
|
||||||
|
self.assertEqual(captured[path]["realtime_start"], "2025-09-30", path)
|
||||||
|
self.assertEqual(captured[path]["realtime_end"], "2025-09-30", path)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
class FredRoutingTests(unittest.TestCase):
|
class FredRoutingTests(unittest.TestCase):
|
||||||
|
|||||||
@@ -143,8 +143,12 @@ def get_macro_data(
|
|||||||
Args:
|
Args:
|
||||||
indicator: A friendly alias (e.g. "cpi", "unemployment", "10y_treasury")
|
indicator: A friendly alias (e.g. "cpi", "unemployment", "10y_treasury")
|
||||||
or a raw FRED series ID (e.g. "CPIAUCSL", "DGS10").
|
or a raw FRED series ID (e.g. "CPIAUCSL", "DGS10").
|
||||||
curr_date: End of the window (yyyy-mm-dd); no later observations are
|
curr_date: The as-of date (yyyy-mm-dd). It bounds the observation window
|
||||||
returned, so a past date never leaks future data.
|
AND pins the data vintage: FRED is queried with
|
||||||
|
``realtime_start = realtime_end = curr_date`` 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).
|
||||||
look_back_days: Trailing window length; ``None`` uses DEFAULT_LOOKBACK_DAYS.
|
look_back_days: Trailing window length; ``None`` uses DEFAULT_LOOKBACK_DAYS.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -157,6 +161,12 @@ def get_macro_data(
|
|||||||
end_dt = datetime.strptime(curr_date, "%Y-%m-%d")
|
end_dt = datetime.strptime(curr_date, "%Y-%m-%d")
|
||||||
start_date = (end_dt - timedelta(days=look_back_days)).strftime("%Y-%m-%d")
|
start_date = (end_dt - timedelta(days=look_back_days)).strftime("%Y-%m-%d")
|
||||||
|
|
||||||
|
# Pin the data vintage to curr_date. FRED defaults both realtime bounds to
|
||||||
|
# today, which serves the LATEST revision of every observation; a single-day
|
||||||
|
# realtime interval asks for the values known as of curr_date instead. This
|
||||||
|
# is applied to both the metadata and observations requests (#1275).
|
||||||
|
realtime = {"realtime_start": curr_date, "realtime_end": curr_date}
|
||||||
|
|
||||||
# Invalid LLM-supplied indicator: return guidance rather than raising, so a
|
# Invalid LLM-supplied indicator: return guidance rather than raising, so a
|
||||||
# bad argument doesn't abort the run (the routing layer also degrades macro
|
# bad argument doesn't abort the run (the routing layer also degrades macro
|
||||||
# data, but a specific message is more useful to the analyst).
|
# data, but a specific message is more useful to the analyst).
|
||||||
@@ -165,7 +175,7 @@ def get_macro_data(
|
|||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
return f"FRED: {e}"
|
return f"FRED: {e}"
|
||||||
|
|
||||||
meta = _request("series", {"series_id": series_id}).get("seriess") or []
|
meta = _request("series", {"series_id": series_id, **realtime}).get("seriess") or []
|
||||||
if not meta:
|
if not meta:
|
||||||
return (
|
return (
|
||||||
f"FRED series '{series_id}' not found. Pass a known alias "
|
f"FRED series '{series_id}' not found. Pass a known alias "
|
||||||
@@ -184,6 +194,7 @@ def get_macro_data(
|
|||||||
"observation_start": start_date,
|
"observation_start": start_date,
|
||||||
"observation_end": curr_date,
|
"observation_end": curr_date,
|
||||||
"sort_order": "asc",
|
"sort_order": "asc",
|
||||||
|
**realtime,
|
||||||
},
|
},
|
||||||
).get("observations", [])
|
).get("observations", [])
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user