update for 2024
This commit is contained in:
@@ -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 =>
|
||||||
|
|||||||
@@ -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 ****
|
||||||
|
|||||||
@@ -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)
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user