mirror of
https://github.com/TauricResearch/TradingAgents.git
synced 2026-09-19 11:15:24 +03:00
fix(dataflows): let the next vendor serve what Alpha Vantage cannot
- an indicator it does not carry raises instead of returning prose the router counts as an answer - ticker news asks for the configured article limit
This commit is contained in:
@@ -183,3 +183,31 @@ def test_the_news_window_includes_the_analysis_day(monkeypatch):
|
|||||||
|
|
||||||
assert seen["time_from"] == "20260310T0000"
|
assert seen["time_from"] == "20260310T0000"
|
||||||
assert seen["time_to"] == "20260314T2359"
|
assert seen["time_to"] == "20260314T2359"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@pytest.mark.parametrize("indicator", ["vwma", "mfi"])
|
||||||
|
def test_an_indicator_this_vendor_lacks_lets_the_next_one_serve_it(indicator):
|
||||||
|
"""Returning prose counts as success to the router, so the chain stops at a
|
||||||
|
vendor that cannot compute the indicator while the next one can."""
|
||||||
|
from tradingagents.dataflows import alpha_vantage_indicator
|
||||||
|
from tradingagents.dataflows.errors import VendorError
|
||||||
|
|
||||||
|
with pytest.raises(VendorError):
|
||||||
|
alpha_vantage_indicator.get_indicator("AAPL", indicator, "2026-05-08", 30)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_ticker_news_asks_for_only_as_many_articles_as_configured(monkeypatch):
|
||||||
|
"""The endpoint returns 50 articles with per-article sentiment arrays by
|
||||||
|
default, and the whole payload went into the prompt."""
|
||||||
|
from tradingagents.dataflows import alpha_vantage_news
|
||||||
|
|
||||||
|
monkeypatch.setattr(alpha_vantage_news, "get_config", lambda: {"news_article_limit": 8})
|
||||||
|
seen = {}
|
||||||
|
monkeypatch.setattr(alpha_vantage_news, "_make_api_request",
|
||||||
|
lambda fn, params: seen.update(params) or "{}")
|
||||||
|
|
||||||
|
alpha_vantage_news.get_news("AAPL", "2026-03-10", "2026-03-14")
|
||||||
|
|
||||||
|
assert seen["limit"] == "8"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from .alpha_vantage_common import AlphaVantageNotConfiguredError, _make_api_request
|
from .alpha_vantage_common import _make_api_request
|
||||||
|
from .errors import NoMarketDataError, VendorError
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -64,8 +65,11 @@ def get_indicator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if indicator not in supported_indicators:
|
if indicator not in supported_indicators:
|
||||||
raise ValueError(
|
# A vendor error, not a caller error: another vendor may compute it, and
|
||||||
f"Indicator {indicator} is not supported. Please choose from: {list(supported_indicators.keys())}"
|
# the router decides. yfinance rejects a name nobody serves.
|
||||||
|
raise NoMarketDataError(
|
||||||
|
symbol, symbol,
|
||||||
|
f"Alpha Vantage does not serve {indicator}; it serves {list(supported_indicators)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
curr_date_dt = datetime.strptime(curr_date, "%Y-%m-%d")
|
curr_date_dt = datetime.strptime(curr_date, "%Y-%m-%d")
|
||||||
@@ -134,12 +138,13 @@ def get_indicator(
|
|||||||
"time_period": str(time_period),
|
"time_period": str(time_period),
|
||||||
"datatype": "csv"
|
"datatype": "csv"
|
||||||
})
|
})
|
||||||
elif indicator == "vwma":
|
|
||||||
# Alpha Vantage doesn't have direct VWMA, so we'll return an informative message
|
|
||||||
# In a real implementation, this would need to be calculated from OHLCV data
|
|
||||||
return f"## VWMA (Volume Weighted Moving Average) for {symbol}:\n\nVWMA calculation requires OHLCV data and is not directly available from Alpha Vantage API.\nThis indicator would need to be calculated from the raw stock data using volume-weighted price averaging.\n\n{indicator_descriptions.get('vwma', 'No description available.')}"
|
|
||||||
else:
|
else:
|
||||||
return f"Error: Indicator {indicator} not implemented yet."
|
# This vendor has no endpoint for the indicator. Raising lets the
|
||||||
|
# router try the next vendor, which computes it; returning prose
|
||||||
|
# counted as a successful answer and ended the chain here.
|
||||||
|
raise NoMarketDataError(
|
||||||
|
symbol, symbol, f"Alpha Vantage does not serve the {indicator} indicator"
|
||||||
|
)
|
||||||
|
|
||||||
# Parse CSV data and extract values for the date range
|
# Parse CSV data and extract values for the date range
|
||||||
lines = data.strip().split('\n')
|
lines = data.strip().split('\n')
|
||||||
@@ -209,11 +214,11 @@ def get_indicator(
|
|||||||
|
|
||||||
return result_str
|
return result_str
|
||||||
|
|
||||||
except AlphaVantageNotConfiguredError:
|
except VendorError:
|
||||||
# Vendor unavailable (no API key). Let it propagate so the router can
|
# Unavailable vendor, throttle, or an indicator this vendor does not
|
||||||
# fall back / emit the no-data sentinel instead of returning this as a
|
# serve. Let it propagate so the router falls back to a vendor that can,
|
||||||
# successful-looking error string.
|
# instead of returning the failure as a successful-looking string.
|
||||||
raise
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning("Alpha Vantage indicator %s failed: %s", indicator, e)
|
logger.warning("Alpha Vantage indicator %s failed: %s", indicator, e)
|
||||||
return f"Error retrieving {indicator} data: {str(e)}"
|
raise NoMarketDataError(symbol, symbol, f"{indicator} unavailable: {e}") from e
|
||||||
|
|||||||
@@ -18,10 +18,13 @@ def get_news(ticker, start_date, end_date) -> dict[str, str] | str:
|
|||||||
Dictionary containing news sentiment data or JSON string.
|
Dictionary containing news sentiment data or JSON string.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Without a limit the endpoint returns 50 articles, each with per-ticker
|
||||||
|
# sentiment arrays, and all of it reaches the prompt.
|
||||||
params = {
|
params = {
|
||||||
"tickers": ticker,
|
"tickers": ticker,
|
||||||
"time_from": format_datetime_for_api(start_date),
|
"time_from": format_datetime_for_api(start_date),
|
||||||
"time_to": format_datetime_for_api(end_date, end_of_day=True),
|
"time_to": format_datetime_for_api(end_date, end_of_day=True),
|
||||||
|
"limit": str(get_config()["news_article_limit"]),
|
||||||
}
|
}
|
||||||
|
|
||||||
return _make_api_request("NEWS_SENTIMENT", params)
|
return _make_api_request("NEWS_SENTIMENT", params)
|
||||||
|
|||||||
Reference in New Issue
Block a user