Tax-Lot Accounting
Tracking the tax consequences of holdings at the lot level: cost basis, holding period, lot relief methods, and wash sales.
Learning outcomes
If you build, operate, or audit any system that holds securities for other people, you will eventually meet tax-lot accounting, and it will not be optional. A brokerage that gets balances right but lots wrong will still mail customers an incorrect tax form, understate or overstate someone’s gain, and answer to a regulator for it. The numbers here flow straight onto a tax return, so the bar for correctness is not “the books balance” but “the IRS, the customer, and the customer’s accountant all agree on a specific dollar figure for a specific sale.” That is a harder bar than most ledger work, and this page is about clearing it.
After studying this page, you can:
- Explain what a tax lot is, why one position in one security can be many lots, and why a lot is the unit at which tax consequences are actually computed.
- Define cost basis and holding period precisely, and trace how each one is set when a lot is created and consumed when a lot is sold.
- Compare the lot relief methods (FIFO, LIFO, HIFO, specific identification, and average cost for funds) and predict how the choice changes the realized gain and the tax due on the same sale.
- Apply the US wash-sale rule: identify the sixty-one-day window, recognize a disallowed loss, and adjust the basis and holding period of the replacement lot correctly.
- Adjust basis for corporate actions (splits, spinoffs, return of capital, mergers) and explain why each one moves basis without being a purchase or a sale.
- State the broker’s cost-basis reporting obligations: covered versus noncovered securities, what Form 1099-B reports, and where the broker’s responsibility ends and the taxpayer’s begins.
- Design the core of a tax-lot engine (lot creation, disposal matching, lot selection, wash-sale detection) and name the failure modes that produce a wrong tax form.
Before we dive in
You do not need a tax background, but you do need a few precise words, because in this domain a loose word produces a wrong number.
A security is a tradable financial instrument: a share of stock, a bond, an exchange-traded fund, a mutual fund. A position is the total quantity of one security you hold, for example two hundred shares of one company. A tax lot (or just lot) is a single acquisition of a security at one time and one price: if you bought one hundred shares in January and another hundred in March, you hold one position of two hundred shares made of two distinct lots. Cost basis (often just basis) is what you paid for a lot, adjusted over time, and it is the number subtracted from the sale price to compute gain or loss. The holding period is how long you have owned a lot, and it decides whether a gain is taxed at the lower long-term rate or the higher short-term rate. Realizing a gain or loss means selling (or otherwise disposing of) the lot, which is the moment the tax consequence crystallizes; until you sell, a gain is unrealized and untaxed.
A disposal (or disposition) is a sale or other event that closes part or all of a lot. Lot relief is the rule that decides which lot or lots a sale draws from when you hold several. A broker is the firm that holds your securities and executes your trades; in the US it has legal duties to report your cost basis to the tax authority. The tax authority in our worked examples is the US Internal Revenue Service, the IRS, because the US has the most prescriptive cost-basis regime and it is the one most systems must implement first; section ten draws the line between what is universal and what is specifically American.
Throughout, dollar amounts are shown as decimals for readability, but a real engine stores money and share quantities as exact integers or fixed-point decimals, never as floating-point numbers, for the same reasons any ledger does. Hold one picture as we go: a position is a stack of lots, each lot remembers what it cost and when it was born, and every sale reaches into that stack and consumes lots according to a rule you choose in advance.
Mental Model
The wrong model, and it is the one almost every beginner carries, is that you own “shares of a company” as a single fungible pile, and that when you sell some shares your gain is the sale price minus your average cost. Under this model a share is a share, interchangeable, with no identity of its own. It is how the position looks on your brokerage home screen: one line, one quantity, one current value.
That model is fine for knowing how much you own and wrong for knowing what you owe. For tax, shares are not fungible. Each lot carries its own cost basis and its own birth date, and those two attributes determine the tax on the day you sell. Two shares sitting in the same account, identical in every market sense, can produce completely different tax outcomes when sold, because one was bought cheaply two years ago (a large long-term gain) and the other was bought expensively last month (a small short-term loss). The right model is that a position is not a pile of identical coins but a stack of labeled receipts, and selling is the act of tearing off specific receipts and reckoning the difference between what each one says you paid and what you just got. The whole discipline of tax-lot accounting is the bookkeeping of those receipts: creating them, labeling them, choosing which to tear off, and adjusting their labels when the world changes the share count or the cost without any buying or selling taking place.
Breaking it down
The core teaching runs in ten steps. The first three establish what a lot is and the two numbers that live on it. The next four are the rules that complicate those numbers in the real world: lot selection, wash sales, corporate actions, and reporting. The last three turn the rules into an engine, examine how that engine fails, and separate the timeless principles from the American specifics.
1. Why a share is not just a share
Start with the economic reason lots exist at all. A government that taxes investment income has to answer a deceptively hard question: when you sell some of a thing you bought repeatedly at different prices, how much profit did you make? If you bought one hundred shares at ten dollars and one hundred more at thirty dollars, then sold one hundred at twenty-five dollars, did you make a fifteen dollar per share profit (against the cheap lot) or lose five dollars per share (against the expensive lot)? Both answers are defensible, and they imply very different taxes. The tax system cannot leave this ambiguous, because the ambiguity is worth money to both the taxpayer and the treasury.
The resolution is to stop treating the position as a blur and to track each acquisition separately. Each purchase becomes a lot with a remembered price and date, and the tax law then specifies, or lets the taxpayer specify within limits, which lots a sale consumes. Once you accept that a sale must be matched to specific acquisitions, every other rule on this page follows: you need to know each lot’s cost (basis), each lot’s age (holding period), a rule for matching sales to lots (relief method), and adjustments for everything that changes a lot’s cost or count without being an ordinary trade (wash sales and corporate actions).
flowchart LR B1["Buy 100 @ $10<br/>Jan 2024"] --> L1["Lot A<br/>100 sh, basis $1000"] B2["Buy 100 @ $30<br/>Mar 2024"] --> L2["Lot B<br/>100 sh, basis $3000"] L1 --> P["Position: 200 shares"] L2 --> P P --> S["Sell 100 @ $25<br/>Which lot?"] S --> G["Gain depends on<br/>which lot is relieved"]
The diagram makes the central fact concrete: the position is a single line on a screen, but underneath it is two lots, and a sale of one hundred shares has no single correct gain until you decide which lot it draws from. That decision is the relief method, and it is the first place where engineering, tax strategy, and customer outcomes collide. A system that forgets a position is made of lots cannot answer the only question that matters at sale time.
2. The anatomy of a tax lot
A lot is a small record, but every field on it is load-bearing. At minimum a lot carries the security it represents, the quantity of shares (or units, or par value for bonds), the acquisition date, the original cost, and the adjusted basis. It often also carries an acquisition type (purchase, gift, inheritance, dividend reinvestment, employee plan), because that type can change how basis and holding period are set, and a flag for whether the security is covered, which decides the broker’s reporting duty (section seven).
The acquisition date and original cost are set at lot creation and, for an ordinary purchase, are simply the trade date and the total amount paid including commissions and fees. But not every lot is born from a purchase, and the alternatives are where mistakes breed. A lot received as a gift generally keeps the donor’s basis and holding period (a carryover basis), with a special dual-basis rule when the gift is worth less than the donor’s basis at the time of the gift. A lot received by inheritance generally gets a stepped-up basis equal to the security’s fair market value on the date of death, and its holding period is automatically treated as long-term regardless of how briefly the heir has held it. A lot created by dividend reinvestment is a genuine purchase, small and frequent, and a single dividend-reinvesting position can accumulate dozens of tiny lots a year, which is exactly the kind of volume that makes manual tracking hopeless and a lot engine necessary.
The reason to model the acquisition type explicitly, rather than just storing a basis number, is that the type drives downstream behavior an engine must reproduce. An inherited lot must be reported as long-term even if sold the next day; a gifted lot may have two possible bases until the sale price is known; a reinvested-dividend lot is covered or noncovered depending on its date. If the engine flattens all of these to “a lot with a basis and a date,” it will produce confident, wrong tax forms in exactly the cases that are hardest for a human to catch.
3. Cost basis and holding period the two numbers that decide the tax
Everything a lot exists to track reduces to two numbers, and it is worth being precise about each.
Cost basis starts as what you paid (price times quantity plus commissions and transaction fees) and then moves over the life of the lot. It is called adjusted basis once anything has changed it. Reinvested dividends do not change an existing lot’s basis (they create new lots), but a return of capital distribution reduces the basis of the lot it is paid on, a stock split rescales basis across more shares, and a wash sale adds disallowed loss to the basis of a replacement lot. When you finally sell, realized gain or loss is the proceeds (sale price minus selling commissions) minus the adjusted basis of the specific lots relieved. A positive number is a gain you may owe tax on; a negative number is a loss that can offset other gains.
Holding period is the elapsed time you have owned the lot, and in the US it draws a sharp line. A gain on a lot held one year or less is short-term and is taxed as ordinary income, at the same rates as your salary, which for high earners is the top marginal bracket. A gain on a lot held more than one year is long-term and is taxed at preferential capital-gains rates, which for most investors are substantially lower. The holding period starts the day after the trade date and includes the day you sell, so the one-year line is crossed the day after the first anniversary of purchase. That single day matters: selling on the anniversary itself is short-term, selling one day later is long-term, and on a large gain the rate difference can be the difference between roughly thirty-seven percent and roughly twenty percent at the federal level.
stateDiagram-v2
[*] --> Held: lot acquired (trade date + 1)
Held --> ShortTerm: sold within 1 year
Held --> LongTerm: sold after more than 1 year
ShortTerm --> [*]: gain taxed as ordinary income
LongTerm --> [*]: gain taxed at preferential rate
note right of LongTerm
The one-year line is crossed the day
AFTER the first anniversary of purchase.
end noteBecause the two numbers interact, a lot’s identity is genuinely two-dimensional: a sale produces both a dollar amount (gain or loss, set by basis) and a character (short or long term, set by holding period). A relief method that only minimizes the dollar gain while ignoring the character can hand the customer a higher tax bill than one that balances both. This is why sophisticated tax-lot selection is not simply “sell the highest-cost lot”: it weighs the size of the gain against whether that gain is taxed at ordinary or preferential rates, and against whether realizing a loss now is more valuable than carrying the lot.
Drag the slider across the boundary and watch the verdict flip at three hundred sixty-six days, one day past the year. That single day is not a rounding artifact; it is a statutory cliff, and a lot engine that computes the holding period off by even one day will misclassify gains and produce a wrong tax form. The rule “more than one year” means strictly greater, which is why three hundred sixty-five days held is still short-term.
4. Lot relief methods and why the choice is worth real money
When a sale could draw from several lots, the lot relief method decides which ones it consumes. The method is not cosmetic: on the same sale, at the same price, different methods produce different realized gains, different short-versus-long classifications, and therefore different tax. This is the single highest-leverage decision in tax-lot accounting, and it is where the investor, the broker, and the tax authority all have a stake.
The classic methods are easiest to see on one worked example. Suppose you hold three lots of a stock: Lot A is one hundred shares bought at ten dollars (long-term), Lot B is one hundred shares at thirty dollars (long-term), and Lot C is one hundred shares at twenty dollars bought last month (short-term). You sell one hundred shares at twenty-five dollars, for proceeds of two thousand five hundred dollars.
The numbers are not a trick of the example; they are the whole point. FIFO realizes the largest gain here because it sells the cheapest, oldest shares. HIFO realizes a loss because it sells the most expensive shares. Specific identification gives the investor the most control, because they can choose to harvest a loss, lock in a long-term gain, or avoid a short-term one, deliberately. Average cost is special: it is permitted only for mutual funds and certain reinvestment plans, it collapses all lots to a single average basis, and once elected for a fund it generally binds future sales of that fund, so it trades control for simplicity.
flowchart TB
S["Sell 100 shares @ $25<br/>proceeds $2500"] --> M{Relief method}
M -->|FIFO| A["Relieve Lot A $1000<br/>gain +$1500 long-term"]
M -->|LIFO| C["Relieve Lot C $2000<br/>gain +$500 short-term"]
M -->|HIFO| B["Relieve Lot B $3000<br/>loss -$500 long-term"]
M -->|Spec ID| X["Investor names the lot<br/>gain or loss by choice"]There is a hard operational constraint hiding in specific identification: to use it, the investor must identify the specific lot to the broker at or before the time of sale, and the broker must confirm that identification. You cannot decide in April, when you do your taxes, that the December sale “should have been” the high-cost lot. The tax rules require the identification to be made contemporaneously and recorded by the broker; absent a valid specific identification, the broker’s default method (commonly FIFO) governs. This is why a brokerage’s tax-lot user interface matters so much: the choice has to be capturable at trade time and stored immutably, because it cannot be reconstructed later. Engineering a system that lets a customer pick a lot after the fact, even with good intentions, is building a machine for producing tax forms that do not match the law.
The economics flow directly into incentives. Investors generally prefer methods that defer or minimize tax, which usually means selling high-cost and long-term lots first (HIFO or specific identification used for loss harvesting). The tax authority is indifferent to the method as long as it is applied consistently and the lots are real, but it cares deeply that the method is fixed before the sale and not gamed afterward. The broker sits in between, obligated to apply the customer’s standing or per-trade election faithfully and to report the result.
5. The wash-sale rule and the loss that disappears
The wash-sale rule is the most counterintuitive piece of tax-lot accounting, and the one that most often produces a wrong number when an engine ignores it. Here is the motivation. Without a rule, an investor sitting on an unrealized loss could sell the lot to realize the deductible loss, then immediately buy the same security back, keeping their economic position essentially unchanged while harvesting a tax benefit. That is too good, so the law disallows it.
The US wash-sale rule says: if you sell a security at a loss and buy a substantially identical security within thirty days before or thirty days after the sale, the loss is disallowed. The window is sixty-one days wide: the thirty days before the sale, the day of the sale, and the thirty days after. “Substantially identical” covers the same stock, and also options and contracts to acquire it; it generally does not treat two different companies in the same industry as identical, but it can reach the same fund bought in a different account.
Critically, the disallowed loss is not gone forever; it is deferred by being added to the basis of the replacement lot, and the replacement lot’s holding period is extended to include the holding period of the lot that was sold. So the loss you could not deduct now reduces your gain (or increases your loss) when you eventually sell the replacement, and the long holding you accrued is not lost. The rule defers the benefit and prevents the abuse; it does not confiscate the loss.
flowchart LR
S["Sell Lot X at a LOSS<br/>e.g. -$500"] --> W{Buy substantially identical<br/>within 30 days before<br/>or 30 days after?}
W -->|No| OK["Loss is allowed<br/>deduct the $500 now"]
W -->|Yes| WS["Wash sale:<br/>$500 loss DISALLOWED now"]
WS --> ADJ["Add $500 to basis of<br/>replacement lot AND<br/>extend its holding period"]Walk a concrete case to feel how the numbers move.
Several engineering-relevant subtleties live in that example. The window is measured in calendar days, not trading days, and it reaches across accounts and even into a spouse’s accounts and the investor’s own IRA, which a single brokerage cannot fully see. A wash sale can be partial: if you sell two hundred shares at a loss and buy back fifty within the window, only the loss on fifty shares is disallowed and proportionally reallocated. Dividend reinvestment is a notorious trap, because an automatic reinvestment purchase inside the window triggers a wash sale the investor never consciously made. And the replacement purchase can occur before the sale, not only after, which surprises people who think of it only as “don’t buy it back.”
Because the rule reaches across accounts, the broker’s wash-sale reporting is necessarily incomplete: a US broker is required to identify and report wash sales only for identical securities in the same account at the same broker. Wash sales spanning two brokers, or a taxable account and an IRA, or two spouses, are the taxpayer’s responsibility to track and adjust. This is a genuine boundary, not a software limitation: no single broker has the data to see the whole picture, so the regulation deliberately scopes the broker’s duty to what it can observe, and pushes the rest onto the taxpayer.
6. Corporate actions and the moving basis
A lot’s basis and share count would be easy to track if they only changed when you bought or sold. They do not. Corporate actions are events initiated by the security’s issuer that change the share count, the basis, or the very identity of the security, without the investor trading anything. An engine that does not handle them will drift away from reality over years, and the drift surfaces as a wrong gain on a sale that may be a decade after the action.
The common actions each move basis in a characteristic way. A forward stock split (say two-for-one) doubles the share count and halves the per-share basis, leaving total basis unchanged: a lot of one hundred shares at fifty dollars basis becomes two hundred shares at twenty-five dollars basis, still five thousand dollars total, and the original holding period carries to all the new shares. A reverse split does the opposite. A stock dividend adds shares and spreads existing basis across the larger count. A return of capital distribution is not income; it is the company handing back part of your investment, so it reduces the basis of the lot it is paid on (and if it ever drives basis below zero, the excess becomes a gain). A spinoff splits one lot’s basis between the parent and the new subsidiary according to a ratio the company publishes, and the spun-off shares inherit the parent lot’s holding period. A merger or acquisition can be tax-free (basis carries into the acquirer’s shares) or taxable (treated as a sale at the deal price), depending on its structure.
flowchart TB L["Lot: 100 sh @ $50<br/>total basis $5000"] --> SPLIT["2-for-1 split"] SPLIT --> L2["200 sh @ $25<br/>total basis $5000 (unchanged)"] L --> ROC["Return of capital: $300"] ROC --> L3["100 sh, basis reduced<br/>to $4700"] L --> SPIN["Spinoff: 30% to NewCo"] SPIN --> L4a["Parent: basis $3500"] SPIN --> L4b["NewCo: basis $1500<br/>same holding period"]
The defining property to internalize is that most corporate actions conserve total basis: a split rearranges it across more shares, a spinoff partitions it between two securities, a merger carries it forward. Return of capital is the notable case that genuinely reduces basis, because economically you got some of your money back. An engine models a corporate action as a transformation on lots, not as a trade: it reads the action’s terms (the ratio, the allocation percentage, the per-share cash), applies them to every affected lot while preserving each lot’s holding period, and records the adjustment so the audit trail explains why a lot that was once one hundred shares at fifty dollars is now two hundred shares at twenty-five. Get the ratio wrong, apply it to the wrong lots, or silently skip an action, and every future sale of that position computes against a corrupted basis. Because actions accumulate, a missed split in year one quietly poisons a gain calculation in year ten.
7. Cost-basis reporting and the broker as tax intermediary
Until 2008 in the US, cost basis was almost entirely the taxpayer’s problem: the broker reported what you sold for (your proceeds) and you were responsible for knowing what you paid. Mismatches were rampant, and the government was leaving tax uncollected because basis was hard to verify. The Emergency Economic Stabilization Act of 2008 changed this by requiring brokers to track and report cost basis to the IRS, phased in by security type starting with stocks acquired on or after January 1, 2011.
This created the central distinction in US cost-basis reporting: covered versus noncovered securities. A covered security is one acquired after its phase-in date (equities from 2011, mutual funds and dividend-reinvestment-plan shares from 2012, certain bonds and options from 2014), for which the broker must report both proceeds and adjusted cost basis to the IRS, and must report whether the gain is short or long term. A noncovered security is one acquired before its phase-in date (or transferred in without basis information), for which the broker reports proceeds but is not required to report basis; the taxpayer supplies it. The same account can hold both, and the same position can be split into a covered piece and a noncovered piece, which an engine must track separately because they are reported on different parts of the form.
The reporting vehicle is Form 1099-B, which a US broker sends to both the customer and the IRS after year-end. It reports, per disposition, the proceeds, the cost basis (for covered lots), the acquisition and sale dates, the short-or-long-term character, and adjustment codes including the wash-sale disallowed amount (code W). The taxpayer carries these figures onto Schedule D and Form 8949 of their return. Because the IRS receives the same 1099-B, a mismatch between what the broker reported and what the taxpayer reports is automatically flagged, which is precisely the enforcement leverage the 2008 law was designed to create.
The 2008 reform turned the broker into a tax intermediary, and that has deep engineering consequences. A brokerage now has to maintain, for every customer, a lot-level history accurate enough to file with the government, survive corporate actions and transfers for years, apply the customer’s chosen relief method, detect same-account wash sales, and emit a 1099-B that the IRS can match. The cost-basis engine stopped being an internal nicety and became a regulated reporting system whose errors are visible to a tax authority. That is the business reality that forces the engineering rigor of the next two sections.
8. Engineering a tax-lot engine
Now build it. A tax-lot engine has four core responsibilities, and getting their interaction right is most of the work: creating lots when securities arrive, matching disposals to lots according to the relief method, selecting which lots a sale relieves, and detecting wash sales. Around those sits the same discipline any financial ledger needs: immutable history, derived quantities, exact arithmetic, and a complete audit trail.
The data model is a position made of lots, plus an append-only log of events that act on them. Each lot stores its security, quantity, acquisition date, original cost, adjusted basis, holding-period start, covered flag, and acquisition type. Events are purchases (which create lots), sales (which relieve lots), corporate actions (which transform lots), and adjustments (such as a wash-sale basis bump). Crucially, you do not mutate a lot’s basis in place and forget the old value; you record the adjustment as an event so the audit trail can explain how a lot reached its current basis. This is the same “store the movements, derive the state” principle that underlies any trustworthy ledger, applied to lots instead of cash balances.
-- A lot is an acquisition of a security at one time and price.
CREATE TABLE lots (
id BIGINT PRIMARY KEY,
account_id BIGINT NOT NULL,
security_id BIGINT NOT NULL,
quantity NUMERIC(28,8) NOT NULL, -- remaining open quantity
acquired_on DATE NOT NULL, -- trade date
holding_start DATE NOT NULL, -- trade date + 1, or inherited rule
original_cost BIGINT NOT NULL, -- minor units, never a float
adjusted_basis BIGINT NOT NULL, -- moves with actions and wash sales
covered BOOLEAN NOT NULL, -- broker must report basis if true
acquisition_type SMALLINT NOT NULL -- purchase, reinvest, gift, inherit
);
-- Disposals relieve specific lots; one sale can touch several lots.
CREATE TABLE relief_legs (
id BIGINT PRIMARY KEY,
disposal_id BIGINT NOT NULL, -- the sale this leg belongs to
lot_id BIGINT NOT NULL, -- the lot relieved
quantity NUMERIC(28,8) NOT NULL, -- shares taken from this lot
proceeds BIGINT NOT NULL, -- allocated sale proceeds, minor units
basis_relieved BIGINT NOT NULL, -- basis consumed from this lot
gain BIGINT NOT NULL, -- proceeds - basis_relieved
term SMALLINT NOT NULL, -- short or long, from holding period
wash_disallowed BIGINT NOT NULL DEFAULT 0 -- disallowed loss, if any
);
The disposal-matching algorithm is the heart of the engine. A sale arrives for some quantity of a security; the engine selects open lots of that security in the order dictated by the relief method (oldest first for FIFO, newest for LIFO, highest-basis first for HIFO, the named lots for specific identification), and consumes them in sequence, splitting the final lot if the sale quantity does not land on a lot boundary. For each consumed lot it allocates a slice of the proceeds, computes the gain as proceeds minus basis relieved, classifies the term from the holding-period start versus the sale date, and writes a relief leg. The lot’s remaining quantity decreases; a fully consumed lot closes.
The animation below shows the whole shape of a disposal at once, from the incoming sale through lot selection, leg creation, wash-sale screening, and the final reported figures, so you can see how the pieces connect before reading the rest of the section.
Wash-sale detection is the trickiest stage because it looks both backward and forward in time. When a sale produces a loss, the engine must scan the sixty-one-day window around the sale date for any acquisition of a substantially identical security in the same account, including dividend reinvestments. If it finds one, it disallows the loss (in whole or in part, proportional to the matched quantity), adds the disallowed amount to the matched replacement lot’s basis, and extends that lot’s holding-period start. The forward-looking part is the hard part operationally: a loss sale today might be washed by a purchase that has not happened yet, thirty days from now, so the engine cannot finalize the loss immediately. Real systems handle this by provisionally booking the loss and re-evaluating it as later purchases land within the window, or by deferring the wash determination until the window closes. Either way, the engine must be able to revise a previously computed gain when a future event reaches back into its window, which is why immutability with append-only adjustments, rather than in-place edits, is not optional here.
Everything else is the ordinary discipline of financial software, but it bites hard here because the output is a government filing. Store money as integer minor units and share quantities as fixed-point decimals, because a lot of fractional reinvested shares with a floating-point quantity will eventually misallocate a sale and produce a basis that does not foot. Make lot creation idempotent under retries, so a duplicated purchase message does not create a phantom lot that later gets sold and reported. And keep the entire history immutable, because the 1099-B you file is a point-in-time assertion that you may have to defend or amend years later, and you can only defend a number you can reconstruct.
9. Failure modes and the controls that catch them
A tax-lot engine fails in ways a plain cash ledger does not, because its output is not a balance but a set of assertions about specific past events that flow onto a tax return. Knowing exactly how it breaks, and what catches each break, is what separates an engine you can file from one you merely hope is right.
The pattern across that list mirrors the lesson from any ledger but with a sharper edge. Some errors are internal and catchable by invariants you can run continuously: the relief legs of a sale must sum to the total sale quantity and total proceeds, a lot’s remaining quantity can never go negative, and a lot’s adjusted basis must equal its original cost plus the sum of its recorded adjustments. Other errors are external and only caught by reconciliation against an outside source of truth: corporate-action terms from the issuer, transfer statements from the prior broker, and ultimately the IRS itself, which matches the 1099-B against the taxpayer’s return. An engine can be internally perfect, with every leg footing and every lot non-negative, and still file a wrong form because it missed a split or a wash sale it could not see. So the controls come in two layers: invariants for internal consistency, and reconciliation against issuers, custodians, and counterparty brokers for external correctness. The tax authority is the final, unforgiving reconciliation, and it is the one you least want to fail.
10. Fundamental principles versus jurisdiction-specific rules
A senior engineer should be able to tell which parts of this page are eternal and which are simply how the United States happens to do it, because a system built for one country and assumed to be universal will fail the moment it crosses a border.
The fundamental principles are jurisdiction-independent. Any income-tax system that taxes investment gains must answer the same three questions: what did you pay (basis), how long did you hold it (period), and which acquisitions does a sale consume (relief). The need to track acquisitions separately, to adjust basis for events that change share count or cost without a trade, and to prevent purely tax-motivated loss harvesting in some form, recurs across tax systems because they all face the same underlying economics. The lot, as the unit at which a tax consequence is computed, is a deep idea, not an American quirk.
The jurisdiction-specific conventions are the exact numbers and rules, and they vary widely. The one-year short-versus-long boundary, the specific long-term rate schedule, the thirty-day-each-way wash-sale window, the covered-security phase-in dates, and Form 1099-B itself are all United States rules. Other systems differ sharply. The United Kingdom, for example, generally requires pooling shares of the same class into a single average-cost pool rather than tracking discrete lots, with its own short-window matching rules for recent purchases, so the very idea of “pick the high-cost lot” does not apply the same way. Canada uses an average-cost basis (the adjusted cost base) and has a superficial-loss rule analogous to but not identical to the wash sale. Some jurisdictions tax gains only above a holding-period threshold, or not at all, or differently for residents and non-residents. The character of a gain, the available relief methods, and the anti-abuse rules are all local law.
flowchart TB P["Fundamental principles<br/>(every gains-tax system)"] --> P1["Track basis per acquisition"] P --> P2["Track holding period"] P --> P3["A rule to match sales to acquisitions"] P --> P4["Adjust basis for non-trade events"] P --> P5["Curb purely tax-motivated loss harvesting"] J["Jurisdiction-specific conventions"] --> J1["US: 1-year line, 61-day wash window, 1099-B, covered/noncovered"] J --> J2["UK: average-cost pooling, own matching rules"] J --> J3["Canada: adjusted cost base, superficial-loss rule"]
The engineering lesson is to build the principles into the core and push the conventions to the edges. Model lots, basis, holding period, relief, and basis-adjusting events as the durable spine, and treat the one-year boundary, the wash-sale window length, the available relief methods, the reporting form, and the covered-security definition as configurable, jurisdiction-scoped policy. A US-only system that hardcodes a thirty-day window and a one-year line into its core logic is not wrong for the US, but it has welded a local convention onto a universal skeleton, and unwelding it later, when the firm opens a second-country book, is far more expensive than parameterizing it from the start. Knowing which is which is the difference between a tax engine and a US tax engine that pretends to be one.
Mastery Questions
-
A customer holds three lots of one stock: Lot A (long-term, very low cost), Lot B (long-term, high cost above the current price), and Lot C (bought last week, modest cost). They want to sell some shares and ask you, the brokerage’s tax-lot system, to minimize this year’s tax. Why is “sell the highest-cost lot” not a complete answer, and what does the system actually have to weigh?
Answer. Highest-cost-first (HIFO) is a good first instinct because it minimizes the dollar gain, and selling Lot B may even realize a deductible loss, which is better than any gain. But minimizing the gain is only half the calculation, because a lot’s tax also depends on its character. Lot C is short-term, so a gain on it is taxed at the higher ordinary-income rate, while gains on the long-term Lots A and B are taxed at preferential rates; a smaller short-term gain can carry more tax than a larger long-term gain. So the system has to weigh the size of the gain against its character, and against the value of realizing a loss now versus preserving it. The genuinely tax-minimizing choice here is likely specific identification of Lot B to harvest its loss, which beats any gain, but if no loss lot existed, the right answer would trade off Lot A’s large long-term gain against Lot C’s small short-term gain rather than blindly taking the smaller number. And whatever it decides, the system must capture that lot election at or before the sale, because a specific identification made after the trade is invalid and the default method would govern instead.
-
Your engine sells a lot at a loss on the last day of November and books the deductible loss immediately. On the tenth of December, a routine dividend reinvestment in the same account buys a few shares of the same security. A junior engineer says the November sale is already finalized, so the December reinvestment cannot affect it. Are they right, and what does this imply about how the engine must be built?
Answer. They are wrong, and this is the central trap of wash-sale handling. The December reinvestment is an acquisition of a substantially identical security within the thirty-day window after the loss sale, so it triggers a wash sale: the November loss (at least the portion matched to the reinvested shares) is disallowed, that disallowed amount must be added to the basis of the reinvested replacement lot, and that lot’s holding period must be extended. A reinvestment counts even though the customer never consciously chose to buy. The implication for the engine is that a loss leg cannot be treated as final until its forward window has closed, because a future acquisition can reach back and change it. The engine must either defer finalizing wash determinations until the window passes, or book the loss provisionally and revise it when later purchases land. Either way it must be able to amend a previously computed gain by appending an adjustment, never by pretending the past is immutable when the law explicitly lets a future event rewrite its tax effect. This is exactly why lots are stored with append-only adjustment events rather than mutated in place.
-
A position transfers into your brokerage from another firm. The shares clearly exist, but the transfer statement with cost-basis and acquisition-date information never arrives. Tax season comes. What should your system report on the 1099-B, and why is reporting “basis unknown” the responsible choice rather than estimating?
Answer. Without a valid transfer statement, the receiving broker does not actually know the lots’ adjusted basis or original acquisition dates, so it cannot substantiate a basis figure. The responsible behavior is to treat the affected lots as noncovered for reporting: report the proceeds when they are sold, but report the basis as not provided rather than reporting a number the system cannot stand behind. The reason estimating is wrong is that the 1099-B is filed with the IRS and is an assertion the broker must be able to defend; a fabricated or guessed basis that is too high understates the customer’s gain and exposes both the customer and the broker when the IRS matches the form, and one that is too low overstates the gain and overtaxes the customer. The correct division of responsibility, which the covered-versus-noncovered regime codifies, is that the broker reports basis only when it genuinely has and can substantiate it; otherwise the taxpayer establishes basis from their own records. The system should therefore flag transferred-in lots with missing or unconfirmed basis, refuse to report an unsubstantiated number to the tax authority, and surface the gap to the customer so they can supply their records, rather than silently inventing a figure that will become a government filing.
Sources & evidence20 claims · 8 cited
Grounded in US Internal Revenue Code and IRS guidance on cost basis, holding periods, the wash-sale rule (Section 1091), and broker cost-basis reporting under the 2008 Emergency Economic Stabilization Act (Form 1099-B, covered vs noncovered). Engineering and architecture claims are internal reasoning consistent with ledger-design principles. Non-US comparisons (UK pooling, Canada ACB) are described at a principled level; exact foreign statutory parameters are not enumerated and remain a minor gap.
- A tax lot is a single acquisition of a security at one time and price, and it is the unit at which tax consequences (gain, loss, and holding-period character) are computed.stable common knowledge
- Cost basis begins as the amount paid (price times quantity plus commissions and fees) and is adjusted over the life of the lot; realized gain or loss is proceeds minus adjusted basis of the lots relieved.verified
- In the US a gain on a security held one year or less is short-term and taxed as ordinary income; a gain on a security held more than one year is long-term and taxed at preferential capital-gains rates.verified
- The holding period starts the day after the trade date and includes the sale date, so 'more than one year' is crossed the day after the first anniversary of purchase; 365 days held is still short-term and 366 days is long-term.verified
- Top federal ordinary-income rates reach roughly 37% while long-term capital-gains rates are commonly 15% or 20%, so the short-versus-long classification can change the tax materially.verified
- Lot relief methods include FIFO, LIFO, HIFO, and specific identification, and on the same sale they can produce different realized gains and different short-or-long classifications.stable common knowledge
- Average cost basis is permitted only for mutual funds and certain dividend-reinvestment-plan shares; once elected for a fund it generally binds future sales of that fund.verified
- To use specific identification, the taxpayer must identify the specific lot to the broker at or before the time of sale and the broker must confirm it; absent a valid specific identification the broker default (commonly FIFO) governs and it cannot be changed after the fact.verified
- The US wash-sale rule disallows a loss if substantially identical securities are bought within 30 days before or 30 days after the loss sale, a 61-day window including the sale day.verified
- A wash-sale disallowed loss is deferred by being added to the basis of the replacement lot, and the replacement lot's holding period is extended to include the holding period of the sold lot.verified
- The wash-sale rule reaches across accounts, including a spouse's accounts and the taxpayer's IRA, and dividend reinvestments inside the window can trigger it; brokers are required to report wash sales only for identical securities in the same account at the same broker.verified
- A stock split conserves total basis: a 2-for-1 split doubles shares and halves per-share basis with total basis unchanged and the original holding period carried to all shares.stable common knowledge
- A return-of-capital distribution is not income; it reduces the basis of the lot it is paid on, and if it drives basis below zero the excess is a gain.verified
- An inherited lot generally receives a stepped-up basis to fair market value on the date of death and is treated as long-term regardless of how briefly the heir has held it; a gifted lot generally takes carryover basis with a dual-basis rule when FMV is below the donor's basis.verified
- The Emergency Economic Stabilization Act of 2008 required brokers to track and report cost basis to the IRS, phased in by security type: equities from January 1, 2011, mutual funds and DRP shares from 2012, and certain bonds and options from 2014.verified
- A covered security is one acquired after its phase-in date for which the broker must report adjusted basis and short/long character; a noncovered security (pre-phase-in or transferred without basis) has proceeds reported but basis supplied by the taxpayer.verified
- Form 1099-B reports per disposition the proceeds, cost basis for covered lots, acquisition and sale dates, short-or-long character, and adjustment codes including wash-sale code W; the IRS receives the same form and matches it against the taxpayer's return.verified
- A tax-lot engine should store lots with append-only adjustment events rather than mutating basis in place, because a wash sale or corporate action can reach back and revise a previously computed figure and the 1099-B must be reconstructable.internal reasoning
- Wash-sale detection must look both backward and forward in time, so a loss leg cannot be finalized until its forward 30-day window closes, requiring provisional booking or deferred determination.internal reasoning
- Fundamental principles (per-acquisition basis, holding period, sale-to-acquisition matching, basis adjustment for non-trade events, and anti-loss-harvesting rules) are jurisdiction-independent, while the one-year line, 61-day window, covered phase-in dates, and Form 1099-B are US-specific; the UK uses average-cost share pooling and Canada uses an adjusted cost base with a superficial-loss rule.verified
Cited sources
- Publication 550: Investment Income and Expenses · US Internal Revenue Service
- Topic No. 409 Capital Gains and Losses; cost basis method guidance · US Internal Revenue Service
- Internal Revenue Code Section 1091 (Loss from wash sales of stock or securities) · US Congress / Internal Revenue Code
- Publication 550 corporate-actions basis adjustments and IRC Sections 301/305/307 · US Internal Revenue Service / Internal Revenue Code
- Publication 551: Basis of Assets · US Internal Revenue Service
- Emergency Economic Stabilization Act of 2008 cost-basis reporting provisions and IRS regulations under IRC Section 6045 · US Congress / US Treasury and IRS
- Instructions for Form 1099-B and Form 8949 · US Internal Revenue Service
- HMRC Capital Gains Manual share identification rules and Canada Revenue Agency adjusted cost base and superficial-loss guidance · HM Revenue and Customs; Canada Revenue Agency