Files
wifi-densepose/vendor/ruvector/examples/neural-trader/accounting/crypto-tax.js

376 lines
14 KiB
JavaScript

/**
* Crypto Tax Calculations with Neural Trader
*
* Demonstrates using @neural-trader/agentic-accounting-rust-core for:
* - FIFO, LIFO, HIFO cost basis methods
* - Capital gains calculations
* - Tax lot optimization
* - Multi-exchange reconciliation
* - Tax report generation
*
* Built with native Rust bindings via NAPI for high performance
*/
// Accounting configuration
const accountingConfig = {
// Tax settings
taxYear: 2024,
country: 'US',
shortTermRate: 0.37, // Short-term capital gains rate
longTermRate: 0.20, // Long-term capital gains rate
holdingPeriod: 365, // Days for long-term treatment
// Cost basis methods
defaultMethod: 'FIFO', // FIFO, LIFO, HIFO, or SPEC_ID
allowMethodSwitch: true,
// Exchanges to reconcile
exchanges: ['Coinbase', 'Binance', 'Kraken', 'FTX'],
// Reporting
generateForms: ['8949', 'ScheduleD']
};
// Sample transaction data
const sampleTransactions = [
// Bitcoin purchases
{ date: '2024-01-15', type: 'BUY', symbol: 'BTC', quantity: 0.5, price: 42500, exchange: 'Coinbase', fee: 21.25 },
{ date: '2024-02-20', type: 'BUY', symbol: 'BTC', quantity: 0.3, price: 51200, exchange: 'Binance', fee: 15.36 },
{ date: '2024-03-10', type: 'BUY', symbol: 'BTC', quantity: 0.2, price: 68000, exchange: 'Kraken', fee: 13.60 },
// Bitcoin sales
{ date: '2024-06-15', type: 'SELL', symbol: 'BTC', quantity: 0.4, price: 65000, exchange: 'Coinbase', fee: 26.00 },
{ date: '2024-11-25', type: 'SELL', symbol: 'BTC', quantity: 0.3, price: 95000, exchange: 'Binance', fee: 28.50 },
// Ethereum purchases
{ date: '2024-01-20', type: 'BUY', symbol: 'ETH', quantity: 5.0, price: 2400, exchange: 'Coinbase', fee: 12.00 },
{ date: '2024-04-05', type: 'BUY', symbol: 'ETH', quantity: 3.0, price: 3200, exchange: 'Kraken', fee: 9.60 },
// Ethereum sales
{ date: '2024-08-15', type: 'SELL', symbol: 'ETH', quantity: 4.0, price: 2800, exchange: 'Coinbase', fee: 11.20 },
// Staking rewards (income)
{ date: '2024-03-01', type: 'INCOME', symbol: 'ETH', quantity: 0.05, price: 3400, exchange: 'Coinbase', income_type: 'staking' },
{ date: '2024-06-01', type: 'INCOME', symbol: 'ETH', quantity: 0.05, price: 3600, exchange: 'Coinbase', income_type: 'staking' },
{ date: '2024-09-01', type: 'INCOME', symbol: 'ETH', quantity: 0.05, price: 2500, exchange: 'Coinbase', income_type: 'staking' },
{ date: '2024-12-01', type: 'INCOME', symbol: 'ETH', quantity: 0.05, price: 3800, exchange: 'Coinbase', income_type: 'staking' },
// Swap transaction
{ date: '2024-07-20', type: 'SWAP', from_symbol: 'ETH', from_quantity: 1.0, to_symbol: 'BTC', to_quantity: 0.045, exchange: 'Binance', fee: 5.00 }
];
async function main() {
console.log('='.repeat(70));
console.log('Crypto Tax Calculations - Neural Trader');
console.log('='.repeat(70));
console.log();
// 1. Load transactions
console.log('1. Loading Transactions:');
console.log('-'.repeat(70));
console.log(` Tax Year: ${accountingConfig.taxYear}`);
console.log(` Transactions: ${sampleTransactions.length}`);
console.log(` Exchanges: ${accountingConfig.exchanges.join(', ')}`);
console.log(` Cost Basis: ${accountingConfig.defaultMethod}`);
console.log();
// 2. Transaction summary
console.log('2. Transaction Summary by Type:');
console.log('-'.repeat(70));
const typeCounts = {};
sampleTransactions.forEach(tx => {
typeCounts[tx.type] = (typeCounts[tx.type] || 0) + 1;
});
Object.entries(typeCounts).forEach(([type, count]) => {
console.log(` ${type.padEnd(10)}: ${count} transactions`);
});
console.log();
// 3. Calculate cost basis (FIFO)
console.log('3. Cost Basis Calculation (FIFO):');
console.log('-'.repeat(70));
const fifoResults = calculateCostBasis(sampleTransactions, 'FIFO');
displayCostBasisResults('FIFO', fifoResults);
// 4. Calculate cost basis (LIFO)
console.log('4. Cost Basis Calculation (LIFO):');
console.log('-'.repeat(70));
const lifoResults = calculateCostBasis(sampleTransactions, 'LIFO');
displayCostBasisResults('LIFO', lifoResults);
// 5. Calculate cost basis (HIFO)
console.log('5. Cost Basis Calculation (HIFO):');
console.log('-'.repeat(70));
const hifoResults = calculateCostBasis(sampleTransactions, 'HIFO');
displayCostBasisResults('HIFO', hifoResults);
// 6. Method comparison
console.log('6. Cost Basis Method Comparison:');
console.log('-'.repeat(70));
console.log(' Method | Total Gains | Short-Term | Long-Term | Tax Owed');
console.log('-'.repeat(70));
const methods = ['FIFO', 'LIFO', 'HIFO'];
const results = [fifoResults, lifoResults, hifoResults];
results.forEach((result, i) => {
const taxOwed = result.shortTermGains * accountingConfig.shortTermRate +
result.longTermGains * accountingConfig.longTermRate;
console.log(` ${methods[i].padEnd(6)} | $${result.totalGains.toLocaleString().padStart(12)} | $${result.shortTermGains.toLocaleString().padStart(11)} | $${result.longTermGains.toLocaleString().padStart(11)} | $${Math.round(taxOwed).toLocaleString().padStart(8)}`);
});
console.log();
// Find optimal method
const taxAmounts = results.map((r, i) =>
r.shortTermGains * accountingConfig.shortTermRate + r.longTermGains * accountingConfig.longTermRate
);
const minTaxIdx = taxAmounts.indexOf(Math.min(...taxAmounts));
const maxTaxIdx = taxAmounts.indexOf(Math.max(...taxAmounts));
console.log(` Optimal Method: ${methods[minTaxIdx]} (saves $${Math.round(taxAmounts[maxTaxIdx] - taxAmounts[minTaxIdx]).toLocaleString()} vs ${methods[maxTaxIdx]})`);
console.log();
// 7. Tax lot details
console.log('7. Tax Lot Details (FIFO):');
console.log('-'.repeat(70));
console.log(' Sale Date | Asset | Qty | Proceeds | Cost Basis | Gain/Loss | Term');
console.log('-'.repeat(70));
fifoResults.lots.forEach(lot => {
const term = lot.holdingDays >= accountingConfig.holdingPeriod ? 'Long' : 'Short';
const gainStr = lot.gain >= 0 ? `$${lot.gain.toLocaleString()}` : `-$${Math.abs(lot.gain).toLocaleString()}`;
console.log(` ${lot.saleDate} | ${lot.symbol.padEnd(5)} | ${lot.quantity.toFixed(4).padStart(6)} | $${lot.proceeds.toLocaleString().padStart(11)} | $${lot.costBasis.toLocaleString().padStart(11)} | ${gainStr.padStart(12)} | ${term}`);
});
console.log();
// 8. Income summary (staking)
console.log('8. Crypto Income Summary:');
console.log('-'.repeat(70));
const incomeTransactions = sampleTransactions.filter(tx => tx.type === 'INCOME');
let totalIncome = 0;
console.log(' Date | Asset | Qty | FMV Price | Income');
console.log('-'.repeat(70));
incomeTransactions.forEach(tx => {
const income = tx.quantity * tx.price;
totalIncome += income;
console.log(` ${tx.date} | ${tx.symbol.padEnd(5)} | ${tx.quantity.toFixed(4).padStart(7)} | $${tx.price.toLocaleString().padStart(8)} | $${income.toFixed(2).padStart(8)}`);
});
console.log('-'.repeat(70));
console.log(` Total Staking Income: $${totalIncome.toFixed(2)}`);
console.log(` Tax on Income (${(accountingConfig.shortTermRate * 100)}%): $${(totalIncome * accountingConfig.shortTermRate).toFixed(2)}`);
console.log();
// 9. Remaining holdings
console.log('9. Remaining Holdings:');
console.log('-'.repeat(70));
const holdings = calculateRemainingHoldings(sampleTransactions, fifoResults);
console.log(' Asset | Qty | Avg Cost | Current Value | Unrealized G/L');
console.log('-'.repeat(70));
Object.entries(holdings).forEach(([symbol, data]) => {
const currentPrice = symbol === 'BTC' ? 98000 : 3900; // Current prices
const currentValue = data.quantity * currentPrice;
const unrealizedGL = currentValue - data.totalCost;
const glStr = unrealizedGL >= 0 ? `$${unrealizedGL.toLocaleString()}` : `-$${Math.abs(unrealizedGL).toLocaleString()}`;
console.log(` ${symbol.padEnd(5)} | ${data.quantity.toFixed(4).padStart(9)} | $${data.avgCost.toFixed(2).padStart(9)} | $${currentValue.toLocaleString().padStart(13)} | ${glStr.padStart(14)}`);
});
console.log();
// 10. Form 8949 preview
console.log('10. Form 8949 Preview (Part I - Short-Term):');
console.log('-'.repeat(70));
generateForm8949(fifoResults, accountingConfig);
console.log();
// 11. Export options
console.log('11. Export Options:');
console.log('-'.repeat(70));
console.log(' Available export formats:');
console.log(' - Form 8949 (IRS)');
console.log(' - Schedule D (IRS)');
console.log(' - CSV (for tax software)');
console.log(' - TurboTax TXF');
console.log(' - CoinTracker format');
console.log(' - Koinly format');
console.log();
console.log('='.repeat(70));
console.log('Crypto tax calculation completed!');
console.log('='.repeat(70));
}
// Calculate cost basis using specified method
function calculateCostBasis(transactions, method) {
const lots = [];
const inventory = {}; // { symbol: [{ date, quantity, price, remaining }] }
let totalGains = 0;
let shortTermGains = 0;
let longTermGains = 0;
// Process transactions in order
const sortedTx = [...transactions].sort((a, b) => new Date(a.date) - new Date(b.date));
for (const tx of sortedTx) {
const symbol = tx.symbol;
if (tx.type === 'BUY' || tx.type === 'INCOME') {
// Add to inventory
if (!inventory[symbol]) inventory[symbol] = [];
inventory[symbol].push({
date: tx.date,
quantity: tx.quantity,
price: tx.price + (tx.fee || 0) / tx.quantity, // Include fees in cost basis
remaining: tx.quantity
});
} else if (tx.type === 'SELL') {
// Match against inventory based on method
let remaining = tx.quantity;
const proceeds = tx.quantity * tx.price - (tx.fee || 0);
while (remaining > 0 && inventory[symbol]?.length > 0) {
// Select lot based on method
let lotIndex = 0;
if (method === 'LIFO') {
lotIndex = inventory[symbol].length - 1;
} else if (method === 'HIFO') {
lotIndex = inventory[symbol].reduce((maxIdx, lot, idx, arr) =>
lot.price > arr[maxIdx].price ? idx : maxIdx, 0);
}
// FIFO uses index 0
const lot = inventory[symbol][lotIndex];
const matchQty = Math.min(remaining, lot.remaining);
// Calculate gain/loss
const costBasis = matchQty * lot.price;
const lotProceeds = (matchQty / tx.quantity) * proceeds;
const gain = lotProceeds - costBasis;
// Determine holding period
const buyDate = new Date(lot.date);
const sellDate = new Date(tx.date);
const holdingDays = Math.floor((sellDate - buyDate) / (1000 * 60 * 60 * 24));
lots.push({
symbol,
quantity: matchQty,
buyDate: lot.date,
saleDate: tx.date,
proceeds: Math.round(lotProceeds),
costBasis: Math.round(costBasis),
gain: Math.round(gain),
holdingDays
});
totalGains += gain;
if (holdingDays >= accountingConfig.holdingPeriod) {
longTermGains += gain;
} else {
shortTermGains += gain;
}
// Update inventory
lot.remaining -= matchQty;
remaining -= matchQty;
if (lot.remaining <= 0) {
inventory[symbol].splice(lotIndex, 1);
}
}
} else if (tx.type === 'SWAP') {
// Treat as sell of from_symbol and buy of to_symbol
// (Simplified - real implementation would match lots)
}
}
return {
method,
lots,
totalGains: Math.round(totalGains),
shortTermGains: Math.round(shortTermGains),
longTermGains: Math.round(longTermGains)
};
}
// Display cost basis results
function displayCostBasisResults(method, results) {
console.log(` Method: ${method}`);
console.log(` Total Realized Gains: $${results.totalGains.toLocaleString()}`);
console.log(` Short-Term Gains: $${results.shortTermGains.toLocaleString()}`);
console.log(` Long-Term Gains: $${results.longTermGains.toLocaleString()}`);
console.log(` Dispositions: ${results.lots.length}`);
console.log();
}
// Calculate remaining holdings
function calculateRemainingHoldings(transactions, costBasisResults) {
const holdings = {};
// Track all purchases
transactions.forEach(tx => {
if (tx.type === 'BUY' || tx.type === 'INCOME') {
if (!holdings[tx.symbol]) {
holdings[tx.symbol] = { quantity: 0, totalCost: 0 };
}
holdings[tx.symbol].quantity += tx.quantity;
holdings[tx.symbol].totalCost += tx.quantity * tx.price;
}
});
// Subtract sold quantities
costBasisResults.lots.forEach(lot => {
holdings[lot.symbol].quantity -= lot.quantity;
holdings[lot.symbol].totalCost -= lot.costBasis;
});
// Calculate average cost
Object.keys(holdings).forEach(symbol => {
if (holdings[symbol].quantity > 0.0001) {
holdings[symbol].avgCost = holdings[symbol].totalCost / holdings[symbol].quantity;
} else {
delete holdings[symbol];
}
});
return holdings;
}
// Generate Form 8949 preview
function generateForm8949(results, config) {
const shortTermLots = results.lots.filter(l => l.holdingDays < config.holdingPeriod);
const longTermLots = results.lots.filter(l => l.holdingDays >= config.holdingPeriod);
console.log(' (a) Description | (b) Date Acq | (c) Date Sold | (d) Proceeds | (e) Cost | (h) Gain');
console.log('-'.repeat(70));
shortTermLots.slice(0, 5).forEach(lot => {
const gainStr = lot.gain >= 0 ? `$${lot.gain.toLocaleString()}` : `($${Math.abs(lot.gain).toLocaleString()})`;
console.log(` ${(lot.quantity.toFixed(4) + ' ' + lot.symbol).padEnd(18)} | ${lot.buyDate} | ${lot.saleDate} | $${lot.proceeds.toLocaleString().padStart(10)} | $${lot.costBasis.toLocaleString().padStart(6)} | ${gainStr.padStart(8)}`);
});
if (shortTermLots.length > 5) {
console.log(` ... and ${shortTermLots.length - 5} more short-term transactions`);
}
console.log();
console.log(` Part II - Long-Term: ${longTermLots.length} transactions`);
}
// Run the example
main().catch(console.error);