Files
docker-finance/container/src/hledger-flow/accounts/bittrex/bittrex-shared.bash
Aaron Fiore f228da98d8 hledger-flow: bittrex: cost-basis work-around for bitcoin.tax
Issue #51 describes at least a few undocumented bitcoin.tax issues:

  - Bicoin.tax gives unexpected cost-basis results when `Fee` is given
    with `Total` (when `Total` is given in place of `Price`). The
    expectation is that bitcoin.tax will perform the cost-basis
    calculation on `Total` when `Fee` is also given.

    However, bitcoin.tax *will* give expected cost-basis results if
    `Price` is given in place of `Total` (with `Fee` also given) *or* if
    `Total` is given *after* local cost-basis adjustments are made (but
    *without* `Fee` given).

    The rationale for why docker-finance doesn't use `Price`:

      * docker-finance has all of the `Total`s; so `Price` isn't
        necessary.

      * Local price information isn't available for most trades (and
        shouldn't be necessary since all `Total`s are available).

  - Additionally, when `Fee` is non-fiat (crypto), it now must be marked
    as a SPEND in order to be disposed (and to produce an accurate
    closing report).

  - Finally, if `FeeCurrency` *does* not match either `Symbol` or
    `Currency` (e.g., BTC-ETH w/ BNB fee), it's unknown if cost-basis
    must be calculated locally as well (if `Total` is given). Local
    calculations cannot be done because `Fee` price information is
    (almost certainly) not available for this type of trade.

Until upstream can assert that attaching the `Fee` will subsequently
adjust the cost-basis of `Total` *and* dispose of the `Fee` in the
process (while also allowing `Total` to be used in place of `Price`),
the `Fee` (and `FeeCurrency`) column(s) must not be populated and values
instead moved to SPEND (as described above).

Upstream is aware of these issues (since May) and they're in the process
of resolution. In the meantime, docker-finance work-arounds should
suffice for all trades that have a fiat `Fee` and/or a
`Fee`/`FeeCurrency` that matches one side of the trading pair.
2024-07-29 16:15:50 -07:00

172 lines
5.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# docker-finance | modern accounting for the power-user
#
# Copyright (C) 2021-2024 Aaron Fiore (Founder, Evergreen Crypto LLC)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# "Libraries"
#
[ -z "$DOCKER_FINANCE_CONTAINER_REPO" ] && exit 1
source "${DOCKER_FINANCE_CONTAINER_REPO}/src/hledger-flow/lib/lib_preprocess.bash" "$1" "$2"
#
# Implementation
#
[ -z "$global_year" ] && exit 1
[ -z "$global_subaccount" ] && exit 1
[ -z "$global_in_path" ] && exit 1
[ -z "$global_out_path" ] && exit 1
#
# Unified output format for all file types:
#
# UID,Date,Type,OrderType,CurrencyOne,CurrencyTwo,Amount,Proceeds,Fees,Destination,TXID,Direction,Subaccount
#
function parse_deposit()
{
# NOTE: headers can be variable, not possible to assert
xsv select "id,completedAt,currencySymbol,quantity,cryptoAddress,txId" "$global_in_path" \
| gawk -v global_year="$global_year" -v global_subaccount="$global_subaccount" \
'{
if (NR<2 || $2 !~ global_year)
next
cmd = "date --utc \"+%F %T\" --date="$2 | getline date
printf $1 OFS # UID
printf date OFS # Date
printf "DEPOSIT" OFS # Type
printf OFS # OrderType
printf $3 OFS # CurrencyOne
printf OFS # CurrencyTwo
printf("%.8f", $4); printf OFS # Amount
printf OFS # Proceeds
printf OFS # Fees
printf OFS # Cost-basis
printf $5 OFS # Destination
printf $6 OFS # TXID
printf "IN" OFS # Direction
printf global_subaccount # Subaccount
printf "\n"
}' FS=, OFS=, >"$global_out_path"
}
function parse_trade()
{
# NOTE:
# - Commissions are paid in base currency (one on the left)
# - Proceeds are divided by quantity.
# WARNING: don't use limit because market orders will be empty
# NOTE: headers can be variable, not possible to assert
xsv select "id,closedAt,direction,marketSymbol,fillQuantity,proceeds,commission" "$global_in_path" \
| gawk -v global_year="$global_year" -v global_subaccount="$global_subaccount" \
'{
if (NR<2 || $2 !~ global_year)
next
cmd = "date --utc \"+%F %T\" --date="$2 | getline date
# For trades, Bittrex doesnt create separate columns for pairs.
# So, turn BTC-USD into two separate columns.
sub(/-/, ",", $4)
# TODO: HACK: see #51 and respective lib_taxes work-around
# Calculate cost-basis
switch ($3)
{
case "BUY":
cost_basis=sprintf("%.8f", $6 + $7)
break
case "SELL":
cost_basis=sprintf("%.8f", $6 - $7)
break
default:
printf "FATAL: unsupported order type: " $3
print $0
exit
}
printf $1 OFS # UID
printf date OFS # Date
printf "TRADE" OFS # Type
printf $3 OFS # OrderType
printf $4 OFS # CurrencyOne
# see above # CurrencyTwo
printf("%.8f", $5); printf OFS # Amount
printf("%.8f", $6); printf OFS # Proceeds
printf("%.8f", $7); printf OFS # Fees
printf cost_basis OFS # Cost-basis
printf OFS # Destination
printf OFS # TXID
printf OFS # Direction
printf global_subaccount # Subaccount
printf "\n"
}' FS=, OFS=, >"$global_out_path"
}
function parse_withdrawal()
{
# NOTE: headers can be variable, not possible to assert
xsv select "id,completedAt,currencySymbol,quantity,txCost,cryptoAddress,txId" "$global_in_path" \
| gawk -v global_year="$global_year" -v global_subaccount="$global_subaccount" \
'{
if (NR<2 || $2 !~ global_year)
next
cmd = "date --utc \"+%F %T\" --date="$2 | getline date
printf $1 OFS # UID
printf date OFS # Date
printf "WITHDRAWAL" OFS # Type
printf OFS # OrderType
printf $3 OFS # CurrencyOne
printf OFS # CurrencyTwo
printf("%.8f", $4); printf OFS # Amount
printf OFS # Proceeds
printf("%.8f", $5); printf OFS # Fees
printf OFS # Cost-basis
printf $6 OFS # Destination
printf $7 OFS # TXID
printf "OUT" OFS # Direction
printf global_subaccount # Subaccount
printf "\n"
}' FS=, OFS=, >"$global_out_path"
}
function main()
{
# REST API provided files
lib_preprocess::test_header "source" && parse_deposit && return 0
lib_preprocess::test_header "direction" && parse_trade && return 0
lib_preprocess::test_header "txCost" && parse_withdrawal && return 0
}
main "$@"
# vim: sw=2 sts=2 si ai et