1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
"""DCF valuation engine — Gordon Growth Model."""
import numpy as np
import pandas as pd
def run_dcf(
fcf_series: pd.Series,
shares_outstanding: float,
wacc: float = 0.10,
terminal_growth: float = 0.03,
projection_years: int = 5,
) -> dict:
"""
Run a DCF model and return per-year breakdown plus intrinsic value per share.
Args:
fcf_series: Annual FCF values, most recent first (yfinance order).
shares_outstanding: Diluted shares outstanding.
wacc: Weighted average cost of capital (decimal, e.g. 0.10).
terminal_growth: Perpetuity growth rate (decimal, e.g. 0.03).
projection_years: Number of years to project FCFs.
Returns:
dict with keys:
intrinsic_value_per_share, total_pv, terminal_value_pv,
fcf_pv_sum, years, projected_fcfs, discounted_fcfs,
growth_rate_used
"""
if fcf_series.empty or shares_outstanding <= 0:
return {}
# Use last N years of FCF (sorted oldest → newest)
historical = fcf_series.sort_index().dropna().values
if len(historical) < 2:
return {}
# Compute average YoY growth rate from historical FCF
growth_rates = []
for i in range(1, len(historical)):
if historical[i - 1] != 0:
g = (historical[i] - historical[i - 1]) / abs(historical[i - 1])
growth_rates.append(g)
# Cap growth rate to reasonable bounds [-0.5, 0.5]
raw_growth = float(np.median(growth_rates)) if growth_rates else 0.05
growth_rate = max(-0.50, min(0.50, raw_growth))
base_fcf = float(historical[-1]) # most recent FCF
# Project FCFs
projected_fcfs = []
for year in range(1, projection_years + 1):
fcf = base_fcf * ((1 + growth_rate) ** year)
projected_fcfs.append(fcf)
# Discount projected FCFs
discounted_fcfs = []
for i, fcf in enumerate(projected_fcfs, start=1):
pv = fcf / ((1 + wacc) ** i)
discounted_fcfs.append(pv)
fcf_pv_sum = sum(discounted_fcfs)
# Terminal value (Gordon Growth Model)
terminal_fcf = projected_fcfs[-1] * (1 + terminal_growth)
terminal_value = terminal_fcf / (wacc - terminal_growth)
terminal_value_pv = terminal_value / ((1 + wacc) ** projection_years)
total_pv = fcf_pv_sum + terminal_value_pv
intrinsic_value_per_share = total_pv / shares_outstanding
return {
"intrinsic_value_per_share": intrinsic_value_per_share,
"total_pv": total_pv,
"terminal_value_pv": terminal_value_pv,
"fcf_pv_sum": fcf_pv_sum,
"years": list(range(1, projection_years + 1)),
"projected_fcfs": projected_fcfs,
"discounted_fcfs": discounted_fcfs,
"growth_rate_used": growth_rate,
"base_fcf": base_fcf,
}
|