Skip to content

Per-wallet lot tracking: end-to-end wiring, IRS FAQ tests, and LTCG fix#155

Open
macanudo527 wants to merge 13 commits into
eprbell:per_wallet_applicationfrom
macanudo527:add_tests_fix_wallet
Open

Per-wallet lot tracking: end-to-end wiring, IRS FAQ tests, and LTCG fix#155
macanudo527 wants to merge 13 commits into
eprbell:per_wallet_applicationfrom
macanudo527:add_tests_fix_wallet

Conversation

@macanudo527

Copy link
Copy Markdown
Collaborator

Summary

  • Wire compute_tax_per_wallet end-to-end via a new -w/--per-wallet CLI flag: runs TransferAnalyzer to split universal InputData into per-wallet InputData objects, computes gain/loss per wallet (respecting in_transaction_2_actual_amount partial lot amounts), and merges results into a single ComputedData. Also fixes _create_unfiltered_gain_and_loss_set to forward partial amounts to the accounting engine.
  • Fix LTCG holding-period boundary: is_long_term_capital_gains() now uses > (strictly greater than 365 days) instead of >=, and uses cost_basis_timestamp so the original acquisition date survives wallet-to-wallet transfers. A holding period of exactly 365 days is short-term per the IRS "more than one year" rule (IRC §1222).
  • Add IRS FAQ-driven tests to test_gain_loss.py covering rules not previously exercised:
    • Q8/Q56 — purchase fee increases cost basis
    • Q13/Q59 — earned crypto (staking/mining/wages) carries FMV-at-receipt as cost basis for a subsequent sale
    • Q16–Q17/Q64–Q66 — crypto-to-crypto exchange is a taxable disposal of the disposed asset
    • Each test links directly to the relevant IRS FAQ anchor URL
  • Update CLAUDE.md with per-wallet architecture documentation: data flow diagrams for both universal and per-wallet paths, expanded key-classes table, per-wallet test file descriptions, and known-limitations entries for artificial InTransactions, cost_basis_timestamp LTCG behavior, TransferAnalyzer ordering requirements, and the incomplete state of GlobalAllocator.

Test plan

  • All 171 tests pass (pytest --tb=native --verbose)
  • -w flag accepted; correctly errors on input data missing explicit intra-transactions between wallets (expected behavior — per-wallet mode requires all transfers to be recorded)
  • Universal mode (-m fifo without -w) unchanged

🤖 Generated with Claude Code

Neal Chambers and others added 8 commits May 25, 2026 22:41
gain_loss.py: change >= to > in is_long_term_capital_gains() so that
exactly 365 days is correctly classified as short-term. IRS defines
long-term as "more than one year" (IRC §1222), not "one year or more".

test_gain_loss.py: four new tests covering IRS Digital Assets FAQ
scenarios that had no unit-level coverage:
- test_ltcg_boundary: 365 days = STCG, 366 = LTCG, leap-year edge case
- test_earn_type_income_recognition: HARDFORK, AIRDROP, MINING, STAKING,
  WAGES, INCOME all produce ordinary income at FMV with zero cost basis
  (Rev. Rul. 2019-24, Notice 2014-21, Rev. Rul. 2023-14, FAQ Q57-61)
- test_donate_gift_disposal_gain_loss: DONATE/GIFT disposals use the
  same proceeds-minus-basis formula as SELL (FAQ Q75-78)
- test_holding_period_resets_after_exchange: received asset holding
  period starts fresh on exchange date regardless of prior lot age (Q74)
- test_fee_out_transaction_gain_loss: FEE-typed disposal recognises
  gain/loss on crypto used purely to pay a network fee (FAQ Q97)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CLAUDE.md: new codebase guide for Claude Code covering architecture,
design conventions, common commands, and a per-test IRS authority map
for test_gain_loss.py.

user_faq.md: added IRS rule citations and guidance notes to seven
sections — airdrops (Rev. Rul. 2019-24), hard forks (Q103-107),
mining (Notice 2014-21 Q8), staking (Rev. Rul. 2023-14), wages
(FAQ Q57-61), crypto-to-crypto swaps (Q74 holding period reset), and
intra-transaction fees (Q81/Q97).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three new tests in test_gain_loss.py covering IRS rules not previously
exercised: purchase fee increases cost basis (Q8/Q56), earned crypto
carries FMV-at-receipt as basis for subsequent sale (Q13/Q59), and
crypto-to-crypto exchange triggers gain/loss on the disposed asset
(Q16-Q17/Q64-Q66). Each test links directly to the relevant IRS FAQ
anchor.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add compute_tax_per_wallet to tax_engine.py: runs TransferAnalyzer to
split universal InputData into per-wallet InputData objects, computes
gain/loss for each wallet (passing in_transaction_2_actual_amount so
partial lot amounts are respected), and merges results into a single
ComputedData. Also fix _create_unfiltered_gain_and_loss_set to forward
in_transaction_2_actual_amount to the accounting engine so per-wallet
partial amounts are honoured in the universal path too.

Wire into rp2_main.py with a -w/--per-wallet CLI flag; transfer
semantics default to the first loaded accounting method.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Document the per-wallet data flow alongside the existing universal path,
expand the key classes table with Account/PerWalletTransactions/
TransferAnalyzer/GlobalAllocator, list the new per-wallet test files,
and add known-limitations entries for artificial InTransactions,
cost_basis_timestamp LTCG behaviour, TransferAnalyzer ordering
requirements, and the incomplete state of GlobalAllocator.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GitHub URLs rate-limit the link checker with 429 responses; retrying
resolves the false positives without needing to ignore the links.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Apply black reformatting to 12 files, fix a line-too-long in global_allocation.py,
move a pylint disable to the def line in abstract_accounting_method.py, and add
W0222/W0511 to .pylintrc (RP2Decimal operator narrowing and TODO markers are
intentional project patterns). Pylint now exits 0 at 10.00/10.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ini section

- global_allocation.py: use 'not in' instead of 'not x in' (flake8 E713)
- rp2_main.py: replace assert with explicit RP2RuntimeError raise (bandit B101),
  remove duplicate ComputedData type annotation on else branch (mypy no-redef),
  add RP2RuntimeError import
- mypy.ini: remove stale [mypy-rp2.per_wallet_tax_engine] section for file that
  no longer exists (was causing mypy warn_unused_configs note)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@macanudo527 macanudo527 requested a review from eprbell May 25, 2026 14:39
@macanudo527 macanudo527 self-assigned this May 25, 2026
@macanudo527 macanudo527 marked this pull request as draft May 25, 2026 14:39
Neal Chambers and others added 3 commits May 25, 2026 23:43
…w_any_expr for test_input_parser

- mypy.ini: remove show_none_errors (dropped in mypy >= 1.0, now unrecognized option)
- mypy.ini: add [mypy-test_input_parser] disallow_any_expr = False — zip over
  TransactionSet yields Union[T, Any] which fires disallow_any_expr throughout
  the loop body; same pattern as other test files in this project
- test_input_parser.py: remove 6 now-redundant # type: ignore comments that
  warn_unused_ignores was flagging once disallow_any_expr was suppressed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ator

'2025-01-01 00:00:00 +0000' (space-separated tz offset) raises ValueError on
Python 3.10; support for that form was added in 3.11. Use the standard ISO 8601
form '2025-01-01T00:00:00+00:00' which works on Python 3.8+.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Investopedia returns 402 for automated link checkers. Replace all four
affected links in supported_countries.md with stable Wikipedia articles:
- fifo.asp / lifo.asp / hifo.asp → FIFO_and_LIFO_accounting
- averagecostmethod.asp → Average_cost_method

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@macanudo527 macanudo527 marked this pull request as ready for review May 25, 2026 14:54
@macanudo527

Copy link
Copy Markdown
Collaborator Author

@eprbell

I took the work that was already done and set Claude Code to it to fix the remaining issues. I asked for it to use the IRS for guidance on digital assets taxes and build out the test cases. It should cover all the major cases, and there are links to the rules that the test cases are testing.

Everything looks sound to me, but I'd like to hear your opinions on it of course. Hopefully you'll have time over the next few months to work through everything. Let me know if you have questions.

Comment thread docs/user_faq.md Outdated
Comment thread docs/user_faq.md Outdated
Comment thread docs/user_faq.md Outdated
Neal Chambers and others added 2 commits June 7, 2026 22:37
Address PR eprbell#155 review comments and expand jargon throughout:
- Fix gift basis rules (IRC §1015): restructure around appreciated vs.
  depreciated property rather than sale-price comparisons
- Add &edition=prelim to all uscode.house.gov URLs
- Expand FICA/FUTA acronyms on first use
- Define "lot" and "lot fraction" on first use
- Replace get_long_term_capital_gain_period() function reference with
  plain English
- Expand "Rev. Rul." to "Revenue Ruling" on first use
- Clarify "out-currency"/"in-currency" with plain-English definitions
- Expand 501(c)(3), annual gift tax exclusion, carry-over basis,
  impermanent loss, LP tokens, collectibles, CME, BSC/BNB
- Rewrite mark-to-market, open positions, and loss carryback in plain English
- Add opening explanations for DeFi bridging and reflexive tokens
- Fix circular "dominion and control" explanation in staking section
- Replace all "RP2 primitives" with "RP2's basic transaction types"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants