Commodities Futures Market Tracker

Commodities Futures Market Tracker 📈

  • 1. Commodity Specs
  • 2. My Tracked Contracts
  • 3. Portfolio Summary

Commodity Specifications

Select a commodity to view its contract specifications.

My Tracked Contracts (Manual Input)

Portfolio Summary & Resources

Overall Portfolio P&L:

Total Unrealized P&L: $0.00


Tracked Contracts Details:

No contracts tracked yet, or P&L not calculated.

View on Exchange »

`; } else { CMFT_commoditySpecsDiv.innerHTML = "

Select a commodity to view its contract specifications.

"; } } function CMFT_formatCurrency(value) { if (isNaN(parseFloat(value))) return "$0.00"; const absValue = Math.abs(parseFloat(value)); const formatted = `$${absValue.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; return parseFloat(value) < 0 ? `-${formatted}` : formatted; } function CMFT_addTrackedContractFields() { if (CMFT_trackedContractCount >= CMFT_MAX_TRACKED_CONTRACTS) { alert(`Maximum of ${CMFT_MAX_TRACKED_CONTRACTS} contracts can be tracked.`); return; } CMFT_trackedContractCount++; const contractId = CMFT_trackedContractCount; const entryDiv = document.createElement('div'); entryDiv.classList.add('cmft-tracked-contract-entry'); entryDiv.id = `cmft-contract-entry-${contractId}`; const today = new Date().toISOString().split('T')[0]; const commodityOptions = Object.keys(CMFT_COMMODITY_DATA).map(key => ``).join(''); const selectedCommodityFromTab1 = CMFT_selectCommodity ? CMFT_selectCommodity.value : ""; entryDiv.innerHTML = `
Tracking Entry #${contractId}

Price Change: N/A

Unrealized P&L/Contract: N/A

Total Unrealized P&L: N/A

`; if (CMFT_trackedContractsContainer) CMFT_trackedContractsContainer.appendChild(entryDiv); // Pre-select commodity if one was chosen in Tab 1 and this is the first added contract const commodityNameSelect = document.getElementById(`cmft-commodityName-${contractId}`); const commodityNameCustomInput = document.getElementById(`cmft-commodityNameCustom-${contractId}`); if (commodityNameSelect && selectedCommodityFromTab1 && CMFT_COMMODITY_DATA[selectedCommodityFromTab1]) { commodityNameSelect.value = selectedCommodityFromTab1; if(commodityNameCustomInput) commodityNameCustomInput.style.display = 'none'; } else if (commodityNameSelect) { commodityNameSelect.value = ""; // Ensure it's on "-- Select --" if(commodityNameCustomInput) commodityNameCustomInput.style.display = 'block'; } if(commodityNameSelect && commodityNameCustomInput) { commodityNameSelect.addEventListener('change', function() { commodityNameCustomInput.style.display = this.value === "" ? 'block' : 'none'; }); } } function CMFT_removeTrackedContract(contractId) { const entryDiv = document.getElementById(`cmft-contract-entry-${contractId}`); if (entryDiv && CMFT_trackedContractsContainer) { CMFT_trackedContractsContainer.removeChild(entryDiv); // Note: Does not decrement CMFT_trackedContractCount to keep IDs unique. // If many are added/removed, this could hit limit sooner. Simpler for now. } CMFT_updatePortfolioSummary(); // Recalculate overall P&L } function CMFT_calculatePnLSingle(contractId, fromPortfolioUpdate = false) { const commodityNameEl = document.getElementById(`cmft-commodityName-${contractId}`); let commodityName = commodityNameEl ? commodityNameEl.value : ""; if (commodityName === "" && document.getElementById(`cmft-commodityNameCustom-${contractId}`)) { // Check custom input if select is empty commodityName = document.getElementById(`cmft-commodityNameCustom-${contractId}`).value; } const entryPriceEl = document.getElementById(`cmft-entryPrice-${contractId}`); const numContractsEl = document.getElementById(`cmft-numContracts-${contractId}`); const currentPriceEl = document.getElementById(`cmft-currentPrice-${contractId}`); const pnlDetailsDiv = document.getElementById(`cmft-pnlDetails-${contractId}`); if (!commodityName || !entryPriceEl || !numContractsEl || !currentPriceEl || !pnlDetailsDiv) { if(pnlDetailsDiv) pnlDetailsDiv.innerHTML = "

Error: Missing input fields for this entry.

"; return 0; // Return 0 PNL if error } const entryPrice = parseFloat(entryPriceEl.value); const numContracts = parseInt(numContractsEl.value); const currentPrice = parseFloat(currentPriceEl.value); if (isNaN(entryPrice) || isNaN(numContracts) || isNaN(currentPrice) || numContracts === 0) { pnlDetailsDiv.innerHTML = `

Price Change: N/A

Unrealized P&L/Contract: N/A

Total Unrealized P&L: N/A

`; if (!fromPortfolioUpdate) CMFT_updatePortfolioSummary(); // Update summary even if this one is N/A return 0; } const commodityInfo = CMFT_COMMODITY_DATA[commodityName]; // Use the selected/typed name if (!commodityInfo) { pnlDetailsDiv.innerHTML = `

Warning: Commodity '${commodityName}' not in database. P&L cannot be calculated accurately without tick size/value. Assuming 1 point = $1.

Total Unrealized P&L (Approx): ${CMFT_formatCurrency((currentPrice - entryPrice) * numContracts)}

`; if (!fromPortfolioUpdate) CMFT_updatePortfolioSummary(); return (currentPrice - entryPrice) * numContracts; // Basic P&L if commodity unknown } const priceChange = currentPrice - entryPrice; // For P&L calc, use tickValue and tickSize // P&L = (Price Change / Tick Size) * Tick Value * Number of Contracts // If short, price change is effectively (Entry - Current) let pnlPerContract; if (numContracts > 0) { // Long pnlPerContract = (priceChange / commodityInfo.tickSize) * commodityInfo.tickValue; } else { // Short pnlPerContract = ((entryPrice - currentPrice) / commodityInfo.tickSize) * commodityInfo.tickValue; } const totalPnl = pnlPerContract * Math.abs(numContracts); pnlDetailsDiv.innerHTML = `

Price Change: ${priceChange.toFixed(commodityName.startsWith("Corn") ? 2: (commodityInfo.tickSize.toString().split('.')[1] || '').length)}

Unrealized P&L/Contract: ${CMFT_formatCurrency(pnlPerContract)}

Total Unrealized P&L: ${CMFT_formatCurrency(totalPnl)}

`; if (!fromPortfolioUpdate) CMFT_updatePortfolioSummary(); // Update overall P&L from single calc return totalPnl; } function CMFT_updatePortfolioSummary() { if (!CMFT_trackedContractsContainer || !CMFT_totalPortfolioPnlSpan || !CMFT_trackedContractsSummaryTableContainer) return; let overallTotalPnl = 0; const contractEntries = CMFT_trackedContractsContainer.querySelectorAll('.cmft-tracked-contract-entry'); let tableHTML = ``; if (contractEntries.length === 0) { CMFT_trackedContractsSummaryTableContainer.innerHTML = "

No contracts currently being tracked.

"; CMFT_totalPortfolioPnlSpan.textContent = CMFT_formatCurrency(0); return; } contractEntries.forEach(entry => { const contractId = entry.id.split('-').pop(); const currentPnlForThisContract = CMFT_calculatePnLSingle(contractId, true); // true to prevent recursion overallTotalPnl += currentPnlForThisContract; const commodityNameEl = document.getElementById(`cmft-commodityName-${contractId}`); let commodityName = commodityNameEl ? commodityNameEl.value : ""; if (commodityName === "" && document.getElementById(`cmft-commodityNameCustom-${contractId}`)) { commodityName = document.getElementById(`cmft-commodityNameCustom-${contractId}`).value || "Custom"; } else if (!commodityName) { commodityName = "N/A"; } const contractMY = document.getElementById(`cmft-contractMonthYear-${contractId}`)?.value || "N/A"; const numContracts = parseInt(document.getElementById(`cmft-numContracts-${contractId}`)?.value) || 0; const entryPrice = parseFloat(document.getElementById(`cmft-entryPrice-${contractId}`)?.value) || 0; const currentPrice = parseFloat(document.getElementById(`cmft-currentPrice-${contractId}`)?.value) || 0; const lastUpdated = document.getElementById(`cmft-lastUpdated-${contractId}`)?.value || "N/A"; const pnlClass = currentPnlForThisContract >= 0 ? 'cmft-profit' : 'cmft-loss'; tableHTML += ``; }); tableHTML += `
Commodity Contract M/Y Pos. Entry Current Last Upd. Total P&L
${commodityName} ${contractMY} ${numContracts} ${entryPrice.toFixed(2)} ${currentPrice.toFixed(2)} ${lastUpdated} ${CMFT_formatCurrency(currentPnlForThisContract)}
`; CMFT_trackedContractsSummaryTableContainer.innerHTML = tableHTML; CMFT_totalPortfolioPnlSpan.className = overallTotalPnl >= 0 ? 'cmft-profit' : 'cmft-loss'; CMFT_totalPortfolioPnlSpan.textContent = CMFT_formatCurrency(overallTotalPnl); } function CMFT_updateAllPnLsAndNavigate(targetTabId) { CMFT_updatePortfolioSummary(); // This will call CMFT_calculatePnLSingle for each CMFT_navigateToTab(targetTabId); } async function CMFT_generatePDF() { if (!CMFT_portfolioSummaryOutput || !CMFT_pdfSpecificTitle) { console.error("CMFT: PDF export content area not found."); alert("Error: Could not generate PDF. Content area missing."); return; } // Ensure summary is up-to-date before PDF generation CMFT_updatePortfolioSummary(); const portfolioContent = document.getElementById('cmft-portfolio-summary-output'); if (!portfolioContent || portfolioContent.innerHTML.includes("No contracts tracked yet")) { // Do not generate PDF if no content or only default message const contractEntries = CMFT_trackedContractsContainer ? CMFT_trackedContractsContainer.querySelectorAll('.cmft-tracked-contract-entry') : []; if (contractEntries.length === 0) { alert("Please add and track at least one contract to generate a report."); return; } } CMFT_pdfSpecificTitle.style.display = 'block'; const originalToolTitle = document.querySelector('.cmft-tool-title'); if (originalToolTitle) originalToolTitle.style.display = 'none'; const pdfContentArea = document.getElementById('cmft-pdfExportContent'); const originalBg = pdfContentArea.style.backgroundColor; pdfContentArea.style.backgroundColor = '#ffffff'; try { const canvas = await html2canvas(pdfContentArea, { scale: 1.5, // Use 1.5 for better quality on potentially dense tables useCORS: true, backgroundColor: '#ffffff', windowWidth: pdfContentArea.scrollWidth, windowHeight: pdfContentArea.scrollHeight }); const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF({ orientation: 'p', // Portrait unit: 'mm', format: 'a4' }); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = pdf.internal.pageSize.getHeight(); const imgProps = pdf.getImageProperties(imgData); const imgRatio = imgProps.width / imgProps.height; let newImgWidth = pdfWidth - 20; let newImgHeight = newImgWidth / imgRatio; let position = 10; // Initial y position if (newImgHeight > pdfHeight - 20) { // Content is taller than one page const pageCanvas = document.createElement('canvas'); pageCanvas.width = canvas.width; // Use original canvas dimensions for slicing const A4_PX_HEIGHT = (pdfHeight - 20) * (canvas.width / newImgWidth); // Effective pixel height of content area on one PDF page pageCanvas.height = A4_PX_HEIGHT; const pageCtx = pageCanvas.getContext('2d'); let srcY = 0; while(srcY < canvas.height) { const srcHeight = Math.min(A4_PX_HEIGHT, canvas.height - srcY); pageCanvas.height = srcHeight; // Important to set this for each slice pageCtx.clearRect(0, 0, pageCanvas.width, pageCanvas.height); // Clear for new slice pageCtx.drawImage(canvas, 0, srcY, canvas.width, srcHeight, 0, 0, canvas.width, srcHeight); const pageImgData = pageCanvas.toDataURL('image/png'); const pageImgProps = pdf.getImageProperties(pageImgData); const pageImgRatio = pageImgProps.width / pageImgProps.height; let pageFinalWidth = pdfWidth - 20; let pageFinalHeight = pageFinalWidth / pageImgRatio; if (srcY > 0) pdf.addPage(); pdf.addImage(pageImgData, 'PNG', 10, position, pageFinalWidth, pageFinalHeight); srcY += srcHeight; } } else { // Fits on one page const x = (pdfWidth - newImgWidth) / 2; pdf.addImage(imgData, 'PNG', x, position, newImgWidth, newImgHeight); } pdf.save('Commodities_Futures_Tracker_Summary.pdf'); } catch (error) { console.error("Error generating PDF:", error); alert("An error occurred while generating the PDF: " + error.message); } finally { CMFT_pdfSpecificTitle.style.display = 'none'; if (originalToolTitle) originalToolTitle.style.display = 'block'; pdfContentArea.style.backgroundColor = originalBg; } } document.addEventListener('DOMContentLoaded', function() { CMFT_initDOMElements(); if (!CMFT_tabs || !CMFT_tabContents) { console.error("CMFT: Tool initialization failed. Essential tab elements missing."); const container = document.getElementById('cmftToolContainer'); if(container) container.innerHTML = "

Error: Tool components failed to load.

"; return; } CMFT_tabs.forEach(tab => { tab.addEventListener('click', function() { if (this.dataset && this.dataset.tab) { CMFT_navigateToTab(this.dataset.tab); } }); }); CMFT_displayCommoditySpecs(); // Initial call for default/empty state CMFT_addTrackedContractFields(); // Add one entry by default CMFT_updatePortfolioSummary(); // Initialize summary display CMFT_navigateToTab('cmft-tab1'); });
Scroll to Top