mistbat cryptocurrency portfolio and tax analyzer
git clone email@example.com:hkhanna/mistbat.git cd mistbat pip install -r requirements.txt
python mistbat.py --help - information on commands and options
python mistbat.py lsev [--remote-update]- list all events
python mistbat.py lstx [--no-group]- list all transactions
python mistbat.py holdings [--aggregated]- list all current holdings
python mistbat.py updatefmv- update any missing fmvs
python mistbat.py tax [--aggregated] [--year]- prepare form 8949. Use the aggregated switch and pass the year.
python mistbat.py currentbasis [--harvest]- show available basis, with optional insight into how to harvest tax losses
python mistbat.py 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.
binance: api_key: KEY secret_key: KEY coinbase: api_key: KEY secret_key: KEY gdax: api_key: KEY secret_key: KEY passphrase: PHRASE
Contains information on how to match Send and Recv events and Shapeshift events.
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.
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.
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.
- Loads the transaction history for each exchange via the API if possible
- Saves the raw responses from each service
- Loader for each exchange has a parser that parses raw response and turns it into Event objects (e.g., Send, Receive, Exchange)
- A separate "annotations" file adds information to each transaction that cannot be obtained from the exchange (e.g., my notes about the trade).
- 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
updatefmvto get the data.
- The transactions are processed with the annotations and the modified transactions are returned.
- Every Send event must have a corresponding Receive event and it can imply the fee implied fee
- On coinbase, sent amount includes fee, received amount does not
- 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
- Transaction ids are in the same form, except the exchange code has x appended, and
- 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.
- With a list of all transactions, can do whatever analysis that needs to be done.
- 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.
- 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.
- 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.