-<p>As usual, if you use Bitcoin and want to show your support of my
-activities, please send Bitcoin donations to my address
-<b><a href="bitcoin:15oWEoG9dUPovwmUL9KWAnYRtNJEkP1u1b">15oWEoG9dUPovwmUL9KWAnYRtNJEkP1u1b</a></b>.</p>
-</description>
- </item>
-
- <item>
- <title>Plain text accounting file from your bitcoin transactions</title>
- <link>http://www.hungry.com/~pere/blog/Plain_text_accounting_file_from_your_bitcoin_transactions.html</link>
- <guid isPermaLink="true">http://www.hungry.com/~pere/blog/Plain_text_accounting_file_from_your_bitcoin_transactions.html</guid>
- <pubDate>Thu, 7 Mar 2024 18:00:00 +0100</pubDate>
- <description><p>A while back I wrote a small script to extract the Bitcoin
-transactions in a wallet in the
-<ahref="https://plaintextaccounting.org/">ledger plain text accounting
-format</a>. The last few days I spent some time to get it working
-better with more special cases. In case it can be useful for others,
-here is a copy:</p>
-
-<p><blockquote><pre>
-#!/usr/bin/python3
-# -*- coding: utf-8 -*-
-# Copyright (c) 2023-2024 Petter Reinholdtsen
-
-from decimal import Decimal
-import json
-import subprocess
-import time
-
-import numpy
-
-def format_float(num):
- return numpy.format_float_positional(num, trim='-')
-
-accounts = {
- u'amount' : 'Assets:BTC:main',
-}
-
-addresses = {
- '<some address>' : 'Assets:bankkonto',
- '<some address>' : 'Assets:bankkonto',
-}
-
-def exec_json(cmd):
- proc = subprocess.Popen(cmd,stdout=subprocess.PIPE)
- j = json.loads(proc.communicate()[0], parse_float=Decimal)
- return j
-
-def list_txs():
- # get all transactions for all accounts / addresses
- c = 0
- txs = []
- txidfee = {}
- limit=100000
- cmd = ['bitcoin-cli', 'listtransactions', '*', str(limit)]
- if True:
- txs.extend(exec_json(cmd))
- else:
- # Useful for debugging
- with open('transactions.json') as f:
- txs.extend(json.load(f, parse_float=Decimal))
- #print txs
- for tx in sorted(txs, key=lambda a: a['time']):
-# print tx['category']
- if 'abandoned' in tx and tx['abandoned']:
- continue
- if 'confirmations' in tx and 0 >= tx['confirmations']:
- continue
- when = time.strftime('%Y-%m-%d %H:%M', time.localtime(tx['time']))
- if 'message' in tx:
- desc = tx['message']
- elif 'comment' in tx:
- desc = tx['comment']
- elif 'label' in tx:
- desc = tx['label']
- else:
- desc = 'n/a'
- print("%s %s" % (when, desc))
- if 'address' in tx:
- print(" ; to bitcoin address %s" % tx['address'])
- else:
- print(" ; missing address in transaction, txid=%s" % tx['txid'])
- print(f" ; amount={tx['amount']}")
- if 'fee'in tx:
- print(f" ; fee={tx['fee']}")
- for f in accounts.keys():
- if f in tx and Decimal(0) != tx[f]:
- amount = tx[f]
- print(" %-20s %s BTC" % (accounts[f], format_float(amount)))
- if 'fee' in tx and Decimal(0) != tx['fee']:
- # Make sure to list fee used in several transactions only once.
- if 'fee' in tx and tx['txid'] in txidfee \
- and tx['fee'] == txidfee[tx['txid']]:
- True
- else:
- fee = tx['fee']
- print(" %-20s %s BTC" % (accounts['amount'], format_float(fee)))
- print(" %-20s %s BTC" % ('Expences:BTC-fee', format_float(-fee)))
- txidfee[tx['txid']] = tx['fee']
-
- if 'address' in tx and tx['address'] in addresses:
- print(" %s" % addresses[tx['address']])
- else:
- if 'generate' == tx['category']:
- print(" Income:BTC-mining")
- else:
- if amount < Decimal(0):
- print(f" Assets:unknown:sent:update-script-addr-{tx['address']}")
- else:
- print(f" Assets:unknown:received:update-script-addr-{tx['address']}")
-
- print()
- c = c + 1
- print("# Found %d transactions" % c)
- if limit == c:
- print(f"# Warning: Limit {limit} reached, consider increasing limit.")
-
-def main():
- list_txs()
-
-main()
-</pre></blockquote></p>
-
-<p>It is more of a proof of concept, and I do not expect it to handle
-all edge cases, but it worked for me, and perhaps you can find it
-useful too.</p>
-
-<p>To get a more interesting result, it is useful to map accounts sent
-to or received from to accounting accounts, using the
-<tt>addresses</tt> hash. As these will be very context dependent, I
-leave out my list to allow each user to fill out their own list of
-accounts. Out of the box, 'ledger reg BTC:main' should be able to
-show the amount of BTCs present in the wallet at any given time in the
-past. For other and more valuable analysis, a account plan need to be
-set up in the <tt>addresses</tt> hash. Here is an example
-transaction:</p>
-
-<p><blockquote><pre>
-2024-03-07 17:00 Donated to good cause
- Assets:BTC:main -0.1 BTC
- Assets:BTC:main -0.00001 BTC
- Expences:BTC-fee 0.00001 BTC
- Expences:donations 0.1 BTC
-</pre></blockquote></p>
-
-<p>It need a running Bitcoin Core daemon running, as it connect to it
-using <tt>bitcoin-cli listtransactions * 100000</tt> to extract the
-transactions listed in the Wallet.</p>
-