Structured Products Investment Analyzer

General Parameters

Protection Features

Upside Features

Downside Features (if not 100% Principal Protected)

Fixed Coupons (Optional)

Product Performance Analysis

Summary of Input Parameters:

Payoff Profile at Maturity:

Illustrative Payoff Chart (Final Value vs. Underlying Performance)

Key Performance Metrics:

Please ensure all general parameters (Investment Amount, Initial Level, Term) are valid positive numbers.

"; paramsSummaryEl.innerHTML = ""; payoffChartEl.innerHTML = ""; keyMetricsEl.innerHTML = ""; downloadPdfBtn.style.display = 'none'; return; } const params = { invAmt: formCache.investmentAmount(), initialS: formCache.underlyingInitialLevel(), term: formCache.termYears(), protection: formCache.principalProtectionLevel(), partRate: formCache.participationRate(), underlyingCapPart: formCache.capOnUnderlyingForParticipation(), // 0 if no cap overallReturnCap: formCache.overallReturnCap(), // 0 if no cap hasBarrier: formCache.hasBarrier(), barrierLvl: formCache.barrierLevel(), hasCoupons: formCache.hasFixedCoupons(), couponRate: formCache.annualFixedCouponRate(), }; // Display input summary let summaryHTML = '
    '; summaryHTML += `
  • Investment Amount: $${params.invAmt.toLocaleString()}
  • `; summaryHTML += `
  • Underlying Initial Level: ${params.initialS}
  • `; summaryHTML += `
  • Term: ${params.term} Years
  • `; summaryHTML += `
  • Principal Protection: ${params.protection*100}%
  • `; summaryHTML += `
  • Upside Participation Rate: ${params.partRate*100}%
  • `; if(params.underlyingCapPart > 0) summaryHTML += `
  • Cap on Underlying for Participation: ${params.underlyingCapPart*100}%
  • `; if(params.overallReturnCap > 0) summaryHTML += `
  • Overall Cap on Product Return (from Underlying): ${params.overallReturnCap*100}%
  • `; if(params.hasBarrier) summaryHTML += `
  • Barrier Level: ${params.barrierLvl*100}% of Initial
  • `; if(params.hasCoupons) summaryHTML += `
  • Annual Fixed Coupon Rate: ${params.couponRate*100}%
  • `; summaryHTML += '
'; paramsSummaryEl.innerHTML = summaryHTML; const scenarios = [-0.75, -0.50, -0.30, -0.20, -0.10, 0, 0.10, 0.20, 0.30, 0.50, 0.75, 1.0]; // Underlying performance let results = []; let tableHTML = ''; scenarios.forEach(underlyingPerf => { const finalS = params.initialS * (1 + underlyingPerf); let productReturnFromUnderlying = 0; let finalPrincipalValue; // 1. Coupons let totalCouponReturn = 0; if (params.hasCoupons) { totalCouponReturn = params.couponRate * params.term; // Simple total coupon percentage } // 2. Principal & Underlying-linked payoff let barrierBreached = false; if (params.hasBarrier && (finalS / params.initialS) < params.barrierLvl) { barrierBreached = true; } if (barrierBreached) { // If barrier is breached, investor is typically exposed to downside from initial, // but floored by principal protection. productReturnFromUnderlying = underlyingPerf; // Follows underlying } else { // No barrier breach, or no barrier exists if (underlyingPerf >= 0) { // Upside let effectiveUnderlyingPerf = underlyingPerf; if (params.underlyingCapPart > 0 && underlyingPerf > params.underlyingCapPart) { effectiveUnderlyingPerf = params.underlyingCapPart; } productReturnFromUnderlying = effectiveUnderlyingPerf * params.partRate; if (params.overallReturnCap > 0 && productReturnFromUnderlying > params.overallReturnCap) { productReturnFromUnderlying = params.overallReturnCap; } } else { // Downside, but no barrier breach (or no barrier) productReturnFromUnderlying = underlyingPerf; // Still exposed to this downside before protection } } // Apply principal protection to the underlying-linked component's return // The minimum return from the principal/underlying component is (ProtectionLevel - 1) // e.g., 100% protection means minimum 0% return from this part. 90% protection means -10% min return. const minReturnFromPrincipalComponent = params.protection - 1; productReturnFromUnderlying = Math.max(minReturnFromPrincipalComponent, productReturnFromUnderlying); // Final product value calculation finalPrincipalValue = params.invAmt * (1 + productReturnFromUnderlying); const finalValueWithCoupons = finalPrincipalValue + (params.invAmt * totalCouponReturn); const totalProductReturnPercentage = (finalValueWithCoupons / params.invAmt) - 1; results.push({ underlyingPerf: underlyingPerf, finalS: finalS, productReturn: totalProductReturnPercentage, finalValue: finalValueWithCoupons }); tableHTML += ``; }); tableHTML += '
Underlying Perf. at MaturityUnderlying Final LevelProduct Return (%)Final Value ($)
${(underlyingPerf * 100).toFixed(0)}% ${finalS.toFixed(2)} ${(totalProductReturnPercentage * 100).toFixed(2)}% $${finalValueWithCoupons.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})}
'; payoffTableContainerEl.innerHTML = tableHTML; // Key Metrics let maxGain = -Infinity, maxLoss = Infinity, breakEvenUnderlying = 'N/A'; results.forEach(r => { if (r.productReturn > maxGain) maxGain = r.productReturn; if (r.productReturn < maxLoss) maxLoss = r.productReturn; }); // Find breakeven (approximate) for (let i = 0; i < results.length -1; i++) { if ((results[i].productReturn < 0 && results[i+1].productReturn >= 0) || (results[i].productReturn > 0 && results[i+1].productReturn <= 0)) { // Linear interpolation for breakeven const r1 = results[i]; const r2 = results[i+1]; if (r2.productReturn - r1.productReturn !== 0) { // Avoid division by zero const underlyingAtBreakEven = r1.underlyingPerf - r1.productReturn * (r2.underlyingPerf - r1.underlyingPerf) / (r2.productReturn - r1.productReturn); breakEvenUnderlying = `${(underlyingAtBreakEven * 100).toFixed(2)}%`; } else if (r1.productReturn === 0) { breakEvenUnderlying = `${(r1.underlyingPerf * 100).toFixed(2)}%`; } break; } else if (results[i].productReturn === 0) { breakEvenUnderlying = `${(results[i].underlyingPerf * 100).toFixed(2)}%`; break; } } keyMetricsEl.innerHTML = `

Maximum Potential Gain (in scenarios shown): ${(maxGain * 100).toFixed(2)}%

Maximum Potential Loss (in scenarios shown): ${(maxLoss * 100).toFixed(2)}%

Approximate Break-Even Underlying Performance: ${breakEvenUnderlying}

`; // Basic Chart (Final Value) payoffChartEl.innerHTML = ''; // Clear previous chart const valuesForChart = results.map(r => r.finalValue); const minVal = Math.min(...valuesForChart, 0); // include 0 in min for scale const maxVal = Math.max(...valuesForChart); const range = maxVal - minVal; results.forEach(r => { const bar = document.createElement('div'); bar.className = 'spia-chart-bar'; let barHeight = 0; if (range > 0) { barHeight = ((r.finalValue - minVal) / range) * 100; // Percentage of max height (200px defined in CSS) } else if (r.finalValue > 0) { // All values are same and positive barHeight = 50; // arbitrary height } bar.style.height = `${Math.max(5, barHeight)}%`; // Min height of 5% for visibility bar.title = `Underlying: ${(r.underlyingPerf*100).toFixed(0)}%, Final Val: $${r.finalValue.toLocaleString()}`; const label = document.createElement('span'); label.className = 'spia-chart-bar-label'; label.textContent = `${(r.underlyingPerf*100).toFixed(0)}%`; bar.appendChild(label); payoffChartEl.appendChild(bar); }); downloadPdfBtn.style.display = 'block'; } // PDF Download downloadPdfBtn?.addEventListener('click', function () { const { jsPDF } = window.jspdf; const pdfOutputArea = document.getElementById('spiaPdfOutputArea'); if (!pdfOutputArea) { alert('PDF output area not found.'); return; } if (payoffTableContainerEl.innerHTML.includes("Please ensure all general parameters")) { alert('Please input valid parameters and analyze before downloading PDF.'); return; } html2canvas(pdfOutputArea, { scale: 2, useCORS: true, backgroundColor: '#ffffff' }) .then(canvas => { 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 = 30; const contentWidth = pdfWidth - 2 * margin; const canvasWidth = canvas.width; const canvasHeight = canvas.height; const ratio = canvasWidth / canvasHeight; let imgWidth = contentWidth; let imgHeight = imgWidth / ratio; if (imgHeight > pdfHeight - 2 * margin) { // Check if it fits with margin imgHeight = pdfHeight - 2 * margin; imgWidth = imgHeight * ratio; } pdf.addImage(imgData, 'PNG', margin, margin, imgWidth, imgHeight); pdf.save('StructuredProduct_Analysis.pdf'); }) .catch(err => { console.error("Error generating PDF:", err); alert("Error generating PDF. See console for details."); }); }); // Initial setup document.getElementById('hasBarrier').dispatchEvent(new Event('change')); document.getElementById('hasFixedCoupons').dispatchEvent(new Event('change')); switchTab('paramsTab'); // Start on the first tab });
Scroll to Top