wealthschemaresourcesarticlesBuilding a tax-aware portfolio rebalancer — the data the engine actually needs
Article

Building a tax-aware portfolio rebalancer — the data the engine actually needs

Lot-level basis, multi-account placement, and wash-sale awareness are non-optional. Without them, the rebalancer can post tax bills that exceed the rebalancing benefit.

WealthSchema StaffTax modelingMay 9, 20263 min read

The rebalancer module inside almost every wealth-tech product looks the same on first inspection: target weights, current weights, drift threshold, list of trades that brings the portfolio back to target. The math is straightforward. The math also produces wrong answers if it's the only thing the engine considers.

A tax-aware rebalancer is a substantially different product. It has to know which lots to sell, which accounts to draw from, and which trades to avoid because they would trigger a wash-sale on a recent harvest. The synthetic data this needs is fundamentally different from what a naive rebalancer needs — and most products in market still operate at the naive level, producing rebalancing trades whose tax cost can exceed the rebalancing benefit.

This article is the working note on what a tax-aware rebalancer has to model and what its test corpus has to contain.

What "tax-aware" actually means

A naive rebalancer minimizes tracking error from the target allocation. A tax-aware rebalancer minimizes tracking error subject to a tax-cost constraint. The constraint can be expressed in a few ways:

Formula
The tax-aware rebalancing problem
min tracking_error(weights_after, target_weights) s.t. tax_cost(trades) ≤ tax_budget no_wash_sale_triggered(trades, recent_harvests) lot_selection respects holding-period preferences account_placement respects asset-location preferences
tax_cost
= Realized capital gains × marginal rate, summed across trades. Includes federal + state + NIIT.
tax_budget
= User-specified annual budget for realized gains. Often $0 (don't realize anything) up to portfolio-specific value.
wash_sale
= 30-day window before and after the trade, across all linked accounts
holding-period preferences
= Prefer LTCG over STCG; prefer losses; respect QSBS 5-year clock
asset-location preferences
= Bonds in tax-deferred, equities in taxable, REITs in tax-deferred, etc.
The constraint set is what differentiates a tax-aware rebalancer from a naive one. Each constraint requires per-lot, per-account data that the naive version doesn't need to know about.

What the data has to look like

The bare-minimum naive rebalancer needs:

  • Current position weights
  • Target position weights
  • Drift threshold

That's it. Three numbers per position.

A tax-aware rebalancer needs:

Tax-aware data requirements

  • Per-lot data — acquisition date, basis, holding period, special-status flags. 30–300 lots per typical position.
  • Per-account data — taxable / IRA / Roth / 401(k) / HSA. Each account has different tax treatment of trades.
  • Cross-account linkage — wash-sale rules apply taxpayer-wide, not account-locally.
  • Recent-harvest history — 60+ day rolling window of realized losses to avoid wash-sale on rebalancing trades.
  • Tax-rate context — household's marginal federal, state, NIIT applicability, AMT status, AGI projection for the year.
  • Holding-period thresholds — long-term vs short-term boundary (1 year + 1 day). Lots within days of crossing should be deferred if possible.
  • Special-status awareness — QSBS lots (5-year clock), Section 1042 lots, ESPP qualifying-disposition status.

The data shape is a meaningful step up. A naive rebalancer can run on $10/month of data; a tax-aware rebalancer needs $50–100/month of data minimum, plus integration with custody systems for live lot updates.

The decision sequence

A tax-aware rebalancer evaluates trades in a specific order:

  1. Step 1
    Identify required position deltas
    Per asset class, compute the trade size needed to bring weights to target. Cap at the drift threshold; don't trade for sub-threshold drift.
  2. Step 2
    Prefer non-realizing trades
    Use new contributions or distributions to add to underweighted positions. Use cash from realized losses to add. Avoid sales when contributions can suffice.
  3. Step 3
    Identify free trades
    Sales in tax-deferred / Roth accounts have no immediate tax cost. If reweighting can be done within those accounts, prefer it.
  4. Step 4
    Identify loss-harvest trades
    Sales of underwater lots in taxable accounts realize losses that offset gains; sometimes lower the after-tax cost of the rebalancing.
  5. Step 5
    Score remaining required taxable trades
    For each remaining required sale, compute the tax cost across lot-selection alternatives. Choose the lot that minimizes tax.
  6. Step 6
    Wash-sale check
    Cross-reference each proposed sale against the 60-day window of activity in linked accounts. Reject any sale that would create a wash-sale.
  7. Step 7
    Validate output
    Total tax cost ≤ budget. No wash-sale triggers. Lot-selection respects preferences. Multi-account placement is consistent.

The decision tree is what separates "rebalancing" from "tax-aware rebalancing." Engines that skip steps 2–4 produce trades that are mathematically correct and tax-incompetent.

Common bugs we see

Across rebalancing engines we've audited:

The synthetic data the test corpus needs

A working test corpus for a tax-aware rebalancer:

 Spread dimensionWhat it covers
Account structureHouseholds with taxable + IRA + Roth + 401(k) + HSA combinations. At least 200 unique combinations across the corpus.
Lot compositionPer position: 30–300 lots with varied acquisition dates, costs, and special-status flags.
Recent harvest historyMix of households with no recent harvests, fresh harvests within 30 days, harvests at 31–60 days. The wash-sale window has to be exercised at boundaries.
Tax-rate variabilityHouseholds at different marginal rates, with and without NIIT exposure, with and without AMT exposure.
Special statusQSBS holdings at various points in the 5-year clock. Section 1042 ESOP rollover holdings. ESPP qualifying-disposition pending lots.
Drift conditionsHouseholds where each asset class is at, above, and below target, with various drift magnitudes. Edge cases at the drift threshold.

A test corpus missing any spread is a corpus where the rebalancer has untested branches. The branches that don't get tested are the branches that ship bugs.

Key takeaways

  • A tax-aware rebalancer is a fundamentally different product from a naive rebalancer. It optimizes tracking error subject to tax-cost, wash-sale, and asset-location constraints.
  • The data step-up is meaningful: per-lot, per-account, cross-account-linked, with recent-harvest history and tax-rate context. Pure position-level data is insufficient.
  • The decision sequence prefers non-realizing trades, then free trades in tax-deferred, then loss-harvest trades, before doing taxed trades — and only the lot-selection-optimal taxed trades that don't trigger wash-sales.
  • Common bugs: wash-sale on rebalance, QSBS qualification vaporized by rebalancing, asset-location ignored in multi-account households.
  • Test corpus needs spread on account structure, lot composition, recent-harvest history, tax-rate variability, special-status holdings, and drift conditions.

Frequently asked questions

Is a fully tax-aware rebalancer worth the engineering investment for a mass-market product?+
Depends on the customer base. For mass-market under $100K accounts, the tax savings from tax-aware rebalancing (vs naive) average $50–200/year — meaningful per customer but not transformative. For HNW accounts ($1M+), the savings can be $5K–$50K/year and often exceed the entire annual fee. Most products start with naive rebalancing and add tax-awareness as they move upmarket; the engineering investment scales with the value capture.
How does tax-aware rebalancing interact with regulatory account types (HSA, 529)?+
HSA and 529 are tax-free for qualified uses, so trades within them have no tax cost — they're effectively free trades for rebalancing. The engine should recognize this and prefer rebalancing within HSA/529 when allocation logic permits. The contribution rules are constrained (annual caps, qualified-use restrictions) but the rebalancing within is unconstrained.
Can the rebalancer use options strategies to defer realization?+
Some sophisticated platforms do. Selling covered calls against an overweight position, or using collars to defer realization, can be tax-efficient alternatives to direct sales. The engineering is meaningfully more complex (option pricing, expiration management, assignment risk) and most rebalancers don't go this direction. For HNW accounts, third-party SMA managers (Aperio, Parametric) have done this successfully.
What about direct indexing — does the rebalancer logic differ?+
Direct indexing is essentially tax-aware rebalancing applied to a 200–500-stock direct portfolio rather than a few funds. The math is the same; the lot count is much higher (thousands of lots across the portfolio). The synthetic test corpus needs to scale up: a direct-indexing rebalancer test should have households with thousands of lots, not hundreds. The wash-sale and lot-selection logic is otherwise identical.