Buy Fee (${resultsData.buyFeeValue}${resultsData.buyFeeType === 'percentage' ? '%' : '$'}): $${resultsData.buyFeeAmount.toFixed(2)}
Sell Side (${resultsData.sellExchangeName})
Total Sell Value (Quantity * Price): $${resultsData.totalSellValue.toFixed(2)}
Sell Fee (${resultsData.sellFeeValue}${resultsData.sellFeeType === 'percentage' ? '%' : '$'}): $${resultsData.sellFeeAmount.toFixed(2)}
Fees & Summary
Transfer Fee: $${resultsData.transferFee.toFixed(2)}
Total Fees (Buy + Sell + Transfer): $${resultsData.totalFees.toFixed(2)}
Total Cost (Buy Value + All Fees): $${resultsData.totalCostIncludingFees.toFixed(2)}
Total Revenue (Sell Value - Sell Fee): $${resultsData.totalRevenueAfterFees.toFixed(2)}
Gross Profit (Before Fees): $${resultsData.grossProfitBeforeFees.toFixed(2)}
Net Profit / Loss:
$${resultsData.netProfit.toFixed(2)} (${resultsData.netProfitPercentage.toFixed(2)}%)
`;
resultsArbitrageEl.style.display = 'block';
}
/**
* Handles the arbitrage calculation process.
*/
function handleArbitrageCalculate() {
const inputs = getArbitrageInputs();
if (!inputs) return;
if (!validateArbitrageInputs(inputs)) {
if(resultsArbitrageEl) resultsArbitrageEl.style.display = 'none';
return;
}
const calculationResults = calculateArbitrage(inputs);
displayArbitrageResults(calculationResults);
}
/**
* Generates and downloads a PDF of the arbitrage results.
*/
function generateArbitragePdf() {
const inputs = getArbitrageInputs();
if (!inputs) {
alert('Could not retrieve input data for PDF generation. Please calculate first.');
return;
}
if (!validateArbitrageInputs(inputs)) {
alert('Invalid inputs. Please correct them before generating PDF.');
return;
}
const resultsData = calculateArbitrage(inputs);
if (!resultsData) {
alert('Could not calculate results for PDF generation. Please try again.');
return;
}
const doc = new jsPDF();
const today = new Date();
const formattedDate = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`;
const title = `Crypto Arbitrage Report: ${resultsData.cryptoName}`;
doc.setFontSize(18);
doc.text(title, 14, 22);
doc.setFontSize(11);
doc.setTextColor(100);
doc.text(`Report Generated: ${formattedDate}`, 14, 30);
const headStyle = { fillColor: [79, 70, 229] }; // Indigo for header
const bodyStyle = { minCellHeight: 9, fontSize: 10 };
const inputSummaryBody = [
['Cryptocurrency', resultsData.cryptoName],
['Quantity', resultsData.quantity.toLocaleString()],
['Transfer Fee', `$${resultsData.transferFee.toFixed(2)}`],
];
doc.autoTable({
startY: 40,
head: [['Trade Overview', '']],
body: inputSummaryBody,
theme: 'striped',
headStyles: headStyle,
styles: bodyStyle,
columnStyles: { 0: { fontStyle: 'bold' } }
});
const buySideBody = [
['Buy Exchange', resultsData.buyExchangeName],
['Buy Price per Unit', `$${resultsData.buyPrice.toFixed(2)}`],
['Total Buy Value (Qty * Price)', `$${resultsData.totalBuyValue.toFixed(2)}`],
['Buy Fee', `${resultsData.buyFeeValue}${resultsData.buyFeeType === 'percentage' ? '%' : '$ (Fixed)'}`],
['Buy Fee Amount', `$${resultsData.buyFeeAmount.toFixed(2)}`],
];
doc.autoTable({
startY: doc.lastAutoTable.finalY + 10,
head: [['Buy Side Details', '']],
body: buySideBody,
theme: 'striped',
headStyles: headStyle,
styles: bodyStyle,
columnStyles: { 0: { fontStyle: 'bold' } }
});
const sellSideBody = [
['Sell Exchange', resultsData.sellExchangeName],
['Sell Price per Unit', `$${resultsData.sellPrice.toFixed(2)}`],
['Total Sell Value (Qty * Price)', `$${resultsData.totalSellValue.toFixed(2)}`],
['Sell Fee', `${resultsData.sellFeeValue}${resultsData.sellFeeType === 'percentage' ? '%' : '$ (Fixed)'}`],
['Sell Fee Amount', `$${resultsData.sellFeeAmount.toFixed(2)}`],
];
doc.autoTable({
startY: doc.lastAutoTable.finalY + 10,
head: [['Sell Side Details', '']],
body: sellSideBody,
theme: 'striped',
headStyles: headStyle,
styles: bodyStyle,
columnStyles: { 0: { fontStyle: 'bold' } }
});
const resultsSummaryBody = [
['Total Cost (Buy Value + All Fees)', `$${resultsData.totalCostIncludingFees.toFixed(2)}`],
['Total Revenue (Sell Value - Sell Fee)', `$${resultsData.totalRevenueAfterFees.toFixed(2)}`],
['Gross Profit (Before Fees)', `$${resultsData.grossProfitBeforeFees.toFixed(2)}`],
['Total Fees Paid', `$${resultsData.totalFees.toFixed(2)}`],
['Net Profit / Loss', `$${resultsData.netProfit.toFixed(2)}`],
['Net Profit / Loss (%)', `${resultsData.netProfitPercentage.toFixed(2)}%`],
];
doc.autoTable({
startY: doc.lastAutoTable.finalY + 10,
head: [['Profitability Summary', '']],
body: resultsSummaryBody,
theme: 'striped',
headStyles: headStyle,
styles: bodyStyle,
columnStyles: { 0: { fontStyle: 'bold', cellWidth: 80 }, 1: { cellWidth: 'auto'} },
didParseCell: function (data) { // Color coding for Net Profit/Loss
if (data.row.index === 4 || data.row.index === 5) { // Net Profit/Loss rows
if (resultsData.netProfit < 0) {
data.cell.styles.textColor = [239, 68, 68]; // Red for loss
} else {
data.cell.styles.textColor = [16, 185, 129]; // Green for profit
}
}
}
});
doc.save(`arbitrage_report_${resultsData.cryptoName.replace(/[^a-zA-Z0-9]/g, '_')}_${formattedDate}.pdf`);
}
// Event listener for DOMContentLoaded
document.addEventListener('DOMContentLoaded', function() {
// Assign DOM elements
cryptoNameEl = document.getElementById('cryptoName');
quantityEl = document.getElementById('quantity');
buyExchangeNameEl = document.getElementById('buyExchangeName');
buyPriceEl = document.getElementById('buyPrice');
buyFeeValueEl = document.getElementById('buyFeeValue');
buyFeeTypeEl = document.getElementById('buyFeeType');
sellExchangeNameEl = document.getElementById('sellExchangeName');
sellPriceEl = document.getElementById('sellPrice');
sellFeeValueEl = document.getElementById('sellFeeValue');
sellFeeTypeEl = document.getElementById('sellFeeType');
transferFeeEl = document.getElementById('transferFee');
errorArbitrageEl = document.getElementById('errorArbitrage');
resultsArbitrageEl = document.getElementById('resultsArbitrage');
// Null checks for critical elements
const criticalElements = [
cryptoNameEl, quantityEl, buyExchangeNameEl, buyPriceEl, buyFeeValueEl, buyFeeTypeEl,
sellExchangeNameEl, sellPriceEl, sellFeeValueEl, sellFeeTypeEl, transferFeeEl,
errorArbitrageEl, resultsArbitrageEl
];
if (criticalElements.some(el => !el)) {
console.error("One or more critical elements are missing from the DOM for the Arbitrage Calculator.");
const body = document.querySelector('body');
if (body && errorArbitrageEl && errorArbitrageEl.style.display === 'none') { // only show if no other error shown
errorArbitrageEl.textContent = "Error: Calculator UI could not be fully initialized. Some fields may be missing. Please refresh.";
errorArbitrageEl.style.display = 'block';
} else if (body && !errorArbitrageEl) { // If errorArbitrageEl itself is missing
const errorDiv = document.createElement('div');
errorDiv.className = 'error-message'; // Use existing styles
errorDiv.textContent = "Critical Error: Calculator UI initialization failed. Please refresh.";
errorDiv.style.display = 'block';
const container = document.querySelector('.calculator-container');
if (container) container.prepend(errorDiv); else body.prepend(errorDiv);
}
}
});