A DCF you can defend
Everyone can recite the five steps. This lesson builds the model and then interrogates it, which produces the three facts that make the recitation defensible — how much of the answer is one division, how wide the range gets when two inputs move by amounts nobody would argue with, and the cross-check that catches a growth rate implying an absurd exit multiple.
What you'll be able to do
- Build unlevered free cash flow from the linked statements rather than from a formula
- Assemble WACC from its parts and say what each part is a required return on
- Quantify how much of the valuation the terminal value owns, and what that implies about forecast precision
- Cross-check a perpetuity growth rate against the exit multiple it silently assumes
Before this: the-three-statements-as-one-system
"Walk me through a DCF" has a canonical five-step answer, and every candidate has it:
- Project free cash flow, typically five to ten years.
- Discount at the weighted average cost of capital.
- Compute a terminal value for everything after the forecast.
- Discount that back too, and sum to get enterprise value.
- Subtract net debt and divide by fully diluted shares to get an implied share price.
That answer is correct and it is not worth much, because everyone gives it. What distinguishes a good answer is what you say next: which step carries the weight, how wrong the output can be, and what you would do to check yourself. Those three things are properties of the model, so the way to know them is to run one.
The pieces, and what each one is a required return on
Unlevered free cash flow (FCFF). Start from EBIT, tax it, add back depreciation and amortisation, subtract capital expenditure and the increase in net working capital. Interest appears nowhere, deliberately — this is cash available to all capital providers, before deciding who gets what, which is why it gets discounted at a blended rate.
This is the cash flow statement rearranged, which is why the previous lesson comes first. If you can derive the cash flow statement from the income statement, free cash flow is not a formula to remember.
WACC. Two legs, and both are required returns rather than costs you write cheques for:
- Cost of equity, from CAPM: the risk-free rate plus beta times the equity risk premium. The risk-free rate should be a government yield of matched duration; beta should be relevered to the target capital structure.
- After-tax cost of debt: what the company borrows at today, times one minus the tax rate, because interest is deductible. The coupon on debt issued five years ago is not the cost of debt.
The weights are market-value weights at the target structure, not book values off the balance sheet.
Terminal value. Two accepted methods. Growth in perpetuity — the last forecast cash flow grown forever at a rate conventionally held to roughly 1%–3%, proxied off long-run GDP or inflation. Or an exit multiple, usually EV/EBITDA taken from comparable companies. The lesson's cell uses the first and then checks it against the second, which is the move most candidates never make.
The spoken version of the answer, in the five steps an interviewer is waiting to hear ticked off. Watch 04:18 for terminal value in particular, then come back — the model below exists to show you how much of the whole valuation those two minutes are responsible for.
Jump to the part you need
Running it
Every input below is an illustrative assumption chosen to make the arithmetic legible. None of them is a market quote, and in a real answer each is a number you would have to source and defend out loud.
// A DCF, built from stated assumptions, then interrogated.
//
// The point of running it rather than reciting it: a DCF is not mostly a
// forecast. It is mostly a terminal value, and the terminal value is one
// division by a small number. This cell prints how much of the answer that
// single division is responsible for, and what happens to the answer when the
// two inputs to it move by amounts nobody would argue with.
//
// Every input below is an ILLUSTRATIVE assumption chosen to make the
// arithmetic legible -- not a market quote. In a real answer each one is a
// number you have to source and defend out loud.
const REV0 = 1000; // last actual year revenue
const GROWTH = [0.08, 0.07, 0.06, 0.05, 0.04]; // stage 1, 5 years
const EBITDA_M = 0.25; // EBITDA margin, held flat
const DA_PCT = 0.06; // D&A as % of revenue
const CAPEX_PCT = 0.07; // capex as % of revenue
const NWC_PCT = 0.10; // NWC as % of INCREMENTAL revenue
const TAX = 0.25;
// WACC from its parts. Both legs are required-return numbers, not costs you pay.
const RF = 0.042; // risk-free: a government yield of matched duration
const ERP = 0.050; // equity risk premium
const BETA = 1.10; // relevered to the target capital structure
const KD = 0.055; // pre-tax cost of debt: what the company borrows at today
const WE = 0.80, WD = 0.20; // MARKET-value weights, at target structure
const KE = RF + BETA * ERP;
const WACC = WE * KE + WD * KD * (1 - TAX);
const NET_DEBT = 300;
const SHARES = 100; // diluted, treasury-stock method
const G = 0.025; // perpetuity growth: <= long-run nominal GDP
/** Stage 1 unlevered free cash flow. FCFF, so no interest anywhere. */
function forecast() {
const rows = [];
let rev = REV0;
for (let i = 0; i < GROWTH.length; i++) {
const prev = rev;
rev = rev * (1 + GROWTH[i]);
const ebitda = rev * EBITDA_M;
const da = rev * DA_PCT;
const ebit = ebitda - da;
const nopat = ebit * (1 - TAX);
const capex = rev * CAPEX_PCT;
const dNWC = (rev - prev) * NWC_PCT;
rows.push({
year: i + 1, rev, ebitda, da, ebit, nopat, capex, dNWC,
fcf: nopat + da - capex - dNWC,
});
}
return rows;
}
/** PV of stage 1 + PV of terminal value. `midYear` applies the convention. */
function value(rows, wacc, g, midYear = false) {
let pv1 = 0;
for (const r of rows) {
const t = midYear ? r.year - 0.5 : r.year;
pv1 += r.fcf / (1 + wacc) ** t;
}
const last = rows[rows.length - 1];
const tv = (last.fcf * (1 + g)) / (wacc - g);
const pvTv = tv / (1 + wacc) ** rows.length; // TV sits at the END of year 5
const ev = pv1 + pvTv;
return { pv1, tv, pvTv, ev, share: pvTv / ev,
equity: ev - NET_DEBT, price: (ev - NET_DEBT) / SHARES,
impliedExit: tv / last.ebitda };
}
const rows = forecast();
const base = value(rows, WACC, G);
console.log("cost of capital");
console.log(" cost of equity = " + (RF * 100).toFixed(1) + "% + " + BETA.toFixed(2) +
" x " + (ERP * 100).toFixed(1) + "% = " + (KE * 100).toFixed(2) + "%");
console.log(" after-tax debt = " + (KD * 100).toFixed(1) + "% x (1 - " + TAX +
") = " + (KD * (1 - TAX) * 100).toFixed(2) + "%");
console.log(" WACC = " + (WE * 100).toFixed(0) + "% x " + (KE * 100).toFixed(2) +
"% + " + (WD * 100).toFixed(0) + "% x " + (KD * (1 - TAX) * 100).toFixed(2) +
"% = " + (WACC * 100).toFixed(2) + "%");
console.log("");
console.log("stage 1 free cash flow (FCFF -- no interest, this is pre-financing)");
console.log(" yr revenue EBITDA EBIT NOPAT capex dNWC FCF discount PV");
for (const r of rows) {
const df = 1 / (1 + WACC) ** r.year;
console.log(
" " + r.year,
r.rev.toFixed(0).padStart(9), r.ebitda.toFixed(1).padStart(8),
r.ebit.toFixed(1).padStart(8), r.nopat.toFixed(1).padStart(8),
r.capex.toFixed(1).padStart(8), r.dNWC.toFixed(1).padStart(7),
r.fcf.toFixed(1).padStart(8), df.toFixed(4).padStart(10),
(r.fcf * df).toFixed(1).padStart(9)
);
}
console.log("");
console.log("bridging to a share price");
console.log(" PV of stage 1 FCF " + base.pv1.toFixed(1).padStart(9));
console.log(" terminal value at yr 5 " + base.tv.toFixed(1).padStart(9) +
" = FCF5 x (1 + g) / (WACC - g)");
console.log(" PV of terminal value " + base.pvTv.toFixed(1).padStart(9));
console.log(" enterprise value " + base.ev.toFixed(1).padStart(9));
console.log(" less net debt " + (-NET_DEBT).toFixed(1).padStart(9));
console.log(" equity value " + base.equity.toFixed(1).padStart(9));
console.log((" / " + SHARES + " diluted shares").padEnd(29) + base.price.toFixed(2).padStart(9) + " per share");
console.log("");
console.log(" terminal value is " + (base.share * 100).toFixed(1) +
"% of enterprise value.");
console.log(" Five years of forecasting -- margins, capex, working capital, the");
console.log(" whole model -- account for the other " + ((1 - base.share) * 100).toFixed(1) + "%.");
console.log("");
// ── Sensitivity: the two inputs to the division that owns 3/4 of the answer ──
const waccs = [WACC - 0.010, WACC - 0.005, WACC, WACC + 0.005, WACC + 0.010];
const gs = [0.015, 0.020, 0.025, 0.030, 0.035];
console.log("implied share price: WACC across, perpetuity growth down");
console.log(" " + waccs.map((w) => ((w * 100).toFixed(2) + "%").padStart(9)).join(""));
for (const g of gs) {
let line = " g " + (g * 100).toFixed(1) + "% ";
for (const w of waccs) line += value(rows, w, g).price.toFixed(2).padStart(9);
console.log(line);
}
const lo = value(rows, waccs[waccs.length - 1], gs[0]).price;
const hi = value(rows, waccs[0], gs[gs.length - 1]).price;
console.log("");
console.log(" Corner to corner: " + lo.toFixed(2) + " to " + hi.toFixed(2) +
" -- a " + (hi / lo).toFixed(1) + "x spread, from moving");
console.log(" WACC by 1 point and g by 1 point. Nobody in the room could tell you");
console.log(" which cell is right. This is why the output of a DCF is a RANGE, and");
console.log(" why a candidate who says \"$" + base.price.toFixed(2) +
"\" with a straight face has misunderstood");
console.log(" the instrument.");
console.log("");
// ── The cross-check almost nobody volunteers ──────────────────────────────
console.log("the cross-check: what exit multiple did your g quietly assume?");
console.log(" g = " + (G * 100).toFixed(1) + "% and WACC = " + (WACC * 100).toFixed(2) +
"% imply a terminal EV/EBITDA of " + base.impliedExit.toFixed(1) + "x");
console.log(" (TV of " + base.tv.toFixed(0) + " over year-5 EBITDA of " +
rows[rows.length - 1].ebitda.toFixed(1) + ")");
console.log("");
console.log(" g implied terminal EV/EBITDA");
for (const g of gs) {
const v = value(rows, WACC, g);
console.log(" " + (g * 100).toFixed(1) + "% " + v.impliedExit.toFixed(1).padStart(5) + "x");
}
console.log("");
console.log(" Run it the other way to sanity-check yourself: if comparable mature");
console.log(" companies trade at 9x EBITDA, then g must satisfy");
console.log(" FCF5 x (1+g)/(WACC-g) = 9 x EBITDA5.");
// Invert for g given a target exit multiple: solve TV = m * EBITDA5.
function gForMultiple(m) {
const last = rows[rows.length - 1];
const target = m * last.ebitda;
// FCF5(1+g)/(WACC-g) = target -> g (FCF5 + target) = target*WACC - FCF5
return (target * WACC - last.fcf) / (last.fcf + target);
}
for (const m of [8, 9, 10, 12]) {
const g = gForMultiple(m);
const v = value(rows, WACC, g);
console.log(" " + (m + "x").padStart(3) + " -> g = " + (g * 100).toFixed(2) + "%" +
" (check: implied multiple " + v.impliedExit.toFixed(1) + "x)" +
(g > 0.03 ? " <- outside the 1-3% convention" : ""));
}
console.log("");
// ── Mid-year convention ───────────────────────────────────────────────────
const mid = value(rows, WACC, G, true);
console.log("mid-year convention: cash arrives through the year, not on 31 Dec.");
console.log(" end-of-year stage 1: price " + base.price.toFixed(2));
console.log(" mid-year stage 1: price " + mid.price.toFixed(2) +
" (+" + ((mid.price / base.price - 1) * 100).toFixed(1) + "%)");
console.log(" Worth about " + ((mid.price / base.price - 1) * 100).toFixed(1) +
"% here, i.e. less than one column of the grid above.");
console.log(" Mention it to show you know it exists; do not spend your airtime on");
console.log(" a refinement an order of magnitude smaller than your error bars.");
# A DCF, built from stated assumptions, then interrogated.
#
# The point of running it rather than reciting it: a DCF is not mostly a
# forecast. It is mostly a terminal value, and the terminal value is one
# division by a small number. This cell prints how much of the answer that
# single division is responsible for, and what happens to the answer when the
# two inputs to it move by amounts nobody would argue with.
#
# Every input below is an ILLUSTRATIVE assumption chosen to make the
# arithmetic legible -- not a market quote. In a real answer each one is a
# number you have to source and defend out loud.
from decimal import Decimal, ROUND_HALF_UP
def fx(x, places=2):
"""JavaScript's `toFixed`, exactly. Python's f-strings round half to EVEN,
JavaScript rounds half AWAY FROM ZERO, and 4.125 (5.5% x 0.75) is an exact
tie -- so an f-string gives 4.12 where `(4.125).toFixed(2)` gives 4.13.
In finance code that is not a cosmetic difference, so make it explicit
rather than letting the language pick a convention for you."""
return str(Decimal(x).quantize(Decimal(1).scaleb(-places), rounding=ROUND_HALF_UP))
REV0 = 1000 # last actual year revenue
GROWTH = [0.08, 0.07, 0.06, 0.05, 0.04] # stage 1, 5 years
EBITDA_M = 0.25 # EBITDA margin, held flat
DA_PCT = 0.06 # D&A as % of revenue
CAPEX_PCT = 0.07 # capex as % of revenue
NWC_PCT = 0.10 # NWC as % of INCREMENTAL revenue
TAX = 0.25
# WACC from its parts. Both legs are required-return numbers, not costs you pay.
RF = 0.042 # risk-free: a government yield of matched duration
ERP = 0.050 # equity risk premium
BETA = 1.10 # relevered to the target capital structure
KD = 0.055 # pre-tax cost of debt: what the company borrows at today
WE, WD = 0.80, 0.20 # MARKET-value weights, at target structure
KE = RF + BETA * ERP
WACC = WE * KE + WD * KD * (1 - TAX)
NET_DEBT = 300
SHARES = 100 # diluted, treasury-stock method
G = 0.025 # perpetuity growth: <= long-run nominal GDP
def forecast():
"""Stage 1 unlevered free cash flow. FCFF, so no interest anywhere."""
rows = []
rev = REV0
# `enumerate` is the idiomatic index-and-value loop; the JavaScript twin
# has to index GROWTH by hand.
for i, g in enumerate(GROWTH):
prev = rev
rev = rev * (1 + g)
ebitda = rev * EBITDA_M
da = rev * DA_PCT
ebit = ebitda - da
nopat = ebit * (1 - TAX)
capex = rev * CAPEX_PCT
d_nwc = (rev - prev) * NWC_PCT
rows.append({
"year": i + 1, "rev": rev, "ebitda": ebitda, "da": da, "ebit": ebit,
"nopat": nopat, "capex": capex, "d_nwc": d_nwc,
"fcf": nopat + da - capex - d_nwc,
})
return rows
def value(rows, wacc, g, mid_year=False):
"""PV of stage 1 + PV of terminal value. `mid_year` applies the convention."""
pv1 = 0
for r in rows:
t = r["year"] - 0.5 if mid_year else r["year"]
pv1 += r["fcf"] / (1 + wacc) ** t
last = rows[-1]
tv = (last["fcf"] * (1 + g)) / (wacc - g)
pv_tv = tv / (1 + wacc) ** len(rows) # TV sits at the END of year 5
ev = pv1 + pv_tv
return {"pv1": pv1, "tv": tv, "pv_tv": pv_tv, "ev": ev, "share": pv_tv / ev,
"equity": ev - NET_DEBT, "price": (ev - NET_DEBT) / SHARES,
"implied_exit": tv / last["ebitda"]}
rows = forecast()
base = value(rows, WACC, G)
print("cost of capital")
print(" cost of equity = " + fx(RF * 100, 1) + "% + " + fx(BETA, 2) +
" x " + fx(ERP * 100, 1) + "% = " + fx(KE * 100, 2) + "%")
print(" after-tax debt = " + fx(KD * 100, 1) + "% x (1 - " + str(TAX) +
") = " + fx(KD * (1 - TAX) * 100, 2) + "%")
print(" WACC = " + fx(WE * 100, 0) + "% x " + fx(KE * 100, 2) +
"% + " + fx(WD * 100, 0) + "% x " + fx(KD * (1 - TAX) * 100, 2) +
"% = " + fx(WACC * 100, 2) + "%")
print("")
print("stage 1 free cash flow (FCFF -- no interest, this is pre-financing)")
print(" yr revenue EBITDA EBIT NOPAT capex dNWC FCF discount PV")
for r in rows:
df = 1 / (1 + WACC) ** r["year"]
print(
" " + str(r["year"]),
fx(r['rev'], 0).rjust(9), fx(r['ebitda'], 1).rjust(8),
fx(r['ebit'], 1).rjust(8), fx(r['nopat'], 1).rjust(8),
fx(r['capex'], 1).rjust(8), fx(r['d_nwc'], 1).rjust(7),
fx(r['fcf'], 1).rjust(8), fx(df, 4).rjust(10),
fx(r['fcf'] * df, 1).rjust(9),
)
print("")
print("bridging to a share price")
print(" PV of stage 1 FCF " + fx(base['pv1'], 1).rjust(9))
print(" terminal value at yr 5 " + fx(base['tv'], 1).rjust(9) +
" = FCF5 x (1 + g) / (WACC - g)")
print(" PV of terminal value " + fx(base['pv_tv'], 1).rjust(9))
print(" enterprise value " + fx(base['ev'], 1).rjust(9))
print(" less net debt " + fx(-NET_DEBT, 1).rjust(9))
print(" equity value " + fx(base['equity'], 1).rjust(9))
print((" / " + str(SHARES) + " diluted shares").ljust(29) + fx(base['price'], 2).rjust(9) + " per share")
print("")
print(" terminal value is " + fx(base['share'] * 100, 1) +
"% of enterprise value.")
print(" Five years of forecasting -- margins, capex, working capital, the")
print(" whole model -- account for the other " + fx((1 - base['share']) * 100, 1) + "%.")
print("")
# -- Sensitivity: the two inputs to the division that owns 3/4 of the answer --
waccs = [WACC - 0.010, WACC - 0.005, WACC, WACC + 0.005, WACC + 0.010]
gs = [0.015, 0.020, 0.025, 0.030, 0.035]
print("implied share price: WACC across, perpetuity growth down")
print(" " + "".join((fx(w * 100, 2) + "%").rjust(9) for w in waccs))
for g in gs:
line = " g " + fx(g * 100, 1) + "% "
for w in waccs:
line += fx(value(rows, w, g)['price'], 2).rjust(9)
print(line)
lo = value(rows, waccs[-1], gs[0])["price"]
hi = value(rows, waccs[0], gs[-1])["price"]
print("")
print(" Corner to corner: " + fx(lo, 2) + " to " + fx(hi, 2) +
" -- a " + fx(hi / lo, 1) + "x spread, from moving")
print(" WACC by 1 point and g by 1 point. Nobody in the room could tell you")
print(" which cell is right. This is why the output of a DCF is a RANGE, and")
print(' why a candidate who says "$' + fx(base['price'], 2) +
'" with a straight face has misunderstood')
print(" the instrument.")
print("")
# -- The cross-check almost nobody volunteers -------------------------------
print("the cross-check: what exit multiple did your g quietly assume?")
print(" g = " + fx(G * 100, 1) + "% and WACC = " + fx(WACC * 100, 2) +
"% imply a terminal EV/EBITDA of " + fx(base['implied_exit'], 1) + "x")
print(" (TV of " + fx(base['tv'], 0) + " over year-5 EBITDA of " +
fx(rows[-1]['ebitda'], 1) + ")")
print("")
print(" g implied terminal EV/EBITDA")
for g in gs:
v = value(rows, WACC, g)
print(" " + fx(g * 100, 1) + "% " + fx(v['implied_exit'], 1).rjust(5) + "x")
print("")
print(" Run it the other way to sanity-check yourself: if comparable mature")
print(" companies trade at 9x EBITDA, then g must satisfy")
print(" FCF5 x (1+g)/(WACC-g) = 9 x EBITDA5.")
# Invert for g given a target exit multiple: solve TV = m * EBITDA5.
def g_for_multiple(m):
last = rows[-1]
target = m * last["ebitda"]
# FCF5(1+g)/(WACC-g) = target -> g (FCF5 + target) = target*WACC - FCF5
return (target * WACC - last["fcf"]) / (last["fcf"] + target)
for m in [8, 9, 10, 12]:
g = g_for_multiple(m)
v = value(rows, WACC, g)
print(" " + f"{m}x".rjust(3) + " -> g = " + fx(g * 100, 2) + "%" +
" (check: implied multiple " + fx(v['implied_exit'], 1) + "x)" +
(" <- outside the 1-3% convention" if g > 0.03 else ""))
print("")
# -- Mid-year convention ----------------------------------------------------
mid = value(rows, WACC, G, True)
print("mid-year convention: cash arrives through the year, not on 31 Dec.")
print(" end-of-year stage 1: price " + fx(base['price'], 2))
print(" mid-year stage 1: price " + fx(mid['price'], 2) +
" (+" + fx((mid['price'] / base['price'] - 1) * 100, 1) + "%)")
print(" Worth about " + fx((mid['price'] / base['price'] - 1) * 100, 1) +
"% here, i.e. less than one column of the grid above.")
print(" Mention it to show you know it exists; do not spend your airtime on")
print(" a refinement an order of magnitude smaller than your error bars.")
cost of capital
cost of equity = 4.2% + 1.10 x 5.0% = 9.70%
after-tax debt = 5.5% x (1 - 0.25) = 4.13%
WACC = 80% x 9.70% + 20% x 4.13% = 8.59%
stage 1 free cash flow (FCFF -- no interest, this is pre-financing)
yr revenue EBITDA EBIT NOPAT capex dNWC FCF discount PV
1 1080 270.0 205.2 153.9 75.6 8.0 135.1 0.9209 124.4
2 1156 288.9 219.6 164.7 80.9 7.6 145.6 0.8481 123.5
3 1225 306.2 232.7 174.6 85.7 6.9 155.4 0.7811 121.4
4 1286 321.5 244.4 183.3 90.0 6.1 164.3 0.7193 118.2
5 1338 334.4 254.1 190.6 93.6 5.1 172.1 0.6624 114.0
bridging to a share price
PV of stage 1 FCF 601.4
terminal value at yr 5 2898.8 = FCF5 x (1 + g) / (WACC - g)
PV of terminal value 1920.3
enterprise value 2521.7
less net debt -300.0
equity value 2221.7
/ 100 diluted shares 22.22 per share
terminal value is 76.2% of enterprise value.
Five years of forecasting -- margins, capex, working capital, the
whole model -- account for the other 23.8%.
implied share price: WACC across, perpetuity growth down
7.59% 8.09% 8.59% 9.09% 9.59%
g 1.5% 23.10 21.08 19.35 17.84 16.52
g 2.0% 24.99 22.65 20.67 18.97 17.50
g 2.5% 27.25 24.51 22.22 20.28 18.61
g 3.0% 30.00 26.73 24.04 21.79 19.89
g 3.5% 33.43 29.43 26.22 23.58 21.38
Corner to corner: 16.52 to 33.43 -- a 2.0x spread, from moving
WACC by 1 point and g by 1 point. Nobody in the room could tell you
which cell is right. This is why the output of a DCF is a RANGE, and
why a candidate who says "$22.22" with a straight face has misunderstood
the instrument.
the cross-check: what exit multiple did your g quietly assume?
g = 2.5% and WACC = 8.59% imply a terminal EV/EBITDA of 8.7x
(TV of 2899 over year-5 EBITDA of 334.4)
g implied terminal EV/EBITDA
1.5% 7.4x
2.0% 8.0x
2.5% 8.7x
3.0% 9.5x
3.5% 10.5x
Run it the other way to sanity-check yourself: if comparable mature
companies trade at 9x EBITDA, then g must satisfy
FCF5 x (1+g)/(WACC-g) = 9 x EBITDA5.
8x -> g = 2.02% (check: implied multiple 8.0x)
9x -> g = 2.71% (check: implied multiple 9.0x)
10x -> g = 3.27% (check: implied multiple 10.0x) <- outside the 1-3% convention
12x -> g = 4.12% (check: implied multiple 12.0x) <- outside the 1-3% convention
mid-year convention: cash arrives through the year, not on 31 Dec.
end-of-year stage 1: price 22.22
mid-year stage 1: price 22.47 (+1.1%)
Worth about 1.1% here, i.e. less than one column of the grid above.
Mention it to show you know it exists; do not spend your airtime on
a refinement an order of magnitude smaller than your error bars.
Three bars and one of them is most of the chart. The terminal value contributes $1,920.3M of a $2,521.7M enterprise value — 76.2% — while five years of explicitly modelled cash flow contributes the other 23.8%. The net-debt bar is the only step that is arithmetic rather than judgement. Look at the two heights before you argue about year-3 revenue growth.
The three things the output tells you that the five steps do not
Terminal value is 76.2% of the answer
Five years of forecasting — every margin, every capex assumption, the working-capital build, all of it — accounts for 23.8% of enterprise value. The remaining three quarters is one line: last year's cash flow, grown once, divided by WACC − g.
This reorders your priorities completely. Arguing about whether year-3 revenue growth is 6% or 7% moves the answer by a rounding error. Arguing about g moves it by tens of percent. And it explains why interviewers press on the terminal value and skip the forecast: they are pressing on the part that matters.
Two defensible inputs produce a 2.0x range
The grid moves WACC by ±1 point and g from 1.5% to 3.5% — every cell of which someone could argue for in a room. Corner to corner the implied share price runs from $16.52 to $33.43, a 2.0x spread.
That is the honest output of a DCF: a range with a shape, not a price. A candidate who answers "the DCF says $22.22" has told the interviewer they do not know how wide their own error bars are. The answer that scores is "$18 to $27 depending mostly on the terminal assumption, and here is which input I am least confident in".
Your growth rate already picked an exit multiple
This is the cross-check most candidates never volunteer, and it is the fastest way to catch yourself.
A perpetuity growth rate implies a terminal EV/EBITDA multiple, whether or not you compute it. Here, g = 2.5% at a WACC of 8.59% implies a terminal multiple of 8.7x — the terminal value of 2,899 over year-5 EBITDA of 334.4. If comparable mature companies in the industry trade at 6x, your "conservative" 2.5% growth rate has quietly assumed a 45% re-rating, and the model is not conservative at all.
The cell also runs the inversion: given a target exit multiple, solve for the g that produces it, then feed that g back through the valuation and print the resulting multiple. The round trip matching is the proof the inversion is right. It shows 8x implying g = 2.02%, 9x implying 2.71%, and 12x requiring 4.12% — well outside the 1%–3% convention the method is normally held to.
The refinement not worth your airtime
The mid-year convention discounts cash flow as if it arrives through the year rather than all on 31 December — year 1 at t = 0.5 instead of t = 1. It is more realistic, and the model shows it is worth +1.1% here: $22.22 becomes $22.47.
That is less than one column of the sensitivity grid. Knowing it exists is worth a sentence; spending two minutes on it while the terminal value sits unexamined is a misallocation the interviewer will notice. The general rule holds beyond this specific refinement: do not spend airtime on precision an order of magnitude smaller than your error bars.
What to change and re-run
- Set
G = 0.01and read the implied multiple. A "conservative" growth rate can imply a terminal multiple so low that it is its own kind of unreasonable. Conservatism has two edges. - Set
BETA = 1.6. Watch WACC rise, the denominatorWACC − gwiden, and the terminal value's share of enterprise value fall. Riskier companies have valuations less dominated by their terminal value, which is counterintuitive until you see it happen. - Set
CAPEX_PCT = DA_PCT. Capital spending equal to depreciation is the steady-state assumption the perpetuity formula implicitly wants; the forecast becomes internally consistent with its own terminal value, and free cash flow rises noticeably. Being able to say why those two should converge in the terminal year is a genuinely senior observation.
The next lesson builds the other model that gets asked about constantly, and it has the same structure of problem: a widely memorised rule of thumb that turns out to be a special case of something more general, and that breaks in a specific and predictable way.