Holdings Breakdown for Beta Calculation
| Asset Name |
Value Invested ($) |
Weight (%) |
Asset Beta (β) |
Weighted Beta |
`;
this.holdingsData.forEach(h => {
resultsHtml += `
| ${h.name} |
${h.value.toLocaleString(undefined, {minimumFractionDigits:2, maximumFractionDigits:2})} |
${(h.weight * 100).toFixed(2)} |
${h.beta.toFixed(3)} |
${h.weightedBeta.toFixed(3)} |
`;
});
resultsHtml += `
| Total / Weighted Average |
${this.calculatedResults.totalPortfolioValue.toLocaleString(undefined, {minimumFractionDigits:2, maximumFractionDigits:2})} |
100.00 |
|
${isNaN(portfolioBeta) ? 'N/A' : portfolioBeta.toFixed(3)} |
`;
this.elements.resultsArea.innerHTML = resultsHtml;
},
generatePDF: async function() {
if (!window.jspdf || !window.html2canvas) {
alert('PDF generation library is not loaded.'); return;
}
if (Object.keys(this.calculatedResults).length === 0 || isNaN(this.calculatedResults.portfolioBeta)) {
alert('Please calculate Alpha & Beta first with valid data.'); return;
}
await new Promise(resolve => setTimeout(resolve, 50));
const { portfolioBeta, expectedReturnCAPM, alpha, totalPortfolioValue } = this.calculatedResults;
const rp = this.getFloat(this.elements.actualPortfolioReturnInput.value, true);
const rf = this.getFloat(this.elements.riskFreeRateInput.value, true);
const rm = this.getFloat(this.elements.marketReturnInput.value, true);
const pdfExportContainer = document.createElement('div');
pdfExportContainer.classList.add('ab-pdf-export-content');
pdfExportContainer.style.width = '800px';
let pdfHtml = `
Portfolio Alpha (α) & Beta (β) Report
`;
pdfHtml += `
Summary Results
`;
pdfHtml += `
`;
pdfHtml += `
Portfolio Beta (βp): ${isNaN(portfolioBeta) ? 'N/A' : portfolioBeta.toFixed(3)}
`;
pdfHtml += `
Expected Return (CAPM): ${isNaN(expectedReturnCAPM) ? 'N/A' : expectedReturnCAPM.toFixed(2)}
`;
pdfHtml += `
Portfolio Alpha (α): ${isNaN(alpha) ? 'N/A' : alpha.toFixed(2)}
`;
pdfHtml += `
`;
pdfHtml += `
Market & Portfolio Inputs (Annualized)
`;
pdfHtml += `
Actual Portfolio Return (Rp): ${rp !== null ? rp.toFixed(2) : 'N/A'} %
`;
pdfHtml += `
Risk-Free Rate (Rf): ${rf !== null ? rf.toFixed(2) : 'N/A'} %
`;
pdfHtml += `
Market Return (Rm): ${rm !== null ? rm.toFixed(2) : 'N/A'} %
`;
pdfHtml += `
Holdings Breakdown for Beta Calculation
`;
const tableClone = this.elements.resultsArea.querySelector('.ab-results-table').cloneNode(true);
tableClone.style.fontSize = '8pt';
pdfHtml += tableClone.outerHTML;
pdfHtml += `
Portfolio Beta (βp) = Sum of (Asset Weight * Asset Beta).
Expected Return (CAPM) = Rf + βp * (Rm - Rf).
Portfolio Alpha (α) = Actual Portfolio Return (Rp) - Expected Return (CAPM).
`;
pdfExportContainer.innerHTML = pdfHtml;
document.body.appendChild(pdfExportContainer);
try {
const canvas = await html2canvas(pdfExportContainer, { scale: 1.5, useCORS: true, logging: false });
document.body.removeChild(pdfExportContainer);
const imgData = canvas.toDataURL('image/png');
const { jsPDF } = window.jspdf;
const pdf = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' });
const pdfWidth = pdf.internal.pageSize.getWidth();
const pdfHeight = pdf.internal.pageSize.getHeight();
const imgProps = pdf.getImageProperties(imgData);
const imgWidth = pdfWidth - 40;
const imgHeight = (imgProps.height * imgWidth) / imgProps.width;
let heightLeft = imgHeight;
let position = 20;
pdf.addImage(imgData, 'PNG', 20, position, imgWidth, imgHeight);
heightLeft -= (pdfHeight - 40);
while (heightLeft > 0) {
position = heightLeft - imgHeight;
pdf.addPage();
pdf.addImage(imgData, 'PNG', 20, position, imgWidth, imgHeight);
heightLeft -= (pdfHeight - 40);
}
pdf.save('Portfolio_Alpha_Beta_Report.pdf');
} catch (error) {
console.error("Error generating PDF:", error);
alert("An error occurred while generating the PDF.");
if (document.body.contains(pdfExportContainer)) document.body.removeChild(pdfExportContainer);
}
}
};
document.addEventListener('DOMContentLoaded', function() {
abApp.init();
});