refactor(dataflows): read company identity and settlement prices through the data layer

- the Yahoo vendor owns get_company_profile and get_closes; agent_utils and the graph no longer import yfinance
- a test keeps vendor libraries inside dataflows
This commit is contained in:
Yijia-Xiao
2026-09-24 00:41:56 +00:00
parent b4479b0c70
commit f197e09dcc
8 changed files with 76 additions and 34 deletions
+4 -6
View File
@@ -3,7 +3,6 @@ import logging
from collections.abc import Mapping
from typing import Any
import yfinance as yf
from langchain_core.messages import HumanMessage, RemoveMessage
# Import tools from separate utility files
@@ -23,6 +22,7 @@ from tradingagents.agents.utils.news_data_tools import (
)
from tradingagents.agents.utils.prediction_markets_tools import get_prediction_markets
from tradingagents.agents.utils.technical_indicators_tools import get_indicators
from tradingagents.dataflows.y_finance import get_company_profile
# Public surface: the data tools are imported here so agents and the graph
# import them from one place, plus the instrument/language helpers defined below.
@@ -106,13 +106,11 @@ def resolve_instrument_identity(ticker: str) -> dict:
ticker-only context rather than failing before analysis starts. Cached so
the lookup happens at most once per ticker per process.
The symbol is normalized first (e.g. ``XAUUSD`` -> ``GC=F``) so identity
resolves for the same instrument the price path actually fetches (#983).
Identity resolves for the same instrument the price path fetches
(``XAUUSD`` -> ``GC=F``, #983).
"""
from tradingagents.dataflows.symbol_utils import normalize_symbol
try:
info = yf.Ticker(normalize_symbol(ticker)).info or {}
info = get_company_profile(ticker)
except Exception as exc: # noqa: BLE001 — fail open, never block the run
logger.debug("Could not resolve instrument identity for %s: %s", ticker, exc)
return {}
+19
View File
@@ -519,3 +519,22 @@ def get_insider_transactions(
except Exception as e:
raise NoMarketDataError(ticker, canonical, f"insider transactions unavailable: {e}") from e
def get_company_profile(ticker: str) -> dict:
"""Yahoo's current profile for ``ticker``: name, sector, industry and the like."""
canonical = normalize_symbol(ticker)
try:
return yf_retry(lambda: yf.Ticker(canonical).info) or {}
except Exception as e:
raise NoMarketDataError(ticker, canonical, f"profile unavailable: {e}") from e
def get_closes(symbol: str, start_date: str, end_date: str) -> pd.Series:
"""Daily closes from ``start_date`` up to, not including, ``end_date``."""
canonical = normalize_symbol(symbol)
try:
history = yf_retry(lambda: yf.Ticker(canonical).history(start=start_date, end=end_date))
except Exception as e:
raise NoMarketDataError(symbol, canonical, f"prices unavailable: {e}") from e
return history["Close"] if "Close" in history else pd.Series(dtype=float)
+6 -16
View File
@@ -8,7 +8,6 @@ from datetime import datetime, timedelta
from pathlib import Path
from typing import Any
import yfinance as yf
from langgraph.prebuilt import ToolNode
# Import the abstract tool methods from agent_utils
@@ -31,6 +30,7 @@ from tradingagents.agents.utils.agent_utils import (
from tradingagents.agents.utils.memory import TradingMemoryLog
from tradingagents.dataflows.config import run_config, set_config
from tradingagents.dataflows.utils import get_current_date, safe_ticker_component
from tradingagents.dataflows.y_finance import get_closes
from tradingagents.default_config import DEFAULT_CONFIG
from tradingagents.llm_clients import create_llm_client
from tradingagents.reporting import write_report_tree
@@ -302,8 +302,6 @@ class TradingAgentsGraph:
the full holding window has not traded (#1169), or the symbol is delisted
or unreachable.
"""
from tradingagents.dataflows.symbol_utils import normalize_symbol
try:
start = datetime.strptime(trade_date, "%Y-%m-%d")
# holding_days counts trading days, so ask for the calendar span they
@@ -311,11 +309,9 @@ class TradingAgentsGraph:
end = start + timedelta(days=round(holding_days * 7 / 5) + 7)
end_str = end.strftime("%Y-%m-%d")
# Normalize so the realized-return lookup hits the same instrument
# the analysis priced (e.g. XAUUSD -> GC=F) (#984). The benchmark is
# already a canonical Yahoo symbol from ``_resolve_benchmark``.
stock = yf.Ticker(normalize_symbol(ticker)).history(start=trade_date, end=end_str)
bench = yf.Ticker(benchmark).history(start=trade_date, end=end_str)
# Closes for the instrument the analysis priced (XAUUSD -> GC=F, #984).
stock = get_closes(ticker, trade_date, end_str)
bench = get_closes(benchmark, trade_date, end_str)
# Require the full holding window in both series. A rerun before it
# has traded leaves the entry pending to retry next run, rather than
@@ -323,14 +319,8 @@ class TradingAgentsGraph:
if len(stock) <= holding_days or len(bench) <= holding_days:
return None, None, None, None
raw = float(
(stock["Close"].iloc[holding_days] - stock["Close"].iloc[0])
/ stock["Close"].iloc[0]
)
bench_ret = float(
(bench["Close"].iloc[holding_days] - bench["Close"].iloc[0])
/ bench["Close"].iloc[0]
)
raw = float((stock.iloc[holding_days] - stock.iloc[0]) / stock.iloc[0])
bench_ret = float((bench.iloc[holding_days] - bench.iloc[0]) / bench.iloc[0])
alpha = raw - bench_ret
# The date of the last price bar used is when this outcome became
# known — the point-in-time cutoff for injecting the lesson (#1251).