mirror of
https://github.com/TauricResearch/TradingAgents.git
synced 2026-09-27 23:12:39 +03:00
refactor(dataflows): group the vendors under dataflows/vendors
- vendors/yahoo: ohlcv (loader and cache), market (prices, indicators), fundamentals (profile, statements, insider), news, snapshot - vendors/alpha_vantage is a package; sec_edgar, fred, polymarket, reddit and stocktwits sit beside it - the one-method StockstatsUtils class is a function; the duplicate Yahoo host constant is gone - tests are named after the modules they cover: test_ohlcv_date_column, test_yahoo_snapshot, and the ohlcv and snapshot aliases
This commit is contained in:
@@ -14,7 +14,12 @@ import pandas as pd
|
||||
import pytest
|
||||
|
||||
from tradingagents.agents.utils import news_data_tools, prediction_markets_tools
|
||||
from tradingagents.dataflows import alpha_vantage_news, polymarket, y_finance
|
||||
from tradingagents.dataflows.vendors import polymarket
|
||||
from tradingagents.dataflows.vendors.alpha_vantage import news as alpha_vantage_news
|
||||
from tradingagents.dataflows.vendors.yahoo import (
|
||||
fundamentals as yahoo_fundamentals,
|
||||
market as yahoo_market,
|
||||
)
|
||||
|
||||
|
||||
def _insider_frame(*dates):
|
||||
@@ -27,8 +32,8 @@ def _insider_frame(*dates):
|
||||
|
||||
def _yf_insider(frame, curr_date):
|
||||
ticker = mock.Mock(insider_transactions=frame)
|
||||
with mock.patch.object(y_finance.yf, "Ticker", return_value=ticker):
|
||||
return y_finance.get_insider_transactions("AAPL", curr_date)
|
||||
with mock.patch.object(yahoo_market.yf, "Ticker", return_value=ticker):
|
||||
return yahoo_fundamentals.get_insider_transactions("AAPL", curr_date)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -121,16 +126,14 @@ def test_insider_rows_are_dated_by_the_trade_not_the_filing():
|
||||
run must not be told these rows were public on their transaction date."""
|
||||
import pandas as pd
|
||||
|
||||
from tradingagents.dataflows import y_finance
|
||||
|
||||
frame = pd.DataFrame({
|
||||
"Shares": [100, 200],
|
||||
"Text": ["Sale at price 10.00 per share.", "Sale at price 11.00 per share."],
|
||||
"Start Date": pd.to_datetime(["2026-05-01", "2026-05-20"]),
|
||||
})
|
||||
ticker = mock.Mock(insider_transactions=frame)
|
||||
with mock.patch.object(y_finance.yf, "Ticker", return_value=ticker):
|
||||
out = y_finance.get_insider_transactions("AAPL", "2026-05-10")
|
||||
with mock.patch.object(yahoo_market.yf, "Ticker", return_value=ticker):
|
||||
out = yahoo_fundamentals.get_insider_transactions("AAPL", "2026-05-10")
|
||||
|
||||
assert "2026-05-01" in out and "2026-05-20" not in out # still bounded by the date
|
||||
assert "transaction date" in out.lower() # and says what the date means
|
||||
@@ -142,13 +145,12 @@ def test_an_indicator_that_could_not_be_read_is_not_shown_as_a_blank_value():
|
||||
"""The per-day fallback returned an empty string for a failed read, so the
|
||||
table rendered a row per day with nothing after the colon: an analyst reads
|
||||
that as "no value on that day" rather than "could not be obtained"."""
|
||||
from tradingagents.dataflows import y_finance
|
||||
from tradingagents.dataflows.errors import VendorError
|
||||
|
||||
with mock.patch.object(y_finance.StockstatsUtils, "get_stock_stats",
|
||||
with mock.patch.object(yahoo_market, "get_stock_stats",
|
||||
side_effect=RuntimeError("cache parse failed")), \
|
||||
pytest.raises(VendorError):
|
||||
y_finance.get_stockstats_indicator("AAPL", "rsi", "2026-05-08")
|
||||
yahoo_market.get_stockstats_indicator("AAPL", "rsi", "2026-05-08")
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -165,12 +167,11 @@ def test_a_yfinance_failure_is_a_vendor_error_not_a_report(func, args):
|
||||
"""Returning the failure as text makes the router count it as an answer, so
|
||||
the chain stops and the analyst reads the error message as if it were data.
|
||||
yfinance serves the default path, so this is the one that matters most."""
|
||||
from tradingagents.dataflows import y_finance
|
||||
from tradingagents.dataflows.errors import VendorError
|
||||
|
||||
with mock.patch.object(y_finance.yf, "Ticker", side_effect=RuntimeError("yahoo hiccup")), \
|
||||
with mock.patch.object(yahoo_market.yf, "Ticker", side_effect=RuntimeError("yahoo hiccup")), \
|
||||
pytest.raises(VendorError):
|
||||
getattr(y_finance, func)(*args)
|
||||
getattr(yahoo_fundamentals, func)(*args)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -179,13 +180,13 @@ def test_a_yfinance_failure_is_a_vendor_error_not_a_report(func, args):
|
||||
("get_global_news_yfinance", ("2026-09-01", 7, 5)),
|
||||
])
|
||||
def test_a_yfinance_news_failure_is_a_vendor_error_not_a_report(func, args):
|
||||
from tradingagents.dataflows import yfinance_news
|
||||
from tradingagents.dataflows.errors import VendorError
|
||||
from tradingagents.dataflows.vendors.yahoo import news as yahoo_news
|
||||
|
||||
target = "Ticker" if "global" not in func else "Search"
|
||||
with mock.patch.object(yfinance_news.yf, target, side_effect=RuntimeError("yahoo hiccup")), \
|
||||
with mock.patch.object(yahoo_news.yf, target, side_effect=RuntimeError("yahoo hiccup")), \
|
||||
pytest.raises(VendorError):
|
||||
getattr(yfinance_news, func)(*args)
|
||||
getattr(yahoo_news, func)(*args)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -195,19 +196,19 @@ def test_an_unreachable_vendor_is_not_reported_as_a_missing_symbol(monkeypatch):
|
||||
company has no balance sheet, when the truth is we could not ask."""
|
||||
import pandas as pd
|
||||
|
||||
from tradingagents.dataflows import stockstats_utils, y_finance
|
||||
from tradingagents.dataflows.errors import NoMarketDataError, VendorRateLimitError
|
||||
from tradingagents.dataflows.vendors.yahoo import ohlcv
|
||||
|
||||
empty = mock.Mock(quarterly_balance_sheet=pd.DataFrame(), balance_sheet=pd.DataFrame())
|
||||
monkeypatch.setattr(y_finance.yf, "Ticker", lambda s: empty)
|
||||
monkeypatch.setattr(yahoo_market.yf, "Ticker", lambda s: empty)
|
||||
|
||||
monkeypatch.setattr(stockstats_utils, "vendor_reachable", lambda url: False)
|
||||
monkeypatch.setattr(ohlcv, "vendor_reachable", lambda url: False)
|
||||
with pytest.raises(VendorRateLimitError, match="unreachable"):
|
||||
y_finance.get_balance_sheet("AAPL", "annual", "2026-09-01")
|
||||
yahoo_fundamentals.get_balance_sheet("AAPL", "annual", "2026-09-01")
|
||||
|
||||
monkeypatch.setattr(stockstats_utils, "vendor_reachable", lambda url: True)
|
||||
monkeypatch.setattr(ohlcv, "vendor_reachable", lambda url: True)
|
||||
with pytest.raises(NoMarketDataError):
|
||||
y_finance.get_balance_sheet("AAPL", "annual", "2026-09-01")
|
||||
yahoo_fundamentals.get_balance_sheet("AAPL", "annual", "2026-09-01")
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -234,27 +235,29 @@ def test_the_price_path_also_tells_an_outage_from_an_unknown_symbol(monkeypatch)
|
||||
delisted symbol either."""
|
||||
import pandas as pd
|
||||
|
||||
from tradingagents.dataflows import stockstats_utils, y_finance
|
||||
from tradingagents.dataflows.errors import NoMarketDataError, VendorRateLimitError
|
||||
from tradingagents.dataflows.vendors.yahoo import ohlcv
|
||||
|
||||
monkeypatch.setattr(y_finance.yf, "Ticker", lambda s: mock.Mock(history=lambda **k: pd.DataFrame()))
|
||||
monkeypatch.setattr(yahoo_market.yf, "Ticker", lambda s: mock.Mock(history=lambda **k: pd.DataFrame()))
|
||||
|
||||
monkeypatch.setattr(stockstats_utils, "vendor_reachable", lambda url: False)
|
||||
monkeypatch.setattr(ohlcv, "vendor_reachable", lambda url: False)
|
||||
with pytest.raises(VendorRateLimitError, match="unreachable"):
|
||||
y_finance.get_YFin_data_online("AAPL", "2026-09-01", "2026-09-10")
|
||||
yahoo_market.get_YFin_data_online("AAPL", "2026-09-01", "2026-09-10")
|
||||
|
||||
monkeypatch.setattr(stockstats_utils, "vendor_reachable", lambda url: True)
|
||||
monkeypatch.setattr(ohlcv, "vendor_reachable", lambda url: True)
|
||||
with pytest.raises(NoMarketDataError):
|
||||
y_finance.get_YFin_data_online("AAPL", "2026-09-01", "2026-09-10")
|
||||
yahoo_market.get_YFin_data_online("AAPL", "2026-09-01", "2026-09-10")
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.parametrize("func, args", [
|
||||
("get_YFin_data_online", ("AAPL", "2025-06-02", "2025-06-06")),
|
||||
("get_balance_sheet", ("AAPL", "quarterly", "2025-06-06")),
|
||||
("get_cashflow", ("AAPL", "quarterly", "2025-06-06")),
|
||||
("get_income_statement", ("AAPL", "quarterly", "2025-06-06")),
|
||||
("get_insider_transactions", ("AAPL", "2025-06-06")),
|
||||
pytest.param(f, a, id=f.__name__) for f, a in (
|
||||
(yahoo_market.get_YFin_data_online, ("AAPL", "2025-06-02", "2025-06-06")),
|
||||
(yahoo_fundamentals.get_balance_sheet, ("AAPL", "quarterly", "2025-06-06")),
|
||||
(yahoo_fundamentals.get_cashflow, ("AAPL", "quarterly", "2025-06-06")),
|
||||
(yahoo_fundamentals.get_income_statement, ("AAPL", "quarterly", "2025-06-06")),
|
||||
(yahoo_fundamentals.get_insider_transactions, ("AAPL", "2025-06-06")),
|
||||
)
|
||||
])
|
||||
def test_a_historical_run_is_not_told_todays_date(func, args):
|
||||
"""A header stamped with the wall clock tells a backtest when it is really running."""
|
||||
@@ -267,8 +270,8 @@ def test_a_historical_run_is_not_told_todays_date(func, args):
|
||||
quarterly_income_stmt=statement,
|
||||
insider_transactions=_insider_frame("2025-05-30"),
|
||||
history=lambda **k: prices)
|
||||
with mock.patch.object(y_finance.yf, "Ticker", return_value=ticker):
|
||||
out = getattr(y_finance, func)(*args)
|
||||
with mock.patch.object(yahoo_market.yf, "Ticker", return_value=ticker):
|
||||
out = func(*args)
|
||||
|
||||
assert date.today().isoformat() not in out
|
||||
|
||||
|
||||
Reference in New Issue
Block a user