wealthschemaresourcesthemesTax-Loss Harvesting
Theme

Tax-Loss Harvesting

From lot-level acquisitions to wash-sale flagging — the engineering view of TLH at scale.

Updated May 7, 20262 min read

Tax-loss harvesting (TLH) is the practice of selling securities at a loss to offset realized gains, then re-establishing exposure with a non-substantially-identical security. It sounds simple. The reality at production scale — across thousands of accounts, hundreds of thousands of lots, with wash-sale rules that span 30 days before and after the sale — is anything but.

This theme covers what your TLH engine actually needs to model, and why aggregate position data is insufficient.

Lots/account
30–300
Typical mass-affluent range
Wash-sale window
±30 days
Per IRC §1091
Disallowed loss
100%
On wash-sale triggered transactions
Carry forward
$3,000/yr
Net capital loss against ordinary income

Why aggregate positions are not enough

A position-level snapshot tells you the holder owns 1,000 shares of VTI at a $215 cost basis. It does not tell you when each lot was acquired, which lots were transferred in from another account, which lots are short-term vs long-term, or whether any portion is subject to a wash-sale disallowance from a sale 22 days ago in a different account.

A working TLH engine needs lot-level data, not aggregates. Every lot carries:

  • acquisition_date and cost_basis per share
  • holding_period derivation (short vs long)
  • wash_sale_disallowed carrying flag and adjusted basis
  • Linked-account purchase history for cross-account wash-sale detection
  • Special-status flags: QSBS (§1202), Section 1256 contracts, restricted stock

What the data shape looks like

A correct TLH lot record is roughly:

{
  "lot_id": "L-2024-03-14-VTI-001",
  "symbol": "VTI",
  "shares": 50,
  "acquisition_date": "2024-03-14",
  "acquisition_method": "purchase",
  "cost_basis_per_share": 218.42,
  "holding_period": "short",
  "wash_sale_disallowed": 0.00,
  "wash_sale_adjustment_basis": null,
  "qsbs_qualified": false,
  "section_1256": false
}

Realistic synthetic data carries 50–300 lots per mass-affluent household, distributed across 3–8 accounts (taxable brokerage, IRA, Roth, HSA, spouse mirror). Equity-comp grants add another layer — see the equity comp theme.

Comparing TLH approaches

 Position-levelLot-level
Wash-sale detectionImpossible to compute correctlyNative — runs against the lot ledger
Holding period mixAggregate onlyPer-lot, accurate to the day
Cross-account mergeManual reconciliationSingle ledger keyed by tax ID
QSBS handlingNot modeledPer-lot flag with §1202 5-year clock
Realistic for backtestingNo — masks real outcomesYes

Edge cases your test data must cover

A serious TLH engine has to handle, and your test data has to exercise:

  • Pre-existing wash-sale disallowances carried into the testing window from prior periods
  • Spouse-account triggers where the wash-sale comes from a separate filing entity
  • IRA-buyback disallowances (the loss is permanently disallowed, not deferred)
  • Same-day buy-and-sell with intra-day price swings
  • Corporate actions (splits, mergers, spin-offs) that re-base lot acquisition dates
  • §1256 60/40 contracts that bypass the standard short/long-term rules
  • QSBS lots approaching the 5-year holding requirement under §1202

Key takeaways

  • Aggregate position data cannot drive a correct TLH engine — you must store lot-level acquisitions.
  • Wash-sale rules apply taxpayer-wide, not account-local; merge all linked accounts before flagging.
  • Cross-account replay (taxable → IRA → spouse) is the most common production bug.
  • QSBS, §1256 contracts, and corporate actions are not edge cases — your test data has to include them or your engine will ship a regression.

Frequently asked questions

How many lots per household should realistic test data have?+
Mass-affluent: 30–80 lots across 3–5 accounts. Pre-retirement / equity-comp households: 150–300 lots across 5–8 accounts. UHNW households can have 500+ when private investments and entity holdings are included.
Does a 401(k) trigger wash-sale on a taxable brokerage loss?+
No — 401(k)s and similar qualified plans are not in the wash-sale taxpayer entity for §1091 purposes. The IRS clarified IRA-side triggers in Rev. Rul. 2008-5 but did not extend to 401(k)s. Your engine should not flag 401(k) purchases as wash-sale triggers.
How is a wash-sale adjustment basis computed?+
The disallowed loss is added to the basis of the replacement shares, and the holding period of the original lot carries over. So a $500 disallowed loss on a sale of $4,000 worth of shares, replaced with $3,800 of new shares, gives the new lot a $4,300 basis.