mirror of
https://github.com/TauricResearch/TradingAgents.git
synced 2026-11-04 00:22:43 +02:00
fix(agents): ask again for an instrument identity whose lookup failed
- only an answered lookup is cached for the process, so one failed request no longer leaves every later run for that ticker without an identity
This commit is contained in:
@@ -115,7 +115,7 @@ def offline(monkeypatch, tmp_path):
|
||||
monkeypatch.setattr(sentiment_analyst, "fetch_stocktwits_messages", lambda *a, **k: "no posts")
|
||||
monkeypatch.setattr(sentiment_analyst, "fetch_reddit_posts", lambda *a, **k: "no posts")
|
||||
monkeypatch.setattr(yahoo_market.yf, "Ticker", lambda s: type("T", (), {"info": {"longName": "NVIDIA"}})())
|
||||
context.resolve_instrument_identity.cache_clear()
|
||||
context._identity.cache_clear()
|
||||
return called
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ from unittest.mock import patch
|
||||
import pytest
|
||||
|
||||
from tradingagents.agents.context import (
|
||||
_identity,
|
||||
build_instrument_context,
|
||||
get_instrument_context_from_state,
|
||||
resolve_instrument_identity,
|
||||
@@ -16,7 +17,7 @@ from tradingagents.agents.context import (
|
||||
@pytest.mark.unit
|
||||
class ResolveInstrumentIdentityTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
resolve_instrument_identity.cache_clear()
|
||||
_identity.cache_clear()
|
||||
|
||||
def test_resolves_company_metadata_from_yfinance(self):
|
||||
with patch("tradingagents.dataflows.vendors.yahoo.market.yf.Ticker") as mock:
|
||||
@@ -54,6 +55,15 @@ class ResolveInstrumentIdentityTests(unittest.TestCase):
|
||||
):
|
||||
self.assertEqual(resolve_instrument_identity("TOTDY"), {})
|
||||
|
||||
def test_a_failed_lookup_is_asked_again(self):
|
||||
# A long process (a backtest) must not run every later cell without an
|
||||
# identity because one request failed.
|
||||
with patch("tradingagents.dataflows.vendors.yahoo.market.yf.Ticker") as mock:
|
||||
type(mock.return_value).info = property(lambda self: (_ for _ in ()).throw(TimeoutError()))
|
||||
self.assertEqual(resolve_instrument_identity("TOTDY"), {})
|
||||
type(mock.return_value).info = {"longName": "TOTO LTD."}
|
||||
self.assertEqual(resolve_instrument_identity("TOTDY")["company_name"], "TOTO LTD.")
|
||||
|
||||
def test_result_is_cached(self):
|
||||
with patch("tradingagents.dataflows.vendors.yahoo.market.yf.Ticker") as mock:
|
||||
mock.return_value.info = {"longName": "TOTO LTD."}
|
||||
|
||||
@@ -25,7 +25,7 @@ def test_identity_lookup_normalizes_symbol(monkeypatch):
|
||||
return {"longName": "Gold Futures", "quoteType": "FUTURE"}
|
||||
|
||||
monkeypatch.setattr(yahoo_market.yf, "Ticker", FakeTicker)
|
||||
au.resolve_instrument_identity.cache_clear()
|
||||
au._identity.cache_clear()
|
||||
|
||||
identity = au.resolve_instrument_identity("XAUUSD")
|
||||
|
||||
|
||||
@@ -52,7 +52,6 @@ def _clean_identity_value(value: Any) -> str | None:
|
||||
return cleaned
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=256)
|
||||
def resolve_instrument_identity(ticker: str) -> dict:
|
||||
"""Resolve deterministic identity metadata (company name, sector, …) for a ticker.
|
||||
|
||||
@@ -64,18 +63,23 @@ def resolve_instrument_identity(ticker: str) -> dict:
|
||||
|
||||
Best-effort by design: if yfinance is unavailable, rate-limited, or doesn't
|
||||
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.
|
||||
ticker-only context rather than failing before analysis starts. An answer
|
||||
is cached for the process; a failed lookup is asked again next time.
|
||||
|
||||
Identity resolves for the same instrument the price path fetches
|
||||
(``XAUUSD`` -> ``GC=F``, #983).
|
||||
"""
|
||||
try:
|
||||
info = get_company_profile(ticker)
|
||||
return _identity(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 {}
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=256)
|
||||
def _identity(ticker: str) -> dict:
|
||||
"""The vendor's identity fields for ``ticker``; raises if the lookup fails."""
|
||||
info = get_company_profile(ticker)
|
||||
identity: dict[str, str] = {}
|
||||
company_name = _clean_identity_value(info.get("longName")) or _clean_identity_value(
|
||||
info.get("shortName")
|
||||
|
||||
Reference in New Issue
Block a user