update for 2024

This commit is contained in:
2024-12-15 20:40:15 -07:00
parent ac86e46258
commit 26b9c7f0d5
4 changed files with 94 additions and 94 deletions

View File

@@ -4,12 +4,12 @@ const fs = require('fs')
const BN = require('bignumber.js') const BN = require('bignumber.js')
const txs = csvToJson.fieldDelimiter(',').getJsonFromCsv(`../Ledger.csv`) const txs = csvToJson.fieldDelimiter(',').getJsonFromCsv(`../Ledger.csv`)
let utxos = [] let lots = []
let dispositions = [] let dispositions = []
const handleBuy = (tx, i) => { const handleBuy = (tx, i) => {
const { Date: date, Asset, Received, Price } = tx const { Date: date, Asset, Received, Price } = tx
utxos.push({ lots.push({
Date: date, Date: date,
Id: i, Id: i,
Asset, Asset,
@@ -19,18 +19,18 @@ const handleBuy = (tx, i) => {
}) })
} }
const createDisposition = (tx, utxo, amount) => { const createDisposition = (tx, lot, amount) => {
const st = new Date(tx.Date).valueOf() - new Date(utxo.Date).valueOf() < 31556926000 const st = new Date(tx.Date).valueOf() - new Date(lot.Date).valueOf() < 31556926000
const costBasis = amount.times(utxo.Price) const costBasis = amount.times(lot.Price)
const proceeds = amount.times(tx.Price) const proceeds = amount.times(tx.Price)
const gainLoss = proceeds.minus(costBasis) const gainLoss = proceeds.minus(costBasis)
const disposition = { const disposition = {
Date: tx.Date, Date: tx.Date,
Asset: tx.Asset, Asset: tx.Asset,
Utxo: utxo.Id, Lot: lot.Id,
Amount: amount.toFixed(8), Amount: amount.toFixed(8),
PurchasePrice: utxo.Price, PurchasePrice: lot.Price,
DateAcquired: utxo.Date, DateAcquired: lot.Date,
SalePrice: tx.Price, SalePrice: tx.Price,
CostBasis: costBasis.toFixed(2), CostBasis: costBasis.toFixed(2),
Proceeds: proceeds.toFixed(2), Proceeds: proceeds.toFixed(2),
@@ -41,13 +41,13 @@ const createDisposition = (tx, utxo, amount) => {
dispositions.push(disposition) dispositions.push(disposition)
} }
const consumeUtxos = (tx) => { const consumeLots = (tx) => {
for (let i = 0; i < utxos.length; i++) { for (let i = 0; i < lots.length; i++) {
let utxo = utxos[i] let lot = lots[i]
if (!utxo) { if (!lot) {
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`) 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`)
utxo = { lot = {
Date: '2017-01-01', Date: '2017-01-01',
Id: -1, Id: -1,
Asset: tx.Asset, 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)) { if (lot.Remaining.gte(tx.Remaining)) {
createDisposition(tx, utxo, tx.Remaining) createDisposition(tx, lot, tx.Remaining)
utxo.Remaining = utxo.Remaining.minus(tx.Remaining) lot.Remaining = lot.Remaining.minus(tx.Remaining)
return return
} else { } else {
createDisposition(tx, utxo, utxo.Remaining) createDisposition(tx, lot, lot.Remaining)
tx.Remaining = tx.Remaining.minus(utxo.Remaining) tx.Remaining = tx.Remaining.minus(lot.Remaining)
utxo.Remaining = new BN(0) lot.Remaining = new BN(0)
return consumeUtxos(tx) return consumeLots(tx)
} }
} }
} }
@@ -75,10 +75,10 @@ const consumeUtxos = (tx) => {
txs.forEach((tx, i) => { txs.forEach((tx, i) => {
if (tx.Received) return handleBuy(tx, i) if (tx.Received) return handleBuy(tx, i)
tx.Remaining = new BN(tx.Sent) 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 const asset = curr.Asset
if (prev[asset]) { if (prev[asset]) {
prev[asset] = prev[asset].plus(curr.Remaining) prev[asset] = prev[asset].plus(curr.Remaining)
@@ -88,7 +88,7 @@ let balances = utxos.reduce((prev, curr) => {
return prev return prev
}, {}) }, {})
utxos = utxos.map(u => ({ lots = lots.map(u => ({
...u, ...u,
Remaining: u.Remaining.toFixed(8), Remaining: u.Remaining.toFixed(8),
})) }))
@@ -100,8 +100,8 @@ balances = Object.keys(balances)
balance: balances[asset].toFixed(8) balance: balances[asset].toFixed(8)
})) }))
converter.json2csv(utxos).then(csv => converter.json2csv(lots).then(csv =>
fs.appendFileSync(`./utxos.csv`, csv) fs.appendFileSync(`./lots.csv`, csv)
) )
converter.json2csv(dispositions).then(csv => converter.json2csv(dispositions).then(csv =>

View File

@@ -3,15 +3,15 @@ const converter = require('json-2-csv')
const fs = require('fs') const fs = require('fs')
const BN = require('bignumber.js') const BN = require('bignumber.js')
const { PDFDocument } = require('pdf-lib') const { PDFDocument } = require('pdf-lib')
const year = 2023 const year = 2024
const txs = csvToJson.fieldDelimiter(',').getJsonFromCsv(`../Ledger.csv`) const txs = csvToJson.fieldDelimiter(',').getJsonFromCsv(`../Ledger.csv`)
let utxos = [] let lots = []
let dispositions = [] let dispositions = []
const handleBuy = (tx, i) => { const handleBuy = (tx, i) => {
const { Date: date, Asset, Received, Price } = tx const { Date: date, Asset, Received, Price } = tx
utxos.push({ lots.push({
Date: date, Date: date,
Id: i, Id: i,
Asset, Asset,
@@ -21,17 +21,17 @@ const handleBuy = (tx, i) => {
}) })
} }
const createDisposition = (tx, utxo, amount, st) => { const createDisposition = (tx, lot, amount, st) => {
const costBasis = amount.times(utxo.Price) const costBasis = amount.times(lot.Price)
const proceeds = amount.times(tx.Price) const proceeds = amount.times(tx.Price)
const gainLoss = proceeds.minus(costBasis) const gainLoss = proceeds.minus(costBasis)
return { return {
Date: tx.Date, Date: tx.Date,
Asset: tx.Asset, Asset: tx.Asset,
Utxo: utxo.Id, Lot: lot.Id,
Amount: amount.toFixed(8), Amount: amount.toFixed(8),
PurchasePrice: utxo.Price, PurchasePrice: lot.Price,
DateAcquired: utxo.Date, DateAcquired: lot.Date,
SalePrice: tx.Price, SalePrice: tx.Price,
CostBasis: costBasis.toFixed(2), CostBasis: costBasis.toFixed(2),
Proceeds: proceeds.toFixed(2), Proceeds: proceeds.toFixed(2),
@@ -41,29 +41,29 @@ const createDisposition = (tx, utxo, amount, st) => {
} }
} }
const consumeUtxos = (tx) => { const consumeLots = (tx) => {
const copy = [] const copy = []
for (i = 0; i < utxos.length; i++) { for (i = 0; i < lots.length; i++) {
copy[i] = utxos[i]; copy[i] = lots[i];
} }
const sorted = copy 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()) .sort((a, b) => new BN(b.Price).minus(a.Price).toNumber())
const txDate = new Date(tx.Date).valueOf() const txDate = new Date(tx.Date).valueOf()
const stUtxo = sorted.find(utxo => txDate - new Date(utxo.Date).valueOf() < 31556926000) const stLot = sorted.find(lot => txDate - new Date(lot.Date).valueOf() < 31556926000)
const ltUtxo = sorted.find(utxo => txDate - new Date(utxo.Date).valueOf() >= 31556926000) const ltLot = sorted.find(lot => txDate - new Date(lot.Date).valueOf() >= 31556926000)
// find the best utxo // find the best lot
let utxo let lot
// decide st vs lt // decide st vs lt
let st let st
if (!stUtxo && !ltUtxo) { if (!stLot && !ltLot) {
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`) 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`)
utxo = { lot = {
Date: '2017-01-01T12:00:00', Date: '2017-01-01T12:00:00',
Id: -1, Id: -1,
Asset: tx.Asset, Asset: tx.Asset,
@@ -71,53 +71,53 @@ const consumeUtxos = (tx) => {
Price: '0', Price: '0',
Remaining: tx.Remaining, Remaining: tx.Remaining,
} }
} else if (!stUtxo) { } else if (!stLot) {
utxo = utxos.find(u => u.Id === ltUtxo.Id) lot = lots.find(u => u.Id === ltLot.Id)
st = false st = false
} else if (!ltUtxo) { } else if (!ltLot) {
utxo = utxos.find(u => u.Id === stUtxo.Id) lot = lots.find(u => u.Id === stLot.Id)
st = true st = true
} else { } else {
const { Tax: stTax } = getProvisional(tx, stUtxo, true) const { Tax: stTax } = getProvisional(tx, stLot, true)
const { Tax: ltTax } = getProvisional(tx, ltUtxo, false) const { Tax: ltTax } = getProvisional(tx, ltLot, false)
if (new BN(stTax).lt(ltTax)) { if (new BN(stTax).lt(ltTax)) {
utxo = utxos.find(u => u.Id === stUtxo.Id) lot = lots.find(u => u.Id === stLot.Id)
st = true st = true
} else { } else {
utxo = utxos.find(u => u.Id === ltUtxo.Id) lot = lots.find(u => u.Id === ltLot.Id)
st = false st = false
} }
} }
if (utxo.Remaining.gte(tx.Remaining)) { if (lot.Remaining.gte(tx.Remaining)) {
const disposition = createDisposition(tx, utxo, tx.Remaining, st) const disposition = createDisposition(tx, lot, tx.Remaining, st)
dispositions.push(disposition) dispositions.push(disposition)
utxo.Remaining = utxo.Remaining.minus(tx.Remaining) lot.Remaining = lot.Remaining.minus(tx.Remaining)
} else { } else {
const disposition = createDisposition(tx, utxo, utxo.Remaining, st) const disposition = createDisposition(tx, lot, lot.Remaining, st)
dispositions.push(disposition) dispositions.push(disposition)
tx.Remaining = tx.Remaining.minus(utxo.Remaining) tx.Remaining = tx.Remaining.minus(lot.Remaining)
utxo.Remaining = new BN(0) lot.Remaining = new BN(0)
consumeUtxos(tx) consumeLots(tx)
} }
} }
const getProvisional = (tx, utxo, st) => { const getProvisional = (tx, lot, st) => {
if (utxo.Remaining.gte(tx.Remaining)) { if (lot.Remaining.gte(tx.Remaining)) {
return createDisposition(tx, utxo, tx.Remaining, st) return createDisposition(tx, lot, tx.Remaining, st)
} else { } else {
return createDisposition(tx, utxo, utxo.Remaining, st) return createDisposition(tx, lot, lot.Remaining, st)
} }
} }
txs.forEach((tx, i) => { txs.forEach((tx, i) => {
if (tx.Received) return handleBuy(tx, i) if (tx.Received) return handleBuy(tx, i)
tx.Remaining = new BN(tx.Sent) 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 const asset = curr.Asset
if (prev[asset]) { if (prev[asset]) {
prev[asset] = prev[asset].plus(curr.Remaining) prev[asset] = prev[asset].plus(curr.Remaining)
@@ -127,7 +127,7 @@ let balances = utxos.reduce((prev, curr) => {
return prev return prev
}, {}) }, {})
utxos = utxos.map(u => ({ lots = lots.map(u => ({
...u, ...u,
Remaining: u.Remaining.toFixed(8), Remaining: u.Remaining.toFixed(8),
})) }))
@@ -147,8 +147,8 @@ converter.json2csv(balances).then(csv =>
fs.appendFileSync(`./balances.csv`, csv) fs.appendFileSync(`./balances.csv`, csv)
) )
converter.json2csv(utxos).then(csv => converter.json2csv(lots).then(csv =>
fs.appendFileSync(`./utxos.csv`, csv) fs.appendFileSync(`./lots.csv`, csv)
) )
// **** 8949 **** // **** 8949 ****

View File

@@ -7,31 +7,31 @@ const coin = process.argv[2]
const pre2022 = csvToJson.fieldDelimiter(',').getJsonFromCsv(`./${coin}.csv`) const pre2022 = csvToJson.fieldDelimiter(',').getJsonFromCsv(`./${coin}.csv`)
const utxos = [] const lots = []
const handleBuy = (utxo) => { const handleBuy = (lot) => {
utxo.Remaining = new BN(utxo.ReceivedQuantity) lot.Remaining = new BN(lot.ReceivedQuantity)
utxos.push(utxo) lots.push(lot)
} }
const handleTrade = (trade) => { const handleTrade = (trade) => {
if (trade.ReceivedCurrency === coin) return handleBuy(trade) if (trade.ReceivedCurrency === coin) return handleBuy(trade)
consumeUtxos(new BN(trade.SentQuantity)) consumeLots(new BN(trade.SentQuantity))
} }
const consumeUtxos = (amount) => { const consumeLots = (amount) => {
for (let i = utxos.length - 1; i >= 0; i--) { for (let i = lots.length - 1; i >= 0; i--) {
const utxo = utxos[i] const lot = lots[i]
if (!utxo.Remaining) continue if (!lot.Remaining) continue
if (utxo.Remaining.gt(amount)) { if (lot.Remaining.gt(amount)) {
utxo.Remaining = utxo.Remaining.minus(amount) lot.Remaining = lot.Remaining.minus(amount)
return return
} else { } else {
amount = amount.minus(utxo.Remaining) amount = amount.minus(lot.Remaining)
utxo.Remaining = 0 lot.Remaining = 0
return consumeUtxos(amount) return consumeLots(amount)
} }
} }
} }
@@ -46,7 +46,7 @@ pre2022.forEach(tx => {
break break
case 'Sell': case 'Sell':
case 'Send': case 'Send':
consumeUtxos(new BN(tx.SentQuantity)) consumeLots(new BN(tx.SentQuantity))
break break
case 'Trade': case 'Trade':
handleTrade(tx) handleTrade(tx)
@@ -55,23 +55,23 @@ pre2022.forEach(tx => {
break break
} }
if (tx.FeeCurrency === coin) consumeUtxos(new BN(tx.FeeAmount)) if (tx.FeeCurrency === coin) consumeLots(new BN(tx.FeeAmount))
}) })
const remaining = utxos const remaining = lots
.filter(utxo => !!utxo.Remaining) .filter(lot => !!lot.Remaining)
.map(utxo => { .map(lot => {
return { return {
Date: utxo.Date, Date: lot.Date,
Asset: coin, Asset: coin,
Credit: utxo.Remaining.toFixed(8), Credit: lot.Remaining.toFixed(8),
Debit: '', Debit: '',
Price: new BN(utxo['ReceivedCostBasis(USD)']).div(utxo.ReceivedQuantity).toFixed(4), Price: new BN(lot['ReceivedCostBasis(USD)']).div(lot.ReceivedQuantity).toFixed(4),
Original: utxo.ReceivedQuantity, Original: lot.ReceivedQuantity,
} }
} }
) )
converter.json2csv(remaining).then(csv => converter.json2csv(remaining).then(csv =>
fs.appendFileSync(`./${coin}-utxos.csv`, csv) fs.appendFileSync(`./${coin}-lots.csv`, csv)
); );