mirror of
https://github.com/TauricResearch/TradingAgents.git
synced 2026-06-16 21:06:15 +03:00
Analyzing a symbol Yahoo Finance does not recognize (e.g. XAUUSD+) could
produce an invented price instead of an error. The agent now either prices
the correct instrument or clearly reports that data is unavailable.
Ticker support:
- Commodities/forex/crypto resolve to the symbol Yahoo actually serves, so
you can enter the common form and it just works:
XAUUSD / XAUUSD+ / GOLD -> GC=F (gold)
USOIL -> CL=F (WTI crude)
EURUSD -> EURUSD=X
BTCUSD -> BTC-USD
SPX500 / NAS100 -> ^GSPC / ^NDX
Native Yahoo symbols (AAPL, GC=F, ^GSPC) keep working unchanged. New
instruments are added by extending the alias table.
Reliability:
- Unknown or delisted symbols now return a clear "data unavailable" result
the agent reports verbatim, instead of a value the model fills in.
- A failed fetch no longer leaves a broken symbol cached until the cache is
cleared by hand.
58 lines
2.1 KiB
Python
58 lines
2.1 KiB
Python
"""Tests for the ticker path-component validator that blocks directory traversal."""
|
|
|
|
import os
|
|
import unittest
|
|
|
|
import pytest
|
|
|
|
from tradingagents.dataflows.utils import safe_ticker_component
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestSafeTickerComponent(unittest.TestCase):
|
|
def test_accepts_common_ticker_formats(self):
|
|
for ticker in ("AAPL", "BRK-B", "BRK.A", "0700.HK", "7203.T", "BHP.AX", "^GSPC"):
|
|
self.assertEqual(safe_ticker_component(ticker), ticker)
|
|
|
|
def test_accepts_futures_and_forex_formats(self):
|
|
# Futures use '=' (GC=F gold, CL=F crude), forex/CFD symbols use '+'.
|
|
for ticker in ("GC=F", "CL=F", "ES=F", "XAUUSD+", "EURUSD+"):
|
|
self.assertEqual(safe_ticker_component(ticker), ticker)
|
|
|
|
def test_rejects_path_separators(self):
|
|
for bad in (".", "..", "../etc", "a/b", "a\\b", "/abs", "..\\..\\x"):
|
|
with self.assertRaises(ValueError):
|
|
safe_ticker_component(bad)
|
|
|
|
def test_rejects_null_byte_and_whitespace(self):
|
|
for bad in ("AAP L", "AAPL\x00", "AAPL\n", "\tAAPL"):
|
|
with self.assertRaises(ValueError):
|
|
safe_ticker_component(bad)
|
|
|
|
def test_rejects_empty_or_non_string(self):
|
|
for bad in ("", None, 123, b"AAPL"):
|
|
with self.assertRaises(ValueError):
|
|
safe_ticker_component(bad)
|
|
|
|
def test_rejects_overlong_input(self):
|
|
with self.assertRaises(ValueError):
|
|
safe_ticker_component("A" * 33)
|
|
|
|
def test_rejects_dot_only_values(self):
|
|
# '.' and '..' pass the regex but traverse when used as a path
|
|
# component (e.g. ``Path(results_dir) / ticker / "logs"``).
|
|
for bad in (".", "..", "...", "...."):
|
|
with self.assertRaises(ValueError):
|
|
safe_ticker_component(bad)
|
|
|
|
def test_traversal_string_does_not_escape_join(self):
|
|
"""Sanity: sanitized values stay within base when joined."""
|
|
base = os.path.realpath("/tmp/cache")
|
|
ticker = safe_ticker_component("AAPL")
|
|
joined = os.path.realpath(os.path.join(base, f"{ticker}.csv"))
|
|
self.assertTrue(joined.startswith(base + os.sep))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|