DCF Model Builder
Overview
This skill creates institutional-quality DCF models for equity valuation following investment banking standards. Each analysis produces a detailed Excel model (with sensitivity analysis included at the bottom of the DCF sheet).
Tools
- Default to using all of the information provided by the user and MCP servers available for data sourcing.
Critical Constraints - Read These First
These constraints apply throughout all DCF model building. Review before starting:
Environment: Office JS vs Python/openpyxl:
- If running inside Excel (Office Add-in / Office JS environment): Use Office JS directly — do NOT use Python/openpyxl. Write formulas via
range.formulas = [["=D19*(1+$B$8)"]]. No separate recalc step needed; Excel calculates natively. Userange.format.*for styling. The same formulas-over-hardcodes rule applies: set.formulas, never.valuesfor derived cells. - If generating a standalone .xlsx file (no live Excel session): Use Python/openpyxl as described below, then run
recalc.pybefore delivery. - The rest of this skill uses openpyxl examples — translate to Office JS API calls when in that environment, but all principles (formula strings, cell comments, section checkpoints, sensitivity table loops) apply identically.
⚠️ Office JS merged cell pitfall: When building section headers with merged cells, do NOT call .merge() then set .values on the merged range — Office JS still reports the range's original dimensions and will throw InvalidArgument: The number of rows or columns in the input array doesn't match the size or dimensions of the range. Instead, write the value to the top-left cell alone, then merge and format the full range:
// WRONG — throws InvalidArgument:
const hdr = ws.getRange("A7:H7");
hdr.merge();
hdr.values = [["MARKET DATA & KEY INPUTS"]]; // 1×1 array vs 1×8 range → fails
// CORRECT — value first on single cell, then merge + format the range:
ws.getRange("A7").values = [["MARKET DATA & KEY INPUTS"]];
const hdr = ws.getRange("A7:H7");
hdr.merge();
hdr.format.fill.color = "#1F4E79";
hdr.format.font.bold = true;
hdr.format.font.color = "#FFFFFF";
This applies to every merged section header in the DCF (market data, scenario blocks, cash flow projection, terminal value, valuation summary, sensitivity tables).
Formulas Over Hardcodes (NON-NEGOTIABLE):
- Every projection, margin, discount factor, PV, and sensitivity cell MUST be a live Excel formula — never a value computed in Python and written as a number
- When using openpyxl:
ws["D20"] = "=D19*(1+$B$8)"is correct;ws["D20"] = calculated_revenueis WRONG - The only hardcoded numbers permitted are: (1) raw historical inputs, (2) assumption drivers (growth rates, WACC inputs, terminal g), (3) current market data (share price, debt balance)
- If you catch yourself computing something in Python and writing the result — STOP. The model must flex when the user changes an assumption.
Verify Step-by-Step With the User (DO NOT build end-to-end):
- After data retrieval → show the user the raw inputs block (revenue, margins, shares, net debt) and confirm before projecting
- After revenue projections → show the projected top line and growth rates, confirm before building margin build
- After FCF build → show the full FCF schedule, confirm logic before computing WACC
- After WACC → show the calculation and inputs, confirm before discounting
- After terminal value + PV → show the equity bridge (EV → equity value → per share), confirm before sensitivity tables
- Catch errors at each stage — a wrong margin assumption discovered after sensitivity tables are built means rebuilding everything downstream
Sensitivity Tables:
- Use an ODD number of rows and columns (standard: 5×5, sometimes 7×7) — this guarantees a true center cell
- Center cell = base case. Build the axis values so the middle row header and middle column header exactly equal the model's actual assumptions (e.g., if base WACC = 9.0%, the middle row is 9.0%; if terminal g = 3.0%, the middle column is 3.0%). The center cell's output must therefore equal the model's actual implied share price — this is the sanity check that the table is built correctly.
- Highlight the center cell with the medium-blue fill (
#BDD7EE) + bold font so it's immediately visible which cell is the base case. - Populate ALL cells (typically 3 tables × 25 cells = 75) with full DCF recalculation formulas
- Use openpyxl loops (or Office JS loops) to write formulas programmatically
- NO placeholder text, NO linear approximations, NO manual steps required
- Each cell must recalculate full DCF for that assumption combination
Cell Comments:
- Add cell comments AS each hardcoded value is created
- Format: "Source: [System/Document], [Date], [Reference], [URL if applicable]"
- Every blue input must have a comment before moving to next section
- Do not defer to end or write "TODO: add source"
Model Layout Planning:
- Define ALL section row positions BEFORE writing any formulas
- Write ALL headers and labels first
- Write ALL section dividers and blank rows second
- THEN write formulas using the locked row positions
- Test formulas immediately after creation
Formula Recalculation:
- Run
python recalc.py model.xlsx 30before delivery - Fix ALL errors until status is "success"
- Zero formula errors required (#REF!, #DIV/0!, #VALUE!, etc.)
Scenario Blocks:
- Create separate blocks for Bear/Base/Bull cases
- Show assumptions horizontally across projection years within each block
- Use IF formulas:
=IF($B$6=1,[Bear cell],IF($B$6=2,[Base cell],[Bull cell])) - Verify formulas reference correct scenario block cells
DCF Process Workflow
Step 1: Data Retrieval and Validation
Fetch data from MCP servers, user provided data, and the web.
Data Sources Priority:
- MCP Servers (if configured) - Structured financial data from providers like Daloopa
- User-Provided Data - Historical financials from their research
- Web Search/Fetch - Current prices, beta, debt and cash when needed
Validation Checklist:
- Verify net debt vs net cash (critical for valuation)
- Confirm diluted shares outstanding (check for recent buybacks/issuances)
- Validate historical margins are consistent with business model
- Cross-check revenue growth rates with industry benchmarks
- Verify tax rate is reasonable (typically 21-28%)
Step 2: Historical Analysis (3-5 years)
Analyze and document:
- Revenue growth trends: Calculate CAGR, identify drivers
- Margin progression: Track gross margin, EBIT margin, FCF margin
- Capital intensity: D&A and CapEx as % of revenue
- Working capital efficiency: NWC changes as % of revenue growth
- Return metrics: ROIC, ROE trends
Create summary tables showing:
Historical Metrics (LTM):
Revenue: $X million
Revenue growth: X% CAGR
Gross margin: X%
EBIT margin: X%
D&A % of revenue: X%
CapEx % of revenue: X%
FCF margin: X%
Step 3: Build Revenue Projections
Methodology:
- Start with latest actual revenue (LTM or most recent fiscal year)
- Apply growth rates for each projection year
- Show both dollar amounts AND calculated growth %
Growth Rate Framework:
- Year 1-2: Higher growth reflecting near-term visibility
- Year 3-4: Gradual moderation toward industry average
- Year 5+: Approaching terminal growth rate
Formula structure:
- Revenue(Y