# Detection rules (use by hand when you can't run `scripts/find_leaks.py`)

These are the same rules the script and the HTML tool use.

## 1. Read the data
- **Columns:** find date, description, amount. If there are separate Debit/Credit (or "Paid out/Paid in", "Uscite/Entrate", "Soll/Haben") columns, the debit column is spending.
- **Sign:** if ≥30% of amounts are negative, charges are negative (typical bank export). If almost all are positive with a few negative payments, charges are positive (typical credit-card export, e.g. Amex). Ignore the other sign (deposits, refunds, card payments).
- **Numbers:** `1,234.56` (US/UK) vs `1.234,56` (EU). A semicolon-separated file almost always uses decimal commas. `(12.99)` and `12.99-` are negative. `CR` = credit, `DR` = debit.
- **Dates:** if any first number > 12 → day/month/year. If any second number > 12 → month/day/year. Otherwise: US → MDY; UK/EU → DMY. ISO `2026-09-03` is unambiguous.

## 2. Skip what isn't a leak
Transfers (Zelle, Venmo, Cash App, "transfer", "bonifico", "giroconto"), credit-card payments ("autopay", "payment thank you"), payroll/salary/deposits, refunds/reversals, rent, mortgage, loans, cash withdrawals, taxes, savings and investment transfers.

## 3. Fees (always a leak)
Description contains: fee, service charge, maintenance, overdraft, NSF, insufficient funds, returned item, foreign transaction, non-sterling, FX, international transaction, cross-border, ATM surcharge, late charge, interest charge, commissione, canone, spese, imposta di bollo, Gebühr, frais, comisión.
Group by type (ATM / foreign transaction / overdraft-NSF / interest / late / account maintenance / other). **Yearly = total in period × 365 ÷ days covered** (use at least 30 days).

## 4. Normalize merchant names
Uppercase. Remove: card network words (POS, DEBIT CARD, PURCHASE, RECURRING, ACH, VISA…), processor prefixes (`SQ *`, `TST*`, `PAYPAL *`, `SP *`), `AMZN.COM/BILL`, everything after `*`, phone numbers, card masks (`XXXX1234`), dates, store numbers and any token containing digits, `.COM`, a trailing US state code, legal suffixes (INC, LLC, LTD, SPA, SRL). Keep the first 3 words.
Then map known brands: e.g. `NETFLIX.COM 866-579-7172 CA` → Netflix; `VZWRLSS*APOCC` → Verizon; `APPLE.COM/BILL` → Apple (App Store), which can hide several subscriptions with different amounts.

## 5. Recurring detection (per merchant)
1. Sort charges by date. Remove **possible duplicates** first: same merchant, same amount (to the cent), within 3 days of each other.
2. Compute gaps between consecutive charges; take the median gap → cadence:
   - 6–8 days weekly (×52) · 13–16 biweekly (×26) · 26–35 monthly (×12) · 56–66 every 2 months (×6) · 85–96 quarterly (×4) · 175–190 semiannual (×2) · 350–380 yearly (×1)
3. At least 60% of gaps must fit that cadence (±1 day).
4. Amounts must be consistent: ≥80% within 5% of the median, **or** at most two price levels in time order (a price change), **or** for bills (phone, internet, insurance, utilities) ≥75% within 35%.
5. If the whole merchant fails (e.g. Apple or Amazon with mixed purchases), group its charges by amount (within 3%) and test each group separately.
6. A known subscription brand seen only once: guess monthly if < 40, yearly if ≥ 40, and mark "Seen once: cadence guessed". If it's under 40 and older than 45 days with no repeat, mark it inactive.

**Yearly cost = latest amount × charges per year.**

## 6. Flags
- **Price up:** latest amount > first amount by ≥ 2% and ≥ 0.50. Show `Price up X% (old → new)`. If ≥ 50%: "Intro price ended?". Extra yearly cost = (new − old) × charges per year.
- **Trial that converted?:** subscription whose first charge comes later than (start of data + cadence max + 3 days), when you have ≥ 60 days of data.
- **Stopped / already cancelled?:** no charge for more than 1.5 × cadence + 3 days before the end of the data → inactive, not counted in totals.
- **Overlap:** 2+ active items in the same category among streaming, music, cloud storage, AI tools, food-delivery memberships, dating, fitness, books/audio, memberships → "1 of N".
- **Possible duplicate:** from step 5.1. Report if the merchant is recurring or a known brand, or the amount is ≥ 25.

## 7. Totals
- Recurring = sum of yearly cost of active recurring items.
- Fees = sum of annualized fees.
- Duplicates = sum of duplicate amounts (one-time).

## 8. Sanity checks before presenting
- Did the counts make sense (most rows read, few skipped)?
- Any huge "recurring" item that's actually rent or a transfer? Remove it.
- Fewer than 60 days of data? Say monthly charges can't be confirmed and ask for more.
