Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PART-3] Add transaction tax report #382

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions workers/loc.api/sync/movements/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ class Movements {
end,
sort: ledgersOrder,
isWithdrawals,
isDeposits
isDeposits,
isExcludePrivate
})

const [
Expand Down Expand Up @@ -182,7 +183,9 @@ class Movements {
currency,
amount,
amountUsd,
subUserId
subUserId,
_id,
exactUsdValue
} = ledger

return {
Expand All @@ -199,7 +202,9 @@ class Movements {
transactionId: '',
note: '',
subUserId,
_isFromLedgers: true
isLedgers: true,
_id,
exactUsdValue
}
})
}
Expand Down
6 changes: 5 additions & 1 deletion workers/loc.api/sync/transaction.tax.report/helpers/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
'use strict'

const TRX_TAX_STRATEGIES = require('./trx.tax.strategies')
const remapTrades = require('./remap-trades')
const remapMovements = require('./remap-movements')

module.exports = {
TRX_TAX_STRATEGIES
TRX_TAX_STRATEGIES,
remapTrades,
remapMovements
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
'use strict'

const {
isForexSymb
} = require('../../helpers')

module.exports = (movements, params) => {
const {
remappedTrxs,
remappedTrxsForConvToUsd
} = params

for (const movement of movements) {
if (
!movement?.currency ||
isForexSymb(movement.currency) ||
!Number.isFinite(movement?.amount) ||
movement.amount === 0 ||
!Number.isFinite(movement?.mtsUpdated)
) {
continue
}

const firstSymb = movement.currency
const lastSymb = 'USD'
const symbSeparator = firstSymb.length > 3
? ':'
: ''

const remappedMovement = {
_id: movement._id,
// NOTE: it means entries are not taken form trades table
isAdditionalTrxMovements: true,
// NOTE: movements can have sub-account transfer entries from ledgers table
isMovements: !movement.isLedgers,
isLedgers: !!movement.isLedgers,
isTrades: false,
symbol: `t${firstSymb}${symbSeparator}${lastSymb}`,
mtsCreate: movement.mtsUpdated,
firstSymb,
lastSymb,
firstSymbPriceUsd: null,
lastSymbPriceUsd: 1,
execAmount: movement.amount,
// NOTE: execPrice = firstSymbPriceUsd and should be set when converting currencies
execPrice: 0,
// NOTE: exactUsdValue can be null on the first launch, for warm-up it's filling from pub-trades
exactUsdValue: movement.exactUsdValue
}

remappedTrxs.push(remappedMovement)

if (
Number.isFinite(movement.exactUsdValue) &&
movement.exactUsdValue > 0
) {
const price = movement.exactUsdValue / movement.amount

remappedMovement.firstSymbPriceUsd = price
remappedMovement.execPrice = price

continue
}

remappedTrxsForConvToUsd.push(remappedMovement)
}

return params
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use strict'

const splitSymbolPairs = require(
'bfx-report/workers/loc.api/helpers/split-symbol-pairs'
)

module.exports = (trades, params) => {
const {
remappedTrxs,
remappedTrxsForConvToUsd
} = params

for (const trade of trades) {
if (
!trade?.symbol ||
!Number.isFinite(trade?.execAmount) ||
trade.execAmount === 0 ||
!Number.isFinite(trade?.execPrice) ||
trade.execPrice === 0 ||
!Number.isFinite(trade?.mtsCreate)
) {
continue
}

const [firstSymb, lastSymb] = splitSymbolPairs(trade.symbol)
trade.firstSymb = firstSymb
trade.lastSymb = lastSymb
trade.firstSymbPriceUsd = null
trade.lastSymbPriceUsd = null
trade.isAdditionalTrxMovements = false
trade.isMovements = false
trade.isLedgers = false
trade.isTrades = true

remappedTrxs.push(trade)

if (lastSymb === 'USD') {
trade.firstSymbPriceUsd = trade.execPrice
trade.lastSymbPriceUsd = 1

continue
}
if (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get this logic.
Is not the exact usd what we are trying to get.
Why would we need the first and last symbol price mapped to usd?
We should add the USD reference, lastSymbPriceUsd etc

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to simplify tax calc I would like to have a distinct data structure: just have firstSymbPrice and lastSymbPrice for pairs eg tETHBTC
At the same time, we can have movements eg BTC deposits, and should consider it like tBTCUSD
exactUsdValue - is the amount in transaction in USD (the logic would be as we planned) that can be used for calc prices of pairs eg tETHBTC
In other words, in tables, have one additional field exactUsdValue
when have exactUsdValue -> calc firstSymbPrice, lastSymbPrice
if don't have exactUsdValue -> get from pub-trades, set exactUsdValue to appropriate tables -> calc firstSymbPrice, lastSymbPrice
having firstSymbPrice, lastSymbPrice can calc tax report
the idea is to not calculate everything in one place

Number.isFinite(trade.exactUsdValue) &&
trade.exactUsdValue > 0
) {
trade.firstSymbPriceUsd = trade.exactUsdValue / trade.execAmount
trade.lastSymbPriceUsd = trade.exactUsdValue / trade.execPrice

continue
}

remappedTrxsForConvToUsd.push(trade)
}

return params
}
117 changes: 116 additions & 1 deletion workers/loc.api/sync/transaction.tax.report/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
'use strict'

const {
TRX_TAX_STRATEGIES
TRX_TAX_STRATEGIES,
remapTrades,
remapMovements
} = require('./helpers')

const { decorateInjectable } = require('../../di/utils')
Expand Down Expand Up @@ -74,9 +76,122 @@ class TransactionTaxReport {
const isFIFO = strategy === TRX_TAX_STRATEGIES.FIFO
const isLIFO = strategy === TRX_TAX_STRATEGIES.LIFO

const {
trxs: trxsForCurrPeriod,
trxsForConvToUsd
} = await this.#getTrxs({
user,
start,
end
})

if (
!Array.isArray(trxsForCurrPeriod) ||
trxsForCurrPeriod.length === 0
) {
return []
}

const {
trxs: trxsForPrevPeriod
} = start > 0
? await this.#getTrxs({
user,
start: 0,
end: start - 1
})
: { trxs: [] }

// TODO:
return []
}

async #getTrxs (params) {
const {
user,
start,
end
} = params ?? {}

const tradesPromise = this.#getTrades(params)
const withdrawalsPromise = this.movements.getMovements({
auth: user,
start,
end,
isWithdrawals: true,
isExcludePrivate: false
})
const depositsPromise = this.movements.getMovements({
auth: user,
start,
end,
isDeposits: true,
isExcludePrivate: false
})

const [
trades,
withdrawals,
deposits
] = await Promise.all([
tradesPromise,
withdrawalsPromise,
depositsPromise
])

const movements = [...withdrawals, ...deposits]
const remappedTrxs = []
const remappedTrxsForConvToUsd = []

remapTrades(
trades,
{ remappedTrxs, remappedTrxsForConvToUsd }
)
remapMovements(
movements,
{ remappedTrxs, remappedTrxsForConvToUsd }
)

const trxs = remappedTrxs
.sort((a, b) => b?.mtsCreate - a?.mtsCreate)
const trxsForConvToUsd = remappedTrxsForConvToUsd
.sort((a, b) => b?.mtsCreate - a?.mtsCreate)

return {
trxs,
trxsForConvToUsd
}
}

async #getTrades ({
user,
start,
end,
symbol
}) {
const symbFilter = (
Array.isArray(symbol) &&
symbol.length !== 0
)
? { $in: { symbol } }
: {}

return this.dao.getElemsInCollBy(
this.ALLOWED_COLLS.TRADES,
{
filter: {
user_id: user._id,
$lte: { mtsCreate: end },
$gte: { mtsCreate: start },
...symbFilter
},
sort: [['mtsCreate', -1]],
projection: this.tradesModel,
exclude: ['user_id'],
isExcludePrivate: false
}
)
}
}

decorateInjectable(TransactionTaxReport, depsTypes)
Expand Down