Multi-Strategy Hedge Fund Performance Analyzer

Fund Details


Add New Strategy

Added Strategies

Strategy Name AUM ($) Return (%) Volatility (%) Sharpe Ratio Action

No strategies added yet.

Please add strategies in the 'Fund & Strategy Inputs' tab first and click 'Next' or refresh this tab.

Multi-Strategy Fund Performance Report

Please add strategies and view the dashboard first. The report content will appear here.

No strategies have been added. Please go to the 'Fund & Strategy Inputs' tab to add strategies.

"; if (dashboardContentEl) dashboardContentEl.innerHTML = message; if (reportOutputEl) reportOutputEl.innerHTML = message; // Destroy existing charts if they exist and there's no data if (mshfpa_aumChartInstance) mshfpa_aumChartInstance.destroy(); if (mshfpa_returnChartInstance) mshfpa_returnChartInstance.destroy(); if (mshfpa_sharpeChartInstance) mshfpa_sharpeChartInstance.destroy(); mshfpa_aumChartInstance = null; mshfpa_returnChartInstance = null; mshfpa_sharpeChartInstance = null; return; } let totalAum = 0; let weightedReturnSum = 0; let weightedVolatilitySum = 0; let weightedSharpeSum = 0; mshfpa_strategies.forEach(s => { totalAum += s.aum; }); if (totalAum > 0) { mshfpa_strategies.forEach(s => { const weight = s.aum / totalAum; weightedReturnSum += weight * s.returnPercent; weightedVolatilitySum += weight * s.volatility; // Simple weighted average, not true portfolio volatility weightedSharpeSum += weight * s.sharpeRatio; // Simple weighted average }); } const strategyNames = mshfpa_strategies.map(s => s.name); const strategyAums = mshfpa_strategies.map(s => s.aum); const strategyReturns = mshfpa_strategies.map(s => s.returnPercent); const strategySharpes = mshfpa_strategies.map(s => s.sharpeRatio); const strategyVolatilities = mshfpa_strategies.map(s => s.volatility); // --- Build HTML Content for Dashboard and Report --- let htmlContent = `

Overall Fund Summary

Total Assets Under Management (AUM): $${totalAum.toLocaleString()}
AUM-Weighted Average Return: ${mshfpa_formatNumber(weightedReturnSum)}%
AUM-Weighted Average Volatility (Illustrative): ${mshfpa_formatNumber(weightedVolatilitySum)}%
AUM-Weighted Average Sharpe Ratio (Illustrative): ${mshfpa_formatNumber(weightedSharpeSum)}

Strategy Breakdown

`; mshfpa_strategies.forEach(s => { const weightPercent = totalAum > 0 ? (s.aum / totalAum * 100) : 0; htmlContent += ` `; }); htmlContent += `
Strategy Name AUM ($) AUM (%) Return (%) Volatility (%) Sharpe Ratio
${s.name} $${s.aum.toLocaleString()} ${mshfpa_formatNumber(weightPercent)}% ${mshfpa_formatNumber(s.returnPercent)}% ${mshfpa_formatNumber(s.volatility)}% ${mshfpa_formatNumber(s.sharpeRatio)}

AUM Allocation by Strategy

Annualized Return by Strategy

Sharpe Ratio by Strategy

`; if(dashboardContentEl) dashboardContentEl.innerHTML = htmlContent; // For PDF, we might want to slightly alter or ensure chart images are captured if(reportOutputEl) reportOutputEl.innerHTML = htmlContent; // Initially same content // --- Initialize/Update Charts --- // Destroy existing charts before redrawing if (mshfpa_aumChartInstance) mshfpa_aumChartInstance.destroy(); if (mshfpa_returnChartInstance) mshfpa_returnChartInstance.destroy(); if (mshfpa_sharpeChartInstance) mshfpa_sharpeChartInstance.destroy(); const chartBackgroundColor = [ 'rgba(54, 162, 235, 0.7)', 'rgba(255, 99, 132, 0.7)', 'rgba(75, 192, 192, 0.7)', 'rgba(255, 206, 86, 0.7)', 'rgba(153, 102, 255, 0.7)', 'rgba(255, 159, 64, 0.7)', 'rgba(199, 199, 199, 0.7)', 'rgba(83, 102, 255, 0.7)', 'rgba(102,255,83,0.7)' ]; const chartBorderColor = chartBackgroundColor.map(color => color.replace('0.7', '1')); // AUM Pie Chart const aumCtx = document.getElementById('mshfpa-aumChart')?.getContext('2d'); if (aumCtx) { mshfpa_aumChartInstance = new Chart(aumCtx, { type: 'pie', data: { labels: strategyNames, datasets: [{ label: 'AUM ($)', data: strategyAums, backgroundColor: chartBackgroundColor, borderColor: chartBorderColor, borderWidth: 1 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom' } } } }); } // Return Bar Chart const returnCtx = document.getElementById('mshfpa-returnChart')?.getContext('2d'); if (returnCtx) { mshfpa_returnChartInstance = new Chart(returnCtx, { type: 'bar', data: { labels: strategyNames, datasets: [{ label: 'Annualized Return (%)', data: strategyReturns, backgroundColor: chartBackgroundColor, borderColor: chartBorderColor, borderWidth: 1 }] }, options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true, ticks: { callback: function(value) { return value + '%';} } } } } }); } // Sharpe Ratio Bar Chart const sharpeCtx = document.getElementById('mshfpa-sharpeChartCanvas')?.getContext('2d'); if (sharpeCtx) { mshfpa_sharpeChartInstance = new Chart(sharpeCtx, { type: 'bar', data: { labels: strategyNames, datasets: [{ label: 'Sharpe Ratio', data: strategySharpes, backgroundColor: chartBackgroundColor, borderColor: chartBorderColor, borderWidth: 1 }] }, options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true } } } }); } } async function mshfpa_downloadPDF() { const downloadButton = document.getElementById('mshfpa-downloadPdfButton'); if (!downloadButton) return; const originalButtonText = downloadButton.textContent; downloadButton.textContent = 'Generating PDF...'; downloadButton.disabled = true; const { jsPDF } = window.jspdf; const reportContentElement = document.getElementById('mshfpa-reportOutputContainerForPdf'); if (!reportContentElement) { alert("Error: Report content element not found for PDF generation."); downloadButton.textContent = originalButtonText; downloadButton.disabled = false; return; } if (mshfpa_strategies.length === 0) { alert("No data to export. Please add strategies first."); downloadButton.textContent = originalButtonText; downloadButton.disabled = false; return; } // Ensure charts are rendered before capturing for PDF // In some cases, a small timeout helps ensure canvas elements are fully rendered // For this version, we assume charts are already rendered by mshfpa_updateDashboardAndReport // If charts are complex or load async, more robust handling might be needed. try { // Temporarily make sure the content is fully visible for html2canvas const originalDisplay = reportContentElement.style.display; reportContentElement.style.display = 'block'; // Ensure it's visible if it wasn't // For better PDF quality, especially with charts: // 1. Clone the report content. // 2. Explicitly set chart dimensions on the cloned canvases if needed. // 3. Convert canvas charts to images and embed them in the cloned content. // This step makes the PDF generation more robust for charts. const clonedReportElement = reportContentElement.cloneNode(true); document.body.appendChild(clonedReportElement); // Append to body to ensure rendering for html2canvas // Replace canvas elements with images in the cloned node const canvasElements = clonedReportElement.querySelectorAll('canvas'); for (const canvas of canvasElements) { // Find original chart instance by canvas ID to get its image let originalChartInstance = null; if (canvas.id === 'mshfpa-aumChart') originalChartInstance = mshfpa_aumChartInstance; else if (canvas.id === 'mshfpa-returnChart') originalChartInstance = mshfpa_returnChartInstance; else if (canvas.id === 'mshfpa-sharpeChartCanvas') originalChartInstance = mshfpa_sharpeChartInstance; if (originalChartInstance) { const imgData = originalChartInstance.toBase64Image(); const img = document.createElement('img'); img.src = imgData; img.style.maxWidth = '100%'; // Ensure image fits within PDF img.style.height = 'auto'; canvas.parentNode.replaceChild(img, canvas); } } // Apply some basic styling to the cloned element for PDF consistency clonedReportElement.style.padding = '20px'; clonedReportElement.style.backgroundColor = 'white'; clonedReportElement.style.fontFamily = getComputedStyle(document.documentElement).getPropertyValue('--mshfpa-font-family').trim(); clonedReportElement.style.color = getComputedStyle(document.documentElement).getPropertyValue('--mshfpa-text-color').trim(); // Ensure table borders are visible in PDF const tablesInClone = clonedReportElement.querySelectorAll('.mshfpa-strategy-table'); tablesInClone.forEach(table => { table.style.borderCollapse = 'collapse'; // Important for html2canvas const ths = table.querySelectorAll('th'); const tds = table.querySelectorAll('td'); [...ths, ...tds].forEach(cell => { cell.style.border = '1px solid #cccccc'; // Light gray border }); ths.forEach(th => { th.style.backgroundColor = '#f0f0f0'; // Light gray header }); }); const canvas = await html2canvas(clonedReportElement, { scale: 2, // Improves quality useCORS: true, // If any external images were used (not in this case) logging: false, onclone: (documentClone) => { // This is useful if you need to modify the cloned document before rendering // Example: Remove scripts or interactive elements that shouldn't be in the PDF } }); const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF({ orientation: 'p', // portrait unit: 'pt', // points format: 'a4' // page format }); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = pdf.internal.pageSize.getHeight(); const imgWidth = canvas.width; const imgHeight = canvas.height; const ratio = Math.min(pdfWidth / imgWidth, pdfHeight / imgHeight); const imgX = (pdfWidth - imgWidth * ratio) / 2; const imgY = 15; // Small top margin pdf.addImage(imgData, 'PNG', imgX, imgY, imgWidth * ratio, imgHeight * ratio); const fundNameForFile = (document.getElementById('mshfpa-fundName')?.value || "FundAnalysis").replace(/[^a-z0-9]/gi, '_').toLowerCase(); pdf.save(`${fundNameForFile}_performance_report.pdf`); // Cleanup document.body.removeChild(clonedReportElement); reportContentElement.style.display = originalDisplay; // Restore original display } catch (error) { console.error("Error generating PDF:", error); alert("An error occurred while generating the PDF. Please check the console for details."); } finally { downloadButton.textContent = originalButtonText; downloadButton.disabled = false; } }
Scroll to Top