aboutsummaryrefslogtreecommitdiff
path: root/components/valuation.py
diff options
context:
space:
mode:
authorTyler <tyler@tylerhoang.xyz>2026-03-30 18:54:56 -0700
committerTyler <tyler@tylerhoang.xyz>2026-03-30 18:54:56 -0700
commit712bbf675cfcb32535d8c494505e566efa347feb (patch)
tree539a874eab3c785e2a8ba9209939bdb3fb4ee978 /components/valuation.py
parentaa3b1a27118ef0efac056ad135a81181bfb15c8e (diff)
Fix EV/EBITDA using income statement EBITDA instead of info dict
yfinance's info["ebitda"] is a miscalculated TTM value for many tickers (e.g. DDOG shows $7.5M when the correct TTM EBITDA is $193.8M). Added get_ebitda_from_income_stmt() which reads directly from t.income_stmt, matching the annual and quarterly figures. Key Ratios and DCF EV/EBITDA valuation now both use this source, with FMP as the preferred override. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'components/valuation.py')
-rw-r--r--components/valuation.py19
1 files changed, 17 insertions, 2 deletions
diff --git a/components/valuation.py b/components/valuation.py
index b6e9d46..a168580 100644
--- a/components/valuation.py
+++ b/components/valuation.py
@@ -9,6 +9,7 @@ from services.data_service import (
get_recommendations_summary,
get_earnings_history,
get_next_earnings_date,
+ get_ebitda_from_income_stmt,
)
from services.fmp_service import (
get_key_ratios,
@@ -148,13 +149,26 @@ def _render_ratios(ticker: str):
val = info.get(yf_key)
return fmt(val) if val is not None else "—"
+ # Compute EV/EBITDA from income statement EBITDA — yfinance's info["ebitda"]
+ # is a known bad value for many tickers (miscalculated TTM aggregation).
+ def _ev_ebitda() -> str:
+ # Prefer FMP if available
+ fmp_val = (ratios or {}).get("enterpriseValueMultipleTTM") or (ratios or {}).get("evToEBITDATTM")
+ if fmp_val is not None:
+ return fmt_ratio(fmp_val)
+ ev = info.get("enterpriseValue")
+ ebitda = get_ebitda_from_income_stmt(ticker)
+ if ev and ebitda and ebitda > 0:
+ return fmt_ratio(ev / ebitda)
+ return "—"
+
rows = [
("Valuation", [
("P/E (TTM)", r("peRatioTTM", "trailingPE")),
("Forward P/E", fmt_ratio(info.get("forwardPE")) if info.get("forwardPE") is not None else "—"),
("P/S (TTM)", r("priceToSalesRatioTTM", "priceToSalesTrailing12Months")),
("P/B", r("priceToBookRatioTTM", "priceToBook")),
- ("EV/EBITDA", r("enterpriseValueMultipleTTM", "enterpriseToEbitda") if ratios.get("enterpriseValueMultipleTTM") is not None else r("evToEBITDATTM", "enterpriseToEbitda")),
+ ("EV/EBITDA", _ev_ebitda()),
("EV/Revenue", r("evToSalesTTM", "enterpriseToRevenue")),
]),
("Profitability", [
@@ -315,7 +329,8 @@ def _render_dcf(ticker: str):
st.divider()
st.markdown("**EV/EBITDA Valuation**")
- ebitda = info.get("ebitda")
+ # Use income statement EBITDA — info["ebitda"] is unreliable in yfinance
+ ebitda = get_ebitda_from_income_stmt(ticker) or info.get("ebitda")
total_debt = info.get("totalDebt") or 0.0
total_cash = info.get("totalCash") or 0.0
ev_ebitda_current = info.get("enterpriseToEbitda")