`;
listEl.appendChild(row);
});
}
}
window.analyzePortfolio = function() {
if (portfolio.length === 0) {
alert('Please add at least one asset to your portfolio before analyzing.');
return;
}
const totalValue = portfolio.reduce((sum, asset) => sum + asset.value, 0);
// Update summary cards
document.getElementById('totalValue').textContent = formatCurrency(totalValue);
document.getElementById('numHoldings').textContent = portfolio.length;
const largestHolding = portfolio.reduce((max, asset) => asset.value > max.value ? asset : max, {value:0});
const largestHoldingEl = document.getElementById('largestHolding');
largestHoldingEl.textContent = largestHolding.name;
largestHoldingEl.title = `${largestHolding.name} (${formatCurrency(largestHolding.value)})`;
// Generate analysis table
const tableContainer = document.getElementById('analysisTableContainer');
let tableHTML = `
| Asset |
Value |
% of Portfolio |
`;
portfolio.sort((a,b) => b.value - a.value).forEach(asset => {
const percentage = totalValue > 0 ? (asset.value / totalValue * 100).toFixed(2) : 0;
tableHTML += `
| ${asset.name} |
${formatCurrency(asset.value)} |
${percentage}% |
`;
});
tableHTML += `
`;
tableContainer.innerHTML = tableHTML;
// --- Chart Data Preparation ---
const typeAllocation = portfolio.reduce((acc, asset) => {
acc[asset.type] = (acc[asset.type] || 0) + asset.value;
return acc;
}, {});
const sectorAllocation = portfolio.reduce((acc, asset) => {
if (asset.sector && asset.sector !== 'N/A') {
acc[asset.sector] = (acc[asset.sector] || 0) + asset.value;
}
return acc;
}, {});
renderCharts(typeAllocation, sectorAllocation);
document.getElementById('analysis-dashboard').classList.remove('hidden');
document.getElementById('analysis-placeholder').classList.add('hidden');
}
function renderCharts(typeData, sectorData) {
const chartColors = ['#4f46e5', '#7c3aed', '#db2777', '#f59e0b', '#10b981', '#3b82f6', '#ef4444', '#6b7280'];
const doughnutOptions = {
responsive: true,
plugins: { legend: { position: 'bottom', labels: { padding: 15, font: { size: 11 } } } }
};
const typeCtx = document.getElementById('assetTypeChart').getContext('2d');
if (assetTypeChart) assetTypeChart.destroy();
assetTypeChart = new Chart(typeCtx, {
type: 'doughnut',
data: {
labels: Object.keys(typeData),
datasets: [{ data: Object.values(typeData), backgroundColor: chartColors }]
},
options: doughnutOptions
});
const sectorCtx = document.getElementById('sectorAllocationChart').getContext('2d');
if (sectorAllocationChart) sectorAllocationChart.destroy();
sectorAllocationChart = new Chart(sectorCtx, {
type: 'doughnut',
data: {
labels: Object.keys(sectorData),
datasets: [{ data: Object.values(sectorData), backgroundColor: chartColors.slice().reverse() }]
},
options: doughnutOptions
});
}
window.generatePdf = function() {
if (!assetTypeChart || !sectorAllocationChart) {
alert("Please analyze your portfolio first to generate a report.");
return;
}
const { jsPDF } = window.jspdf;
const pdf = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' });
const pageW = pdf.internal.pageSize.getWidth();
const margin = 15;
let yPos = 20;
pdf.setFont("helvetica", "bold");
pdf.setFontSize(22);
pdf.setTextColor('#312e81'); // indigo-900
pdf.text("Investment Portfolio Analysis", pageW / 2, yPos, { align: 'center' });
yPos += 8;
pdf.setFontSize(10);
pdf.setTextColor(100, 116, 139);
pdf.text(`Report Generated: ${new Date().toLocaleDateString('en-US')}`, pageW / 2, yPos, { align: 'center' });
yPos += 15;
// --- Summary Section ---
pdf.setFontSize(16);
pdf.setFont("helvetica", "bold");
pdf.setTextColor(17, 24, 39);
pdf.text("Portfolio Snapshot", margin, yPos);
yPos += 8;
pdf.setFontSize(11);
pdf.setFont("helvetica", "normal");
pdf.text(`Total Value: ${document.getElementById('totalValue').textContent}`, margin, yPos);
pdf.text(`Total Holdings: ${document.getElementById('numHoldings').textContent}`, pageW/2, yPos);
yPos += 15;
// --- Charts ---
const chartW = (pageW - margin * 3) / 2;
const chartH = chartW;
pdf.text("Asset Allocation", margin + chartW/2, yPos, { align: 'center' });
pdf.text("Sector Allocation", margin * 2 + chartW + chartW/2, yPos, { align: 'center' });
yPos += 5;
const chartImg1 = assetTypeChart.canvas.toDataURL('image/png', 1.0);
const chartImg2 = sectorAllocationChart.canvas.toDataURL('image/png', 1.0);
pdf.addImage(chartImg1, 'PNG', margin, yPos, chartW, chartH);
pdf.addImage(chartImg2, 'PNG', margin * 2 + chartW, yPos, chartW, chartH);
yPos += chartH + 15;
// --- Detailed Table ---
const totalValue = portfolio.reduce((sum, asset) => sum + asset.value, 0);
const tableData = portfolio.map(asset => [
asset.name,
formatCurrency(asset.value),
`${(asset.value / totalValue * 100).toFixed(2)}%`,
asset.type,
asset.sector
]);
pdf.autoTable({
startY: yPos,
head: [['Asset', 'Value', '% of Portfolio', 'Type', 'Sector']],
body: tableData,
theme: 'grid',
headStyles: { fillColor: '#4338ca' }, // indigo-700
margin: { left: margin, right: margin }
});
pdf.save('Investment-Portfolio-Analysis.pdf');
}
// Initial setup
populateWithSampleData();
});