fix(dataflows): map Shanghai .SH tickers to Yahoo's .SS (#1260)

- normalize_symbol maps 600519.SH to 600519.SS
- the alpha benchmark is resolved from the normalized symbol
This commit is contained in:
Yijia-Xiao
2026-09-15 00:04:25 +00:00
parent fadc698e20
commit 2942655f70
4 changed files with 15 additions and 2 deletions

View File

@@ -598,6 +598,8 @@ class TestDeferredReflection:
"benchmark_map": DEFAULT_CONFIG["benchmark_map"]} "benchmark_map": DEFAULT_CONFIG["benchmark_map"]}
assert TradingAgentsGraph._resolve_benchmark(mock_graph, "600519.SS") == "000001.SS" assert TradingAgentsGraph._resolve_benchmark(mock_graph, "600519.SS") == "000001.SS"
assert TradingAgentsGraph._resolve_benchmark(mock_graph, "000001.SZ") == "399001.SZ" assert TradingAgentsGraph._resolve_benchmark(mock_graph, "000001.SZ") == "399001.SZ"
# .SH is the exchange's own suffix; Yahoo spells Shanghai .SS (#1260)
assert TradingAgentsGraph._resolve_benchmark(mock_graph, "600519.SH") == "000001.SS"
def test_resolve_benchmark_us_ticker_defaults_to_spy(self): def test_resolve_benchmark_us_ticker_defaults_to_spy(self):
"""US tickers (no dotted suffix) take the empty-suffix entry.""" """US tickers (no dotted suffix) take the empty-suffix entry."""

View File

@@ -69,6 +69,10 @@ class TestNormalizeSymbol(unittest.TestCase):
def test_hk_code_case_insensitive_suffix(self): def test_hk_code_case_insensitive_suffix(self):
self.assertEqual(normalize_symbol("09992.hk"), "9992.HK") self.assertEqual(normalize_symbol("09992.hk"), "9992.HK")
def test_shanghai_sh_suffix_maps_to_yahoo_ss(self):
self.assertEqual(normalize_symbol("600519.sh"), "600519.SS")
self.assertEqual(normalize_symbol("600519.SS"), "600519.SS")
@pytest.mark.unit @pytest.mark.unit
class TestNoMarketDataError(unittest.TestCase): class TestNoMarketDataError(unittest.TestCase):

View File

@@ -11,6 +11,7 @@ differ from the broker / TradingView / MT5 style symbols users often type:
BTCUSD BTC-USD crypto pairs use a ``-`` separator BTCUSD BTC-USD crypto pairs use a ``-`` separator
SPX500, US500 ^GSPC index CFDs map to Yahoo index symbols SPX500, US500 ^GSPC index CFDs map to Yahoo index symbols
09992.HK, 700.HK 9992.HK, 0700.HK HK codes are zero-padded to 4 digits 09992.HK, 700.HK 9992.HK, 0700.HK HK codes are zero-padded to 4 digits
600519.SH 600519.SS Yahoo spells Shanghai ``.SS``
Passing the raw broker symbol to Yahoo returns an empty result, which the Passing the raw broker symbol to Yahoo returns an empty result, which the
agents previously received as free text and could hallucinate a price agents previously received as free text and could hallucinate a price
@@ -75,6 +76,7 @@ _YAHOO_SAFE = re.compile(r"^[A-Za-z0-9._\-\^=]+$")
# HKEX codes as Yahoo spells them: the number zero-padded to 4 digits (#957). # HKEX codes as Yahoo spells them: the number zero-padded to 4 digits (#957).
_HK_CODE = re.compile(r"^(\d{1,5})\.HK$") _HK_CODE = re.compile(r"^(\d{1,5})\.HK$")
_SHANGHAI_SH = re.compile(r"^(\d{6})\.SH$")
# Crypto quote currencies that all map to Yahoo's USD pair. Yahoo lists only # Crypto quote currencies that all map to Yahoo's USD pair. Yahoo lists only
@@ -115,7 +117,8 @@ def normalize_symbol(raw: str) -> str:
3. Forex rule: six letters that are two ISO currency codes -> ``PAIR=X``. 3. Forex rule: six letters that are two ISO currency codes -> ``PAIR=X``.
4. HK rule: a numeric ``.HK`` code -> Yahoo's 4-digit padding 4. HK rule: a numeric ``.HK`` code -> Yahoo's 4-digit padding
(``09992.HK`` -> ``9992.HK``, ``700.HK`` -> ``0700.HK``). (``09992.HK`` -> ``9992.HK``, ``700.HK`` -> ``0700.HK``).
5. Otherwise the upper-cased symbol is returned unchanged (plain 5. Shanghai rule: ``600519.SH`` -> ``600519.SS``.
6. Otherwise the upper-cased symbol is returned unchanged (plain
equities, ETFs, Yahoo-native symbols like ``GC=F`` or ``^GSPC``). equities, ETFs, Yahoo-native symbols like ``GC=F`` or ``^GSPC``).
A trailing ``+`` (broker CFD marker, e.g. ``XAUUSD+``) is stripped before A trailing ``+`` (broker CFD marker, e.g. ``XAUUSD+``) is stripped before
@@ -138,6 +141,8 @@ def normalize_symbol(raw: str) -> str:
canonical = f"{s}=X" canonical = f"{s}=X"
elif hk := _HK_CODE.match(s): elif hk := _HK_CODE.match(s):
canonical = f"{int(hk.group(1)):04d}.HK" canonical = f"{int(hk.group(1)):04d}.HK"
elif sh := _SHANGHAI_SH.match(s):
canonical = f"{sh.group(1)}.SS"
else: else:
canonical = s canonical = s

View File

@@ -274,11 +274,13 @@ class TradingAgentsGraph:
entry, which is the right default because the alpha calculation works entry, which is the right default because the alpha calculation works
in USD. in USD.
""" """
from tradingagents.dataflows.symbol_utils import normalize_symbol
explicit = self.config.get("benchmark_ticker") explicit = self.config.get("benchmark_ticker")
if explicit: if explicit:
return explicit return explicit
benchmark_map = self.config.get("benchmark_map", {}) benchmark_map = self.config.get("benchmark_map", {})
ticker_upper = ticker.upper() ticker_upper = normalize_symbol(ticker)
for suffix, benchmark in benchmark_map.items(): for suffix, benchmark in benchmark_map.items():
if suffix and ticker_upper.endswith(suffix.upper()): if suffix and ticker_upper.endswith(suffix.upper()):
return benchmark return benchmark