eprbell / dali-rp2

DaLI (Data Loader Interface) is a data loader and input generator for RP2 (https://pypi.org/project/rp2), the privacy-focused, free, open-source cryptocurrency tax calculator: DaLI removes the need to manually prepare RP2 input files. Just like RP2, DaLI is also free, open-source and it prioritizes user privacy.
https://pypi.org/project/dali-rp2/
Apache License 2.0
65 stars 43 forks source link

Coinbase Pro IntraTransaction fee and spot price #28

Closed stevendavis closed 2 years ago

stevendavis commented 2 years ago

My debug log shows a transaction like this (I've redacted a bunch of stuff):

dali/DEBUG: Self-contained transaction: IntraTransaction: plugin=Coinbase Pro raw_data={"id": "aaa", "amount": "-100.06", "balance": "0.003", "created_at": "bbb", "type": "transfer", "details": {"transfer_id": "ccc", "transfer_type": "withdraw"}}//{"id": "ddd", "type": "withdraw", "created_at": "eee", "completed_at": "fff", "canceled_at": null, "processed_at": "ggg", "account_id": "hhh", "user_id": "iii", "user_nonce": "jjj", "amount": "100.06", "details": {"fee": "1.06", "subtotal": "99.0", "sent_to_address": "kkk", "coinbase_account_id": "lll", "crypto_transaction_hash": "mmm", "tx_service_transaction_id": "nnn", "coinbase_payment_method_id": ""}, "idem": "ooo"}

For this transaction, DaLi does not have any information from the terminating side of the transfer because I have not provided it yet. The generated rp2 file shows the total amount of the transfer (100.06), but it looks like some of the data elements are not used. fee = 1.06 subtotal = 99.0

When I eventually provide the data for the terminating side of the transfer, should I expect rp2 will calculate the transfer fee?

Another observation is that the spot price is not filled in. DaLi correctly identified the asset, and I was expecting the spot price to be filled when using the -s option. What am I doing wrong?

eprbell commented 2 years ago

For intra-transactions the fee is implicitly defined as crypto_sent - crypto_received: when you add the missing half of the transaction it will get populated. The spot price should have been populated: are you sure you passed the -s option? If you did can you instrument the code around https://github.com/eprbell/dali-rp2/blob/main/src/dali/transaction_resolver.py#L202? Add a few LOGGER.debug calls to see what is happening. If -s has been passed it should get inside _update_spot_price_from_web() and it should read the price from Coinbase Pro.

stevendavis commented 2 years ago

Dali is indeed calling _update_spot_price_from_web, but the transaction spot_price is None, and it returns right away. Not sure how to fix this without breaking something else. What do you recommend?

eprbell commented 2 years ago

Can you add more LOGGER.debug calls inside _update_spot_price_from_web() to see what path it is taking? Also: does this happen for only one transaction or for all of them?

stevendavis commented 2 years ago

In my limited testing, _update_spot_price_from_web() always exits without doing anything, but for different reasons depending on the transaction.

For "match" transactions (BUY), the spot price is a numeric value upon entry to _update_spot_price_from_web(), so it exits without needing to fetch the price from the web.

For "transfer" / "withdraw" transactions. The function is exiting in this snippet:

    if transaction.spot_price is None:  # type: ignore
        return transaction
eprbell commented 2 years ago

Thanks for running the code with instrumentation: that gave me enough context to understand what is happening. This is not a bug, but intended behavior. In intra-transaction the only field that has an effect on taxes (or more precisely on the in-out coin flow) is crypto_fee, which is crypto_sent - crypto_received, as explained above. Since this is an unresolved transaction (there is only one half of it), either crypto_sent or crypto_received are missing and therefore crypto_fee cannot be computed: so the spot_price is also not computed, because it would be useless (it only affects the "taxable" part of the transaction, which is crypto_fee). If you were to complete the transaction by providing the missing half, then the transaction resolver would notice that both crypto_sent and crypto_received are present and therefore it would compute crypto_fee and resolve spot_price. Can you try that and let me know if it works?

stevendavis commented 2 years ago

Thanks for the detailed explanation. Tested again with manual intra CSV for the receiving side and the spot price was fetched from Coinbase Pro. Closing the issue.