`;
const ctx = document.getElementById('simulation-chart').getContext('2d');
if (simulationChart) simulationChart.destroy();
const datasets = data.paths.map((path, index) => ({
label: `Simulation ${index + 1}`,
data: path,
borderColor: `rgba(5, 150, 105, 0.25)`,
borderWidth: 1.5,
pointRadius: 0,
fill: false,
}));
const labels = Array.from({ length: data.params.T + 1 }, (_, i) => `Day ${i}`);
simulationChart = new Chart(ctx, {
type: 'line',
data: { labels, datasets },
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false },
tooltip: { enabled: false },
title: {
display: true,
text: `${data.params.numSims} Simulated Asset Price Paths over ${data.params.T} Days`
}
},
scales: {
y: {
title: { display: true, text: 'Asset Price ($)' },
ticks: {
callback: value => '$' + value.toFixed(2)
}
},
x: {
title: { display: true, text: 'Trading Days' }
}
},
animation: {
duration: 0
}
}
});
pdfButtonContainer.classList.remove('hidden');
}
/**
* Handles PDF Download
*/
async function handlePdfDownload() {
if (!lastSimulationData) {
alert("Please run a simulation first.");
return;
}
const { jsPDF } = window.jspdf;
const pdfContainer = document.getElementById('pdf-container');
const pdfContent = document.getElementById('pdf-content');
// 1. Populate the hidden PDF template with data
document.getElementById('pdf-report-date').textContent = `Generated on: ${new Date().toLocaleDateString()}`;
const params = lastSimulationData.params;
document.getElementById('pdf-params').innerHTML = `
Initial Price: $${params.S0.toFixed(2)}
Time Horizon: ${params.T} Days
Annual Return: ${(params.mu * 100).toFixed(2)}%
Annual Volatility: ${(params.sigma * 100).toFixed(2)}%
Simulations: ${params.numSims}
`;
const stats = lastSimulationData.stats;
document.getElementById('pdf-stats').innerHTML = `
Average Price: $${stats.average.toFixed(2)}
Median Price: $${stats.median.toFixed(2)}
5th Percentile: $${stats.p5.toFixed(2)}
95th Percentile: $${stats.p95.toFixed(2)}
`;
// 2. Add chart image to the PDF template
const chartImgData = simulationChart.toBase64Image();
document.getElementById('pdf-chart-container').innerHTML = `

`;
try {
// 3. Render the populated template to a canvas
const canvas = await html2canvas(pdfContent, {
scale: 2, // Higher resolution for better quality
useCORS: true,
logging: false
});
// 4. Create PDF and add the canvas image
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({
orientation: 'portrait',
unit: 'pt',
format: 'a4'
});
const pdfWidth = pdf.internal.pageSize.getWidth();
const pdfHeight = pdf.internal.pageSize.getHeight();
const margin = 40;
const contentWidth = pdfWidth - margin * 2;
const imgProps = pdf.getImageProperties(imgData);
const contentHeight = (imgProps.height * contentWidth) / imgProps.width;
let currentHeight = contentHeight > pdfHeight - margin * 2 ? pdfHeight - margin * 2 : contentHeight;
pdf.addImage(imgData, 'PNG', margin, margin, contentWidth, currentHeight);
pdf.save('Quantitative_Model_Report.pdf');
} catch (error) {
console.error("Failed to generate PDF:", error);
alert("An error occurred while generating the PDF. Please try again.");
} finally {
// Clean up the PDF template
document.getElementById('pdf-chart-container').innerHTML = '';
}
}
// --- Event Listeners ---
tabs.forEach((tab, index) => {
tab.btn.addEventListener('click', () => switchTab(index));
});
prevBtn.addEventListener('click', () => switchTab(currentTab - 1));
nextBtn.addEventListener('click', () => switchTab(currentTab + 1));
runSimulationBtn.addEventListener('click', runSimulation);
downloadPdfBtn.addEventListener('click', handlePdfDownload);
// --- Initialization ---
updateNavButtons();
});