交易智能體賣出股票時容易弄錯税務批次與洗售規則
Wash sales and tax lots: what a trading agent gets wrong when it sells
交易智能體若未指定出售的税務批次並檢查洗售窗口,可能錯估股票出售的應税收益或虧損。XYZ 示例中,FIFO 選 lot A 的估算税額為 $320,選 lot B 則為 -$320,差額 $640;若出售前後 30 日買入實質相同股份,虧損可能暫時不能抵扣。
Ask a trading agent to sell 50 shares and it will usually just send the order. The broker then picks which shares you sold, and by default that's first in, first out. That one default can be the difference between owing tax and booking a loss.
There's also the wash sale rule, which most agents don't track at all.
Here's what goes wrong and how to check it before the order goes out.
Which shares did you sell
If you bought the same stock at different times and prices, each buy is a separate tax lot. When you sell part of the position, the lot you sell sets your gain or loss.
Say an agent holds 100 shares of XYZ in two lots and the price is now $100:
- Lot A: 50 shares at $80, bought 2025-11-05
- Lot B: 50 shares at $120, bought 2026-02-10
Sell 50 shares with FIFO and you sell lot A. That's a $1,000 short-term gain. Sell lot B instead and it's a $1,000 short-term loss. Same order, same price, a $2,000 swing in taxable income.
Most brokers let you pick lots (specific identification), but someone has to tell them. If the agent doesn't, you get the default.
Short-term vs long-term
Lots held more than a year are taxed as long-term gains, which is usually a much lower rate. So the date on each lot matters too. Selling a gaining lot a few days before it turns long-term can cost real money for no reason.
The wash sale rule
If you sell at a loss and buy substantially identical shares within 30 days before or after the sale, the loss is disallowed for now. It gets added to the basis of the replacement shares instead. The rule is 26 U.S.C. 1091.
Agents trip on this all the time. They sell a loser, then their strategy buys the same ticker back a week later. Or they bought more 2 weeks ago and then sell an older lot at a loss. Either way the loss they planned on doesn't count this year.
Checking it with one call
I built a service called Agent Financial Advisor and one of its tools is tax_lot_plan. You give it the lots, the sale, and recent buys. It compares FIFO, LIFO, HIFO and a minimum-tax pick, splits short-term and long-term, and checks the wash sale window.
Here's the example above. I'm using 32% short-term and 15% long-term rates:
curl -X POST https://agent-financial-advisor.agent-financial-advisor.workers.dev/v1/tools/tax_lot_plan \
-H "content-type: application/json" \
-d '{
"mode": "sell",
"symbol": "XYZ",
"lots": [
{"lot_id": "A", "symbol": "XYZ", "quantity": 50, "cost_basis_per_share": 80, "acquired_date": "2025-11-05"},
{"lot_id": "B", "symbol": "XYZ", "quantity": 50, "cost_basis_per_share": 120, "acquired_date": "2026-02-10"}
],
"sell": {"quantity": 50, "price": 100, "date": "2026-10-26"},
"recent_transactions": [],
"tax_rates": {"short_term": 0.32, "long_term": 0.15}
}'
Response, trimmed:
{
"recommended": {
"method": "lifo",
"lots": [{"lot_id": "B", "quantity": 50, "term": "short", "gain": -1000, "est_tax": -320}],
"est_tax": -320
},
"savings_vs_fifo": 640,
"comparisons": [
{"method": "fifo", "short_term_gain": 1000, "long_term_gain": 0, "est_tax": 320},
{"method": "lifo", "short_term_gain": -1000, "long_term_gain": 0, "est_tax": -320},
{"method": "hifo", "short_term_gain": -1000, "long_term_gain": 0, "est_tax": -320},
{"method": "min_tax", "short_term_gain": -1000, "long_term_gain": 0, "est_tax": -320}
],
"wash_sale": {
"loss": -1000,
"replacement_buys_in_window": [],
"likely_wash_sale": false,
"avoid_buying_until": "2026-11-26"
},
"lots_turning_long_term_soon": [
{"lot_id": "A", "long_term_on": "2026-11-06", "days_away": 11}
]
}
FIFO would owe about $320. Picking lot B gives a loss worth about $320 the other way, so $640 apart. It also says lot A turns long-term in 11 days, which is worth knowing before anything sells it. And it gives a date: don't buy XYZ back before 2026-11-26 or the loss gets washed.
Now add a recent buy
Same position, but the agent also bought another 50 shares at $105 on 2026-10-12, two weeks ago. That's lot C. I changed lot A to an older date too, so it's already long-term. The sale is the same:
curl -X POST https://agent-financial-advisor.agent-financial-advisor.workers.dev/v1/tools/tax_lot_plan \
-H "content-type: application/json" \
-d '{
"mode": "sell",
"symbol": "XYZ",
"lots": [
{"lot_id": "A", "symbol": "XYZ", "quantity": 50, "cost_basis_per_share": 80, "acquired_date": "2025-03-14"},
{"lot_id": "B", "symbol": "XYZ", "quantity": 50, "cost_basis_per_share": 120, "acquired_date": "2026-02-10"},
{"lot_id": "C", "symbol": "XYZ", "quantity": 50, "cost_basis_per_share": 105, "acquired_date": "2026-10-12"}
],
"sell": {"quantity": 50, "price": 100, "date": "2026-10-26"},
"recent_transactions": [{"symbol": "XYZ", "side": "buy", "date": "2026-10-12", "quantity": 50}],
"tax_rates": {"short_term": 0.32, "long_term": 0.15}
}'
Trimmed:
{
"recommended": {
"method": "hifo",
"lots": [{"lot_id": "B", "quantity": 50, "term": "short", "gain": -1000, "est_tax": -320}]
},
"savings_vs_fifo": 470,
"wash_sale": {
"loss": -1000,
"replacement_buys_in_window": [{"symbol": "XYZ", "side": "buy", "date": "2026-10-12", "quantity": 50}],
"likely_wash_sale": true,
"avoid_buying_until": "2026-11-26",
"note": "Purchases within 30 days of this loss sale make some or all of the loss nondeductible now; it shifts into the replacement shares' basis. Lots bought in the window and being sold now do not count as replacements."
}
}
The lot pick still looks best on paper, but likely_wash_sale is now true. The $1,000 loss won't count this year because of the buy on 2026-10-12. The agent that planned on a $320 tax benefit gets nothing for now.
How I'd wire it into an agent
Before any sell order:
- Pull lots and the last 30 days of buys from the broker.
- Call
tax_lot_planwith the sale. - If
likely_wash_saleis true, stop and decide if the loss matters. If it does, sell a different lot or wait. - Send the order with the recommended lot IDs as specific identification, and confirm them with the broker by settlement.
- Save
avoid_buying_untiland block buys of that ticker untill then. That includes IRAs and dividend reinvestment, which agents forget about.
Step 5 is the one that matters most. Most wash sales come from the buy after the sale, not before.
There's also mode: "harvest", which takes positions plus current prices and lists losses worth harvesting. Same wash sale checks.
Try it
Tool list and schemas are at https://agent-financial-advisor.agent-financial-advisor.workers.dev/llms.txt. There's an MCP endpoint at /mcp too. You get 5 free calls a day per IP, or 25 a day with a free key. The rules_digest tool is free and returns the current wash sale, holding period and settlement rules with sources.
Outputs are automated checks, not tax advice. Talk to an actual tax person before relying on any of this with real money.
來源:DEV Community · dev.to