export type DcfInputs = { baseFcf: number; /** enterprise_value - equity_value; holds net_debt + preferred + minority interest */ equityBridge: number; sharesOutstanding: number; }; export type DcfParams = { wacc: number; terminalGrowth: number; projectionYears: number; growthRate: number; }; export type DcfComputed = { intrinsicValuePerShare: number; enterpriseValue: number; equityValue: number; fcfPvSum: number; terminalValuePv: number; }; export type AdvancedDcfInputs = { baseRevenue: number; revenueGrowth: number[]; ebitdaMargin: number[]; dnaPctRevenue: number[]; capexPctRevenue: number[]; nwcPctDeltaRev: number[]; taxRate: number[]; }; export type AdvancedDcfParams = { wacc: number; terminalGrowth: number; projectionYears: number; sharesOutstanding: number; equityBridge: number; }; export function computeDcf( inputs: DcfInputs, params: DcfParams ): DcfComputed | { error: string } { const { baseFcf, equityBridge, sharesOutstanding } = inputs; const { wacc, terminalGrowth, projectionYears, growthRate } = params; if (wacc <= 0) return { error: "WACC must be greater than 0%." }; if (terminalGrowth >= wacc) return { error: "Terminal growth must be lower than WACC." }; if (baseFcf <= 0) return { error: "DCF not meaningful with zero or negative base FCF." }; const projected = Array.from( { length: projectionYears }, (_, i) => baseFcf * (1 + growthRate) ** (i + 1) ); const fcfPvSum = projected.reduce( (sum, fcf, i) => sum + fcf / (1 + wacc) ** (i + 0.5), 0 ); const terminalFcf = projected[projected.length - 1] * (1 + terminalGrowth); const terminalValue = terminalFcf / (wacc - terminalGrowth); const terminalValuePv = terminalValue / (1 + wacc) ** (projectionYears - 0.5); const enterpriseValue = fcfPvSum + terminalValuePv; const equityValue = enterpriseValue - equityBridge; const intrinsicValuePerShare = equityValue / sharesOutstanding; return { intrinsicValuePerShare, enterpriseValue, equityValue, fcfPvSum, terminalValuePv }; } export function computeAdvancedDcf( inputs: AdvancedDcfInputs, params: AdvancedDcfParams ): DcfComputed | { error: string } { const { baseRevenue, revenueGrowth, ebitdaMargin, dnaPctRevenue, capexPctRevenue, nwcPctDeltaRev, taxRate, } = inputs; const { wacc, terminalGrowth, projectionYears, sharesOutstanding, equityBridge } = params; if (wacc <= 0) return { error: "WACC must be greater than 0%." }; if (terminalGrowth >= wacc) return { error: "Terminal growth must be lower than WACC." }; if (baseRevenue <= 0) return { error: "Base revenue must be greater than 0." }; if (sharesOutstanding <= 0) return { error: "Shares outstanding must be greater than 0." }; const assumptions = [ revenueGrowth, ebitdaMargin, dnaPctRevenue, capexPctRevenue, nwcPctDeltaRev, taxRate, ]; if (assumptions.some((arr) => arr.length < projectionYears)) { return { error: "Projection assumptions must cover every projection year." }; } let revenue = baseRevenue; const projectedFcf: number[] = []; for (let year = 0; year < projectionYears; year++) { const priorRevenue = revenue; revenue = priorRevenue * (1 + revenueGrowth[year]); const ebitda = revenue * ebitdaMargin[year]; const dna = revenue * dnaPctRevenue[year]; const ebit = ebitda - dna; const nopat = ebit * (1 - taxRate[year]); const capex = revenue * capexPctRevenue[year]; const deltaRev = year === 0 ? 0 : revenue - priorRevenue; const nwcChange = deltaRev * nwcPctDeltaRev[year]; projectedFcf.push(nopat + dna - capex - nwcChange); } const fcfPvSum = projectedFcf.reduce( (sum, fcf, i) => sum + fcf / (1 + wacc) ** (i + 0.5), 0 ); const terminalFcf = projectedFcf[projectedFcf.length - 1] * (1 + terminalGrowth); const terminalValue = terminalFcf / (wacc - terminalGrowth); const terminalValuePv = terminalValue / (1 + wacc) ** (projectionYears - 0.5); const enterpriseValue = fcfPvSum + terminalValuePv; const equityValue = enterpriseValue - equityBridge; const intrinsicValuePerShare = equityValue / sharesOutstanding; return { intrinsicValuePerShare, enterpriseValue, equityValue, fcfPvSum, terminalValuePv }; } export type SensitivityGrid = { wacc: number[]; terminalGrowth: number[]; impliedPrices: (number | null)[][]; }; export function computeSensitivityGrid( baseWacc: number, baseTerminalGrowth: number, priceFor: (wacc: number, terminalGrowth: number) => number | null ): SensitivityGrid { const waccValues = [ Math.max(0.03, baseWacc - 0.02), Math.max(0.03, baseWacc - 0.01), baseWacc, baseWacc + 0.01, baseWacc + 0.02, ]; const terminalGrowthValues = [ Math.max(0, baseTerminalGrowth - 0.015), Math.max(0, baseTerminalGrowth - 0.01), baseTerminalGrowth, baseTerminalGrowth + 0.01, baseTerminalGrowth + 0.015, ]; const impliedPrices = waccValues.map((w) => terminalGrowthValues.map((g) => { if (g >= w) return null; try { return priceFor(w, g); } catch { return null; } }) ); return { wacc: waccValues, terminalGrowth: terminalGrowthValues, impliedPrices }; }