${currentRate.toFixed(4)}
${change.toFixed(4)} (${changePct.toFixed(2)}%)
`;
// Render chart
const ctx = document.getElementById('historical-chart').getContext('2d');
if (historicalChart) historicalChart.destroy();
historicalChart = new Chart(ctx, {
type: 'line',
data: {
labels: Array.from({length: timePeriods}, (_, i) => `Day ${i + 1}`),
datasets: [{
label: `Exchange Rate for ${selectedPair}`,
data: exchangeRateSeries,
borderColor: '#3B82F6',
backgroundColor: 'rgba(59, 130, 246, 0.1)',
fill: true,
tension: 0.1
}]
},
options: { responsive: true, maintainAspectRatio: false }
});
};
const renderCorrelationMatrix = (matrix, currencies) => {
let table = '
';
table += '| | ';
currencies.forEach(c => table += `${c} | `);
table += '
';
currencies.forEach(c1 => {
table += `| ${c1} | `;
currencies.forEach(c2 => {
const val = matrix[c1][c2];
const absVal = Math.abs(val);
let cellClass = 'correlation-low';
if (c1 === c2) cellClass = 'correlation-perfect';
else if (absVal >= 0.7) cellClass = 'correlation-high';
else if (absVal >= 0.4) cellClass = 'correlation-medium';
table += `${val.toFixed(2)} | `;
});
table += '
';
});
table += '
';
correlationContainer.innerHTML = table;
};
// --- CALCULATION LOGIC ---
const calculateCorrelationMatrix = () => {
const currencies = Object.keys(currencyData);
const matrix = {};
const mean = (arr) => arr.reduce((a, b) => a + b) / arr.length;
const stdDev = (arr, avg) => Math.sqrt(arr.map(x => Math.pow(x - avg, 2)).reduce((a, b) => a + b) / arr.length);
currencies.forEach(c1 => {
matrix[c1] = {};
const series1 = currencyData[c1];
const mean1 = mean(series1);
const stdDev1 = stdDev(series1, mean1);
currencies.forEach(c2 => {
if (c1 === c2) {
matrix[c1][c2] = 1;
return;
}
const series2 = currencyData[c2];
const mean2 = mean(series2);
const stdDev2 = stdDev(series2, mean2);
let covariance = 0;
for (let i = 0; i < timePeriods; i++) {
covariance += (series1[i] - mean1) * (series2[i] - mean2);
}
covariance /= timePeriods;
const correlation = covariance / (stdDev1 * stdDev2);
matrix[c1][c2] = isNaN(correlation) ? 0 : correlation;
});
});
return { matrix, currencies: currencies };
};
// --- EVENT HANDLERS & UPDATERS ---
const updateAndRenderAll = () => {
renderPairSelector();
renderDashboard();
const { matrix, currencies } = calculateCorrelationMatrix();
renderCorrelationMatrix(matrix, currencies);
};
pairSelector.addEventListener('change', renderDashboard);
updateDashboardBtn.addEventListener('click', () => {
updateAndRenderAll();
switchTab('dashboard'); // Go to dashboard after updating
});
configTableBody.addEventListener('change', e => {
const target = e.target;
const currency = target.closest('tr').dataset.currency;
if (target.classList.contains('currency-value-input')) {
const period = parseInt(target.dataset.period);
currencyData[currency][period] = parseFloat(target.value);
}
});
configTableBody.addEventListener('focusout', e => {
if (e.target.classList.contains('currency-name-input')) {
const oldName = e.target.closest('tr').dataset.currency;
const newName = e.target.value.toUpperCase();
if (oldName !== newName && newName && !currencyData[newName]) {
const data = currencyData[oldName];
delete currencyData[oldName];
currencyData[newName] = data;
renderConfigTable(); // Re-render to update dataset attributes
} else {
e.target.value = oldName; // Revert if invalid or duplicate
}
}
});
configTableBody.addEventListener('click', e => {
if (e.target.classList.contains('remove-currency-btn')) {
const currencyToRemove = e.target.closest('tr').dataset.currency;
delete currencyData[currencyToRemove];
renderConfigTable();
}
});
addCurrencyBtn.addEventListener('click', () => {
let newName = 'NEW';
let counter = 1;
while(currencyData[newName]) {
newName = `NEW${counter++}`;
}
currencyData[newName] = Array(timePeriods).fill(0);
renderConfigTable();
});
downloadPdfBtn.addEventListener('click', async () => {
const pdfContainer = document.getElementById('pdf-container');
pdfContainer.classList.remove('hidden');
// Clone chart and matrix into a temporary container for clean capture
const chartCanvas = document.getElementById('historical-chart');
const matrixEl = document.getElementById('correlation-matrix-container');
pdfContainer.innerHTML = `
Forex Insights Report: ${pairSelector.value}
Currency Correlation Matrix
${matrixEl.innerHTML}
`;
await new Promise(resolve => setTimeout(resolve, 100)); // Allow DOM to update
html2canvas(pdfContainer.firstElementChild, { scale: 2 }).then(canvas => {
const imgData = canvas.toDataURL('image/png');
const { jsPDF } = window.jspdf;
const pdf = new jsPDF({ orientation: 'portrait', unit: 'pt', format: 'a4' });
const pdfWidth = pdf.internal.pageSize.getWidth();
const pdfHeight = pdf.internal.pageSize.getHeight();
const margin = 40;
const contentWidth = pdfWidth - margin * 2;
const canvasAspectRatio = canvas.width / canvas.height;
const contentHeight = contentWidth / canvasAspectRatio;
const finalHeight = contentHeight > pdfHeight - margin*2 ? pdfHeight - margin*2 : contentHeight;
pdf.addImage(imgData, 'PNG', margin, margin, contentWidth, finalHeight);
pdf.save('Forex-Insights-Report.pdf');
pdfContainer.innerHTML = '';
pdfContainer.classList.add('hidden');
});
});
// --- INITIALIZATION ---
initialize();
});