+ <div class="entry">
+ <div class="title">
+ <a href="https://people.skolelinux.org/pere/blog/Plain_text_accounting_file_from_your_bitcoin_transactions.html">Plain text accounting file from your bitcoin transactions</a>
+ </div>
+ <div class="date">
+ 7th March 2024
+ </div>
+ <div class="body">
+ <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>
+
+<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>
+
+ </div>
+ <div class="tags">
+
+
+ Tags: <a href="https://people.skolelinux.org/pere/blog/tags/bitcoin">bitcoin</a>, <a href="https://people.skolelinux.org/pere/blog/tags/english">english</a>.
+
+
+ </div>
+ </div>
+ <div class="padding"></div>
+