AI Impact Score
${score}/100
Post-Event Value
${formatCurrency(postEventValue)}
AI Narrative Analysis
${scenario.narrative}
Portfolio Value Waterfall
`;
renderWaterfallChart(results);
}
function renderWaterfallChart(results) {
const ctx = document.getElementById('impactChart')?.getContext('2d');
if (!ctx) return;
if (chartInstance) chartInstance.destroy();
const { preEventValue, postEventValue, classTotals } = results;
const labels = ['Initial Value'];
const data = [];
let cumulative = 0;
data.push([0, preEventValue]);
cumulative = preEventValue;
for(const assetClass in classTotals) {
if(classTotals[assetClass].pre !== 0) { // Only show classes present in portfolio
labels.push(assetClass);
const change = classTotals[assetClass].change;
data.push([cumulative, cumulative + change]);
cumulative += change;
}
}
labels.push('Final Value');
data.push([0, postEventValue]);
chartInstance = new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: 'Portfolio Value',
data: data,
backgroundColor: (c) => {
const isTotal = c.dataIndex === 0 || c.dataIndex === data.length - 1;
if (isTotal) return '#4b5563'; // gray-600
const value = c.raw;
return value[1] > value[0] ? '#22c55e' : '#ef4444'; // green/red
},
borderColor: '#ffffff',
borderWidth: 1,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: false,
ticks: { callback: value => '$' + (value / 1000) + 'k' }
}
},
plugins: {
legend: { display: false },
title: { display: true, text: 'Change in Portfolio Value by Asset Class' },
tooltip: {
callbacks: {
label: function(context) {
const isTotal = context.dataIndex === 0 || context.dataIndex === data.length - 1;
const value = context.raw;
if(isTotal) return `Value: ${formatCurrency(value[1])}`;
const change = value[1] - value[0];
return `Change: ${formatCurrency(change)}`;
}
}
}
}
}
});
}
// --- PDF Download ---
function downloadPDF() {
const pdfElement = document.getElementById('pdf-output');
if (!pdfElement) return alert("Could not find content to download.");
const { jsPDF } = window.jspdf;
html2canvas(pdfElement, { scale: 2, useCORS: true, backgroundColor: '#ffffff' })
.then(canvas => {
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({ orientation: 'p', unit: 'px', format: [canvas.width, canvas.height] });
pdf.addImage(imgData, 'PNG', 0, 0, canvas.width, canvas.height);
pdf.save('Macro-Event-Impact-Report.pdf');
}).catch(err => {
console.error("PDF Generation Error:", err);
alert("An error occurred while generating the PDF.");
});
}
function formatCurrency(val) {
return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(val);
}