No hedging instruments added yet.
`;
return;
}
container.innerHTML = '';
state.hedges.forEach(h => {
const div = document.createElement('div');
div.className = 'p-3 border rounded-md bg-white flex justify-between items-center';
let description = '';
if (h.type === 'futures') {
description = `
${h.contracts} x ${h.action.charAt(0).toUpperCase() + h.action.slice(1)} Futures @ $${h.price.toFixed(2)}`;
} else {
description = `
${h.contracts} x Buy ${h.optionType.charAt(0).toUpperCase() + h.optionType.slice(1)} @ Strike $${h.strike.toFixed(2)} (Premium: $${h.premium.toFixed(2)})`;
}
div.innerHTML = `
${description}
`;
container.appendChild(div);
});
// Add event listeners to new remove buttons
container.querySelectorAll('button').forEach(btn => {
btn.addEventListener('click', (e) => removeHedge(Number(e.currentTarget.dataset.id)));
});
}
function renderAnalysisTable(results) {
const table = document.getElementById('analysis-table');
const formatCurrency = (val) => val.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
let tableHTML = `
| Future Price |
Unhedged P/L |
Hedge P/L |
Net Hedged P/L |
Effective Price |
`;
results.forEach(r => {
const netPLClass = r.netPL >= 0 ? 'text-green-600' : 'text-red-600';
tableHTML += `
| ${formatCurrency(r.futurePrice)} |
${formatCurrency(r.unhedgedPL)} |
${formatCurrency(r.hedgePL)} |
${formatCurrency(r.netPL)} |
${formatCurrency(r.effectivePrice)} |
`;
});
tableHTML += '';
table.innerHTML = tableHTML;
}
// --- CHARTING ---
function createAnalysisChart() {
const ctx = document.getElementById('analysisChart')?.getContext('2d');
if (ctx) {
analysisChart = new Chart(ctx, {
type: 'line',
data: { labels: [], datasets: [] },
options: {
responsive: true, maintainAspectRatio: false,
scales: {
x: { title: { display: true, text: 'Future Market Price ($)' } },
y: { title: { display: true, text: 'Profit / Loss ($)' } }
},
plugins: { tooltip: { mode: 'index', intersect: false } }
}
});
}
}
function updateAnalysisChart(results) {
if (!analysisChart) return;
analysisChart.data.labels = results.map(r => r.futurePrice.toFixed(2));
analysisChart.data.datasets = [
{ label: 'Unhedged P/L', data: results.map(r => r.unhedgedPL), borderColor: '#f87171', tension: 0.1, fill: false },
{ label: 'Net Hedged P/L', data: results.map(r => r.netPL), borderColor: '#2563eb', tension: 0.1, fill: false, borderWidth: 3 },
];
if (state.hedges.length > 0) {
analysisChart.data.datasets.push(
{ label: 'Hedge P/L', data: results.map(r => r.hedgePL), borderColor: '#34d399', tension: 0.1, fill: false, borderDash: [5, 5] },
);
}
analysisChart.update();
}
// --- TAB NAVIGATION ---
function updateActiveTab() {
tabPanels.forEach((panel, index) => panel.classList.toggle('hidden', index !== currentTabIndex));
tabs.forEach((tab, index) => {
tab.classList.toggle('tab-btn-active', index === currentTabIndex);
tab.classList.toggle('tab-btn-inactive', index !== currentTabIndex);
tab.classList.toggle('border-blue-600', index === currentTabIndex);
tab.classList.toggle('border-transparent', index !== currentTabIndex);
});
updateNavButtons();
}
function updateNavButtons() {
prevBtn.disabled = currentTabIndex === 0;
nextBtn.disabled = currentTabIndex === tabs.length - 1;
prevBtn.classList.toggle('opacity-50', prevBtn.disabled);
nextBtn.classList.toggle('opacity-50', nextBtn.disabled);
}
function navigateNext() { if (currentTabIndex < tabs.length - 1) { currentTabIndex++; updateActiveTab(); } }
function navigatePrev() { if (currentTabIndex > 0) { currentTabIndex--; updateActiveTab(); } }
// --- PDF DOWNLOAD ---
function downloadPDF() {
const { jsPDF } = window.jspdf;
const doc = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' });
const primaryColor = '#1e40af'; // blue-800
const today = new Date().toLocaleDateString('en-US');
// Page Header
const addHeader = (title) => {
doc.setFillColor('#dbeafe'); // blue-100
doc.rect(0, 0, 210, 28, 'F');
doc.setFontSize(20);
doc.setTextColor(primaryColor);
doc.setFont('helvetica', 'bold');
doc.text(title, 14, 18);
doc.setFontSize(10);
doc.setTextColor('#374151');
doc.text(`Report Date: ${today}`, 200, 18, { align: 'right' });
};
// --- Page 1: Summary ---
addHeader('Commodity Hedging Strategy');
let yPos = 40;
const addSection = (title, content) => {
doc.setFontSize(14);
doc.setTextColor(primaryColor);
doc.text(title, 14, yPos);
yPos += 7;
doc.setFontSize(11);
doc.setTextColor('#1f2937');
content.forEach(line => {
doc.text(line, 14, yPos);
yPos += 6;
});
yPos += 4;
};
const commodityName = commoditySelect.options[commoditySelect.selectedIndex].text;
addSection('Physical Position', [
`Commodity: ${commodityName}`,
`Position: ${state.position.type.charAt(0).toUpperCase() + state.position.type.slice(1)}`,
`Quantity: ${state.position.quantity.toLocaleString()} ${state.position.unit}`,
`Assumed Market Price: $${state.position.price.toFixed(2)}`,
]);
if (state.hedges.length > 0) {
const hedgeContent = state.hedges.map(h => {
if (h.type === 'futures') {
return `${h.action.charAt(0).toUpperCase() + h.action.slice(1)} ${h.contracts} Futures Contract(s) @ $${h.price.toFixed(2)}`;
} else {
return `Buy ${h.contracts} ${h.optionType.charAt(0).toUpperCase() + h.optionType.slice(1)} Option(s) | Strike: $${h.strike.toFixed(2)}, Premium: $${h.premium.toFixed(2)}`;
}
});
addSection('Hedging Strategy', hedgeContent);
} else {
addSection('Hedging Strategy', ['No hedges applied.']);
}
// --- Page 2: Analysis ---
if(document.getElementById('analysis-table').rows.length > 1) {
doc.addPage();
addHeader('Scenario Analysis');
if (analysisChart) {
doc.addImage(analysisChart.toBase64Image('image/jpeg', 0.8), 'JPEG', 14, 40, 182, 90);
}
const head = [['Future Price', 'Unhedged P/L', 'Hedge P/L', 'Net P/L', 'Effective Price']];
const body = Array.from(document.getElementById('analysis-table').rows).slice(1).map(row =>
Array.from(row.cells).map(cell => cell.innerText)
);
doc.autoTable({
startY: 140,
head: head,
body: body,
theme: 'grid',
headStyles: { fillColor: primaryColor }
});
}
doc.save(`Hedging_Strategy_${commodityName.replace(' ','_')}_${today.replace(/\//g, '-')}.pdf`);
}
// --- RUN INITIALIZATION ---
initialize();
});