# `zfin audit`

Two modes in one command: a portfolio hygiene check, and reconciliation
against a brokerage export.

```
Usage: zfin audit [opts]
```

**Flagless** runs the hygiene check -- stale manual prices,
accounts overdue for update, stale tax-loss-harvested figures,
unhandled stock splits, and auto-discovered brokerage-file candidates.
**With brokerage flags**, it reconciles your portfolio against the
export (treating the brokerage as source of truth) and reports
discrepancies.

## Options

| Flag               | Effect                                                                     |
|--------------------|----------------------------------------------------------------------------|
| `--verbose`        | Show full reconciliation output even when clean.                           |
| `--stale-days <N>` | Manual-price staleness threshold (default 3).                              |
| `--fidelity <CSV>` | Fidelity positions CSV ("All accounts" -> Positions tab -> Download).      |
| `--schwab <CSV>`   | Schwab per-account positions CSV.                                          |
| `--schwab-summary` | Schwab account summary: paste from the summary page to stdin, then Ctrl-D. |

Reconciliation matches export accounts to yours via `institution::` and
`account_number::` in [`accounts.srf`](../config/accounts-srf.md); an
unmatched account is reported as "unmapped."

The hygiene check also flags newly-appeared lots worth at least
$10,000 in a **Large new lots - confirm source** section, so you can
confirm whether each is a real contribution or an unrecorded transfer.
The cutoff is per account -- raise or lower it on an account's record
via [`audit_large_lot_threshold`](../config/accounts-srf.md#audit_large_lot_threshold)
in `accounts.srf` (e.g. to silence a noisy ESPP account).

It also lists held symbols that had a stock split *after* a lot's
purchase date but haven't opted into automatic split adjustment, in an
**Unhandled stock splits** section. Each needs a
[`splits_current_through`](../config/metadata-srf.md#stock-split-adjustment)
date on its `metadata.srf` row -- without it, that holding's shares (and
every value derived from them) are misstated across the split.

Accounts carrying a hand-declared
[`harvested`](../config/accounts-srf.md#harvested-and-harvested_date)
figure get a **Stale harvested figures** section once the figure is more
than 90 days old, or if it has no `harvested_date` at all. The section
is omitted entirely for portfolios where no account declares one. Past
12 months the annotation itself retires, and the nag says
`no longer displayed` so it can't vanish without explanation.

Accounts declaring
[`tax_mix_*`](../config/accounts-srf.md#mixed-tax-treatment) carve-outs
get the same treatment in a separate **Stale tax-mix figures** section,
on the same 90-day clock. One difference worth knowing: a stale tax mix
**keeps applying**. It feeds real breakdown totals, so retiring it would
silently move your pre-tax vs post-tax picture. This nag is the only
consequence of letting it age.

## Example (hygiene check)

```bash
ZFIN_HOME=examples/pre-retirement-both zfin audit
```

```
  Portfolio hygiene

  Stale manual prices (>3 days - --stale-days to configure)
    (none)

  Accounts overdue for update (weekly default - set update_cadence in accounts.srf)
    Sam 401k                         weekly    no update history found
    Joint taxable                    weekly    no update history found
```

> Brokerage exports contain real account numbers and holdings. Keep
> them out of any git repo and delete them after reconciling.

## See also

- [Audit against your brokerage](../../guides/audit-against-brokerage.md) -- the workflow.
- [`import`](import.md) -- build a portfolio file *from* an export instead.
- [`accounts.srf` reference](../config/accounts-srf.md) -- `update_cadence`, `institution`, `account_number`.
- [`metadata.srf` reference](../config/metadata-srf.md#stock-split-adjustment) -- `splits_current_through` for the unhandled-splits check.

---

[CLI command reference](index.md)
