| Metric |
Conventional Bond |
Green Bond |
| Yield to Maturity (%): |
${results.conventionalYield.toFixed(2)}% |
${results.greenBondYield.toFixed(2)}% |
| Calculated Price ($): |
$${results.conventionalBondPrice.toFixed(2)} |
$${results.greenBondPrice.toFixed(2)} |
| Price Difference (Green vs. Conventional): |
$${results.priceDifference.toFixed(2)} |
Greenium: Refers to the yield advantage (lower yield) of a green bond compared to a conventional bond of similar characteristics. A positive greenium (e.g., 10 bps) means the green bond has a lower yield and thus a higher price.
A negative value indicates a "brownium" (higher yield / lower price for the green bond).
Note: This calculation uses a simplified annual compounding for illustration. Actual bond pricing can be more complex.
`;
resultsOutput.innerHTML = tableHtml;
}
/**
* Renders the yield comparison chart.
* @param {Object} results - Results from analyzeGreenBond.
*/
function renderYieldChart(results) {
if (!results) {
chartPlaceholder.classList.remove('hidden');
if (yieldChartInstance) yieldChartInstance.destroy();
return;
}
chartPlaceholder.classList.add('hidden'); // Hide placeholder
const ctx = document.getElementById('yieldComparisonChart').getContext('2d');
if (yieldChartInstance) {
yieldChartInstance.destroy();
}
yieldChartInstance = new Chart(ctx, {
type: 'bar',
data: {
labels: ['Conventional Bond Yield', 'Green Bond Yield'],
datasets: [{
label: 'Yield to Maturity (%)',
data: [results.conventionalYield, results.greenBondYield],
backgroundColor: ['#60A5FA', '#34D399'], /* Blue 400, Green 400 */
borderColor: ['#3B82F6', '#10B981'], /* Blue 500, Green 500 */
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
},
title: {
display: true,
text: 'Bond Yield Comparison',
font: { size: 18, family: 'Inter', weight: 'bold' },
color: '#1F2937'
},
tooltip: {
callbacks: {
label: function(context) {
return `${context.label}: ${context.parsed.y.toFixed(2)}%`;
}
}
}
},
scales: {
y: {
beginAtZero: false,
title: {
display: true,
text: 'Yield (%)',
font: { size: 14, family: 'Inter' },
color: '#333'
},
ticks: {
callback: function(value) { return value + '%'; },
font: { family: 'Inter' }
}
},
x: {
ticks: { font: { family: 'Inter' } }
}
}
}
});
}
/**
* Renders the price comparison chart.
* @param {Object} results - Results from analyzeGreenBond.
*/
function renderPriceChart(results) {
if (!results) {
chartPlaceholder.classList.remove('hidden');
if (priceChartInstance) priceChartInstance.destroy();
return;
}
chartPlaceholder.classList.add('hidden'); // Hide placeholder
const ctx = document.getElementById('priceComparisonChart').getContext('2d');
if (priceChartInstance) {
priceChartInstance.destroy();
}
priceChartInstance = new Chart(ctx, {
type: 'bar',
data: {
labels: ['Conventional Bond Price', 'Green Bond Price'],
datasets: [{
label: 'Calculated Price ($)',
data: [results.conventionalBondPrice, results.greenBondPrice],
backgroundColor: ['#FCA5A5', '#FDBA74'], /* Red 300, Orange 300 */
borderColor: ['#EF4444', '#F97316'], /* Red 500, Orange 500 */
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
},
title: {
display: true,
text: 'Bond Price Comparison',
font: { size: 18, family: 'Inter', weight: 'bold' },
color: '#1F2937'
},
tooltip: {
callbacks: {
label: function(context) {
return `${context.label}: $${context.parsed.y.toFixed(2)}`;
}
}
}
},
scales: {
y: {
beginAtZero: false,
title: {
display: true,
text: 'Price ($)',
font: { size: 14, family: 'Inter' },
color: '#333'
},
ticks: {
callback: function(value) { return '$' + value; },
font: { family: 'Inter' }
}
},
x: {
ticks: { font: { family: 'Inter' } }
}
}
}
});
}
/**
* Handles the PDF download functionality.
* Captures the results output and chart canvases to generate a PDF report.
*/
window.downloadPdf = async function() {
if (typeof html2canvas === 'undefined' || typeof jspdf === 'undefined' || typeof jspdf.jsPDF === 'undefined') {
displayMessage('PDF generation libraries not loaded. Please try again or refresh.');
return;
}
hideMessage();
const { jsPDF } = jspdf;
const doc = new jsPDF('p', 'pt', 'a4');
const elementsToCapture = [
document.getElementById('resultsOutput'),
document.getElementById('chartsTab') // Capture the entire charts tab which contains both canvases
];
let yPos = 40;
doc.setFontSize(22);
doc.setTextColor(51, 51, 51);
doc.text('Green Bond Yield Analysis Report', doc.internal.pageSize.getWidth() / 2, yPos, { align: 'center' });
yPos += 30;
doc.setFontSize(12);
doc.setTextColor(100, 100, 100);
doc.text(`Generated on: ${new Date().toLocaleDateString()}`, doc.internal.pageSize.getWidth() / 2, yPos, { align: 'center' });
yPos += 40;
for (const element of elementsToCapture) {
try {
const wasHidden = element.classList.contains('hidden');
if (wasHidden) {
element.classList.remove('hidden');
}
// For charts, ensure they are rendered before capturing
if (element.id === 'chartsTab' && (yieldChartInstance === null || priceChartInstance === null)) {
// If charts aren't rendered, skip or display an error
console.warn("Charts not yet rendered for PDF capture. Skipping charts section.");
if (wasHidden) { element.classList.add('hidden'); }
continue;
}
const canvas = await html2canvas(element, {
scale: 2,
useCORS: true,
backgroundColor: '#ffffff',
// Specifically for chartsTab, ensure width is not constrained by its flex container
width: element.offsetWidth,
height: element.offsetHeight
});
if (wasHidden) {
element.classList.add('hidden');
}
const imgData = canvas.toDataURL('image/png');
const imgWidth = 550;
const imgHeight = (canvas.height * imgWidth) / canvas.width;
if (yPos + imgHeight > doc.internal.pageSize.getHeight() - 40) {
doc.addPage();
yPos = 40;
}
doc.addImage(imgData, 'PNG', (doc.internal.pageSize.getWidth() - imgWidth) / 2, yPos, imgWidth, imgHeight);
yPos += imgHeight + 30;
} catch (error) {
console.error('Error capturing element for PDF:', error);
displayMessage('Failed to generate part of the PDF. Please ensure all data is loaded and visible before downloading.');
}
}
doc.save('Green_Bond_Yield_Report.pdf');
};
// Initial setup: Show the input tab and update navigation buttons when the DOM is ready
showTab('input');
});