diff options
| author | Tyler <tyler@tylerhoang.xyz> | 2026-04-02 18:54:01 -0700 |
|---|---|---|
| committer | Tyler <tyler@tylerhoang.xyz> | 2026-04-02 18:54:01 -0700 |
| commit | ccfbce79a66b2d8aa136fddbed7c61c7436f2733 (patch) | |
| tree | ed1d0f883a904c1a677b013d8c6ed08bafc11d72 /services | |
| parent | ee9691828bc06334c58a58902615b1c0b654f39a (diff) | |
Improve DCF model accuracy
Diffstat (limited to 'services')
| -rw-r--r-- | services/data_service.py | 24 | ||||
| -rw-r--r-- | services/valuation_service.py | 15 |
2 files changed, 36 insertions, 3 deletions
diff --git a/services/data_service.py b/services/data_service.py index 67f2e7b..bfd1290 100644 --- a/services/data_service.py +++ b/services/data_service.py @@ -684,3 +684,27 @@ def get_free_cash_flow_series(ticker: str) -> pd.Series: return (op + capex).dropna() except KeyError: return pd.Series(dtype=float) + + +@st.cache_data(ttl=3600) +def get_free_cash_flow_ttm(ticker: str) -> float | None: + """Return trailing-twelve-month free cash flow from quarterly cash flow statements.""" + t = yf.Ticker(ticker.upper()) + cf_q = t.quarterly_cashflow + if cf_q is None or cf_q.empty: + return None + + if "Free Cash Flow" in cf_q.index: + vals = cf_q.loc["Free Cash Flow"].iloc[:4].dropna() + if len(vals) == 4: + return float(vals.sum()) + + try: + op = cf_q.loc["Operating Cash Flow"].iloc[:4].dropna() + capex = cf_q.loc["Capital Expenditure"].iloc[:4].dropna() + if len(op) == 4 and len(capex) == 4: + return float((op + capex).sum()) + except KeyError: + return None + + return None diff --git a/services/valuation_service.py b/services/valuation_service.py index 357c679..1230aa5 100644 --- a/services/valuation_service.py +++ b/services/valuation_service.py @@ -50,6 +50,7 @@ def run_dcf( terminal_growth: float = 0.03, projection_years: int = 5, growth_rate_override: float | None = None, + base_fcf_override: float | None = None, total_debt: float = 0.0, cash_and_equivalents: float = 0.0, preferred_equity: float = 0.0, @@ -78,7 +79,7 @@ def run_dcf( historical_growth = compute_historical_growth_rate(fcf_series) growth_rate = historical_growth if historical_growth is not None else 0.05 - base_fcf = float(historical[-1]) + base_fcf = float(base_fcf_override) if base_fcf_override is not None else float(historical[-1]) if base_fcf <= 0: return { "error": ( @@ -136,6 +137,8 @@ def run_ev_ebitda( ebitda: float, total_debt: float, total_cash: float, + preferred_equity: float, + minority_interest: float, shares_outstanding: float, target_multiple: float, ) -> dict: @@ -149,11 +152,13 @@ def run_ev_ebitda( implied_ev = ebitda * target_multiple net_debt = (total_debt or 0.0) - (total_cash or 0.0) - equity_value = implied_ev - net_debt + other_claims = (preferred_equity or 0.0) + (minority_interest or 0.0) + equity_value = implied_ev - net_debt - other_claims return { "implied_ev": implied_ev, "net_debt": net_debt, + "other_claims": other_claims, "equity_value": equity_value, "implied_price_per_share": equity_value / shares_outstanding, "target_multiple_used": target_multiple, @@ -164,6 +169,8 @@ def run_ev_revenue( revenue: float, total_debt: float, total_cash: float, + preferred_equity: float, + minority_interest: float, shares_outstanding: float, target_multiple: float, ) -> dict: @@ -177,11 +184,13 @@ def run_ev_revenue( implied_ev = revenue * target_multiple net_debt = (total_debt or 0.0) - (total_cash or 0.0) - equity_value = implied_ev - net_debt + other_claims = (preferred_equity or 0.0) + (minority_interest or 0.0) + equity_value = implied_ev - net_debt - other_claims return { "implied_ev": implied_ev, "net_debt": net_debt, + "other_claims": other_claims, "equity_value": equity_value, "implied_price_per_share": equity_value / shares_outstanding, "target_multiple_used": target_multiple, |
