-
Notifications
You must be signed in to change notification settings - Fork 47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
USA to drop universal application: implement per-wallet application #135
Comments
Some useful links on this topic:
|
This is what the great magnificent supercomputer in the sky aka CoPilot had to say about this: To modify the project to track transactions by individual wallets and calculate the cost basis based on the purchase price for the crypto asset in each wallet, you will need to make changes to several parts of the codebase. Here is a step-by-step guide to help you implement this feature:
Step 1: Update the TransactionSet ClassAdd wallet information to the Step 2: Modify the InputData ClassUpdate the Step 3: Update the GainLossSet ClassModify the Step 4: Modify tax_engine.pyUpdate the Here is an example of how you can modify the tax_engine.py: def _create_unfiltered_taxable_event_set(configuration: Configuration, input_data: InputData) -> Dict[str, TransactionSet]:
wallet_taxable_event_sets: Dict[str, TransactionSet] = {}
for wallet in input_data.wallets:
taxable_event_set: TransactionSet = TransactionSet(configuration, "MIXED", input_data.asset, MIN_DATE, MAX_DATE)
for transaction_set in [
input_data.get_unfiltered_in_transaction_set(wallet),
input_data.get_unfiltered_out_transaction_set(wallet),
input_data.get_unfiltered_intra_transaction_set(wallet),
]:
for entry in transaction_set:
transaction = cast(AbstractTransaction, entry)
if transaction.is_taxable():
taxable_event_set.add_entry(transaction)
wallet_taxable_event_sets[wallet] = taxable_event_set
return wallet_taxable_event_sets
def _create_unfiltered_gain_and_loss_set(
configuration: Configuration, accounting_engine: AccountingEngine, input_data: InputData, wallet_taxable_event_sets: Dict[str, TransactionSet]
) -> Dict[str, GainLossSet]:
wallet_gain_loss_sets: Dict[str, GainLossSet] = {}
for wallet, taxable_event_set in wallet_taxable_event_sets.items():
gain_loss_set: GainLossSet = GainLossSet(configuration, input_data.asset, MIN_DATE, MAX_DATE)
new_accounting_engine: AccountingEngine = accounting_engine.__class__(accounting_engine.years_2_methods)
taxable_event_iterator: Iterator[AbstractTransaction] = iter(cast(Iterable[AbstractTransaction], taxable_event_set))
acquired_lot_iterator: Iterator[InTransaction] = iter(cast(Iterable[InTransaction], input_data.get_unfiltered_in_transaction_set(wallet)))
new_accounting_engine.initialize(taxable_event_iterator, acquired_lot_iterator)
try:
gain_loss: GainLoss
taxable_event: AbstractTransaction
acquired_lot: Optional[InTransaction]
taxable_event_amount: RP2Decimal
acquired_lot_amount: RP2Decimal
total_amount: RP2Decimal = ZERO
(taxable_event, acquired_lot, taxable_event_amount, acquired_lot_amount) = _get_next_taxable_event_and_acquired_lot(
new_accounting_engine, None, None, ZERO, ZERO
)
while taxable_event:
AbstractTransaction.type_check("taxable_event", taxable_event)
if acquired_lot is None:
raise RP2RuntimeError("Parameter 'acquired_lot' is None")
InTransaction.type_check("acquired_lot", acquired_lot)
if taxable_event_amount == acquired_lot_amount:
gain_loss = GainLoss(configuration, taxable_event_amount, taxable_event, acquired_lot)
total_amount += taxable_event_amount
gain_loss_set.add_entry(gain_loss)
(taxable_event, acquired_lot, taxable_event_amount, acquired_lot_amount) = _get_next_taxable_event_and_acquired_lot(
new_accounting_engine, None, None, ZERO, ZERO
)
elif taxable_event_amount < acquired_lot_amount:
gain_loss = GainLoss(configuration, taxable_event_amount, taxable_event, acquired_lot)
total_amount += taxable_event_amount
gain_loss_set.add_entry(gain_loss)
(taxable_event, acquired_lot, taxable_event_amount, acquired_lot_amount) = _get_next_taxable_event_and_acquired_lot(
new_accounting_engine, taxable_event, acquired_lot, ZERO, acquired_lot_amount - taxable_event_amount
)
else:
gain_loss = GainLoss(configuration, acquired_lot_amount, taxable_event, acquired_lot)
total_amount += acquired_lot_amount
gain_loss_set.add_entry(gain_loss)
(taxable_event, acquired_lot, taxable_event_amount, acquired_lot_amount) = _get_next_taxable_event_and_acquired_lot(
new_accounting_engine, taxable_event, None, taxable_event_amount - acquired_lot_amount, ZERO
)
except AcquiredLotsExhaustedException:
raise RP2ValueError("Total in-transaction crypto value < total taxable crypto value") from None
except TaxableEventsExhaustedException:
pass
wallet_gain_loss_sets[wallet] = gain_loss_set
return wallet_gain_loss_sets
def compute_tax(configuration: Configuration, accounting_engine: AccountingEngine, input_data: InputData) -> Dict[str, ComputedData]:
Configuration.type_check("configuration", configuration)
AccountingEngine.type_check("accounting_engine", accounting_engine)
InputData.type_check("input_data", input_data)
wallet_taxable_event_sets = _create_unfiltered_taxable_event_set(configuration, input_data)
wallet_gain_loss_sets = _create_unfiltered_gain_and_loss_set(configuration, accounting_engine, input_data, wallet_taxable_event_sets)
computed_data: Dict[str, ComputedData] = {}
for wallet, gain_loss_set in wallet_gain_loss_sets.items():
computed_data[wallet] = ComputedData(
input_data.asset,
wallet_taxable_event_sets[wallet],
gain_loss_set,
input_data,
configuration.from_date,
configuration.to_date,
)
return computed_data This example assumes that the TransactionSet and GainLossSet classes can handle wallet-specific transactions. By following these steps, you can modify the project to track transactions by individual wallets and calculate the cost basis based on the purchase price for the crypto asset in each wallet. It goes on to say that class AccountingEngine:
def __init__(self, years_2_methods: Dict[int, str]):
self.years_2_methods = years_2_methods
self.wallets = {}
def initialize(self, taxable_event_iterator: Iterator[AbstractTransaction], acquired_lot_iterator: Iterator[InTransaction], wallet: str):
self.wallets[wallet] = {
"taxable_event_iterator": taxable_event_iterator,
"acquired_lot_iterator": acquired_lot_iterator,
}
def get_next_taxable_event_and_amount(
self,
wallet: str,
taxable_event: Optional[AbstractTransaction],
acquired_lot: Optional[InTransaction],
taxable_event_amount: RP2Decimal,
acquired_lot_amount: RP2Decimal,
) -> Tuple[AbstractTransaction, Optional[InTransaction], RP2Decimal, RP2Decimal]:
wallet_data = self.wallets[wallet]
taxable_event_iterator = wallet_data["taxable_event_iterator"]
acquired_lot_iterator = wallet_data["acquired_lot_iterator"]
# Logic to get the next taxable event and acquired lot for the specified wallet
# ...
def get_acquired_lot_for_taxable_event(
self,
wallet: str,
taxable_event: AbstractTransaction,
acquired_lot: Optional[InTransaction],
taxable_event_amount: RP2Decimal,
acquired_lot_amount: RP2Decimal,
) -> Tuple[AbstractTransaction, Optional[InTransaction], RP2Decimal, RP2Decimal]:
wallet_data = self.wallets[wallet]
acquired_lot_iterator = wallet_data["acquired_lot_iterator"]
# Logic to get the acquired lot for the specified taxable event and wallet
# ... Basically, just adding wallet storage to each part of the engine. Is there something I'm missing? This is AI generated, so please go over it carefully. |
Thanks, I'll read it. I had some rough ideas on how to approach the problem:
My only problem right now is finding the time to work on it... |
I think we can take it piece by piece. The first piece is to modify the initial reading in of the data to sort the different lots into different wallets. We can probably start there and build out some tests for it. I should have about the next month or so to work on and submit code. I don't think it will take that much time as long as we are pretty systematic about it. For example, I think the first step is to create a function in I guess this function would sort in and out transactions pretty easily, just whatever exchange they happened on. Then, intra transaction will be split into non-taxable in and out transactions in their respective wallets. I think this handles this first step right?
Then we can cycle through the wallets probably in a multithread way to process all the transactions using the current engine. That will cover the next step:
And finally merge all the I can probably put the code together as long as you can review it by the end of the year. |
Yes, that sounds reasonable, however I'll add a few more considerations that complicate the picture slightly:
There are additional complications such as which method to use for transfers (FIFO, LIFO, etc.). Some options:
I think we should start option one or two. I think we should write a brief high level design of this feature first: let me see if I can come up with a quick document in the weekend, to start the discussion. |
Sorry, I guess I didn't realize until just now that this only applies to 2025, so for when we file taxes in 2026. For some reason, I thought we had to have this ready for filing taxes in 2025. I was in a panic. I guess we still have time, but if you outline what is needed I can try to have a whack at it. |
Yes, according to the Reddit thread, new rules are effective from 1/1/2025. So we have 1 year to figure it out. |
I'm making progress on the design of per-wallet application but it is still unfinished. I realized we can apply the existing accounting method layer to the new logic to pick which lot to transfer, which is nice. However we need a few additions to the existing infrastructure:
Here's the unfinished design so far. How to switch from universal to per-wallet from one year to the next is still TBD. |
Okay, I just looked up details about Japan, and they use universal wallet and you can make use of FIFO, LIFO, etc... based on all of your holdings as a whole. So, we will have to combine universal wallet with FIFO, etc... Does your plan account for that? |
Universal application + FIFO/LIFO/HIFO/LOFO is already supported today (you can even change from one accounting method to another year over year). See: https://github.com/eprbell/rp2/blob/main/docs/user_faq.md#can-i-change-accounting-method. Based on what you're saying it sounds like we can add more accounting methods here: https://github.com/eprbell/rp2/blob/main/src/rp2/plugin/country/jp.py#L41 The design I'm working on supports any combination of per-wallet/universal application + FIFO/LIFO/HIFO/LOFO (including changing from one combination to another year over year). This high-generality approach is proving both interesting and hard to do, so it will require some more time to iron out all the details. It's good that we can reuse the existing accounting method infrastructure for lot selection in transfers, but the problem goes deeper than I thought. When I'm closer to something presentable, we can discuss it and brainstorm a bit. |
The per-wallet application design is more or less complete. It can still be improved, but it captures most of the concepts: feel free to take a look and let me know what you think. Next I will probably do a bit of prototyping to make sure the ideas behind the design hold water. |
@eprbell I read through it and it looks pretty sound. I'll have to give it some time and read through it again just to double check, but I think this will handle what we need. Thanks for working it out. It looks like a lot of work and you gave some good examples. |
I'm making good progress on the implementation and unit testing of the transfer analysis function. Sorry, @macanudo527, I know you expressed some interest in working on this: I wanted to write a prototype to verify the design, but ended up finding more and more corner cases, and adjusting the code accordingly to deal with them. So basically what started as a prototype is becoming the real thing. I will open a PR for this though: it would be good to get your feedback before merging. |
No worries, I can just spend time on some of the other core functions instead. Looking forward to it. I'll be out for the end of the year (Dec 23rd - Jan 7th), but after that I can look at it. |
Sounds good (we're in no rush). Have a great time during the holidays! |
US tax payers, watch this informative interview by Andreas Antonopulous on per-wallet application in the US. |
Hi. Hope you don't mind me commenting as a non-code-contributing member of the RP2 community. I watched the Andreas Antonopulous interview. Honestly, I found it extremely difficult to follow due to him constantly interrupting the guest. I found this video by "Crypto Tax Girl" to be much more clear and easy to follow. If you have a chance to watch it, I'd be interested if you feel it lines up with your understanding of these changes. A have a few questions if I may:
Thoughts? Thanks for all you do to make RP2 available and up-to-date! Much appreciated. |
Feedback is always welcome from anybody (code-contributing or not). Thanks for the video: I haven't watched it yet, but I will in the weekend. I think the main takeaway from Andreas' video is to follow the procedure he and his guest recommended before 12/31/24: basically consolidate everything into one wallet (if possible). The Description in the video summarizes the steps to take. This will simplify accounting going forward. As for your questions:
Hope this helps. |
Thank you for the reply! FWIW, I reviewed the design document -- twice -- but since it's very code centric, and I'm not familiar with the overall application design, I wasn't able to understand all that much about the approach being taken. (That's not a issue. It just is what it is.) Regarding your reply, let me see if I understand: So there are "transfer semantics" and "accounting method", each of which could be FIFO, LIFO, HIFO, or LOFO. Does that mean that if "transfer" is set to FIFO and "accounting" is set to HIFO that, when a transfer is done, the basis with the oldest date (first in) will be moved to the new wallet. And, similarly, when a token is sold within a wallet, the basis with the highest price (highest in) will be associated with the sale? Am I understanding that correctly? Assuming I am, I'm still not clear what happens to a token when it is transferred to another wallet and "transfer semantics" is FIFO. Does it get assign a new "in date" in the destination wallet or does it retain its original "in date" from when it was purchased? In my original post I asked it this way:
At the time I imagined each wallet would have a "queue" of transactions, but now I understand it's probably more like a pool of transactions that can be sorted in any way at runtime, as needed, based on whether a transfer is being done ("transfer semantics") or a sell is being done ("accounting method"). Is that correct? That being the case, I would guess that the transferred token (and corresponding basis) would retain the original purchase date even after it is moved to the destination wallet. Here's an example (a variant of yours from the design):
If both "transfer semantics" and "accounting method" are FIFO, does that mean that the Does that make any sense? Hopefully. Thoughts? As for the second question, I'm not sure what you mean by this:
I understand that, per Andreas, it probably makes sense to try and consolidate wallets as much as possible. But are you going to actually make a "declaration" before 1/1/25 and either email it to yourself or use the blockchain time-stamping approach Andreas suggested to have something that can be provided to the IRS, if needed, as proof of claiming "safe harbor"? And, if so, what is that declaration going to contain? Thanks! |
I'm replying inline, however keep in mind that what I'm saying is based on my current ideas for a design that is still in flux. So don't make tax decisions solely based on what I'm describing here, because it may change. This is why I was highlighting Andreas' solution: it makes your crypto accounting simple and clear, regardless of what happens with tax software.
Yes to both questions. The transfer semantics is what is used to populate per-wallet queues from the universal queue that is used up to the end of 2024 (it is also used when transferring from one per-wallet queue to another).
Good question. The current idea is to create an artificial InTransaction in the "to" wallet. This artificial InTransaction has:
I think this was already answered above. It goes into a new queue that is specific to wallet B (that's the whole idea behind per-wallet application). However you need to consider the case in which you have 1 BTC in wallet A and you transfer 0.5 BTC to wallet B. In this case you're splitting the original lot. Currently this is captured by leaving the 1 BTC in the queue of wallet A and creating an artificial transaction for 0.5 BTC in wallet B. The two transactions are linked and are updated together by the tax engine (for more on this check this).
Not quite: see explanation above about one queue per wallet.
In your example, using FIFO for everything, the 4/1 OutTransaction would use the 2/1 InTransaction. Note that the Kraken queue would also have an arificial InTransaction on 3/1 containing 4 BTC and linked to the 1/1 transaction. But in your example the artificial transaction is not exercised because the 2/1 transaction has enough funds to cover the 2 BTC of the OutTransaction. If the OutTransaction had, say, 7 BTC instead of 2, then the code would use first the entire 2/1 lot and then 2 BTC from the artficial InTransaction (this also causes its parent transaction 1/1 to be updated as explained here).
I mean that RP2 won't let the user select which lot goes into which wallet queue arbitrarily. RP2 will take an algorithmic approach: the user selects the transfer semantics and the code moves the funds around.
This is probably a question for your tax advisor, but the rough idea is to move everything to one single wallet and then take snapshots of all accounts, generate an RP2 report and timestamp everything on Dec 31st. By moving to a single wallet you're essentially causing universal and per-wallet approach to match, because there is now only one wallet having one queue with all the funds. Thanks for asking questions and engaging in conversation. It's good to brainstorm the ideas behind the design and see if they hold water. |
Thanks for the reply! Agreed about the conversation and brainstorming. Even though I'm not coding this, I find it very helpful for my own understanding.
This was a surprise to me, so I decided to post basically this exact question on the Reddit forum to see if @JustinCPA would respond, which he did. He seems to say the opposite of what you've said. I feel like this could be an issue for the current design -- at least for US taxpayers, and assuming @JustinCPA is correct. Thoughts? |
Sounds like you found a bug in the design! Thanks for getting in the weeds and asking JustinCPA: his explanation is convincing. The bug is that the artificial InTransaction was using the timestamp of the transfer instead of the timestamp of the from InTransaction. I already fixed the code so that the behavior is as explained by Justin. I will be posting some initial code for the transfer analysis algorithm in a PR soon (together with unit tests). |
Great! Thanks! One thing that comes to mind: You may well be taking care of this already, so forgive me if you are, but you may want to make sure that transfers, if they are using the timestamp of the "from"
Of course the transactions on 2/1 and 3/1 seem obviously invalid when looked at like that. But, I could imagine a scenario where the 5 BTC transferred on 4/1 from Coinbase to Kraken inherit the "from" Keep in mind I don't understand the overall app design or the design for these changes. I'm just looking at this as an outsider. Thanks! |
One more thought: Is it possible to use different transfer semantics to:
Don't think that I would need this, but -- if I'm understanding correctly -- others may. Take a look at this post for context. Basically, I think some may want to use HIFO for populating the wallets from the universal queue, and then FIFO going forward (as I understand that is required for "global allocation" / non-spec-ID). |
Ah, good point. This means that my previous approach was only 50% wrong :-) because the artificial transaction needs both timestamps: one for holding period and the other for fund availability. Let me think a bit on how to best model this: we probably need a subclass of InTransaction to capture this.
No worries: your feedback as a user has been very valuable. Keep it coming! |
Interesting: the current design allows for changing transfer semantics and accounting method year over year in any combination supported by the country plugin. What you're describing would be an extra one-time only transfer semantics for initial per-wallet queue population: this is not supported yet. With the existing design you could select HIFO transfer semantics in 2025 and then switch to FIFO in following years: not exactly what you're asking for but it's an approximation. I think we should finish the basic design and implementation of per-wallet application and then we can think about something like this as a potential advanced feature. |
Of course. Just figured I'd mention it for your awareness. (I personally don't anticipate needing it.)
I suppose, though I don't think that would comply with the IRS requirements. I'm no expert, but my understanding is that it has to either be FIFO or specific identification going forward. I totally get your point, though. "One step at a time" :-) |
The tax engine isn't US-specific so it allows this general behavior: then the country plugin can impose constraints as needed on a country-specific basis. |
Just wanted to share this post. It's a newer @JustinCPA post than the one posted above. I found it quite helpful. I'm trying to make sure I have all my proverbial "ducks in a row" before the end of the year. From Justin's post:
As I understand it, one has to use either "Global Allocation" or "Specific Allocation" to determine how the basis from Universal application is applied to individual wallets. ("Global Allocation" sounds much easier to me than "Specific Allocation", so that's what I'm planning on.) The thing is, I understand that RP2 is intending to support different methods for the transition from Universal to Per-wallet, such as FIFO. But as I think about it more, it feels like that is insufficient. (Either that or I'm missing something -- which is very possible.) A method such as FIFO takes care of pulling basis from the Universal queue, but how does it account for allocating that basis to 1-n wallets? Wallets do not have any inherent ordering. They just exist. A method is needed to determine the ordering of wallets, right? Justin provides examples such as:
Can you shed some light on how this will be supported with RP2? My understanding is that, in order to be in compliance, it will be necessary to *individually document this before the end of 2024. At at this moment I'm not clear on what allocation method RP2 will be using. (I can't just say FIFO, because that doesn't cover wallet selection.) Thoughts? Thanks! |
Thanks for the additional feedback. The current RP2 per-wallet design uses a combination of transfers (IntraTransactions) plus the new concept of transfer semantics (which can be FIFO, LIFO, etc.) to determine where funds go. So the "Global Allocation Method" in the current version of the design would be something like: funds are directed to wallets based on transfers and within each transfer the cost basis is chosen using transfer semantics. However choosing "Specific Allocation Method" might also work, because RP2 tracks lot pairings precisely, so when the time comes to file taxes the RP2 full report will describe in detail how cost basis has been allocated (my understanding is that when picking "Specific Allocation Method" you don't have to declare the exact allocation by Dec 31st, but correct me if I'm wrong). Again, these are just thoughts I'm giving while design and implementation are still ongoing: unfortunately this change of rules didn't give us enough time to design and implement this well in advance (also none of this is tax or financial advice). I think the best bet is to follow the advice in Andreas' video, because, after consolidation, your situation becomes agnostic to what happens with tax software. We could also implement methods like those suggested by Justin, but unfortunately there won't be even time to finish the basic implementation by Dec 31st. |
Thanks for the reply!
Per Justin (in the post above):
Can you elaborate on "directed to wallets based on transfers"? Wouldn't that require going back to the beginning of RP2 transaction history to do the allocation? And wouldn't that result in somehow changing history for prior tax years? I guess I don't understand how this would work.
I totally understand! And I appreciate all your effort on this. It's a tricky situation. (I'm just trying to understand as best I can to try and be in compliance as best I can by the deadline.) |
OK, that's what I thought. What I was saying is that you would let RP2 do the allocation and then use the full report as the documentation of Specific Allocation.
The transfer analysis algorithm from the new design follows funds movements to understand how funds are moved around. Then it uses transfer semantics to decide how to assign cost basis. For example, if you have:
Assuming you selected FIFO transfer semantics, it would result in:
If instead you selected LIFO transfer semantics, the result would be:
In the first case the Global Allocation Method is: funds are directed to wallets based on transfers and within each transfer the cost basis is chosen using FIFO. In the second case the Global Allocation Method is: funds are directed to wallets based on transfers and within each transfer the cost basis is chosen using LIFO. As for the going back to the beginning of history, yes, that is required (even before the per-wallet change), however for years before 2024 RP2 will continue to use universal application and from 2025 it will switch to per-wallet.
You and I both :-) |
I apologize for missing earlier discussions. As an engineer, not a tax advisor, I want to share my thoughts on the new rules regarding capital gains. Your interpretation is correct. While edge cases can be complex, users can follow simple guidelines. To defer capital gains, avoid FIFO, especially with Bitcoin's upward trend. LIFO or HIFO are preferable, which I advocated in issue #79. Spec-ID would be too burdensome for me, so I will stick with HIFO. Now, HIFO applies to each wallet individually, increasing the developer's burden but also requiring users to consolidate wallets before 2025 and plan for the future. Ensure your DaLI-RP2 input files are accurate for tax reporting. Consolidating wallets is wise; consider moving older coins to one wallet and segregating using additional wallets for short and long-term capital gains. Thanks again to eprbell and macanudo527 for your great work in the community. |
Thanks for the detailed explanation. However, I still feel unsure about the approach. (That may just be due to lack of understanding on my part.)
Let's consider a more complex example involving both universal application (for 2024) and per-wallet (for 2025). Let's assume FIFO throughout for simplicity.
Because of universal application, the basis for the 3/1/24 OutTransaction will be pulled from the 1/1/24 Coinbase InTransaction due to FIFO, even though the actual BTC was sold from Kraken. So now the pool of unused basis in the universal "pool" looks like this:
And the actual BTC on exchanges looks like this
So now in 2025 we're on per-wallet application. Let's take two scenarios. Scenario A:
Scenario B:
How will these scenarios be handled? What basis will be assigned for each 5/1/25 OutTransaction? Will the scenarios result in the same cost basis being assigned or different? I would argue that the cost basis for these two scenarios must be different under per-wallet application (assuming different BTC price on 1/1/24 and 2/1/24). The basis needs to essentially be pre-assigned (implicitly or explicitly) to Coinbase and Kraken before the transaction of 5/1/25 occurs. The OutTransaction on 5/1/25 cannot affect the assignment of basis, it can only be affected by the basis already assigned. Thoughts? Thanks. |
The example presented by @gbtorrance is not handled by the design. Specifically the Universal Application section is not actually an example of universal application. It is actually an example of per-wallet application. In the example of using universal application, FIFO transfer method, and FIFO accounting method the balances of each wallet will not match the cost basis units of the lots as they are transferred. This means that if you are switching from universal application to per-wallet application from one year to the next year there must be a step in between to assign cost basis to fill in the spent cost basis that will be missing from each wallet. |
You're asking all the right questions! Your scenario made me realize that my approach (using the transfer analysis algorithm as Global Allocation Method) doesn't work: we'll have to implement the Global Allocation Methods described by Justin (initially I thought we would implement them later next year as a nice to have, but instead they are actually required and need to be part of the design from the start). The good news is that the transfer analysis algorithm I'm implementing is still correct: it just can't be used for Global Allocation, but it can be used in steady state. Your example made me realize the problem. If we use the transfer analysis algorithm as Global Allocation Method (like I was planning to do) we end up with not enough cost basis lots on Coinbase. This is because on 3/1/24 we end up with 5 BTC of unused basis on Coinbase but then on 5/1/25 we are selling 10BTC: in a per-wallet setup the cost basis for the 10 BTC must all come from Coinbase (and we only have 5 BTC of unused basis there, even though there are 10 BTC on the balance). This discrepancy must be the reason they introduced the Global/Specific Allocation Methods: essentially a reset of the cost basis assignments before starting out with the new per-wallet regimen. So the answer to your question is:
So the TLDR is: the existing design is fine as is, but we'll need to add the Global Allocation Methods described by Justin, which will be used once before switching from Universal Application to Per-Wallet Application. I will need some time to add this new feature to the design and implementation, but this is a great step in the right direction. Again, thank you so much for engaging in conversation and asking lots of good questions. This is helping both my understanding of the new rules and the robustness of the design. |
Looks like @orientalperil and I came to a similar conclusion around the same time: the design needs an extra step to run the Global Allocation Method when switching from Universal Application to Per-wallet Application. |
Sounds great! Thanks for the feedback! For reference, here are the Global Allocation Methods Justin referenced in his post:
(Now that I think about it, "Lowest cost basis to highest balance" and "Highest cost basis to lowest balance" seem the same to me. For the second one (final bullet) maybe he meant, "Lowest cost basis to lowest balance" or "Highest cost basis to highest balance"?) I went back to see what Crypto Tax Girl said about this. She is recommending to her clients, "Highest Cost Allocated First" as the Global Allocation Method. On the surface this seems insufficient, as it says nothing about the wallet ordering. But in this section below she does mention allocating first to the most active wallets. But she also makes a distinction between "hosted" (CEXs) and "unhosted" (HW or SW wallets). It's a little unclear. I think the section is worth watching, though. Just a few minutes. https://youtu.be/oaJh6k1QoPk?t=1377 Just to throw something out there, do you see any feasibility in RP2 supporting specifying a custom wallet ordering for Global Allocation? For example, the ordering specified for Whether a custom wallet order can be specified in RP2 or it only supports completely deterministic methods like those suggested by Justin, it would be nice to have a pretty solid list of the methods you feel comfortable RP2 supporting. That way we can choose one to document for Safe Harbor before EOY. Thanks! |
Just wanted to share this discussion I had with Justin today. I think it's relevant to this overall effort. (I think we kind of touched on it earlier in this thread, also, but it keeps coming up in my mind as a point of confusion.) IMO, basis should remain associated with the date the coin was originally purchased. (But there are others that seemingly disagree.) |
I started thinking about this today, so this is not even part of the design: consider it not set in stone. My initial thoughts are that we can probably let the user decide the Global Allocation Method in a special section of the .ini file as follows:
Global Allocation would work more or less this way:
This should cover all the methods described by Justin (and more). Let me know if you find any holes in this logic. |
I would say: let's assume the date doesn't change unless we have overwhelming evidence to the contrary. |
I think that is the safest approach. Almost certainly it will be accepted by the IRS. (The other approach, I'm not so sure.) |
I think this makes sense. I do have a bit of concern that the IRS may not like the custom wallet ordering. I posted a question to Justin about this, and will let you know what he says. Hopefully it's allowed. But if it is used, it does put a bit more burden on the user to make sure they clearly and correctly document their chosen Global Allocation Method, and that it is aligned with the configured wallet ordering in RP2.
Can you elaborate a bit on how wallet queues are used in RP2? It seems to me you should be able to use any Accounting Method for the Global Allocation (to determine what lots go into what wallets). But then you should also be able to choose any Accounting Method for subsequent transfer processing (which presumably includes processing of sales). Important note: For the Accounting Method for transfer processing, it is understood that U.S. taxpayers would likely have to use FIFO going forward. The flexibility to choose another method would be for non-U.S. taxpayers. To restate the question: Can you clarify how sorted ordering of queues plays into the ability to select any Accounting Method for any tax year (for transfer processing)? On a different subject, I was thinking that some of the Global Allocation Methods suggested by Justin can be open to some confusion and, if they are going to be handled directly by RP2 (rather than by a custom wallet ordering), it's worth making sure everyone is one the same page. What does wallet "highest balance" mean? Highest balance of the particular coin or highest balance of all coins in the wallet? I would assume of the particular coin being processed. (It doesn't really make sense otherwise.) What does "most active wallet" mean? Is it based on number of transactions or recency of transactions? Is it number of transactions for the particular coin or for all coins? Is it number of transactions since inception of the wallet or number of transactions in a more recent time period? (If I had to guess I'd say number of transactions since inception for the particular coin being processed.) Just throwing that out for consideration. RP2 implementing the wallet selection deterministically does require being clear on what the Global Allocation Methods actually mean. But if RP2 just supports custom wallet selection order, then it's not really an issue. It would be up to the user. |
Now that I think about it, there is a potential issue. It depends on one's interpretation of the Global Allocation methods. If, for example, one interprets wallet "highest balance" as meaning "highest balance of the particular coin", then having a single custom ordering of wallets in RP2 would not allow this method to be properly replicated. Wallet A may have the highest balance of Token X, but Wallet B may have the highest balance of Token Y. It's possible to manually order the wallets to guarantee "Lowest cost basis to highest balance" for Token X or for Token Y, but not for both. Make sense? Sorry to always be the bearer of bad news :-( Maybe having a deterministic wallet allocation is going to be required? |
On the assumption that custom wallet ordering will not be suitable due to "highest balance of the particular coin" issue referenced above... do you think it's comfortably doable to implement deterministic ordering of wallets by per-coin balance for the Global Allocation? If so, I'd like to go ahead and create a Safe Harbor declaration as follows: "Highest Cost Basis to Highest Balance" (or maybe highest to lowest or lowest to highest; not 100% sure yet). Just want to make sure you foresee no issues with implementation. Thoughts? Thanks! |
FWIW, Justin confirmed that he believes custom wallet ordering would be acceptable. It's unfortunate about the per-coin issue. https://www.reddit.com/r/CryptoTax/comments/1hk31yd/comment/m4i60ew/ |
Sorry for all the messages. I'm just thinking outloud/online here. I suppose custom wallet ordering would be an acceptable approach for RP2, on the understanding that it wouldn't be able to replicated the more generic Global Allocation Methods Justin mentioned. Whatever you think is best. Please just let me know when you can so I can make an appropriate declaration before EOY. Thanks. |
Yes, per-coin ordering of wallets should be doable. Perhaps we can have a default order that can be optionally overridden on a per per-coin basis. |
Great! Thanks for confirming. I'll assume the ability to order wallets by holding of each coin for Global Allocation when filling out my Safe Harbor declaration.
Sounds good. |
Discussion on this topic started here: #134
The text was updated successfully, but these errors were encountered: