Skip to main content
Glama
anikam13

finledger

by anikam13
README.md
# FinLedger

A local financial ledger with three read surfaces, plus an MCP server so an agent can query them:

- **expenses**: spending accounts, reconciled against peer reimbursements
- **investments**: brokerage balances, holdings, and activity
- **income**: payroll (gross, withholdings, take-home)

Expenses and investments share a store and nothing else. An account carries an explicit `domain`, and every expense query joins through it, so a share purchase cannot reach the reimbursement matcher.

The matcher is rules-first. It abstains when it is not sure. A human or agent confirms before a match shrinks net spend.

Real transactions, paystubs, and API keys stay on your machine (`~/.finledger/ledger.db`, `.env`). This repository ships code and a **synthetic** eval set only.

## Install

Python 3.10+. The ledger, matcher, MCP loop, and eval harness are standard library. `pypdf` is optional and only needed to import paystub PDFs.

```bash
pip install -e .
```

## MCP server

```bash
python3 -m finledger.mcp_server --db ~/.finledger/ledger.db
```

18 tools, namespaced `expenses_*`, `investments_*`, and `income_*`, plus `savings_rate`, `accounts`, `sync`, and `reconcile`.

Cursor example (`~/.cursor/mcp.json`):

```json
{
  "mcpServers": {
    "finledger": {
      "command": "python3",
      "args": ["-m", "finledger.mcp_server"]
    }
  }
}
```

The default ledger path is `~/.finledger/ledger.db`.

## CLI

```bash
finledger sync
finledger reconcile
finledger review
finledger serve
```

`finledger serve` opens a local desk on `:8770`. On macOS you can also double-click `scripts/Expense Desk.command`.

Optional live sources (put credentials in `.env`, never commit it):

| Source | How |
|---|---|
| shim.finance (card + Venmo) | `SHIM_API_KEY` |
| SnapTrade (checking + brokerages) | `SNAPTRADE_CLIENT_ID`, `SNAPTRADE_CONSUMER_KEY`, `SNAPTRADE_USER_ID`, `SNAPTRADE_RECOVER` |
| Card CSV backfill | `finledger sync --card-csv PATH --card-account-id ID` |
| Paystub PDF | `python3 scripts/paystub_import.py statement.pdf` (needs `pypdf`) |

Copy `.env.example` to `.env`.

## Public eval

The published gold set is synthetic. It is generated by `scripts/make_sample_data.py` and lives in `data-sample/`. Every transaction row is marked `synthetic: true`. It is a coverage set for matcher behaviours (clean match, fan-in, bundling, peer-outbound parent, merchant credits, weak notes, cross-source twins). It is not anonymized real data.

```bash
python3 scripts/eval_public.py
# equivalent:
python3 -m reconcile.eval --matcher rules_v0
```

### Numbers you can reproduce from this repo

Measured on `data-sample/` with `rules_v0` (window ±90 days, auto-match threshold 0.75):

| Metric | Value |
|---|---|
| evaluable examples | 22 (2 `unsure` labels skipped) |
| exact-set accuracy | 72.7% (16/22) |
| edge P / R / F1 | 81.2% / 61.9% / 70.3% |
| auto-match precision | 85.7% (n=14; 12 tp, 2 fp) |
| auto-match recall of gold matches | 66.7% |

Re-run `python3 scripts/eval_public.py` to refresh these. Two remaining false positives on the sample are real matcher weaknesses, including the emoji recall-lift rule overreaching.

### What is not in this repo

A private gold set of about 92 labeled examples was used during development. It names real people and real purchases and is not published. Do not treat the sample numbers as a reproduction of that set. Different corpus, different size, different scores.

This repository does not claim 100% auto-match precision.

## Matcher

See [`reconcile/README.md`](reconcile/README.md). Short version:

| Name | Behavior |
|---|---|
| `baseline` | Nearest charge within ±$1 and a date window. Empty if none. |
| `rules_v0` | Amount fit + date + note/merchant overlap, with vetoes. Auto-match if confidence >= 0.75; else abstain. |
| `rules_v0_llm` | Same as `rules_v0`, then optional Groq call on review residue (`GROQ_API_KEY`). |

Label the sample corpus with `python3 labeling/app.py` (defaults to `data-sample/`).

## License

MIT

TDQS

B3/5.0

Scored across 18 tools

Disambiguation5/5

Each tool has a clearly defined role within its domain: expenses, investments, income, savings, or general ledger management. The distinct prefixes (expenses_, investments_, income_) prevent cross-domain confusion, and within domains, tools like spend vs. outstanding vs. runway vs. review_queue are semantically distinct. Even the less prefixed tools (reconcile, sync, accounts) are obviously separate from the financial queries.

Naming Consistency4/5

The dominant pattern is domain-prefix + descriptive noun (e.g., expenses_spend, investments_balances, income_summary), which is highly consistent. However, a few tools like reconcile, sync, accounts, and savings_rate do not follow the prefix scheme, creating minor inconsistency. The syntax is all snake_case with no verb conjugations, so the deviation is not jarring.

Tool Count4/5

Eighteen tools is on the higher end of typical MCP servers, but the breadth of the domain—expenses, investments, income, savings, notes, accounts, sync—justifies the count. Each tool addresses a specific financial question or action, and none feel redundant. The size is slightly above the ideal 3-15 range but well within the acceptable limit for a comprehensive financial ledger service.

Completeness4/5

The tool surface covers the core lifecycle of financial data aggregation: reading balances, transactions, and summaries, plus actions like reconciliation and decision resolution. There are minor gaps, such as no direct way to create or edit transactions or notes, but these are likely handled externally or intentionally read-only. The inclusion of sync and review queues ensures operational completeness for the stated purpose.

Maintenance

ActivityMaintained
ResponsivenessNo issues