mirror of
https://github.com/TauricResearch/TradingAgents.git
synced 2026-06-16 21:06:15 +03:00
fix(data): normalize symbols on the identity and reflection paths
resolve_instrument_identity and the reflection return lookup queried Yahoo with the raw ticker, so broker/forex/commodity symbols (XAUUSD, BTCUSD, EURUSD) failed identity or could mismatch the priced instrument even though the price path already normalized them. Route both through normalize_symbol (#983, #984).
This commit is contained in:
54
tests/test_symbol_normalization_paths.py
Normal file
54
tests/test_symbol_normalization_paths.py
Normal file
@@ -0,0 +1,54 @@
|
||||
"""Symbol normalization must apply on every yfinance path, not just price fetch.
|
||||
|
||||
Regression tests for #983 (instrument identity) and #984 (reflection returns):
|
||||
a broker symbol like XAUUSD must resolve to the same Yahoo symbol (GC=F) that
|
||||
the price path uses, so identity and realized-return lookups hit the right
|
||||
instrument instead of failing/mismatching.
|
||||
"""
|
||||
import pandas as pd
|
||||
|
||||
import tradingagents.agents.utils.agent_utils as au
|
||||
import tradingagents.graph.trading_graph as tg
|
||||
from tradingagents.graph.trading_graph import TradingAgentsGraph
|
||||
|
||||
|
||||
def test_identity_lookup_normalizes_symbol(monkeypatch):
|
||||
seen = {}
|
||||
|
||||
class FakeTicker:
|
||||
def __init__(self, symbol):
|
||||
seen["symbol"] = symbol
|
||||
|
||||
@property
|
||||
def info(self):
|
||||
return {"longName": "Gold Futures", "quoteType": "FUTURE"}
|
||||
|
||||
monkeypatch.setattr(au.yf, "Ticker", FakeTicker)
|
||||
au.resolve_instrument_identity.cache_clear()
|
||||
|
||||
identity = au.resolve_instrument_identity("XAUUSD")
|
||||
|
||||
assert seen["symbol"] == "GC=F" # normalized, not the raw broker symbol
|
||||
assert identity.get("company_name") == "Gold Futures"
|
||||
|
||||
|
||||
def test_fetch_returns_normalizes_symbol(monkeypatch):
|
||||
queried = []
|
||||
|
||||
class FakeTicker:
|
||||
def __init__(self, symbol):
|
||||
queried.append(symbol)
|
||||
|
||||
def history(self, *args, **kwargs):
|
||||
return pd.DataFrame({"Close": [100.0, 101.0, 102.0, 103.0, 104.0, 105.0, 106.0]})
|
||||
|
||||
monkeypatch.setattr(tg.yf, "Ticker", FakeTicker)
|
||||
|
||||
# _fetch_returns does not use ``self``; call unbound to avoid building the graph.
|
||||
raw, alpha, days = TradingAgentsGraph._fetch_returns(
|
||||
None, "XAUUSD", "2025-01-02", holding_days=5, benchmark="SPY"
|
||||
)
|
||||
|
||||
assert queried[0] == "GC=F" # stock symbol normalized (#984)
|
||||
assert queried[1] == "SPY" # benchmark left as the canonical symbol
|
||||
assert raw is not None and days is not None
|
||||
@@ -70,9 +70,14 @@ def resolve_instrument_identity(ticker: str) -> dict:
|
||||
recognise the ticker, we return ``{}`` and the caller falls back to
|
||||
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).
|
||||
"""
|
||||
from tradingagents.dataflows.symbol_utils import normalize_symbol
|
||||
|
||||
try:
|
||||
info = yf.Ticker(ticker.upper()).info or {}
|
||||
info = yf.Ticker(normalize_symbol(ticker)).info or {}
|
||||
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 {}
|
||||
|
||||
@@ -232,12 +232,17 @@ class TradingAgentsGraph:
|
||||
actual_holding_days)`` or ``(None, None, None)`` if price data is
|
||||
unavailable (too recent, delisted, or network error).
|
||||
"""
|
||||
from tradingagents.dataflows.symbol_utils import normalize_symbol
|
||||
|
||||
try:
|
||||
start = datetime.strptime(trade_date, "%Y-%m-%d")
|
||||
end = start + timedelta(days=holding_days + 7) # buffer for weekends/holidays
|
||||
end_str = end.strftime("%Y-%m-%d")
|
||||
|
||||
stock = yf.Ticker(ticker).history(start=trade_date, end=end_str)
|
||||
# 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)
|
||||
|
||||
if len(stock) < 2 or len(bench) < 2:
|
||||
|
||||
Reference in New Issue
Block a user