# Tax Methodology — Unrealized PnL, Lots, and Harvesting

This document explains the tax logic used by the vibe-dashboard tax tool
(`/workspace/bin/vibe-tax-lots`) and how it maps to the raw per-lot fields in
`/workspace/vibe-dashboard/data/equity_tax_lots.json`.

The goal is **tax-loss harvesting** and profit-taking awareness: identify lots
that are below their cost basis (losses you could realize to offset gains/income)
and lots that are above it (gains worth taking), all bucketed by **short-term**
vs **long-term** holding period.

---

## Per-lot unrealized PnL

For every open lot, unrealized PnL is:

```
unrealized PnL (per lot) = (current_price − cost_per_share) × quantity_available
```

where

- **`current_price`** — the live last-trade price fetched per symbol via the MCP
  tool `get_equity_quotes` (parsed from `last_trade_price`).
- **`cost_per_share`** — the lot's per-share cost basis, from the tax-lot record.
- **`quantity_available`** — the number of shares in the lot still available to
  transact.

A **loss** is when `current_price < cost_per_share` (PnL negative); a **gain** is
when `current_price > cost_per_share`.

### Real data fields (from `equity_tax_lots.json`)

Each element of a symbol's `tax_lots` array looks like:

```json
{
  "open_lot_id": "2f33a4dd-fa99-54a9-bb54-91aeb51abc5c",
  "open_tran_type": "buy",
  "order_id": "900000261774455",
  "quantity": "89.000000",
  "quantity_available": "89.000000",
  "is_selectable": true,
  "cost_per_share": "94.310000",
  "tax_cost_basis": "8393.580000",
  "open_date": "2026-06-26",
  "term": "st"
}
```

Field meanings used by the PnL math:

| Field                | Meaning                                                          |
|----------------------|------------------------------------------------------------------|
| `quantity_available` | shares open in this lot (used as the PnL quantity)               |
| `cost_per_share`     | per-share cost basis (used as the PnL cost)                      |
| `tax_cost_basis`     | `cost_per_share × quantity` (total basis for the lot)            |
| `open_date`          | lot open date `YYYY-MM-DD`, used to compute holding period       |
| `term`               | `"st"` (short-term) or `"lt"` (long-term) — pre-computed by Robinhood |
| `is_selectable`      | whether the lot is selectable for tax-lot optimization           |

Note the lot fields are delivered as **strings**, so the tool converts them with
`float(...)` before arithmetic.

---

## Short-term vs long-term: the 365-day threshold

A holding is **short-term** if it has been held **less than 365 days**, and
**long-term** if held **365 days or more**.

The tool's `is_st(open_date)` function computes holding period as:

```
held_days = (2026-08-23 − open_date).days     # reference date hard-coded in the tool
is_short_term = held_days < 365
```

(That date is the tool's snapshot reference date; in production it should be
"today", but the comparison is the same.) Robinhood's `term` field agrees —
`"st"` vs `"lt"` — and can be used as a cross-check.

**Why it matters:** short-term gains are taxed at ordinary income rates (higher),
while long-term gains get preferential (lower) capital-gains rates. The dashboard
buckets PnL accordingly so you can tell which losses offset which gains.

---

## The wash-sale rule (30 days, substantially identical)

A **wash sale** occurs when you sell a security at a loss and buy a
**substantially identical** security within **30 days before or after** the sale
(a 61-day window centered on the sale).

Consequences:

- The loss is **disallowed** for tax purposes in the current period.
- The disallowed loss is added to the **cost basis** of the replacement shares
  (so the loss is deferred, not lost, but the clock resets on the new holding
  period).

For the dashboard's purposes: `is_selectable` marks lots Robinhood considers
harvestable, and the `PORTFOLIO_BRIEF.md` explicitly warns to **mind the
wash-sale rule** when harvesting a leveraged ETF like **TSLL** — buying back the
same fund (or a substantially identical one) within 30 days would disallow the
loss.

---

## Why harvesting short-term losses is especially valuable

Priorities for tax-loss harvesting, in order of value:

1. **Short-term losses** are the most valuable — they offset **short-term gains**
   (taxed at your ordinary income rate, so the offset saves the most tax) and
   any **excess** can offset up to **$3,000 of ordinary income** per year
   (with unused losses carrying forward to future years).
2. **Long-term losses** offset long-term gains first, then short-term gains, then
   (excess) ordinary income.

So realizing a short-term loss before it becomes long-term (i.e. selling while the
`term` is still `"st"`) captures the highest tax benefit. The 2024 realized ~$67.8k
gain already banked (see `PORTFOLIO_BRIEF.md`) means harvesting losses this year
offsets that current/future-year gain.

---

## Lot selection and partial harvesting

The dashboard emphasizes **harvesting amounts you're willing to stay out of the
position**, because of the wash-sale rule: once you sell a lot, you should NOT
rebuy a substantially identical security for 30 days, or the loss is disallowed.

- **Harvest only lots you can afford to be out of** for the 30-day wash window
  (or plan to wait).
- **Partial-harvest a mixed position.** A position like **TSLL** is a leveraged
  ETF with **mixed lots**: some lots are at a loss and some at a gain. You can
  sell *only the losing lots* (leave the gaining lots in place) to bank the losses
  while keeping exposure to the position's upside. The per-lot table makes this
  visible: e.g. TSLL shows ST losses + LT losses alongside ST/LT gains, and
  `PORTFOLIO_BRIEF.md` records "TSLL mixed — $9,110 losses vs $4,219 gains."
- **Cleanest harvest candidates** are positions where *all* lots are losses in
  the same bucket — e.g. **CRWV** (`$5,751` short-term loss across all lots), which
  is the cleanest candidate in the brief.

---

## Reading the tool's output table

```
SYM       PRICE LOTS    ST_LOSS$    LT_LOSS$    ST_GAIN$    LT_GAIN$
CRWV       8.42    6       5,751           0           0           0
TSLL       9.78   28         961       8,149       1,180       3,039
...
TOTAL  unrealized losses -> short-term $6,755, long-term $8,182, combined $14,937
```

- `LOTS` — number of open tax lots for the symbol.
- `ST_LOSS$` / `LT_LOSS$` — sum of unrealized losses on short-term / long-term lots.
- `ST_GAIN$` / `LT_GAIN$` — sum of unrealized gains accordingly.
- The `TOTAL` line sums all unrealized losses and forwards them into each snapshot.

---

> Informational, not tax advice. Confirm treatment (wash sales, carryover, holding
> periods) with a qualified tax professional before acting.
