Convertible Arbitrage Profitability Calculator

Convertible Bond Details e.g., 98 for a bond trading at 98% of par. Number of shares received if $1000 of face value is converted.
Underlying Stock Details
Position & Timeframe Total quantity of convertible bonds in the position. The period for which aggregate income/costs will be shown in the report. Annual figures are primary.

Please fill in the inputs and click "Next" or "Calculate" on the Inputs tab, or re-click this tab's button.

Convertible Arbitrage Profitability Report

Error: Please ensure all input fields are valid numbers.

"; if (!isReport) document.getElementById('capc-analysisOutput').innerHTML = errorMsg; else { document.getElementById('capc-reportInputsSummary').innerHTML = errorMsg; document.getElementById('capc-reportAnalysisResults').innerHTML = ""; document.getElementById('capc-reportChartContainer').innerHTML = ""; } return; } // Calculations // Conversion ratio per single bond (assuming bondFaceValuePerBond input is used to scale) const conversionRatioPerBond = conversionRatioPer1000FV * (bondFaceValuePerBond / 1000); const totalBondFaceValue = numBonds * bondFaceValuePerBond; const totalBondCost = numBonds * (bondPricePer100 / 100) * bondFaceValuePerBond; const totalSharesShorted = numBonds * conversionRatioPerBond; const initialShortValue = totalSharesShorted * stockPrice; // Annual Figures const annualCouponIncome = totalBondFaceValue * annualCouponRate; const annualDividendCost = totalSharesShorted * annualDividendPerShare; const annualStockBorrowCost = initialShortValue * stockBorrowFeeRate; const netAnnualCashFlow = annualCouponIncome - annualDividendCost - annualStockBorrowCost; const netAnnualYieldOnBondCost = totalBondCost > 0 ? (netAnnualCashFlow / totalBondCost) * 100 : 0; // Total Over Horizon const totalCouponIncomeHorizon = annualCouponIncome * investmentHorizonYears; const totalDividendCostHorizon = annualDividendCost * investmentHorizonYears; const totalStockBorrowCostHorizon = annualStockBorrowCost * investmentHorizonYears; const netCashFlowHorizon = netAnnualCashFlow * investmentHorizonYears; // Optional: Parity & Implied Conversion Price const bondParityValue = stockPrice * conversionRatioPerBond; // Parity value of one bond const impliedConversionPrice = (bondPricePer100 / 100 * bondFaceValuePerBond) / conversionRatioPerBond; // --- Display Results --- let resultsHTML = `

Position Setup & Initial Costs

Total Bonds Purchased: ${numBonds.toLocaleString()}
Total Face Value of Bonds: ${capc_formatCurrency(totalBondFaceValue)}
Total Cost of Bonds Purchased: ${capc_formatCurrency(totalBondCost)}
Total Shares to Short (Delta Hedge): ${totalSharesShorted.toLocaleString()} shares
Initial Value of Short Position: ${capc_formatCurrency(initialShortValue)}

Annual Profitability Analysis

Gross Annual Coupon Income: ${capc_formatCurrency(annualCouponIncome)}
Annual Dividend Payout on Short: (${capc_formatCurrency(annualDividendCost)})
Annual Stock Borrowing Cost: (${capc_formatCurrency(annualStockBorrowCost)})

Net Annual Cash Flow: ${capc_formatCurrency(netAnnualCashFlow)}
Net Annual Yield on Bond Cost: ${capc_formatPercent(netAnnualYieldOnBondCost)}

Bond Valuation Metrics (per bond)

Conversion Ratio (per bond): ${conversionRatioPerBond.toFixed(2)} shares
Bond Parity Value (Stock Price * Conv. Ratio): ${capc_formatCurrency(bondParityValue)}
Implied Conversion Price (Bond Cost / Conv. Ratio): ${capc_formatCurrency(impliedConversionPrice)}
${investmentHorizonYears > 1 || investmentHorizonYears !== 1 ? `

Total Over Investment Horizon (${investmentHorizonYears} Years)

Total Coupon Income: ${capc_formatCurrency(totalCouponIncomeHorizon)}
Total Dividend Payout: (${capc_formatCurrency(totalDividendCostHorizon)})
Total Stock Borrow Cost: (${capc_formatCurrency(totalStockBorrowCostHorizon)})

Net Cash Flow Over Horizon: ${capc_formatCurrency(netCashFlowHorizon)}
` : ''} `; let chartHTML = `

Annual Income vs. Expenses

`; if (isReport) { let inputsSummaryHTML = `

Inputs Summary

Bond Purchase Price: ${capc_formatCurrency(bondPricePer100, '')}/$100 FV
Bond Face Value: ${capc_formatCurrency(bondFaceValuePerBond)}
Annual Coupon Rate: ${capc_formatPercent(annualCouponRate*100)}
Conversion Ratio: ${conversionRatioPer1000FV} shares per $1000 FV
Current Stock Price: ${capc_formatCurrency(stockPrice)}
Annual Dividend/Share: ${capc_formatCurrency(annualDividendPerShare)}
Stock Borrow Fee: ${capc_formatPercent(stockBorrowFeeRate*100)}
Number of Bonds: ${numBonds.toLocaleString()}
Investment Horizon: ${investmentHorizonYears} Years
`; document.getElementById('capc-reportInputsSummary').innerHTML = inputsSummaryHTML; document.getElementById('capc-reportAnalysisResults').innerHTML = resultsHTML; document.getElementById('capc-reportChartContainer').innerHTML = chartHTML; // Add chart to report tab } else { document.getElementById('capc-analysisOutput').innerHTML = resultsHTML + chartHTML; // Add chart to analysis tab } // Chart if (capc_incomeExpenseChart) { capc_incomeExpenseChart.destroy(); } const ctx = document.getElementById('capc-profitabilityChart')?.getContext('2d'); if (ctx) { capc_incomeExpenseChart = new Chart(ctx, { type: 'bar', data: { labels: ['Gross Coupon Income', 'Dividend Cost', 'Stock Borrow Cost', 'Net Annual Cash Flow'], datasets: [{ label: 'Amount ($)', data: [annualCouponIncome, -annualDividendCost, -annualStockBorrowCost, netAnnualCashFlow], backgroundColor: [ 'rgba(40, 167, 69, 0.7)', // Income (Green) 'rgba(220, 53, 69, 0.7)', // Expense (Red) 'rgba(220, 53, 69, 0.5)', // Expense (Lighter Red) netAnnualCashFlow >= 0 ? 'rgba(0, 123, 255, 0.7)' : 'rgba(255, 193, 7, 0.7)' // Net (Blue or Amber) ], borderColor: [ 'rgba(40, 167, 69, 1)', 'rgba(220, 53, 69, 1)', 'rgba(220, 53, 69, 0.8)', netAnnualCashFlow >= 0 ? 'rgba(0, 123, 255, 1)' : 'rgba(255, 193, 7, 1)' ], borderWidth: 1 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true, ticks: { callback: function(value) { return capc_formatCurrency(value, '$'); } } } } } }); } } async function capc_downloadPDF() { const downloadButton = document.getElementById('capc-downloadPdfButton'); if (!downloadButton) return; const originalButtonText = downloadButton.textContent; downloadButton.textContent = 'Generating PDF...'; downloadButton.disabled = true; const { jsPDF } = window.jspdf; const reportContentElement = document.getElementById('capc-reportContainerForPdf'); if (!reportContentElement) { alert("Error: Report content element not found."); downloadButton.textContent = originalButtonText; downloadButton.disabled = false; return; } // Make sure results are up-to-date for the PDF capc_calculateAndDisplay(true); // Allow chart to render before capturing await new Promise(resolve => setTimeout(resolve, 500)); try { // Clone the report content for PDF generation to isolate it and potentially modify for print const clonedReportElement = reportContentElement.cloneNode(true); // Ensure chart is part of the cloned content const chartCanvas = document.getElementById('capc-profitabilityChart'); if (chartCanvas && capc_incomeExpenseChart) { const chartImgData = capc_incomeExpenseChart.toBase64Image(); const img = document.createElement('img'); img.src = chartImgData; img.style.maxWidth = '90%'; img.style.height = 'auto'; img.style.display = 'block'; img.style.margin = '15px auto'; const chartContainerInClone = clonedReportElement.querySelector('#capc-profitabilityChart'); if (chartContainerInClone) { // if canvas is directly in report structure (as it is) chartContainerInClone.parentNode.replaceChild(img, chartContainerInClone); } else { // fallback if chart is deeply nested or ID changes in clone const targetChartDiv = clonedReportElement.querySelector('.capc-chart-section canvas'); // more generic if(targetChartDiv) targetChartDiv.parentNode.replaceChild(img, targetChartDiv); } } // Append to body to ensure rendering IF complex CSS dependencies exist, then remove document.body.appendChild(clonedReportElement); const canvas = await html2canvas(clonedReportElement, { scale: 2, // Improves quality useCORS: true, logging: false, width: clonedReportElement.scrollWidth, // ensure full width is captured height: clonedReportElement.scrollHeight, // ensure full height is captured windowWidth: clonedReportElement.scrollWidth, windowHeight: clonedReportElement.scrollHeight }); document.body.removeChild(clonedReportElement); // Clean up clone const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' }); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = pdf.internal.pageSize.getHeight(); const imgWidth = canvas.width; const imgHeight = canvas.height; // Calculate the aspect ratio const canvasAspectRatio = imgWidth / imgHeight; const pageAspectRatio = pdfWidth / pdfHeight; let newImgWidth, newImgHeight; let xOffset = 0; let yOffset = 15; // top margin if (imgHeight > pdfHeight - (2 * yOffset) || imgWidth > pdfWidth - (2* xOffset)) { // if image is larger than printable area if (canvasAspectRatio > (pdfWidth / (pdfHeight - 2 * yOffset))) { // image is wider newImgWidth = pdfWidth - (2 * xOffset); newImgHeight = newImgWidth / canvasAspectRatio; } else { // image is taller newImgHeight = pdfHeight - (2 * yOffset); newImgWidth = newImgHeight * canvasAspectRatio; } } else { // image is smaller than page newImgWidth = imgWidth; newImgHeight = imgHeight; } // Center the image xOffset = (pdfWidth - newImgWidth) / 2; pdf.addImage(imgData, 'PNG', xOffset, yOffset, newImgWidth, newImgHeight); pdf.save('Convertible_Arbitrage_Report.pdf'); } catch (error) { console.error("Error generating PDF:", error); alert("An error occurred while generating the PDF. Please check the console."); } finally { downloadButton.textContent = originalButtonText; downloadButton.disabled = false; } }
Scroll to Top