diff --git a/tests/test_yahoo_snapshot.py b/tests/test_yahoo_snapshot.py index 07dbc3d7e..804ff8970 100644 --- a/tests/test_yahoo_snapshot.py +++ b/tests/test_yahoo_snapshot.py @@ -73,3 +73,47 @@ class TestTool: {"symbol": "COF", "curr_date": "2026-05-20"} ) assert "Verified market data snapshot for COF" in out + + +@pytest.mark.unit +class TestPricesKeepTheirDigits: + """A price prints with its significant digits: a sub-cent coin is not 0.00, a rate keeps its pips.""" + + @pytest.mark.parametrize("value, text", [ + (0.00000503123456, "0.00000503123"), + (1.123456, "1.12346"), + (763.960022, "763.96"), + (81074.1875, "81074.19"), + (100.0, "100.00"), + (-0.0012345678, "-0.00123457"), + (0.0, "0.00"), + ]) + def test_price_text(self, value, text): + from tradingagents.dataflows.vendors.yahoo.common import price_text + assert price_text(value) == text + + def test_the_snapshot_shows_a_sub_cent_close(self, monkeypatch): + data = _sample_ohlcv() + for col in ("Open", "High", "Low", "Close"): + data[col] = data[col] * 0.0000001 + monkeypatch.setattr(validator, "load_ohlcv", lambda s, d, fill_gaps=True: data) + text = validator.build_verified_market_snapshot("SHIB-USD", "2026-05-20") + assert "| Close | 0.0000135 |" in text and "| Close | 0.00 |" not in text + + def test_the_price_table_shows_a_sub_cent_close(self, monkeypatch): + from tradingagents.dataflows.vendors.yahoo import market + + days = pd.bdate_range("2026-05-11", "2026-05-15") + frame = pd.DataFrame({"Open": 0.00000501, "High": 0.00000512, "Low": 0.00000499, + "Close": 0.00000503, "Volume": 10**9}, index=days) + + class _Ticker: + def __init__(self, symbol): + pass + + def history(self, **kwargs): + return frame + + monkeypatch.setattr(market.yf, "Ticker", _Ticker) + text = market.get_YFin_data_online("SHIB-USD", "2026-05-11", "2026-05-15") + assert "0.00000503" in text and ",0.0," not in text diff --git a/tradingagents/dataflows/vendors/yahoo/common.py b/tradingagents/dataflows/vendors/yahoo/common.py index 7ab0afec2..e19fb9aa2 100644 --- a/tradingagents/dataflows/vendors/yahoo/common.py +++ b/tradingagents/dataflows/vendors/yahoo/common.py @@ -1,6 +1,8 @@ -"""What every Yahoo request shares: its retry, and how a failure or an empty answer is reported.""" +"""What every Yahoo request shares: its retry, how a failure or an empty answer is +reported, and how a price is written out.""" import logging +import math import time import yfinance as yf @@ -14,6 +16,19 @@ logger = logging.getLogger(__name__) YAHOO_HOST = "https://query2.finance.yahoo.com" +def price_text(value: float) -> str: + """A price with six significant digits and at least two decimals. + + Fixed decimals print a sub-cent coin as 0.00 and a currency pair without its + pips; this keeps both readable. + """ + if not math.isfinite(value): + return str(value) + decimals = 2 if value == 0 else max(2, 5 - math.floor(math.log10(abs(value)))) + whole, _, fraction = f"{value:.{decimals}f}".partition(".") + return f"{whole}.{fraction.rstrip('0').ljust(2, '0')}" + + def raise_for_empty(symbol: str, canonical: str, what: str) -> None: """Report an empty Yahoo answer as an absence, or as an outage if it is one. diff --git a/tradingagents/dataflows/vendors/yahoo/market.py b/tradingagents/dataflows/vendors/yahoo/market.py index b099caabf..922b55761 100644 --- a/tradingagents/dataflows/vendors/yahoo/market.py +++ b/tradingagents/dataflows/vendors/yahoo/market.py @@ -9,7 +9,7 @@ from stockstats import wrap from tradingagents.dataflows.errors import NoMarketDataError, VendorError from tradingagents.dataflows.symbols import normalize_symbol -from tradingagents.dataflows.vendors.yahoo.common import raise_for_empty, yf_retry +from tradingagents.dataflows.vendors.yahoo.common import price_text, raise_for_empty, yf_retry from tradingagents.dataflows.vendors.yahoo.ohlcv import _assert_ohlcv_not_stale, load_ohlcv logger = logging.getLogger(__name__) @@ -48,11 +48,9 @@ def get_YFin_data_online( # turns into one clear unavailable signal (#1021). _assert_ohlcv_not_stale(data, end_date, symbol, canonical) - # Round numerical values to 2 decimal places for cleaner display - numeric_columns = ["Open", "High", "Low", "Close", "Adj Close"] - for col in numeric_columns: + for col in ("Open", "High", "Low", "Close", "Adj Close"): if col in data.columns: - data[col] = data[col].round(2) + data[col] = data[col].map(price_text, na_action="ignore") csv_string = data.to_csv() diff --git a/tradingagents/dataflows/vendors/yahoo/snapshot.py b/tradingagents/dataflows/vendors/yahoo/snapshot.py index 3bec5ebc5..ab3c48bda 100644 --- a/tradingagents/dataflows/vendors/yahoo/snapshot.py +++ b/tradingagents/dataflows/vendors/yahoo/snapshot.py @@ -17,6 +17,7 @@ from stockstats import wrap from tradingagents.dataflows.errors import NoMarketDataError from tradingagents.dataflows.symbols import normalize_symbol +from tradingagents.dataflows.vendors.yahoo.common import price_text from tradingagents.dataflows.vendors.yahoo.ohlcv import load_ohlcv # A fixed, common indicator set so the snapshot is the same shape every run. @@ -59,7 +60,7 @@ def _fmt(value) -> str: if isinstance(value, (int,)): return str(value) if isinstance(value, float): - return f"{value:.2f}" + return price_text(value) return str(value)