High-Yield Debt Portfolio Risk Calculator

High-Yield Debt Portfolio Risk Calculator

Enter Bond Details


Current Portfolio

Your added bonds will appear here.

Set Illustrative Risk Parameters

This is a general assumption for illustrative loss estimation.
A parallel shift in interest rates to illustrate potential price sensitivity (simplified).

Note: Default probabilities are illustrative and pre-set internally based on rating categories for this simplified calculator. They are not market-derived probabilities.

Portfolio Risk Report

Click "Calculate Risk Report" to view the analysis. Ensure portfolio and parameters are set.

Weighted Average Years to Maturity: ${weightedAvgMaturity.toFixed(2)} years

`; reportHTML += `

Rating Distribution (by Principal):

`; if (Object.keys(ratingDistribution).length > 0) { reportHTML += `
    `; for (const rating in ratingDistribution) { const percentage = totalPrincipal > 0 ? (ratingDistribution[rating] / totalPrincipal * 100).toFixed(2) : 0; reportHTML += `
  • ${rating}: $${ratingDistribution[rating].toLocaleString()} (${percentage}%)
  • `; } reportHTML += `
`; } else { reportHTML += `

No bonds in portfolio to show distribution.

`; } reportHTML += `

Illustrative Risk Metrics:

`; reportHTML += `

Assumed Recovery Rate: ${(recoveryRate * 100).toFixed(0)}%

`; reportHTML += `

Illustrative Expected Loss (Annualized, Simplified): $${illustrativeExpectedLoss.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})}

`; reportHTML += `

Note: Expected Loss is highly illustrative, based on simplified default probabilities and recovery rate.

`; reportHTML += `

Assumed Interest Rate Shock: ${(interestRateShock * 100).toFixed(2)}% points

`; reportHTML += `

Illustrative Portfolio Value Change from Rate Shock (Simplified): $${illustrativeInterestRateImpact.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})}

`; reportHTML += `

Note: Interest rate impact is a very simplified approximation using weighted average maturity as a duration proxy. Actual impact can vary significantly.

`; riskReportOutputDiv.innerHTML = reportHTML; if(pdfButtonContainer) pdfButtonContainer.style.display = 'block'; } if (calculateRiskBtn) { calculateRiskBtn.addEventListener('click', calculateAndDisplayRiskReport); } // --- PDF Download --- function loadJsPdfIfNeeded(callback) { if (jsPdfLoaded) { if (callback) callback(); return; } const script = document.createElement('script'); script.src = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js'; script.onload = () => { jsPdfLoaded = true; console.log("jsPDF loaded dynamically."); if (callback) callback(); }; script.onerror = () => { console.error("Failed to load jsPDF. PDF functionality unavailable."); alert("Error: Could not load PDF library."); }; document.head.appendChild(script); } function downloadReportAsPdf() { if (!jsPdfLoaded) { alert("PDF library not loaded."); return; } if (!riskReportData) { alert("No report data to download. Please calculate the report first."); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF({ unit: 'pt', format: 'a4' }); const r = riskReportData; const pageMargin = 40; const pageWidth = doc.internal.pageSize.getWidth() - 2 * pageMargin; let y = pageMargin; function addMainTitle(text) { doc.setFontSize(18); doc.setFont(undefined, 'bold'); doc.setTextColor(139, 0, 0); // Dark Red doc.text(text, doc.internal.pageSize.getWidth() / 2, y, { align: 'center' }); y += 35; } function addSectionTitle(text) { if (y > doc.internal.pageSize.getHeight() - 80) { doc.addPage(); y = pageMargin; } doc.setFontSize(14); doc.setFont(undefined, 'bold'); doc.setTextColor(178, 34, 34); // Firebrick doc.text(text, pageMargin, y); y += 25; } function addLine(key, value, keyColor = [51,51,51], valueColor = [51,51,51], keyStyle = 'bold', valueStyle = 'normal') { if (y > doc.internal.pageSize.getHeight() - 40) { doc.addPage(); y = pageMargin; } doc.setFontSize(10); doc.setFont(undefined, keyStyle); doc.setTextColor(keyColor[0], keyColor[1], keyColor[2]); doc.text(key, pageMargin, y); doc.setFont(undefined, valueStyle); doc.setTextColor(valueColor[0], valueColor[1], valueColor[2]); const valueText = String(value); doc.text(valueText, pageMargin + 200, y, { align: 'left', maxWidth: pageWidth - 200 - 5 }); y += 18; } function addListItem(text) { if (y > doc.internal.pageSize.getHeight() - 35) { doc.addPage(); y = pageMargin; } doc.setFontSize(10); doc.setFont(undefined, 'normal'); doc.setTextColor(51,51,51); const splitText = doc.splitTextToSize(`• ${text}`, pageWidth - 15); doc.text(splitText, pageMargin + 15, y); y += (doc.getTextDimensions(splitText).h) + 4; } function addInfo(text) { if (y > doc.internal.pageSize.getHeight() - 50) { doc.addPage(); y = pageMargin; } doc.setFontSize(8.5); doc.setFont(undefined, 'italic'); doc.setTextColor(100,100,100); const splitText = doc.splitTextToSize(text, pageWidth); doc.text(splitText, pageMargin, y); y += (doc.getTextDimensions(splitText).h) + 10; } addMainTitle("High-Yield Debt Portfolio Risk Report"); addSectionTitle("Portfolio Summary"); addLine("Total Portfolio Principal:", `$${r.totalPrincipal.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})}`); addLine("Weighted Average Coupon:", `${r.weightedAvgCoupon.toFixed(2)}%`); addLine("Weighted Avg. Yrs to Maturity:", `${r.weightedAvgMaturity.toFixed(2)} years`); addSectionTitle("Portfolio Composition (Bonds)"); if (r.portfolio.length > 0) { r.portfolio.forEach(bond => { if (y > doc.internal.pageSize.getHeight() - 70) { doc.addPage(); y = pageMargin; } // Check space before each bond addListItem(`${bond.bondName} - Principal: $${bond.principal.toLocaleString()}, Coupon: ${bond.coupon}%, Maturity: ${bond.maturity} yrs, Rating: ${bond.rating}`); }); } else { addListItem("No bonds in portfolio."); } y += 5; addSectionTitle("Rating Distribution (by Principal)"); if (Object.keys(r.ratingDistribution).length > 0) { for (const rating in r.ratingDistribution) { const percentage = r.totalPrincipal > 0 ? (r.ratingDistribution[rating] / r.totalPrincipal * 100).toFixed(2) : 0; addListItem(`${rating}: $${r.ratingDistribution[rating].toLocaleString()} (${percentage}%)`); } } else { addListItem("No bonds to show distribution."); } y += 5; addSectionTitle("Illustrative Risk Metrics"); addLine("Assumed Recovery Rate:", `${(r.recoveryRate * 100).toFixed(0)}%`); addLine("Illustrative Expected Loss (Annualized, Simplified):", `$${r.illustrativeExpectedLoss.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})}`); addInfo("Note: Expected Loss is highly illustrative, based on simplified default probabilities (pre-set by rating category) and the user-defined recovery rate. It is not a prediction."); addLine("Assumed Interest Rate Shock:", `${(r.interestRateShock * 100).toFixed(2)}% points`); addLine("Illustrative Portfolio Value Change from Rate Shock (Simplified):", `$${r.illustrativeInterestRateImpact.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})}`); addInfo("Note: Interest rate impact is a very simplified approximation using weighted average maturity as a proxy for duration. Actual market value changes can differ significantly based on complex factors not modeled here."); // Footer const pageCount = doc.internal.getNumberOfPages(); for (let i = 1; i <= pageCount; i++) { doc.setPage(i); doc.setFontSize(8); doc.setTextColor(150); doc.text(`Page ${i} of ${pageCount} - High-Yield Debt Portfolio Risk Calculator`, pageMargin, doc.internal.pageSize.getHeight() - 20); doc.text(`Generated: ${new Date().toLocaleString()}`, doc.internal.pageSize.getWidth() - pageMargin - doc.getTextWidth(new Date().toLocaleString()), doc.internal.pageSize.getHeight() - 20); } doc.save('High_Yield_Debt_Portfolio_Risk_Report.pdf'); } if (downloadPdfBtn) { downloadPdfBtn.addEventListener('click', () => loadJsPdfIfNeeded(downloadReportAsPdf)); } // --- Initialization --- renderPortfolioTable(); // Initial render for empty state showTab(0); loadJsPdfIfNeeded(); // Attempt to load jsPDF early });
Scroll to Top