diff --git a/2023-f8949.pdf b/2024-f8949.pdf similarity index 100% rename from 2023-f8949.pdf rename to 2024-f8949.pdf diff --git a/fifo/index.js b/fifo/index.js index 8b944b6..0ec1964 100644 --- a/fifo/index.js +++ b/fifo/index.js @@ -4,12 +4,12 @@ const fs = require('fs') const BN = require('bignumber.js') const txs = csvToJson.fieldDelimiter(',').getJsonFromCsv(`../Ledger.csv`) -let utxos = [] +let lots = [] let dispositions = [] const handleBuy = (tx, i) => { const { Date: date, Asset, Received, Price } = tx - utxos.push({ + lots.push({ Date: date, Id: i, Asset, @@ -19,18 +19,18 @@ const handleBuy = (tx, i) => { }) } -const createDisposition = (tx, utxo, amount) => { - const st = new Date(tx.Date).valueOf() - new Date(utxo.Date).valueOf() < 31556926000 - const costBasis = amount.times(utxo.Price) +const createDisposition = (tx, lot, amount) => { + const st = new Date(tx.Date).valueOf() - new Date(lot.Date).valueOf() < 31556926000 + const costBasis = amount.times(lot.Price) const proceeds = amount.times(tx.Price) const gainLoss = proceeds.minus(costBasis) const disposition = { Date: tx.Date, Asset: tx.Asset, - Utxo: utxo.Id, + Lot: lot.Id, Amount: amount.toFixed(8), - PurchasePrice: utxo.Price, - DateAcquired: utxo.Date, + PurchasePrice: lot.Price, + DateAcquired: lot.Date, SalePrice: tx.Price, CostBasis: costBasis.toFixed(2), Proceeds: proceeds.toFixed(2), @@ -41,13 +41,13 @@ const createDisposition = (tx, utxo, amount) => { dispositions.push(disposition) } -const consumeUtxos = (tx) => { - for (let i = 0; i < utxos.length; i++) { - let utxo = utxos[i] +const consumeLots = (tx) => { + for (let i = 0; i < lots.length; i++) { + let lot = lots[i] - if (!utxo) { - console.error(`WARNING: No utxo available! Assuming $0 cost basis:\n-Date:${tx.Date}\n-Asset: ${tx.Asset}\n-Sent: ${tx.Sent}\n-Remaining: ${tx.Remaining.toFixed(8)}\n`) - utxo = { + if (!lot) { + console.error(`WARNING: No lot available! Assuming $0 cost basis:\n-Date:${tx.Date}\n-Asset: ${tx.Asset}\n-Sent: ${tx.Sent}\n-Remaining: ${tx.Remaining.toFixed(8)}\n`) + lot = { Date: '2017-01-01', Id: -1, Asset: tx.Asset, @@ -57,17 +57,17 @@ const consumeUtxos = (tx) => { } } - if (utxo.Asset !== tx.Asset || utxo.Remaining.isZero()) continue + if (lot.Asset !== tx.Asset || lot.Remaining.isZero()) continue - if (utxo.Remaining.gte(tx.Remaining)) { - createDisposition(tx, utxo, tx.Remaining) - utxo.Remaining = utxo.Remaining.minus(tx.Remaining) + if (lot.Remaining.gte(tx.Remaining)) { + createDisposition(tx, lot, tx.Remaining) + lot.Remaining = lot.Remaining.minus(tx.Remaining) return } else { - createDisposition(tx, utxo, utxo.Remaining) - tx.Remaining = tx.Remaining.minus(utxo.Remaining) - utxo.Remaining = new BN(0) - return consumeUtxos(tx) + createDisposition(tx, lot, lot.Remaining) + tx.Remaining = tx.Remaining.minus(lot.Remaining) + lot.Remaining = new BN(0) + return consumeLots(tx) } } } @@ -75,10 +75,10 @@ const consumeUtxos = (tx) => { txs.forEach((tx, i) => { if (tx.Received) return handleBuy(tx, i) tx.Remaining = new BN(tx.Sent) - return consumeUtxos(tx) + return consumeLots(tx) }) -let balances = utxos.reduce((prev, curr) => { +let balances = lots.reduce((prev, curr) => { const asset = curr.Asset if (prev[asset]) { prev[asset] = prev[asset].plus(curr.Remaining) @@ -88,7 +88,7 @@ let balances = utxos.reduce((prev, curr) => { return prev }, {}) -utxos = utxos.map(u => ({ +lots = lots.map(u => ({ ...u, Remaining: u.Remaining.toFixed(8), })) @@ -100,8 +100,8 @@ balances = Object.keys(balances) balance: balances[asset].toFixed(8) })) -converter.json2csv(utxos).then(csv => - fs.appendFileSync(`./utxos.csv`, csv) +converter.json2csv(lots).then(csv => + fs.appendFileSync(`./lots.csv`, csv) ) converter.json2csv(dispositions).then(csv => diff --git a/hifo/index.js b/hifo/index.js index 247b3da..947644f 100644 --- a/hifo/index.js +++ b/hifo/index.js @@ -3,15 +3,15 @@ const converter = require('json-2-csv') const fs = require('fs') const BN = require('bignumber.js') const { PDFDocument } = require('pdf-lib') -const year = 2023 +const year = 2024 const txs = csvToJson.fieldDelimiter(',').getJsonFromCsv(`../Ledger.csv`) -let utxos = [] +let lots = [] let dispositions = [] const handleBuy = (tx, i) => { const { Date: date, Asset, Received, Price } = tx - utxos.push({ + lots.push({ Date: date, Id: i, Asset, @@ -21,17 +21,17 @@ const handleBuy = (tx, i) => { }) } -const createDisposition = (tx, utxo, amount, st) => { - const costBasis = amount.times(utxo.Price) +const createDisposition = (tx, lot, amount, st) => { + const costBasis = amount.times(lot.Price) const proceeds = amount.times(tx.Price) const gainLoss = proceeds.minus(costBasis) return { Date: tx.Date, Asset: tx.Asset, - Utxo: utxo.Id, + Lot: lot.Id, Amount: amount.toFixed(8), - PurchasePrice: utxo.Price, - DateAcquired: utxo.Date, + PurchasePrice: lot.Price, + DateAcquired: lot.Date, SalePrice: tx.Price, CostBasis: costBasis.toFixed(2), Proceeds: proceeds.toFixed(2), @@ -41,29 +41,29 @@ const createDisposition = (tx, utxo, amount, st) => { } } -const consumeUtxos = (tx) => { +const consumeLots = (tx) => { const copy = [] - for (i = 0; i < utxos.length; i++) { - copy[i] = utxos[i]; + for (i = 0; i < lots.length; i++) { + copy[i] = lots[i]; } const sorted = copy - .filter(utxo => utxo.Asset === tx.Asset && !utxo.Remaining.isZero()) + .filter(lot => lot.Asset === tx.Asset && !lot.Remaining.isZero()) .sort((a, b) => new BN(b.Price).minus(a.Price).toNumber()) const txDate = new Date(tx.Date).valueOf() - const stUtxo = sorted.find(utxo => txDate - new Date(utxo.Date).valueOf() < 31556926000) - const ltUtxo = sorted.find(utxo => txDate - new Date(utxo.Date).valueOf() >= 31556926000) + const stLot = sorted.find(lot => txDate - new Date(lot.Date).valueOf() < 31556926000) + const ltLot = sorted.find(lot => txDate - new Date(lot.Date).valueOf() >= 31556926000) - // find the best utxo - let utxo + // find the best lot + let lot // decide st vs lt let st - if (!stUtxo && !ltUtxo) { - console.error(`WARNING: No utxo available! Assuming $0 cost basis:\n-Date:${tx.Date}\n-Asset: ${tx.Asset}\n-Sent: ${tx.Sent}\n-Remaining: ${tx.Remaining.toFixed(8)}\n`) - utxo = { + if (!stLot && !ltLot) { + console.error(`WARNING: No lot available! Assuming $0 cost basis:\n-Date:${tx.Date}\n-Asset: ${tx.Asset}\n-Sent: ${tx.Sent}\n-Remaining: ${tx.Remaining.toFixed(8)}\n`) + lot = { Date: '2017-01-01T12:00:00', Id: -1, Asset: tx.Asset, @@ -71,53 +71,53 @@ const consumeUtxos = (tx) => { Price: '0', Remaining: tx.Remaining, } - } else if (!stUtxo) { - utxo = utxos.find(u => u.Id === ltUtxo.Id) + } else if (!stLot) { + lot = lots.find(u => u.Id === ltLot.Id) st = false - } else if (!ltUtxo) { - utxo = utxos.find(u => u.Id === stUtxo.Id) + } else if (!ltLot) { + lot = lots.find(u => u.Id === stLot.Id) st = true } else { - const { Tax: stTax } = getProvisional(tx, stUtxo, true) - const { Tax: ltTax } = getProvisional(tx, ltUtxo, false) + const { Tax: stTax } = getProvisional(tx, stLot, true) + const { Tax: ltTax } = getProvisional(tx, ltLot, false) if (new BN(stTax).lt(ltTax)) { - utxo = utxos.find(u => u.Id === stUtxo.Id) + lot = lots.find(u => u.Id === stLot.Id) st = true } else { - utxo = utxos.find(u => u.Id === ltUtxo.Id) + lot = lots.find(u => u.Id === ltLot.Id) st = false } } - if (utxo.Remaining.gte(tx.Remaining)) { - const disposition = createDisposition(tx, utxo, tx.Remaining, st) + if (lot.Remaining.gte(tx.Remaining)) { + const disposition = createDisposition(tx, lot, tx.Remaining, st) dispositions.push(disposition) - utxo.Remaining = utxo.Remaining.minus(tx.Remaining) + lot.Remaining = lot.Remaining.minus(tx.Remaining) } else { - const disposition = createDisposition(tx, utxo, utxo.Remaining, st) + const disposition = createDisposition(tx, lot, lot.Remaining, st) dispositions.push(disposition) - tx.Remaining = tx.Remaining.minus(utxo.Remaining) - utxo.Remaining = new BN(0) - consumeUtxos(tx) + tx.Remaining = tx.Remaining.minus(lot.Remaining) + lot.Remaining = new BN(0) + consumeLots(tx) } } -const getProvisional = (tx, utxo, st) => { - if (utxo.Remaining.gte(tx.Remaining)) { - return createDisposition(tx, utxo, tx.Remaining, st) +const getProvisional = (tx, lot, st) => { + if (lot.Remaining.gte(tx.Remaining)) { + return createDisposition(tx, lot, tx.Remaining, st) } else { - return createDisposition(tx, utxo, utxo.Remaining, st) + return createDisposition(tx, lot, lot.Remaining, st) } } txs.forEach((tx, i) => { if (tx.Received) return handleBuy(tx, i) tx.Remaining = new BN(tx.Sent) - return consumeUtxos(tx) + return consumeLots(tx) }) -let balances = utxos.reduce((prev, curr) => { +let balances = lots.reduce((prev, curr) => { const asset = curr.Asset if (prev[asset]) { prev[asset] = prev[asset].plus(curr.Remaining) @@ -127,7 +127,7 @@ let balances = utxos.reduce((prev, curr) => { return prev }, {}) -utxos = utxos.map(u => ({ +lots = lots.map(u => ({ ...u, Remaining: u.Remaining.toFixed(8), })) @@ -147,8 +147,8 @@ converter.json2csv(balances).then(csv => fs.appendFileSync(`./balances.csv`, csv) ) -converter.json2csv(utxos).then(csv => - fs.appendFileSync(`./utxos.csv`, csv) +converter.json2csv(lots).then(csv => + fs.appendFileSync(`./lots.csv`, csv) ) // **** 8949 **** diff --git a/lifo-conversion/index.js b/lifo-conversion/index.js index cf4bac1..3564f8c 100644 --- a/lifo-conversion/index.js +++ b/lifo-conversion/index.js @@ -7,31 +7,31 @@ const coin = process.argv[2] const pre2022 = csvToJson.fieldDelimiter(',').getJsonFromCsv(`./${coin}.csv`) -const utxos = [] +const lots = [] -const handleBuy = (utxo) => { - utxo.Remaining = new BN(utxo.ReceivedQuantity) - utxos.push(utxo) +const handleBuy = (lot) => { + lot.Remaining = new BN(lot.ReceivedQuantity) + lots.push(lot) } const handleTrade = (trade) => { if (trade.ReceivedCurrency === coin) return handleBuy(trade) - consumeUtxos(new BN(trade.SentQuantity)) + consumeLots(new BN(trade.SentQuantity)) } -const consumeUtxos = (amount) => { - for (let i = utxos.length - 1; i >= 0; i--) { - const utxo = utxos[i] +const consumeLots = (amount) => { + for (let i = lots.length - 1; i >= 0; i--) { + const lot = lots[i] - if (!utxo.Remaining) continue + if (!lot.Remaining) continue - if (utxo.Remaining.gt(amount)) { - utxo.Remaining = utxo.Remaining.minus(amount) + if (lot.Remaining.gt(amount)) { + lot.Remaining = lot.Remaining.minus(amount) return } else { - amount = amount.minus(utxo.Remaining) - utxo.Remaining = 0 - return consumeUtxos(amount) + amount = amount.minus(lot.Remaining) + lot.Remaining = 0 + return consumeLots(amount) } } } @@ -46,7 +46,7 @@ pre2022.forEach(tx => { break case 'Sell': case 'Send': - consumeUtxos(new BN(tx.SentQuantity)) + consumeLots(new BN(tx.SentQuantity)) break case 'Trade': handleTrade(tx) @@ -55,23 +55,23 @@ pre2022.forEach(tx => { break } - if (tx.FeeCurrency === coin) consumeUtxos(new BN(tx.FeeAmount)) + if (tx.FeeCurrency === coin) consumeLots(new BN(tx.FeeAmount)) }) -const remaining = utxos - .filter(utxo => !!utxo.Remaining) - .map(utxo => { +const remaining = lots + .filter(lot => !!lot.Remaining) + .map(lot => { return { - Date: utxo.Date, + Date: lot.Date, Asset: coin, - Credit: utxo.Remaining.toFixed(8), + Credit: lot.Remaining.toFixed(8), Debit: '', - Price: new BN(utxo['ReceivedCostBasis(USD)']).div(utxo.ReceivedQuantity).toFixed(4), - Original: utxo.ReceivedQuantity, + Price: new BN(lot['ReceivedCostBasis(USD)']).div(lot.ReceivedQuantity).toFixed(4), + Original: lot.ReceivedQuantity, } } ) converter.json2csv(remaining).then(csv => - fs.appendFileSync(`./${coin}-utxos.csv`, csv) + fs.appendFileSync(`./${coin}-lots.csv`, csv) );