Free Cash Flow Sensitivity Analysis Tool

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.

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 += `

${variable} Sensitivity (Total FCF over Projection Period)

`; scenarios[variable].forEach(scenario => { const formattedValue = (variable === 'Revenue Growth Rate') ? formatPercentage(scenario.value / 100) : formatPercentage(scenario.value / 100); tableHtml += ` `; }); tableHtml += `
${variable} Total Projected FCF
${formattedValue} ${formatCurrency(scenario.totalFCF)}
`; } 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 = ``; for (let i = 0; i < data.length; i++) { tableHtml += ``; } tableHtml += ``; 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 => { tableHtml += ``; data.forEach(yearData => { tableHtml += ``; }); tableHtml += ``; }); tableHtml += `
Line Item${data[i].year === 0 ? 'Current Year' : `Year ${data[i].year}`}
${displayNames[item]}${formatCurrency(yearData[item])}
`; detailedProjectionsOutputDiv.innerHTML = tableHtml; } /** * Resets all input fields to their default values and clears output sections. */ function resetForm() { // Reset Initial Financials inputs initialRevenueInput.value = "10000000"; initialCogsPctRevenueInput.value = "40"; initialOpexPctRevenueInput.value = "30"; initialDepreciationPctRevenueInput.value = "5"; initialCapexPctRevenueInput.value = "3"; initialNcwcPctRevenueInput.value = "10"; taxRateInput.value = "25"; // Reset Projection & Base Case Assumptions inputs projectionYearsInput.value = "5"; baseRevenueGrowthRateInput.value = "10"; baseCogsPctRevenueInput.value = "40"; baseCapexPctRevenueInput.value = "3"; baseNcwcPctRevenueInput.value = "10"; // Reset Sensitivity Variables inputs sensRevenueGrowthMinInput.value = "5"; sensRevenueGrowthMaxInput.value = "15"; sensRevenueGrowthStepInput.value = "2.5"; sensCogsPctRevenueMinInput.value = "35"; sensCogsPctRevenueMaxInput.value = "45"; sensCogsPctRevenueStepInput.value = "2.5"; sensCapexPctRevenueMinInput.value = "1"; sensCapexPctRevenueMaxInput.value = "5"; sensCapexPctRevenueStepInput.value = "1"; // Clear stored results window.fcfAnalysisResults = null; // Clear output divs sensitivityResultsOutputDiv.innerHTML = '

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(); });
Scroll to Top