Open westofpluto opened 2 years ago
Glad you find it useful, thanks! In the case you're describing you can still use RP2: just leave out the transactions/lots you have already sold in previous years.
E.g. suppose your first year of trading BTC was 2020 and you have a situation like this: a) 2020-2-5: buy 1 BTC b) 2020-5-5: buy 2 BTC c) 2020: 8-1: sell 1.5 BTC d) 2021: more transactions... Let's also assume you were using FIFO and in 2021 you didn't use RP2. This means you sold all of lot a) and 0.5 BTC from lot b).
So if you want to start using RP2 this year for your 2021 taxes, you would just leave out what you already sold and enter the following in the input spreadsheet: b) 2020-5-5: buy 1.5 BTC d) 2021: more transactions...
This is because lot a), part of lot b) and lot c) are already accounted for in the pre-RP2 system you were using.
Of course you still need to keep all the documentation for previous years as well as for the current year. Also keep in mind that you will need to keep the same accounting method you were using previously: if you want to switch accounting method (e.g. from FIFO to LIFO) you will need to speak to a tax professional. If RP2 is helpful to you, please consider giving the project a star.
BTW in the example above you can use the Notes column to remind yourself why lot b) only has 1.5 BTC instead of 2, in case you look at the data years in the future.
Aha! Yes this makes sense. If I have an Excel spreadsheet of my portfolio at start of 2021, with current lots of each coin/token and dates and cost basis when they were bought, you are completely right - I can just use these as prior year "transactions" and all will be well.
I have another couple of questions:
Finally a suggestion: if it isn't in there already, it would be really cool to let the user select a date/tim/timestamp and have the code show profit/loss per coin (a portfolio and PNL snapshot) up to that point of time.
Thanks again!
It also looks like a SWAP between coins has to be specified as two transaction. For example, if I am on Pancakeswap and swap BNB for CAKE, then I have to specify two transaction: one for selling BNB to USD and a second for buying CAKE with the USD. However, the transaction records I get regarding this transaction (have to download using my wallet address on bscscan.com) only show a single transaction: swapping BNB for CAKE. You might want to add an additional category of transactions (SWAP) to account for this.
Good questions. What I tried to do in this first version of RP2 is to identify the basic building blocks that can capture the meaning of crypto transactions types from a tax perspective. This means that there may be higher-level transaction types that can be expressed as a combination of primitive transaction types: an example is swapping coins (as you say, it's expressible with a buy + a sell transaction). The design principle has been one of minimalism: I tried to resist the temptation of adding types that were not absolutely necessary, at least in this initial pre-1.0 phase.
With that said, there are two things to consider when refining the design: 1) is the current list of primitive transaction types incomplete? I.e. are real-world crypto transactions that are not expressible with the existing list? 2) are some high level transactions are frequent/useful enough to deserve their own transaction type, even though it's not primitive?
Point number 1) is high-priority: if we're missing a primitive type, then it means that certain crypto transaction cannot be captured in RP2 and this needs to be addressed ASAP.
Point number 2) is nice to have but lower priority: before committing to higher-level types I would like for RP2 to mature a bit more and gain more users.
With this introduction out of the way, let's get to your questions/comments.
pure cost transactions: this is an interesting one. I guess semantically it would be the same as a SELL transaction or am I missing something else that makes it different? In other words, is there anything tax-wise that distinguishes burning gas from selling? Genuine question: I have to think about this and input is welcome.
STAKING and INTRA: as you mentioned, the STAKING type captures the earnings. INTRA transactions capture movements of funds between accounts: they have only one implicit type (MOVE) that cannot be set by the user. So I think you capture the scenario you're describing with one INTRA transaction to the pool, one or more STAKING transactions for the rewards and one INTRA transaction from the pool (if you withdraw your crypto from the pool).
MATIC and bridging: I don't know much about MATIC, so I have a few questions. Are Coinbase/MATIC and MATIC/MATIC two different tokens? Do they have different prices?
profit/loss up to a date in time: this is a cool suggestion. Right now RP2 already generates yearly end-of-year gain/losses for every coin in the Summary tab of the rp2_full_report.ods output. Can you share more about your scenario and why non-end-of-the-year gain/loss might be useful?
swapping coins: partially answered above. As mentioned, my main concern is to ensure RP2 can capture everything with its primitives, at least in this early phase, but I'm open to introducing higher level concepts later after some more maturation. As for the discrepancy with your records, you can use the Notes field to explain.
Anyway, hope this helps, thanks for the great feedback and questions.
Hi again, and thanks for the thoughtful comments!
COST transaction: I think a COST is a special type of SELL. You would have to set the "spot price" for the SELL to zero, which looks a little strange. Or you would have to set the crypto fee and fiat fee to be the total cost. It looks a little clunky. It would be great to be more clear and have a COST label so it would be obvious. And perhaps the code could be modified so that if it is a COST type, then it gets handled as a cost without requiring any funky looking input data.
DEFI transactions: One thing that is problematic in your code is that it appears to assume that any buy or sell transaction will be paid either in USD or in whatever currency is bought or sold. In DEFI this is not the case. If I am trading in my Metamask wallet on the Binance Smart Chain, all fees are paid in BNB. So if I want to swap one Binance Smart Chain token for another (say BUSD for CAKE), my fees will still be paid in BNB, not BUSD and not CAKE. It would be best to have columns that specify "fee currency" and "fee currency spot price" so that the fees in USD can be more easily calculated.
BRIDGING: Yes this gets a little confusing. As you know, there are now lots of blockchains that run Ethereum Virtual Machines (EVMs) which allows these blockchains to have their own tokens. Ethereum has its vast set of tokens and Polygon (MATIC) has its own set of tokens. However it gets more complicated than that: it turns out that not only is MATIC the native coin on the Polygon chain - there is also a version of MATIC that exists as an Ethereum token on the Ethereum chain. They always have the exact same price and name, they just exist on different chains. So if I buy MATIC at Coinbase, what I am really buying is the MATIC token that exists on the Ethereum chain. If I send that to my Metamask wallet, it will appear as an ETH token in the ETH network on my Metamask. If I want to use it in Polyon projects, I have to use something called the polygon bridge (at wallet.polygon.technology). I hook up my wallet and tell this site to bridge my MATIC (ETH token) over to the Polygon network. When it is finished, I now have the same amount of MATIC on the Polygon network and all my MATIC on the ETH network is consumed. To do all this I have to pay gas fees in ETH. When I want to send the MATIC back to Coinbase, I have to do the process in reverse: bridge it back to ETH. (You'd be amazed how many people forget this step and lose all their MATIC sending Polygon MATIC back to Coinbase).
In any case, I would guess that you could treat the bridge operation as an INTRA: you are basically transferring MATIC to yourself, just from one network to another. But again, you pay fees in ETH, not MATIC. Not sure how to account for this in the existing code.
PNL at datetime: One use case for this is for example what happened to me this past year. In 2021 I was doing the normal buy and hold for various cryptos until maybe August. I had only say 150 transactions total. Then in mid August I got into DEFI with all kinds of trading and bridging and staking, rebase DAOs, etc. I suddenly had about 2000 more transactions. It would be nice to see what my PNL was up through the time I switched from basic investing to DEFI.
A second use case -probably even more important - is this: The code and calculations are only as good as your transaction data. Garbage in, garbage out. And with 2000 transactions, it is easy to miss transactions or duplicate them or generally get them wrong. If we have to go through an iterative process to enter data, run calcs and fix the data wherever necessary, it would be really useful to compute PNL up to a given date. This makes it much easier to pinpoint where our input data likely went wrong.
Finally, there is a particular type of project that currently no software seems to handle properly. This is a type of investment where you invest some number of XYZ coins (whatever) and those coins are permalocked in the project. You can never withdraw them. Instead, the project gives you some percent daily return of these coins. In some cases this return can be maxed out by some set of project rules while in other cases it goes on forever. An example of the former is DRIP (drip.community) where you put in say 100 DRIP and then you get 1 DRIP per day back for a max of 365 days. An example of the latter is STRONG (strongblock.io) where you buy a "node" that consumes 10 STRONG from your wallet. But after the, the node produces 0.1 STRONG per day, forever.
The IRS is exceedingly unclear how to tax this and if you ask 10 CPAs you might get 10 different answers. A "reasonable" assumption is that if you invest 100 XYZ coins and get 1 back per day, the project is returning your 100 coins back to you every day, so your taxable income on the first 100 is just the price on the day it is returned minus the price you paid for it on the day you invested. After that, all coins are like a STAKING reward and are fully taxable. The only way I can figure out how to replicate this in your software is to use my own python code when creating these transactions and keep track of how many coins I have invested in a given project versus how many I have claimed back. The initial investment is just an INTRA transaction. For coins that are returned to me from my initial investment, I have to have two transactions: one that represents the staking reward at the current price, and an OUT transaction that represents the burning of the coins at the price I paid for them when I initially invested. In other words, this second transaction would be a pure COST transaction and it would happen only when we try to take out our tokens. We can't deduct the full investment amount at the time we invest, we can only deduct the cost when we take our daily reward back from this locked pool. I'm not sure how your code could be adjusted to handle this, or perhaps it is just up to us to keep track of which coins are returned from the pool and which are pure profit staking rewards.
I'm open to any suggestions on this.
I just looked at the INTRA table more closely and it seems to assume that INTRA transactions have no fees or are paid in fees in the coin being sent. This is an invalid assumption in DEFI. If I send 100 CAKE from one BSC wallet to another then the fees are paid in BNB not CAKE. Also, even when sending BNB, the fees are not deducted from the coins I send, they are taken out as gas fees from any BNB I have in my sending wallet. For example, If I have 102 BNB in one wallet and I send 100 BNB to a second wallet, the TRX fee may be 0.005 BNB, so I will still get 100 BNB in my second wallet but in my sending wallet I will have 1.995 BNB remaining instead of 2 BNB.
This is turning into a really cool discussion! I haven't played with DEFI much, so your detailed messages are quite educational for me. I think our goal should be to produce the following for each of the use cases you gave:
To that end, I created a Wiki document to capture the outcome of this brainstorming. Feel free to edit, fix or comment on it as needed.
Note that the workarounds may be a little clunky, but they are useful because they allow us to define precisely the operational semantics of a given high-level operation. If a workaround is not possible, then we have a missing-primitive problem.
As mentioned previously, my main focus right now is on foundation primitives rather than higher level ones. I'm thinking about this like a compiler, in which a higher-level language gets translated to a lower-level one. Perhaps there could be a higher level of abstraction (possibly even a separate project), that will allow users to define high-level transaction types, which will get translated to lower-level RP2 (somewhat similar to C and ASM): e.g. SWAP -> BUY + SELL. All this is not set in stone and I'm open to feedback: it's just my current train of thought.
Ok, let's get to the issues:
Cost-only transactions: I think the spot price is not important here because it's just used internally to compute fiat equivalent values if the user doesn't provide them. So I think operationally this could be captured as an OUT/SELL primitive transaction with either:
INTRA transactions: they do allow for a fee (it's just crypto_sent - crypto_received). However you're right that the fee is assumed to be denominated in the same currency that was sent (see next bullet).
Fees Denominated in a third currency (neither fiat, nor transaction currency): this could be expressed with a Cost-only transaction in the third currency (e.g. BNB in your example). Alternatively there could be a way to express the fee in a third currency as you suggested. I like the cost-only option because it creates fewer dependencies at the design level (every transaction expresses amounts in fiat or its own currency). Or perhaps we could have a hybrid approach in which we have a separate cost transaction but it is somehow "linked" from the original transaction as a fee. I have to think about this some more.
Bridging (MATIC example): this should be expressible with the existing primitives without too much difficulty. There are two options:
Lock currency + reward (is there a name for these kind of DEFI operations?): given that CPAs disagree on how to interpret this I would try to avoid introducing new concepts for this one, until there is more clarity on the tax meaning. Your interpretation seems reasonable, so let's go with that for now. A potential way to express it would be:
Profit/loss at point in time: got it. Thanks for the context. I'll try to do this in the next week or so.
Feel free to correct me if I didn't get the operational semantics (or anything else) right. Once we have the operational semantics in place we can decide what the next step should be.
Thanks a ton for all the feedback, context and ideas!
BTW, I implemented the PNL at datetime you requested: check the from/to time filters with arbitrary dates (-f and -t command line options) in the latest version.
Quick update on DeFi in RP2. I ended up implementing FEE-only transactions as a primitive (see issue #16): I have received several requests for them and I realized that this concept is important enough to deserve its own transaction type. So with the latest version of RP2 you can just set the transaction type of an out-transaction to "Fee" and it will work as a fee-only transaction: there is no need to use the sell transaction workaround anymore. I also updated the DeFi Brainstorming Wiki. See also relevant FAQ.
This is a useful article on the topic of DeFi and taxes: https://cryptotrader.tax/blog/defi-crypto-tax-guide. It lists several real-world DeFi scenarios with ideas on how to handle them (all of these scenarios can be modeled with RP2).
I just added the ability to pass crypto_fee to in-transactions (I received numerous requests for this). With the latest RP2 you can pass either crypto_fee or fiat_fee (or neither) but not both.
Note that passing crypto_fee causes RP2 to generate one more artificial, fee-only out-transaction to model the crypto_fee (in addition to the normal in-transaction). These two transactions are generated with notes that explain the situation. If the user passed in unique_id to the original transaction, the two generated transactions share the same unique_id so they are easy to correlate in the output file.
This is a really interesting and useful project, nice work! But unless I am not understanding something correctly, there is a problem here: this code must be used from the very first transaction across all years of trading crypto. What if I already have a detailed snapshot of my crypto portfolio at the start of 2021, including coins/tokens, dates of purchase and cost basis for coins/tokens purchased on that date. I would like to use just my transactions from 2021 and that portfolio snapshot to compute my taxes for 2021. Is there any way to do this? If not, where would I start in the code to update the code to handle this?