Free Cash Flow Sensitivity Analysis Tool
Analyze the impact of key assumptions on your projected Free Cash Flow.
1. Enter Initial Data & Assumptions
Initial Financials (Current Year - Year 0)
Projection & Base Case Assumptions
Sensitivity Variables
Define ranges for sensitivity analysis. The tool will calculate total FCF for each step within the range, holding other variables constant at their base case values.
2. Free Cash Flow Sensitivity Analysis Results (Total FCF over Projection Period)
Calculate the model to see Sensitivity Analysis Results.
3. Detailed FCF Projections (Base Case)
Calculate the model to see Detailed Projections.
Please fix input errors to see results.
'; detailedProjectionsOutputDiv.innerHTML = 'Please fix input errors to see results.
'; window.fcfAnalysisResults = null; return; } // Generate scenarios const sensitivityScenarios = {}; // Scenario 1: Revenue Growth Rate Sensitivity sensitivityScenarios['Revenue Growth Rate'] = []; for (let rate = sensRevenueGrowthMin; rate <= sensRevenueGrowthMax; rate += sensRevenueGrowthStep) { const scenarioAssumptions = { ...baseAssumptions, revenueGrowthRate: rate }; const projectedFCFData = projectFCF(initialValues, scenarioAssumptions, projectionYears); const totalFCF = projectedFCFData.slice(1).reduce((sum, yearData) => sum + yearData.fcf, 0); // Sum from Year 1 sensitivityScenarios['Revenue Growth Rate'].push({ value: rate, totalFCF: totalFCF }); } // Scenario 2: COGS as % of Revenue Sensitivity sensitivityScenarios['COGS as % of Revenue'] = []; for (let cogsPct = sensCogsPctRevenueMin; cogsPct <= sensCogsPctRevenueMax; cogsPct += sensCogsPctRevenueStep) { const scenarioAssumptions = { ...baseAssumptions, cogsPctRevenue: cogsPct }; const projectedFCFData = projectFCF(initialValues, scenarioAssumptions, projectionYears); const totalFCF = projectedFCFData.slice(1).reduce((sum, yearData) => sum + yearData.fcf, 0); sensitivityScenarios['COGS as % of Revenue'].push({ value: cogsPct, totalFCF: totalFCF }); } // Scenario 3: CAPEX as % of Revenue Sensitivity sensitivityScenarios['CAPEX as % of Revenue'] = []; for (let capexPct = sensCapexPctRevenueMin; capexPct <= sensCapexPctRevenueMax; capexPct += sensCapexPctRevenueStep) { const scenarioAssumptions = { ...baseAssumptions, capexPctRevenue: capexPct }; const projectedFCFData = projectFCF(initialValues, scenarioAssumptions, projectionYears); const totalFCF = projectedFCFData.slice(1).reduce((sum, yearData) => sum + yearData.fcf, 0); sensitivityScenarios['CAPEX as % of Revenue'].push({ value: capexPct, totalFCF: totalFCF }); } // Calculate Base Case Detailed Projections const baseCaseDetailedProjections = projectFCF(initialValues, baseAssumptions, projectionYears); // Store results for PDF window.fcfAnalysisResults = { inputs: { ...initialValues, projectionYears }, baseAssumptions: baseAssumptions, sensitivityRanges: { sensRevenueGrowthMin, sensRevenueGrowthMax, sensRevenueGrowthStep, sensCogsPctRevenueMin, sensCogsPctRevenueMax, sensCogsPctRevenueStep, sensCapexPctRevenueMin, sensCapexPctRevenueMax, sensCapexPctRevenueStep }, sensitivityScenarios: sensitivityScenarios, baseCaseDetailedProjections: baseCaseDetailedProjections }; // Display results displaySensitivityResults(sensitivityScenarios); displayDetailedProjections(baseCaseDetailedProjections); openTab('results'); // Go to results tab after calculation showMessage('Free Cash Flow sensitivity analysis complete!', 'success'); } /** * Displays the sensitivity analysis results. * @param {object} scenarios The sensitivity analysis scenarios. */ function displaySensitivityResults(scenarios) { let tableHtml = ``;
for (const variable in scenarios) {
tableHtml += `
`;
}
tableHtml += `
`;
sensitivityResultsOutputDiv.innerHTML = tableHtml;
}
/**
* Displays the detailed FCF projections for the base case.
* @param {Array} data The detailed FCF projection data.
*/
function displayDetailedProjections(data) {
let tableHtml = `${variable} Sensitivity (Total FCF over Projection Period)
| ${variable} | Total Projected FCF |
|---|---|
| ${formattedValue} | ${formatCurrency(scenario.totalFCF)} |
| Line Item | `; for (let i = 0; i < data.length; i++) { tableHtml += `${data[i].year === 0 ? 'Current Year' : `Year ${data[i].year}`} | `; } tableHtml += `
|---|---|
| ${displayNames[item]} | `; data.forEach(yearData => { tableHtml += `${formatCurrency(yearData[item])} | `; }); tableHtml += `
Calculate the model to see Sensitivity Analysis Results.
'; detailedProjectionsOutputDiv.innerHTML = 'Calculate the model to see Detailed Projections.
'; // Reset to the first tab openTab('inputs'); hideMessage(); } /** * Downloads the Free Cash Flow analysis as a PDF. */ function downloadPDF() { if (!window.fcfAnalysisResults) { showMessage('Please perform the analysis first.', 'info'); return; } const { jsPDF } = window.jspdf; const pdf = new jsPDF('l', 'mm', 'a4'); // 'l' for landscape, 'mm', 'a4' size (better for wide tables) const BLACK_COLOR = [31, 41, 55]; // Tailwind gray-900 equivalent const BLUE_HEADER_COLOR = [29, 78, 216]; // Tailwind blue-700 equivalent let y = 15; // Y-coordinate for placing content // Title pdf.setFontSize(22); pdf.setFont('helvetica', 'bold'); pdf.setTextColor(BLACK_COLOR[0], BLACK_COLOR[1], BLACK_COLOR[2]); pdf.text('Free Cash Flow Sensitivity Analysis Report', 148, y, { align: 'center' }); // Centered for landscape y += 15; // --- 1. Input Parameters & Base Case Assumptions Section --- pdf.setFontSize(16); pdf.setFont('helvetica', 'bold'); pdf.text('1. Input Parameters & Base Case Assumptions', 15, y); y += 8; const inputs = window.fcfAnalysisResults.inputs; const baseAssumptions = window.fcfAnalysisResults.baseAssumptions; const inputData = [ ['Initial Revenue', formatCurrency(inputs.initialRevenue)], ['Initial COGS as % of Revenue', formatPercentage(inputs.initialCogsPctRevenue / 100)], ['Initial Operating Expenses as % of Revenue', formatPercentage(inputs.initialOpexPctRevenue / 100)], ['Initial Depreciation as % of Revenue', formatPercentage(inputs.initialDepreciationPctRevenue / 100)], ['Initial CAPEX as % of Revenue', formatPercentage(inputs.initialCapexPctRevenue / 100)], ['Initial NCWC as % of Revenue', formatPercentage(inputs.initialNcwcPctRevenue / 100)], ['Tax Rate', formatPercentage(inputs.taxRate / 100)], ['Projection Years', inputs.projectionYears], ['Base Revenue Growth Rate', formatPercentage(baseAssumptions.revenueGrowthRate / 100)], ['Base COGS as % of Revenue', formatPercentage(baseAssumptions.cogsPctRevenue / 100)], ['Base CAPEX as % of Revenue', formatPercentage(baseAssumptions.capexPctRevenue / 100)], ['Base NCWC as % of Revenue', formatPercentage(baseAssumptions.ncwcPctRevenue / 100)] ]; pdf.autoTable({ startY: y, head: [['Parameter', 'Value']], body: inputData, theme: 'grid', styles: { fontSize: 8, cellPadding: 2, overflow: 'linebreak', lineColor: 200, lineWidth: 0.1 }, headStyles: { fillColor: BLUE_HEADER_COLOR, textColor: 255, fontStyle: 'bold' }, margin: { left: 15, right: 15 }, columnStyles: { 0: { cellWidth: 100 }, 1: { cellWidth: 'auto' } } }); y = pdf.autoTable.previous.finalY + 15; // --- 2. Sensitivity Analysis Results Section --- const sensitivityScenarios = window.fcfAnalysisResults.sensitivityScenarios; for (const variable in sensitivityScenarios) { // Add new page if content won't fit if (y + 30 + (sensitivityScenarios[variable].length * 7) > pdf.internal.pageSize.height - 15) { pdf.addPage('l', 'a4'); // Add a new page in landscape y = 15; // Reset Y } pdf.setFontSize(16); pdf.setFont('helvetica', 'bold'); pdf.text(`2. ${variable} Sensitivity (Total FCF over Projection Period)`, 15, y); y += 8; const headers = [variable, 'Total Projected FCF']; const tableBody = sensitivityScenarios[variable].map(scenario => { const formattedValue = (variable === 'Revenue Growth Rate') ? formatPercentage(scenario.value / 100) : formatPercentage(scenario.value / 100); return [formattedValue, { content: formatCurrency(scenario.totalFCF), styles: { halign: 'right' } }]; }); pdf.autoTable({ startY: y, head: [headers], body: tableBody, theme: 'grid', styles: { fontSize: 8, cellPadding: 2, overflow: 'linebreak', lineColor: 200, lineWidth: 0.1 }, headStyles: { fillColor: BLUE_HEADER_COLOR, textColor: 255, fontStyle: 'bold' }, margin: { left: 15, right: 15 }, columnStyles: { 0: { cellWidth: 70 }, 1: { cellWidth: 'auto' } } }); y = pdf.autoTable.previous.finalY + 10; } // --- 3. Detailed Projections (Base Case) Section --- const detailedData = window.fcfAnalysisResults.baseCaseDetailedProjections; const addDetailedProjectionsTable = (data, startY) => { if (startY + 30 + (10 * 7) > pdf.internal.pageSize.height - 15) { // Estimate space needed pdf.addPage('l', 'a4'); startY = 15; } pdf.setFontSize(16); pdf.setFont('helvetica', 'bold'); pdf.text('3. Detailed FCF Projections (Base Case)', 15, startY); startY += 8; const headers = ['Line Item']; for (let i = 0; i < data.length; i++) { headers.push(data[i].year === 0 ? 'Current Year' : `Year ${data[i].year}`); } const tableBody = []; const lineItems = [ 'revenue', 'cogs', 'opex', 'ebit', 'nopat', 'depreciation', 'capex', 'ncwc', 'changeInNcwc', 'fcf' ]; const displayNames = { revenue: 'Revenue', cogs: 'Cost of Goods Sold (COGS)', opex: 'Operating Expenses', ebit: 'EBIT', nopat: 'NOPAT (Net Operating Profit After Tax)', depreciation: 'Depreciation', capex: 'Capital Expenditures (CAPEX)', ncwc: 'Non-Cash Working Capital (NCWC)', changeInNcwc: 'Change in NCWC', fcf: 'Free Cash Flow (FCF)' }; lineItems.forEach(item => { const row = [displayNames[item]]; data.forEach(yearData => { row.push({ content: formatCurrency(yearData[item]), styles: { halign: 'right' } }); }); tableBody.push(row); }); pdf.autoTable({ startY: startY, head: [headers], body: tableBody, theme: 'grid', styles: { fontSize: 7, cellPadding: 1, overflow: 'linebreak', lineColor: 200, lineWidth: 0.1 }, headStyles: { fillColor: BLUE_HEADER_COLOR, textColor: 255, fontStyle: 'bold' }, margin: { left: 15, right: 15 }, didParseCell: function (data) { if (data.column.index > 0) { // All data columns are right-aligned data.cell.styles.halign = 'right'; } } }); return pdf.autoTable.previous.finalY + 15; }; y = addDetailedProjectionsTable(detailedData, y); pdf.save('Free_Cash_Flow_Sensitivity_Report.pdf'); } /** * Initializes DOM element references once the document is fully loaded. */ document.addEventListener('DOMContentLoaded', () => { // Initial Financials Inputs initialRevenueInput = document.getElementById('initialRevenue'); initialCogsPctRevenueInput = document.getElementById('initialCogsPctRevenue'); initialOpexPctRevenueInput = document.getElementById('initialOpexPctRevenue'); initialDepreciationPctRevenueInput = document.getElementById('initialDepreciationPctRevenue'); initialCapexPctRevenueInput = document.getElementById('initialCapexPctRevenue'); initialNcwcPctRevenueInput = document.getElementById('initialNcwcPctRevenue'); taxRateInput = document.getElementById('taxRate'); // Projection & Base Case Assumptions Inputs projectionYearsInput = document.getElementById('projectionYears'); baseRevenueGrowthRateInput = document.getElementById('baseRevenueGrowthRate'); baseCogsPctRevenueInput = document.getElementById('baseCogsPctRevenue'); baseCapexPctRevenueInput = document.getElementById('baseCapexPctRevenue'); baseNcwcPctRevenueInput = document.getElementById('baseNcwcPctRevenue'); // Sensitivity Variables Inputs sensRevenueGrowthMinInput = document.getElementById('sensRevenueGrowthMin'); sensRevenueGrowthMaxInput = document.getElementById('sensRevenueGrowthMax'); sensRevenueGrowthStepInput = document.getElementById('sensRevenueGrowthStep'); sensCogsPctRevenueMinInput = document.getElementById('sensCogsPctRevenueMin'); sensCogsPctRevenueMaxInput = document.getElementById('sensCogsPctRevenueMax'); sensCogsPctRevenueStepInput = document.getElementById('sensCogsPctRevenueStep'); sensCapexPctRevenueMinInput = document.getElementById('sensCapexPctRevenueMin'); sensCapexPctRevenueMaxInput = document.getElementById('sensCapexPctRevenueMax'); sensCapexPctRevenueStepInput = document.getElementById('sensCapexPctRevenueStep'); // Output Elements sensitivityResultsOutputDiv = document.getElementById('sensitivityResultsOutput'); detailedProjectionsOutputDiv = document.getElementById('detailedProjectionsOutput'); messageBox = document.getElementById('messageBox'); // Initialize navigation button states updateNavigationButtons(); });