My personal accounting setup
I use a pretty complicated double entry accounting setup to handle my personal finances. This note should serve as a broad overview and inspiration.
GnuCash - the centerpiece
All the transactions are stored in GnuCash. This is a double-entry accounting software that stores all the data in a SQLite database. For me, it mostly serves as a ledger, though I do visit the GUI from time to time to categorize transactions that do not get categorized automatically and to resolve any discrepancies.
The database can be accessed from Python using the piecash library, which is central to the rest of the setup.
housekeeper - the semi-automated data synchronizer
This is a set of scripts that run semi-automatically and serve to pull data into GnuCash from various sources. The primary sources are:
Fio Bank
The bank I use has an API that is freely accessible to individuals (something that is not exactly common in Czech banking). The fiobank Python library conveniently wraps this API. Housekeeper pulls the transactions from the API and imports them into GnuCash, avoiding duplicates by (mis)using the transaction's unique ID we get from the bank.
In practice, an invariant that I maintain is that every split of a transaction in the "Assets:Bank:Fio" account corresponds to exactly one transaction in the bank's UI.
This is also where the bulk of the categorization happens. I find that a categorization algorithm that is effectively a bunch of if-else statements is good enough to classify something like 80% of my transactions.
if "UBER" in description:
return "Expenses:Transport:Taxi"
elif "TESCO" in description:
return "Expenses:Groceries"
# ...
I prefer to explicitly not categorize something that I am not 100% certain about (or possibly cannot even be certain about, such as a store that sells both groceries and medical supplies). These transactions are left uncategorized, with the other end hanging in the "Imbalance" GnuCash account until I manually categorize them.
It is worth noting that Gnucash supports arbitrary amount of splits per transaction, meaning that a single bank transaction can easily be split into multiple categories.
Of course being a fully fledged double-entry accounting software, I can even send a part of a transaction to another asset account, such as an "Assets:Money:Owed to me" account that I can use to track money that friends owe me.
Brokers
I maintain accounts with multiple brokers, and housekeeper serves to synchronize the transaction from within the broker accounts into GnuCash. One aspect I want to preserve is that housekeeper has read-only access to the broker accoutns. So, even if a broker has an API (or there exists a wrapper around the web interface API), I do not use it if it would require write access.
eBroker
This is a Czech broker that has an impressively antiquated web interface. No API is available, I just download the CSV file with transactions and run a manual import script. Over the years, some rather curious edge cases have been discovered, such as a misaccounted dividend that then got corrected in a later transaction.
Degiro
Here, the workflow is mostly identical to eBroker --- I download a CSV file from the Transactions page and run a manual import script.
IBKR
Nothing implemented yet.
capital.py - the report generator
yfinance This is a large Python script that serves to generate various statistics, from per-category expense reports to stock portfolio gains. This is run in cron every day and results in an HTML file with various graphs and tables.
The script ingests data from GnuCash using piecash, stock prices from yfinance, and then generates a report using Jinja2 with Plotly serving as the plotting backend.