Sovereign Wealth Fund Risk & Return Forecasting Model
Project fund performance and analyze sensitivity to key drivers over time. This is a deterministic model based on expected values and simplified risk metrics.
1. Enter Fund Details & Investment Assumptions
General Fund Information
Enter a positive value for contribution, negative for withdrawal.
Commonly 95% or 99%. Higher means more extreme potential loss.
Asset Allocation & Expected Performance (Base Case)
Allocation percentages must sum to 100%.
Total Allocation: 100.0%
2. Base Case Fund Projections
Calculate the model to see Base Case Projections.
3. Sensitivity Analysis
Sensitivity Variables
Select a key variable and define its range for sensitivity analysis. The model will calculate the Total Ending AUM for each step.
Run sensitivity analysis to see results.
Run sensitivity analysis to see results.
'; // Reset to the first tab openTab('inputs'); hideMessage(); } /** * Downloads the Sovereign Wealth Fund analysis as a PDF. */ function downloadPDF() { if (!window.swfAnalysisResults) { 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('Sovereign Wealth Fund Risk & Return 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.swfAnalysisResults.inputs; const inputData = [ ['Initial AUM', formatCurrency(inputs.initialAum)], ['Projection Years', inputs.projectionYears], ['Annual Net Contribution / (Withdrawal)', formatCurrency(inputs.annualContribution)], ['Annual Inflation Rate', formatPercentage(inputs.inflationRate)], ['Risk-Free Rate', formatPercentage(inputs.riskFreeRate)], ['VaR Confidence Level', `${inputs.varConfidenceLevel}%`], ['Portfolio Expected Return (Derived)', formatPercentage(inputs.portfolioExpectedReturn)], ['Portfolio Volatility (Derived)', formatPercentage(inputs.portfolioVolatility)], ]; 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 + 10; // Asset Class Details Table pdf.setFontSize(14); pdf.setFont('helvetica', 'bold'); pdf.text('Asset Class Allocation & Performance:', 15, y); y += 7; const assetData = [ ['Asset Class', 'Allocation (%)', 'Expected Return (%)', 'Volatility (%)'], ['Public Equities', (inputs.allocations.equities * 100).toFixed(1), (inputs.returns.equities * 100).toFixed(1), (inputs.volatilities.equities * 100).toFixed(1)], ['Fixed Income', (inputs.allocations.fixedIncome * 100).toFixed(1), (inputs.returns.fixedIncome * 100).toFixed(1), (inputs.volatilities.fixedIncome * 100).toFixed(1)], ['Real Estate', (inputs.allocations.realEstate * 100).toFixed(1), (inputs.returns.realEstate * 100).toFixed(1), (inputs.volatilities.realEstate * 100).toFixed(1)], ['Private Equity/Alts', (inputs.allocations.alternatives * 100).toFixed(1), (inputs.returns.alternatives * 100).toFixed(1), (inputs.volatilities.alternatives * 100).toFixed(1)], ]; pdf.autoTable({ startY: y, head: [assetData[0]], body: assetData.slice(1), 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: 50 }, 1: { cellWidth: 40, halign: 'right' }, 2: { cellWidth: 40, halign: 'right' }, 3: { cellWidth: 40, halign: 'right' } } }); y = pdf.autoTable.previous.finalY + 15; // --- 2. Base Case Projections Section --- const baseCaseData = window.swfAnalysisResults.baseCaseProjections; const addBaseCaseTable = (data, startY) => { if (startY + 30 + (data.length * 7) > pdf.internal.pageSize.height - 15) { pdf.addPage('l', 'a4'); startY = 15; } pdf.setFontSize(16); pdf.setFont('helvetica', 'bold'); pdf.text('2. Base Case Fund Projections', 15, startY); startY += 8; const headers = [ 'Year', 'Starting AUM', 'Net Contribution', 'Investment Return', 'Ending AUM', 'Annual Return (Nominal)', 'Annual Return (Real)', 'Cumulative Return (Nominal)', 'Cumulative Return (Real)', 'Portfolio Volatility', `VaR (${inputs.varConfidenceLevel}%)` ]; const tableBody = data.map(yearData => [ yearData.year, { content: formatCurrency(yearData.startingAUM), styles: { halign: 'right' } }, { content: formatCurrency(yearData.netContribution), styles: { halign: 'right' } }, { content: formatCurrency(yearData.investmentReturn), styles: { halign: 'right' } }, { content: formatCurrency(yearData.endingAUM), styles: { halign: 'right', fontStyle: 'bold' } }, { content: yearData.annualReturnNominal !== null ? formatPercentage(yearData.annualReturnNominal) : 'N/A', styles: { halign: 'right' } }, { content: yearData.annualReturnReal !== null ? formatPercentage(yearData.annualReturnReal) : 'N/A', styles: { halign: 'right' } }, { content: yearData.cumulativeReturnNominal !== null ? formatPercentage(yearData.cumulativeReturnNominal) : 'N/A', styles: { halign: 'right' } }, { content: yearData.cumulativeReturnReal !== null ? formatPercentage(yearData.cumulativeReturnReal) : 'N/A', styles: { halign: 'right' } }, { content: yearData.portfolioVolatility !== null ? formatPercentage(yearData.portfolioVolatility) : 'N/A', styles: { halign: 'right' } }, { content: yearData.varValue !== null ? formatPercentage(yearData.varValue) : 'N/A', styles: { halign: 'right' } } ]); pdf.autoTable({ startY: startY, head: [headers], body: tableBody, theme: 'grid', styles: { fontSize: 6, cellPadding: 1.5, overflow: 'linebreak', lineColor: 200, lineWidth: 0.1 }, headStyles: { fillColor: BLUE_HEADER_COLOR, textColor: 255, fontStyle: 'bold' }, margin: { left: 15, right: 15 }, columnStyles: { 0: { cellWidth: 10 }, 1: { cellWidth: 25 }, 2: { cellWidth: 25 }, 3: { cellWidth: 25 }, 4: { cellWidth: 25 }, 5: { cellWidth: 20 }, 6: { cellWidth: 20 }, 7: { cellWidth: 20 }, 8: { cellWidth: 20 }, 9: { cellWidth: 20 }, 10: { cellWidth: 20 } } }); return pdf.autoTable.previous.finalY + 15; }; y = addBaseCaseTable(baseCaseData, y); // --- 3. Sensitivity Analysis Section --- const sensitivityAnalysis = window.swfAnalysisResults.sensitivityAnalysis; for (const variable in sensitivityAnalysis) { // Determine display name and format function let displayVarName = ''; let formatFunc = formatPercentage; // Default for rates/percentages if (variable === 'portfolioReturn') { displayVarName = 'Overall Portfolio Expected Return (%)'; } else if (variable === 'annualContribution') { displayVarName = 'Annual Net Contribution ($)'; formatFunc = formatCurrency; // Override for currency } else if (variable === 'inflationRate') { displayVarName = 'Annual Inflation Rate (%)'; } else if (variable === 'publicEquitiesReturn') { displayVarName = 'Public Equities Expected Return (%)'; } else if (variable === 'fixedIncomeReturn') { displayVarName = 'Fixed Income Expected Return (%)'; } else if (variable === 'realEstateReturn') { displayVarName = 'Real Estate Expected Return (%)'; } else if (variable === 'alternativesReturn') { displayVarName = 'Private Equity/Alts Expected Return (%)'; } if (y + 30 + (sensitivityAnalysis[variable].length * 7) > pdf.internal.pageSize.height - 15) { pdf.addPage('l', 'a4'); y = 15; } pdf.setFontSize(16); pdf.setFont('helvetica', 'bold'); pdf.text(`3. Sensitivity to ${displayVarName}`, 15, y); y += 8; const headers = [displayVarName, 'Total Ending AUM']; const tableBody = sensitivityAnalysis[variable].map(scenario => { const formattedValue = formatFunc(scenario.value / (variable.includes('Return') || variable.includes('Rate') ? 100 : 1)); return [formattedValue, { content: formatCurrency(scenario.endingAUM), 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: 100 }, 1: { cellWidth: 'auto' } } }); y = pdf.autoTable.previous.finalY + 10; } pdf.save('SWF_Risk_Return_Forecast_Report.pdf'); } /** * Initializes DOM element references once the document is fully loaded. */ document.addEventListener('DOMContentLoaded', () => { // General Fund Information Inputs initialAumInput = document.getElementById('initialAum'); projectionYearsInput = document.getElementById('projectionYears'); annualContributionInput = document.getElementById('annualContribution'); inflationRateInput = document.getElementById('inflationRate'); riskFreeRateInput = document.getElementById('riskFreeRate'); varConfidenceLevelInput = document.getElementById('varConfidenceLevel'); // Asset Allocation & Expected Performance Inputs allocEquitiesInput = document.getElementById('allocEquities'); retEquitiesInput = document.getElementById('retEquities'); volEquitiesInput = document.getElementById('volEquities'); allocFixedIncomeInput = document.getElementById('allocFixedIncome'); retFixedIncomeInput = document.getElementById('retFixedIncome'); volFixedIncomeInput = document.getElementById('volFixedIncome'); allocRealEstateInput = document.getElementById('allocRealEstate'); retRealEstateInput = document.getElementById('retRealEstate'); volRealEstateInput = document.getElementById('volRealEstate'); allocAlternativesInput = document.getElementById('allocAlternatives'); retAlternativesInput = document.getElementById('retAlternatives'); volAlternativesInput = document.getElementById('volAlternatives'); totalAllocationValueSpan = document.getElementById('totalAllocationValue'); // Add event listeners for allocation inputs to update total sum document.querySelectorAll('#assetAllocationInputs input[id^="alloc"]').forEach(input => { input.addEventListener('input', updateTotalAllocation); }); updateTotalAllocation(); // Initial call to set the sum on load // Sensitivity Variables Inputs sensitivityVariableSelect = document.getElementById('sensitivityVariable'); sensMinInput = document.getElementById('sensMin'); sensMaxInput = document.getElementById('sensMax'); sensStepInput = document.getElementById('sensStep'); // Output Elements baseCaseOutputDiv = document.getElementById('baseCaseOutput'); sensitivityOutputDiv = document.getElementById('sensitivityOutput'); messageBox = document.getElementById('messageBox'); // Initialize navigation button states updateNavigationButtons(); });