A tax lot is a group of shares bought at the same time at one purchase price. Lots of the same asset are not equivalent when sold: their gain or loss and holding period can produce different current tax effects. Tracking lots turns a share-level sale into a choice among specific acquisition records. section 13.2.3
Choose lots for a sale
With a current price of $110, four lots bought at $80, $85, $105, and $115 have different gains and holding periods. Applying 40% to short-term gains and 20% to long-term gains yields effective rates of 7.5%, 11.8%, 1.9%, and -1.7%. To sell 35 shares, sell the 10-share loss lot, all 20 shares of the 1.9% lot, and 5 shares of the 7.5% lot. section 13.2.3
Listing 13.3 Calculating tax exposure
import datetime as dt
lt_gains_rate = 0.20
income_rate = .40
current_price = 110
current_date = dt.date(2022, 5, 11)
lots = pd.DataFrame({'price': [80, 85, 105, 115],
'quantity': [25, 30, 20, 10],
'purchase_date': ['2021-02-10', '2021-07-21',
'2021-11-23', '2022-03-25']})
tax_info = {}
for i in lots.index:
lot_info = lots.iloc[i]
purchase_date = dt.date.fromisoformat(lot_info['purchase_date'])
holding_period = (current_date - purchase_date).days
if holding_period <= 365:
lot_rate = income_rate
else:
lot_rate = lt_gains_rate
purchase_price = lot_info['price']
gain = (current_price / purchase_price - 1)
effective_rate = gain * lot_rate
tax_info[i] = pd.Series({'holding_period': holding_period,
'applicable_rate': lot_rate,
'pct_gain': gain,
'effective_rate': effective_rate})
tax_info = pd.DataFrame(tax_info).T
This calculation derives each lot’s holding period, applicable rate, percentage gain, and effective tax rate from its purchase date, purchase price, quantity, and the current price. section 13.2.3
A practical lot-selection rule works incrementally:
- Calculate each lot’s holding period and gain or loss at the current price. section 13.2.3
- Sort lots by effective tax rate, from lowest to highest. section 13.2.3
- Sell from the best-ranked lot until it is exhausted or the requested share count is met, then continue to the next lot. section 13.2.3
For portfolio rebalancing, represent each current lot separately rather than using one sell variable per asset. Buy variables can be keyed by ticker, while sell variables use ticker and purchase date; positions aggregate starting holdings with selected trades. This lets an optimizer weigh tax effects alongside target-portfolio constraints and objectives. section 13.3.1 section 13.3.2
Lot records also support ongoing accounting. A backtest marks every lot to market, calculates tax for each selected sale, and retains lot-level holdings when computing portfolio weights. Tax-loss harvesting additionally needs closed-lot sale dates and prices so potential wash-sale restrictions and cost-basis adjustments can be evaluated. section 13.4.2 section 14.2.2
When harvesting losses, first identify sellable lots and lots that block purchases, then consider only eligible losses. Candidate lots can be ordered by tax benefit, while trade costs and opportunity costs determine whether a proposed replacement trade is worthwhile. section 14.2.2 section 14.3.3
