aboutsummaryrefslogtreecommitdiff
path: root/components/valuation.py
diff options
context:
space:
mode:
authorOpenclaw <openclaw@mail.tylerhoang.xyz>2026-03-29 01:12:24 -0700
committerOpenclaw <openclaw@mail.tylerhoang.xyz>2026-03-29 01:12:24 -0700
commit547997cbd069e9b958b12a8da38b3a4a257e29e5 (patch)
treedbae519a7c6c8f2d803e58e9a77f9a9db73da969 /components/valuation.py
parentad6b0b59c2a4f557d6d9d7fe9810c2ba7627580d (diff)
Fix valuation methodology and documentation
Diffstat (limited to 'components/valuation.py')
-rw-r--r--components/valuation.py33
1 files changed, 30 insertions, 3 deletions
diff --git a/components/valuation.py b/components/valuation.py
index 62ee1e3..82e0f0d 100644
--- a/components/valuation.py
+++ b/components/valuation.py
@@ -61,7 +61,7 @@ def _render_ratios(ticker: str):
rows = [
("Valuation", [
("P/E (TTM)", r("peRatioTTM", "trailingPE")),
- ("Forward P/E", r("priceEarningsRatioTTM", "forwardPE")),
+ ("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")),
@@ -99,6 +99,15 @@ def _render_dcf(ticker: str):
info = get_company_info(ticker)
shares = info.get("sharesOutstanding") or info.get("floatShares")
current_price = info.get("currentPrice") or info.get("regularMarketPrice")
+ total_debt = info.get("totalDebt") or 0.0
+ cash_and_equivalents = (
+ info.get("totalCash")
+ or info.get("cash")
+ or info.get("cashAndCashEquivalents")
+ or 0.0
+ )
+ preferred_equity = info.get("preferredStock") or 0.0
+ minority_interest = info.get("minorityInterest") or 0.0
if not shares:
st.info("Shares outstanding not available — DCF cannot be computed.")
@@ -143,22 +152,40 @@ def _render_dcf(ticker: str):
terminal_growth=terminal_growth,
projection_years=projection_years,
growth_rate_override=fcf_growth_pct / 100,
+ total_debt=total_debt,
+ cash_and_equivalents=cash_and_equivalents,
+ preferred_equity=preferred_equity,
+ minority_interest=minority_interest,
)
if not result:
st.warning("Insufficient data to run DCF model.")
return
+ if result.get("error"):
+ st.warning(result["error"])
+ return
iv = result["intrinsic_value_per_share"]
m1, m2, m3, m4 = st.columns(4)
- m1.metric("Intrinsic Value / Share", fmt_currency(iv))
+ m1.metric("Equity Value / Share", fmt_currency(iv))
if current_price:
upside = (iv - current_price) / current_price
m2.metric("Current Price", fmt_currency(current_price))
m3.metric("Upside / Downside", f"{upside * 100:+.1f}%", delta=f"{upside * 100:+.1f}%")
m4.metric("FCF Growth Used", f"{result['growth_rate_used'] * 100:.1f}%")
+ st.caption(
+ "DCF is modeled on firm-level free cash flow, so enterprise value is bridged to equity value "
+ "using cash and debt before calculating per-share value."
+ )
+
+ bridge1, bridge2, bridge3, bridge4 = st.columns(4)
+ bridge1.metric("Enterprise Value", fmt_large(result["enterprise_value"]))
+ bridge2.metric("Net Debt", fmt_large(result["net_debt"]))
+ bridge3.metric("Equity Value", fmt_large(result["equity_value"]))
+ bridge4.metric("Terminal Value PV", fmt_large(result["terminal_value_pv"]))
+
st.write("")
years = [f"Year {y}" for y in result["years"]]
@@ -173,7 +200,7 @@ def _render_dcf(ticker: str):
textposition="outside",
))
fig.update_layout(
- title="PV of Projected FCFs + Terminal Value (Billions)",
+ title="Enterprise Value Build: PV of Forecast FCFs + Terminal Value (Billions)",
yaxis_title="USD (Billions)",
plot_bgcolor="rgba(0,0,0,0)",
paper_bgcolor="rgba(0,0,0,0)",