Current Portfolio Value
${formatCurrency(impactResults.currentValue)}
Best-Case Scenario (${impactResults.bestCase}%)
${formatCurrency(impactResults.bestCaseValue)}
Impact: ${formatImpact(impactResults.bestCaseImpact)}
Detailed Stock Breakdown
| Ticker |
Current Value |
Worst-Case Impact |
Best-Case Impact |
${impactResults.details.map(d => `
| ${d.ticker} |
${formatCurrency(d.currentValue)} |
${formatImpact(d.worstCaseImpact)} |
${formatImpact(d.bestCaseImpact)} |
`).join('')}
`;
};
const renderPortfolioTab = () => {
let stockForms = portfolio.map(stock => `
`).join('');
return `
`;
};
const renderEventTab = () => {
return `
`;
};
// --- DATA & EVENT HANDLERS ---
window.addStock = () => {
portfolio.push({ id: Date.now(), ticker: 'NEW', shares: 0, avgPrice: 0 });
renderCurrentTab();
};
window.updateStock = (id, key, value) => {
const stock = portfolio.find(s => s.id === id);
if(stock) stock[key] = (key === 'ticker') ? value : parseFloat(value) || 0;
};
window.deleteStock = (id) => {
portfolio = portfolio.filter(s => s.id !== id);
renderCurrentTab();
};
window.updateEvent = (key, value) => {
eventScenario[key] = (key === 'eventName') ? value : parseFloat(value) || 0;
};
const handleImpactCalculation = () => {
let currentValue = 0;
let bestCaseValue = 0;
let worstCaseValue = 0;
const details = [];
portfolio.forEach(stock => {
const stockValue = stock.shares * stock.avgPrice;
const bestImpact = stockValue * (eventScenario.bestCase / 100);
const worstImpact = stockValue * (eventScenario.worstCase / 100);
currentValue += stockValue;
bestCaseValue += stockValue + bestImpact;
worstCaseValue += stockValue + worstImpact;
details.push({
ticker: stock.ticker,
currentValue: stockValue,
bestCaseImpact: bestImpact,
worstCaseImpact: worstImpact
});
});
impactResults = {
eventName: eventScenario.eventName,
bestCase: eventScenario.bestCase,
worstCase: eventScenario.worstCase,
currentValue,
bestCaseValue,
worstCaseValue,
bestCaseImpact: bestCaseValue - currentValue,
worstCaseImpact: worstCaseValue - currentValue,
details
};
switchTab(0);
};
// --- CHART & PDF ---
const renderChart = () => {
if (impactChart) impactChart.destroy();
const ctx = document.getElementById('impactChart')?.getContext('2d');
if (!ctx || !impactResults) return;
impactChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ['Portfolio Value'],
datasets: [
{ label: 'Worst Case', data: [impactResults.worstCaseValue], backgroundColor: '#ef4444' },
{ label: 'Current', data: [impactResults.currentValue], backgroundColor: '#6b7280' },
{ label: 'Best Case', data: [impactResults.bestCaseValue], backgroundColor: '#22c55e' }
]
},
options: {
responsive: true, maintainAspectRatio: false,
scales: { y: { beginAtZero: true, ticks: { callback: (v) => `$${v.toLocaleString()}` } } },
plugins: { title: { display: true, text: 'Portfolio Value Scenarios' } }
}
});
};
window.downloadPDF = () => {
if (!impactResults) return;
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
doc.setFontSize(20);
doc.text("Stock Market Event Impact Report", doc.internal.pageSize.getWidth() / 2, 20, { align: 'center' });
doc.setFontSize(12);
doc.text(`Event: ${impactResults.eventName}`, doc.internal.pageSize.getWidth() / 2, 28, { align: 'center' });
const summaryText = `
Current Portfolio Value: $${impactResults.currentValue.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})}
Best-Case Scenario (${impactResults.bestCase}%): $${impactResults.bestCaseValue.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})}
(Potential Gain: $${impactResults.bestCaseImpact.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})})
Worst-Case Scenario (${impactResults.worstCase}%): $${impactResults.worstCaseValue.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})}
(Potential Loss: $${impactResults.worstCaseImpact.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})})
`;
doc.text(summaryText, 14, 40);
const finalY = impactChart ? 90 : 80;
if(impactChart) {
const chartImage = impactChart.toBase64Image();
doc.addImage(chartImage, 'PNG', 14, finalY, 180, 90);
}
const head = [['Ticker', 'Current Value ($)', 'Worst-Case Impact ($)', 'Best-Case Impact ($)']];
const body = impactResults.details.map(d => [
d.ticker,
d.currentValue.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}),
d.worstCaseImpact.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}),
d.bestCaseImpact.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})
]);
doc.autoTable({ startY: finalY + (impactChart ? 100 : 10), head: head, body: body, theme: 'striped', headStyles: { fillColor: [37, 99, 235] } });
doc.save('Stock-Impact-Report.pdf');
};
// --- INITIALIZATION ---
DOM.nextBtn.addEventListener('click', () => switchTab(currentTab + 1));
DOM.prevBtn.addEventListener('click', () => switchTab(currentTab - 1));
DOM.tabButtons.forEach((btn, i) => btn.addEventListener('click', () => switchTab(i)));
renderCurrentTab();
updateNavButtons();
});