Skip to content


Repository files navigation

mistbat cryptocurrency portfolio and tax analyzer


git clone
(create venv if desired)
cd mistbat
pip install -r requirements.txt


python --help - information on commands and options

  • python lsev [--remote-update] - list all events
  • python lstx [--no-group] - list all transactions
  • python holdings [--aggregated] - list all current holdings
  • python updatefmv - update any missing fmvs
  • python tax [--aggregated] [--year] - prepare form 8949. Use the aggregated switch and pass the year.
  • python currentbasis [--harvest] - show available basis, with optional insight into how to harvest tax losses
  • python remoteupdate <exchange> - update transactions from remote

Every tax season, I run remoteupdate on the exchanges I use (usually coinbase and gdax). Then, I edit manual_obs.yaml to add electrum events and update tx_match.yaml to match up events into transactions. From there, the tax --aggregated --year <year> command usually gives me what I need.


All configuration files are stored in ~/.config/mistbat/ or another directory defined by the XDG_CONFIG_HOME environment variables.


Contains the API keys and secrets for exchanges.

  api_key: KEY
  secret_key: KEY
  api_key: KEY
  secret_key: KEY
  api_key: KEY
  secret_key: KEY
  passphrase: PHRASE


Contains information on how to match Send and Recv events and Shapeshift events.

See tx_match.yaml.example for the format.


Annotations to apply to each transaction. Annotations are things like notes and which groups the transaction belongs to.

See tx_annotations.yaml.example for the format.


Any manually specified observations go in this file. This would include things like transactions that are not on an exchange, e.g., between electrum wallets.

See manual_obs.yaml.example for the format.

How It Works


  • "observations" (obs) are raw transaction data I'm getting from somewhere like coinbase
  • "events" (ev) are objects Send, Receive, Exchange generated directly from observations
  • "transactions" (tx) are SendReceive (a pair of Send and Receive events) or, if it's not a transaction between my wallets, it can be a Spend (lone Send) or Earn (lone Receive for earning/gaining money). It can also be ExchangeTx which is just an Exchange event promoted to a transaction.


  1. Loads the transaction history for each exchange via the API if possible
  2. Saves the raw responses from each service
  3. Loader for each exchange has a parser that parses raw response and turns it into Event objects (e.g., Send, Receive, Exchange)
  4. A separate "annotations" file adds information to each transaction that cannot be obtained from the exchange (e.g., my notes about the trade).
  5. The fmv information is added to each transaction where it hasn't been provided by the loader. It pulls the data from a cached file. If the fmv info isn't available, it will exit and ask you to run updatefmv to get the data.
  6. The transactions are processed with the annotations and the modified transactions are returned.
  7. Every Send event must have a corresponding Receive event and it can imply the fee implied fee
  8. On coinbase, sent amount includes fee, received amount does not
  9. Event ids are in the form of a three letter code for the exchange and a 5 char hash generated from the event data. e.g., coi-42ih5
  10. Transaction ids are in the same form, except the exchange code has x appended, and
  11. If its a SendReceive tx, then the four letter code is srtx (since its not just one exchange). The 5 character hash is the last two characters from the send hash, a '/' character and the last two characters from the receive hash.
  12. With a list of all transactions, can do whatever analysis that needs to be done.


  1. We get the fmv of cryptocurrencies that were not provided by the loader by polling the cryptocompare API and saving the fmv of the currency. The number we get is for EOD GMT.
  2. For exchanges between cryptocurrencies, we "imply" the fee based on the fmvs of the exchanged coins. A lot of times, this results in a negative fee (probably due to fluctuations in prices before fmv is captured at EOD), in which case we just say the fee is 0 for tax purposes.
  3. We always use the "implied" fee rather than the reported fee, since the missing value in the exchange is really the fee in the transaction.


mistbat - Cryptocurrency Portfolio Analyzer and Tax Calculator







No releases published


No packages published