Compare commits

..

5 Commits

Author SHA1 Message Date
98674c5b3b container: lib_taxes: add support for bitcoin.tax TransferAccount column
- Is backward compatible to all rules without TransferAccount added.
  * TransferAccount value must be added via custom rules.

- Upstream appears to support TransferAccount for all years even though
  it was introduced specifically for tax years 2025+ (mileage may vary).
2026-04-03 13:57:23 -07:00
f3473beb00 container: lib_taxes: update bitcoin.tax docs/supported tags 2026-04-03 13:53:32 -07:00
b4c7aa7db2 Merge pull request #339 into master
71153490 container: hledger-flow: coinbase: refactor native/network calc columns (Aaron Fiore)
59ec5a90 container: hledger-flow: coinbase: add on-chain native tx amount column (Aaron Fiore)
2026-04-02 16:30:17 -07:00
71153490eb container: hledger-flow: coinbase: refactor native/network calc columns 2026-04-01 12:59:04 -07:00
59ec5a9010 container: hledger-flow: coinbase: add on-chain native tx amount column
for calculating native (fiat) amount without fee. Can be used with
custom tax rules to calculate native amount of any on-chain spends.
2026-04-01 12:57:33 -07:00
3 changed files with 103 additions and 42 deletions

View File

@@ -298,10 +298,17 @@ function lib_taxes::__parse_args()
function lib_taxes::__taxes()
{
#
# Supported tags (locally "global")
local -r _income_tags=("INCOME" "GIFTIN" "MINING" "BORROW")
local -r _spends_tags=("SPEND" "DONATION" "GIFT" "REPAY")
local -r _trades_tags=("BUY" "SELL" "SWAP" "MATCH" "REBATE" "RAW_TRADE" "PARTIAL_TRADE")
#
local -r _income_tags=("INCOME" "INTEREST" "GIFTIN" "MINING" "BORROW" "DEPOSIT")
local -r _spends_tags=("SPEND" "DONATION" "GIFT" "REPAY" "WITHDRAWAL")
local _trades_tags
_trades_tags+=("BUY" "SELL" "SWAP") # Upstream
_trades_tags+=("MATCH" "REBATE" "RAW_TRADE" "PARTIAL_TRADE") # Unique to `dfi`
declare -r _trades_tags
local _print_year="$global_arg_year"
[ -z "$_print_year" ] && _print_year="all"
@@ -355,49 +362,63 @@ function lib_taxes::__taxes_print()
for _arg in "${_arg_tag[@]}"; do
case "$_arg" in
income)
#
# Date (date and time as YYYY-MM-DD HH:mm:ss Z)
# Action (INCOME, GIFTIN, MINING or BORROW)
# Account (account or wallet name, e.g. Coinbase or Blockchain)
# Symbol (BTC, ETH, LTC, etc)
# Volume (number of coins received)
# Currency (optional, specify alternative to your default currency, such as USD, GBP or EUR)
# Action (INCOME, INTEREST, GIFTIN, MINING, BORROW or DEPOSIT)
# Account (account or wallet name, e.g. Coinbase or Ledger)
# Symbol (BTC, ETH, USDC, etc)
# Volume (amount of crypto received)
# Total (Fair price or value in Currency or your home currency, or blank for market value
# Currency (optional, specify alternative to your default currency, such as USD, GBP or EUR)
# TransferAccount (name of sending account for DEPOSIT)
# Memo (optional, name of sender or item sold)
# For example,
# Date,Action,Account,Symbol,Volume
# 2020-01-01 13:00:00 -0800,INCOME,"Blockchain Wallet",BTC,1
#
# For example:
#
# Date,Action,Account,Symbol,Volume
# 2020-01-01 13:00:00 -0800,INCOME,"Blockchain Wallet",BTC,1
#
local _tags=("${_income_tags[@]}")
local _is_income=true
;;
spend | spends)
#
# Date (date and time as YYYY-MM-DD HH:mm:ss Z)
# Action (SPEND, DONATION, GIFT or REPAY)
# Account (name of account or wallet, e.g. Coinbase or Blockchain)
# Symbol (BTC, LTC, ETH, etc)
# Volume (number of coins spent)
# Currency (optional, specify alternative to your default currency, such as USD, GBP or EUR)
# Action (SPEND, DONATION, GIFT, REPAY or WITHDRAWAL)
# Account (name of account or wallet, e.g. Coinbase or Ledger)
# Symbol (BTC, ETH, USDC, etc)
# Volume (amount of crypto spent or sent)
# Total (Fair price or cost in Currency or your home currency, or blank for market value
# Currency (optional, specify alternative to your default currency, such as USD, GBP or EUR)
# TransferAccount (name of receiving account for WITHDRAWAL)
# Memo (optional, name of recipient or item purchased)
# For example,
# Date,Action,Account,Symbol,Volume,Total,Currency
# 2020-01-01 13:00:00 -0800,SPEND,"Blockchain Wallet",BTC,1,
#
# For example:
#
# Date,Action,Account,Symbol,Volume,Total,Currency
# 2020-01-01 13:00:00 -0800,SPEND,"Blockchain Wallet",BTC,1,500,USD
#
local _tags=("${_spends_tags[@]}")
local _is_spends=true
;;
trade | trades)
#
# Date (date and time as YYYY-MM-DD HH:mm:ss Z)
# Action (BUY, SELL or SWAP)
# Account (override the exchange or wallet name, e.g. Coinbase)
# Symbol (BTC, LTC, DASH, etc)
# Volume (number of coins traded)
# Currency (specify currency such as USD, GBP, EUR or coins, BTC or LTC)
# Account (override the exchange or wallet name, e.g. Coinbase)
# Total (you can use the total Currency amount or price per coin)
# Price (price per coin in Currency or blank for lookup)
# FeeCurrency (currency of fee if different than Currency)
# Fee (any additional costs of the trade)
# For example,
# Date,Action,Account,Symbol,Volume,Price,Currency,Fee
# 2020-01-01 13:00:00 -0800,BUY,Coinbase,BTC,1500,USD,5.50
# FeeCurrency (currency of fee if different than Currency)
#
# For example:
#
# Date,Action,Account,Symbol,Volume,Price,Currency,Fee
# 2020-01-01 13:00:00 -0800,BUY,Coinbase,BTC,1,500,USD,5.50
#
local _tags=("${_trades_tags[@]}")
local _is_trades=true
;;
@@ -434,16 +455,17 @@ function lib_taxes::__taxes_print()
| xan select '"posting-comment"' \
| tail -n +2 \
| sed -e 's:"::g' -e '/^$/d' \
| gawk -v tag="$_tag" -v is_trades="$_is_trades" \
| gawk \
-v tag="$_tag" \
-v is_income="$_is_income" -v is_spends="$_is_spends" -v is_trades="$_is_trades" \
'BEGIN {
FS=OFS=","
str_one="Date" OFS "Action" OFS "Account" OFS "Symbol" OFS "Volume" OFS "Currency" OFS "Total"
str_two="FeeCurrency" OFS "Fee"
str_three="TransferAccount"
has_fee = (is_trades ? 1 : 0)
printf (has_fee ? str_one OFS str_two : str_one OFS "Memo")
printf (is_trades ? str_one OFS str_two : str_one OFS "Memo" OFS str_three)
printf "\n"
}
{
@@ -456,12 +478,14 @@ function lib_taxes::__taxes_print()
Volume = $5
Currency = $6
Total = $7
Memo = (has_fee ? "" : $8)
if (has_fee)
Memo = (is_trades ? "" : $8)
if (is_trades)
{
FeeCurrency = (has_fee && $9 != "" ? $8 : "")
FeeCurrency = ($9 != "" ? $8 : "")
Fee = (FeeCurrency && $9 != 0 ? $9 : "")
}
# NOTE: TransferAccount value added by custom-rules
TransferAccount = (is_trades ? "" : $9)
# Total is a literal 0. Get market value from bitcoin.tax
if (Total == 0) {Total=""}
@@ -544,7 +568,7 @@ function lib_taxes::__taxes_print()
# TODO: HACK: print SPEND line for non-fiat fee (see #51)
# NOTE: cost-basis *MUST* be calculated above or within preprocess
if (is_trades && has_fee && FeeCurrency != "USD" && Fee)
if (is_trades && FeeCurrency != "USD" && Fee)
{
printf Date OFS "SPEND" OFS Account OFS
printf FeeCurrency OFS Fee OFS "USD"
@@ -569,7 +593,7 @@ function lib_taxes::__taxes_print()
printf Currency OFS
printf Total OFS
if (has_fee)
if (!is_income)
{
# TODO: HACK: see #51 (and related lib_taxes work-around)
# NOTE: cost-basis *MUST* be calculated within preprocess
@@ -579,12 +603,19 @@ function lib_taxes::__taxes_print()
}
else
{
printf FeeCurrency OFS Fee
if (is_trades)
{
printf FeeCurrency OFS Fee
}
else
{
printf Memo OFS TransferAccount
}
}
}
else
{
printf Memo
printf Memo OFS TransferAccount
}
printf "\n"
@@ -1240,6 +1271,13 @@ function lib_taxes::__reports_patch()
&& sort -ru -o "$_spends" "$_spends"
fi
# Allow impl to add to header while providing backward compat for all rules (repo/custom)
local -r _files=("$_income" "$_spends" "$_trades")
local -r _csvclean=("csvclean" "--fill-short-rows")
for _file in "${_files[@]}"; do
[[ -f "$_file" && -s "$_file" ]] && echo "$("${_csvclean[@]}" "${_file}")" >"${_file}"
done
#
# Verify success of patches
#
@@ -1282,7 +1320,7 @@ function lib_taxes::__reports_obfs()
# NOTE: functions inherit local vars
case "$_arg_tag" in
income | spends)
local _all_columns="Date,Action,Account,Symbol,Volume,Currency,Total,Memo"
local _all_columns="Date,Action,Account,Symbol,Volume,Currency,Total,Memo,TransferAccount"
local _obfs_columns=("Account" "Memo")
;;
trades)

View File

@@ -176,15 +176,37 @@ function __parse()
# managed within the rules file.
#
if ($7 > 0 && $5 > 0) {printf("%.8f", $7 / $5)}; printf OFS # native_amount_price
if ($7 > 0 && $5 > 0) {printf("%.8f", ($7 / $5) * $16)}; printf OFS # native_network_transaction_fee_amount
if ($7 > 0 && $5 > 0)
{
native_amount_price = $7 / $5
printf("%.8f", native_amount_price); printf OFS
native_network_transaction_fee_amount = native_amount_price * $16
printf("%.8f", native_network_transaction_fee_amount); printf OFS
}
else
{
printf OFS # native_amount_price
printf OFS # native_network_transaction_fee_amount
}
#
# Add new column to calculate the difference between amount and network fee,
# i.e., amount_amount - network_transaction_fee_amount
# Add new columns to calculate the difference between amount and network fee
#
if ($16 > 0) {printf("%.8f", $5 - $16)}; printf OFS # network_transaction_amount_amount
if ($16 > 0)
{
network_transaction_amount_amount = $5 - $16
printf("%.8f", network_transaction_amount_amount); printf OFS
native_network_transaction_amount = network_transaction_amount_amount * native_amount_price
printf("%.8f", native_network_transaction_amount); printf OFS
}
else
{
printf OFS # network_transaction_amount_amount
printf OFS # native_network_transaction_amount
}
#
# Advanced Trade: add new column for calculating real value amount

View File

@@ -15,7 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
fields account_id,coinbase_id,type,status_,amount_amount,amount_currency,native_amount_amount,native_amount_currency,created_at,resource,resource_path,description,network_status,network_network_name,network_hash,network_transaction_fee_amount,network_transaction_fee_currency,to_resource,to_address,to_email,from_resource,from_resource_path,from_id,from_name,cancelable,idem,buy_total_amount,buy_total_currency,buy_subtotal_amount,buy_subtotal_currency,buy_fee_amount,buy_fee_currency,buy_id,buy_payment_method_name,sell_total_amount,sell_total_currency,sell_subtotal_amount,sell_subtotal_currency,sell_fee_amount,sell_fee_currency,sell_id,sell_payment_method_name,trade_fee_amount,trade_fee_currency,trade_id,trade_payment_method_name,advanced_trade_fill_fill_price,advanced_trade_fill_product_id,advanced_trade_fill_order_id,advanced_trade_fill_commission,advanced_trade_fill_order_side,native_amount_price,native_network_transaction_fee_amount,network_transaction_amount_amount,advanced_trade_fill_real_value_amount,advanced_trade_fill_pair_lhs,advanced_trade_fill_pair_rhs,advanced_trade_fill_cost_basis_amount,taxed_at,direction,subaccount
fields account_id,coinbase_id,type,status_,amount_amount,amount_currency,native_amount_amount,native_amount_currency,created_at,resource,resource_path,description,network_status,network_network_name,network_hash,network_transaction_fee_amount,network_transaction_fee_currency,to_resource,to_address,to_email,from_resource,from_resource_path,from_id,from_name,cancelable,idem,buy_total_amount,buy_total_currency,buy_subtotal_amount,buy_subtotal_currency,buy_fee_amount,buy_fee_currency,buy_id,buy_payment_method_name,sell_total_amount,sell_total_currency,sell_subtotal_amount,sell_subtotal_currency,sell_fee_amount,sell_fee_currency,sell_id,sell_payment_method_name,trade_fee_amount,trade_fee_currency,trade_id,trade_payment_method_name,advanced_trade_fill_fill_price,advanced_trade_fill_product_id,advanced_trade_fill_order_id,advanced_trade_fill_commission,advanced_trade_fill_order_side,native_amount_price,native_network_transaction_fee_amount,network_transaction_amount_amount,native_network_transaction_amount,advanced_trade_fill_real_value_amount,advanced_trade_fill_pair_lhs,advanced_trade_fill_pair_rhs,advanced_trade_fill_cost_basis_amount,taxed_at,direction,subaccount
# The above fields; in order and in human-readable form:
#
@@ -73,6 +73,7 @@ fields account_id,coinbase_id,type,status_,amount_amount,amount_currency,native_
# native_amount_price
# native_network_transaction_fee_amount
# network_transaction_amount_amount
# native_network_transaction_amount
# advanced_trade_fill_real_value_amount
# advanced_trade_fill_pair_lhs
# advanced_trade_fill_pair_rhs