mirror of
https://github.com/TauricResearch/TradingAgents.git
synced 2026-09-19 19:25:24 +03:00
fix(dataflows): fail closed when the Alpha Vantage date trim fails
- get_stock requests the full daily series up to today, so trimming to the requested window is the only thing keeping bars after end_date out of a historical run - the trim caught every exception, warned, and returned the untrimmed body, so a parse failure fed future prices into a backtest with no usable signal that it had happened - let a parse failure propagate instead: the routing layer already logs the vendor failure, falls through to the next vendor, and surfaces the real error if none can serve
This commit is contained in:
@@ -3,7 +3,8 @@
|
||||
Regressions for #990 (no request timeout -> can hang), #991 (invalid-key
|
||||
responses mislabeled as rate limits and silently treated as transient), and
|
||||
#1115 (fundamentals look-ahead filter never ran because the payload is a JSON
|
||||
string, not a dict).
|
||||
string, not a dict), and the date trim that keeps post-end_date bars out of a
|
||||
historical run.
|
||||
"""
|
||||
import json
|
||||
|
||||
@@ -11,6 +12,7 @@ import pytest
|
||||
|
||||
import tradingagents.dataflows.alpha_vantage_common as av
|
||||
import tradingagents.dataflows.alpha_vantage_fundamentals as avf
|
||||
import tradingagents.dataflows.alpha_vantage_stock as avs
|
||||
|
||||
|
||||
class _FakeResponse:
|
||||
@@ -94,3 +96,40 @@ def test_fundamentals_no_curr_date_passes_through(monkeypatch):
|
||||
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"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Date trim (see the rationale on the unguarded trim in alpha_vantage_common)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
_DAILY_CSV = (
|
||||
"timestamp,open,high,low,close,volume\n"
|
||||
"2024-05-13,1,1,1,1,10\n" # after end_date -> must never be served
|
||||
"2024-05-10,1,1,1,1,10\n"
|
||||
"2024-05-09,1,1,1,1,10\n"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_stock_data_is_trimmed_to_the_requested_window(monkeypatch):
|
||||
monkeypatch.setattr(avs, "_make_api_request", lambda *a, **k: _DAILY_CSV)
|
||||
out = avs.get_stock("IBM", "2024-05-09", "2024-05-10")
|
||||
assert "2024-05-10" in out and "2024-05-09" in out
|
||||
assert "2024-05-13" not in out, "bar after end_date leaked into the window"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_unparseable_body_is_never_served_untrimmed(monkeypatch):
|
||||
"""The trim used to swallow the failure and return the whole body, putting
|
||||
bars after end_date into a backtest. It must raise instead."""
|
||||
monkeypatch.setattr(avs, "_make_api_request",
|
||||
lambda *a, **k: "timestamp,close\nnot-a-date,1\n")
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
avs.get_stock("IBM", "2024-05-09", "2024-05-10")
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_empty_body_still_passes_through(monkeypatch):
|
||||
monkeypatch.setattr(avs, "_make_api_request", lambda *a, **k: "")
|
||||
assert avs.get_stock("IBM", "2024-05-09", "2024-05-10") == ""
|
||||
|
||||
@@ -128,24 +128,18 @@ def _filter_csv_by_date_range(csv_data: str, start_date: str, end_date: str) ->
|
||||
if not csv_data or csv_data.strip() == "":
|
||||
return csv_data
|
||||
|
||||
try:
|
||||
# Parse CSV data
|
||||
# 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.
|
||||
df = pd.read_csv(StringIO(csv_data))
|
||||
|
||||
# Assume the first column is the date column (timestamp)
|
||||
date_col = df.columns[0]
|
||||
df[date_col] = pd.to_datetime(df[date_col])
|
||||
|
||||
# Filter by date range
|
||||
start_dt = pd.to_datetime(start_date)
|
||||
end_dt = pd.to_datetime(end_date)
|
||||
|
||||
filtered_df = df[(df[date_col] >= start_dt) & (df[date_col] <= end_dt)]
|
||||
|
||||
# Convert back to CSV string
|
||||
return filtered_df.to_csv(index=False)
|
||||
|
||||
except Exception as e:
|
||||
# If filtering fails, return original data with a warning
|
||||
print(f"Warning: Failed to filter CSV data by date range: {e}")
|
||||
return csv_data
|
||||
|
||||
Reference in New Issue
Block a user