fix(yahoo): print each fundamentals money figure with the currency it is in (#1456)

- price levels carry the quote currency (GBp for London), market cap and per-share figures its main unit (GBP), and income figures the reporting currency (TWD for TSMC's ADR)
This commit is contained in:
Yijia-Xiao
2026-10-03 21:27:43 +00:00
parent 5ca37b5088
commit 810b4cc48d
2 changed files with 70 additions and 13 deletions
+40
View File
@@ -37,3 +37,43 @@ def test_an_absent_percent_field_is_left_out():
out = _fundamentals({"longName": "JPMorgan Chase & Co.", "profitMargins": 0.35})
assert "Dividend Yield" not in out and "Debt to Equity" not in out
@pytest.mark.unit
def test_each_money_figure_carries_the_currency_it_is_in():
"""Yahoo quotes London in pence but states EPS and market cap in pounds, and
the income figures in the reporting currency (#1456)."""
out = _fundamentals({"longName": "Shell plc", "currency": "GBp", "financialCurrency": "USD",
"marketCap": 203440193536, "trailingEps": 3.42, "bookValue": 29.1,
"fiftyTwoWeekHigh": 3800.0, "fiftyDayAverage": 3500.5,
"totalRevenue": 296600993792, "netIncomeToCommon": 25970999296})
assert "Market Cap: 203440193536 GBP" in out
assert "EPS (TTM): 3.42 GBP" in out and "Book Value: 29.1 GBP" in out
assert "52 Week High: 3800.0 GBp" in out and "50 Day Average: 3500.5 GBp" in out
assert "Revenue (TTM): 296600993792 USD" in out and "Net Income: 25970999296 USD" in out
@pytest.mark.unit
def test_an_adr_quotes_in_dollars_and_reports_in_its_home_currency():
out = _fundamentals({"longName": "TSMC", "currency": "USD", "financialCurrency": "TWD",
"marketCap": 2365000000000, "trailingEps": 13.21, "totalRevenue": 4440492343296})
assert "Market Cap: 2365000000000 USD" in out and "EPS (TTM): 13.21 USD" in out
assert "Revenue (TTM): 4440492343296 TWD" in out
@pytest.mark.unit
def test_a_figure_with_no_stated_currency_is_printed_bare():
out = _fundamentals({"longName": "X", "marketCap": 1000, "totalRevenue": 50})
assert "Market Cap: 1000\n" in out + "\n" and "Revenue (TTM): 50" in out and "None" not in out
@pytest.mark.unit
def test_income_figures_without_a_stated_reporting_currency_are_printed_bare():
"""The quote currency is not evidence of the reporting currency (an ADR quotes
in USD and reports in TWD), so an unknown one is not guessed."""
out = _fundamentals({"longName": "X", "currency": "USD", "marketCap": 1000, "totalRevenue": 50})
assert "Market Cap: 1000 USD" in out and "Revenue (TTM): 50\n" in out + "\n"
+30 -13
View File
@@ -16,6 +16,9 @@ from tradingagents.dataflows.vendors.yahoo.common import (
yf_retry,
)
# Quote currencies Yahoo gives in a minor unit, and the main unit of each.
_MAIN_UNIT = {"GBp": "GBP", "GBX": "GBP", "ZAc": "ZAR", "ILA": "ILS"}
def get_fundamentals(
ticker: Annotated[str, "ticker symbol of the company"],
@@ -43,27 +46,41 @@ def get_fundamentals(
# 78.4 is 78.4%, a ratio of 0.78) but the margins and returns as fractions,
# so each carries its unit.
dividend_yield, debt_to_equity = info.get("dividendYield"), info.get("debtToEquity")
# Money figures come in three currencies (#1456): price levels in the quote
# currency, which can be a minor unit (London in pence, GBp); market cap and
# per-share figures in that currency's main unit (GBP), as Yahoo's own P/E
# and P/B are computed against them; income figures in the currency the
# company reports in (an ADR quotes in USD, reports in TWD), or bare when
# Yahoo does not say which.
quote = info.get("currency")
main = _MAIN_UNIT.get(quote, quote)
reported = info.get("financialCurrency")
def money(key, currency):
value = info.get(key)
return None if value is None else f"{value} {currency}" if currency else value
fields = [
("Name", info.get("longName")),
("Sector", info.get("sector")),
("Industry", info.get("industry")),
("Market Cap", info.get("marketCap")),
("Market Cap", money("marketCap", main)),
("PE Ratio (TTM)", info.get("trailingPE")),
("Forward PE", info.get("forwardPE")),
("PEG Ratio", info.get("pegRatio")),
("Price to Book", info.get("priceToBook")),
("EPS (TTM)", info.get("trailingEps")),
("Forward EPS", info.get("forwardEps")),
("EPS (TTM)", money("trailingEps", main)),
("Forward EPS", money("forwardEps", main)),
("Dividend Yield", None if dividend_yield is None else f"{dividend_yield}%"),
("Beta", info.get("beta")),
("52 Week High", info.get("fiftyTwoWeekHigh")),
("52 Week Low", info.get("fiftyTwoWeekLow")),
("50 Day Average", info.get("fiftyDayAverage")),
("200 Day Average", info.get("twoHundredDayAverage")),
("Revenue (TTM)", info.get("totalRevenue")),
("Gross Profit", info.get("grossProfits")),
("EBITDA", info.get("ebitda")),
("Net Income", info.get("netIncomeToCommon")),
("52 Week High", money("fiftyTwoWeekHigh", quote)),
("52 Week Low", money("fiftyTwoWeekLow", quote)),
("50 Day Average", money("fiftyDayAverage", quote)),
("200 Day Average", money("twoHundredDayAverage", quote)),
("Revenue (TTM)", money("totalRevenue", reported)),
("Gross Profit", money("grossProfits", reported)),
("EBITDA", money("ebitda", reported)),
("Net Income", money("netIncomeToCommon", reported)),
("Profit Margin", info.get("profitMargins")),
("Operating Margin", info.get("operatingMargins")),
("Return on Equity", info.get("returnOnEquity")),
@@ -71,8 +88,8 @@ def get_fundamentals(
("Debt to Equity", None if debt_to_equity is None
else f"{debt_to_equity}% ({debt_to_equity / 100:.2f}x)"),
("Current Ratio", info.get("currentRatio")),
("Book Value", info.get("bookValue")),
("Free Cash Flow", info.get("freeCashflow")),
("Book Value", money("bookValue", main)),
("Free Cash Flow", money("freeCashflow", reported)),
]
lines = [f"{label}: {v}" for label, v in fields if v is not None]