mirror of
https://github.com/TauricResearch/TradingAgents.git
synced 2026-11-03 16:12:44 +02:00
fix(yahoo): print prices with their significant digits (#1567)
- the price table and the verified snapshot keep six significant digits and at least two decimals, so a sub-cent close is no longer 0.00 and a currency pair keeps its pips
This commit is contained in:
@@ -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
|
||||
|
||||
+16
-1
@@ -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.
|
||||
|
||||
|
||||
+3
-5
@@ -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()
|
||||
|
||||
|
||||
+2
-1
@@ -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)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user