Build Monarch Money

KINDAreplaces $14.99/mosaves $179.88/yrback to the verdict

0%0 of 20 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A local ledger built from file imports and, optionally, one aggregation provider: deduplicated transactions, rules-based categorisation you can correct, accounts and balances rolled into net worth, budgets that compare against real spending, and reports that reconcile to the penny.

estimated effort multi-daythe files for this build are in the project pack

RuntimePython 3.12 with SQLiteImportFile parsers per institution, plus one optional aggregation providerMoneyInteger minor units, never floating pointCategorisationOrdered rules you write, with corrections that create rulesReportsHand-drawn SVG

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Runs the importers, reconciliation and reports.

    Get it python.org or your package manager, then a virtual environment.

    Verify python3 --version prints 3.12 or higher

  2. have readyfree

    Why Deduplication, sign conventions and transfer pairing are all invisible with one account and one month, and they are the parts that make the numbers trustworthy.

    Get it Download the export files your bank already offers. Include a credit card, because its sign convention will differ.

    Verify At least two accounts and twelve months are available, including one credit card

  3. have readyfree

    Why Reconciliation needs a ground truth. Without asserted balances you cannot know whether your ledger is complete.

    Get it From the statements you already receive, one balance per account per month.

    Verify At least three asserted balances per account are recorded

  4. decidenothing for file import; roughly 15 to 40 US dollars a year for a personal aggregation service

    Why It determines cost and region and is worth deciding before building rather than after. File import works everywhere and costs nothing.

    Get it If you are in the United States, SimpleFIN Bridge at around 15 US dollars a year suits read-only personal finance, and Teller offers a limited free developer tier. In Europe, Enable Banking offers restricted production access for your own accounts. Verify current terms and pricing yourself, because this market changes.

    Verify The decision, the provider, its cost and its data access are written down

  5. decidefree with a drive you own

    Why This database is a complete record of your finances. It deserves a better backup story than a file in a synced folder.

    Get it An encrypted archive to a destination you control. Decide before you import a year of history.

    Verify An encrypted backup has been created and restored once

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- accounts(id, name, institution, kind, currency, opened_at, closed_at) · kind is current, savings, credit, investment or asset
- transactions(id, account_id, posted_on, amount_minor, description, raw_description, external_id, import_id, category_id, transfer_pair_id, notes)
- imports(id, account_id, source, file_hash, imported_at, row_count, duplicate_count)
- rules(id, priority, match_field, pattern, category_id, enabled, created_from_correction)
- categories(id, parent_id, name, kind) · kind is expense, income or transfer
- balances(account_id, on_date, balance_minor) · asserted from statements, used to reconcile
- budgets(id, category_id, period, amount_minor)
- A transfer between your own accounts is not income and not spending. Pairing them is what makes every total believable.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
DB_PATHrequired./data/ledger.dbThe ledger. A complete record of your finances, so treat the file and its backups accordingly.
BASE_CURRENCYrequiredGBPReporting currency. Multi-currency accounts convert into it for totals.
TRANSFER_WINDOW_DAYSrequired4How far apart two sides of a transfer may be before they stop being proposed as a pair.
AGGREGATORrequirednoneWhich aggregation provider, if any. The value none means file import only, which is a legitimate final state.
AGGREGATOR_TOKENsecretoptionalreplace-with-provider-tokenProvider credential, stored outside the database and never committed.
STALE_CONNECTION_HOURSoptional48How long without a refresh before an aggregation connection is reported as stale.

The build, in order

  1. Importing without duplicating

    You can import the same file twice and end up with one copy of each transaction.

    1. Your own, not a sample. Real exports have inconsistent date formats and stray encoding.

      Files src/import/institution.py

    2. Everywhere. Formatting happens at display and nowhere else.

      Files src/money.py

    3. Date, amount, normalised description and account. Record the file hash so a re-import is detected.

      Files src/dedupe.py

    done when · tick each as it passes
    watch out
    • Storing money as a floating point number introduces errors that accumulate across thousands of rows and make a report disagree with a statement by a few pence, which destroys trust in the whole thing. Use integer minor units everywhere and format only for display.
    • Sign conventions differ between institutions and between account types: some exports show a credit card payment as positive and some as negative. Normalise on import per institution and record the convention, or every credit card total will be inverted.
  2. Reconciling to a statement

    Your computed balance matches the bank's, or you can see exactly where it does not.

    1. From statements, per account per date. These are the ground truth you reconcile against.

      Files src/balances.py

    2. Compute running balance and find the first date where it diverges. That narrows the search.

      Files src/reconcile.py

    3. Show the discrepancy. Write a test that deletes a transaction and asserts the gap appears.

      Files tests/test_reconcile.py

    done when · tick each as it passes
    watch out
    • A tool that silently inserts a balancing adjustment to make the numbers agree hides the missing transactions that caused the gap, and you will trust a total that is wrong. Show the discrepancy, never correct it automatically.
  3. Transfers, which are neither income nor spending

    Money moved between your own accounts stops appearing as both.

    1. Same amount, opposite sign, two of your accounts, within a few days. Propose rather than assume.

      Files src/transfers.py

    2. A wrong automatic pairing is worse than an unpaired transfer, because it hides real spending.

      Files src/transfers.py

    3. Confirmed transfers leave income and expense entirely. Verify totals change by the expected amount.

      Files src/reports.py

    done when · tick each as it passes
    watch out
    • Unpaired transfers inflate both income and spending by the same amount, which makes every category total and every budget wrong while the net figure still looks fine. This is the single most common reason a personal finance tool's numbers are quietly untrustworthy.
  4. Categories you can actually fix

    Spending is categorised by rules you wrote, and correcting one teaches the rule.

    1. First match wins, with the matching rule recorded on the transaction so a wrong category is traceable.

      Files src/rules.py

    2. Offer, do not assume. A one-off correction and a standing rule are different intentions.

      Files src/correct.py

    3. With the affected count shown before it runs.

      Files src/recategorise.py

    done when · tick each as it passes
    watch out
    • Silently recategorising history whenever a rule changes rewrites last year's totals, so any comparison over time becomes meaningless. Apply new rules going forward and make retroactive recategorisation deliberate, with the affected count shown first.
  5. Budgets and net worth

    You can see spending against a plan and what you own against what you owe.

    1. Category spending with transfers excluded and uncategorised shown separately. Verify against a hand calculation.

      Files src/budgets.py

    2. A property or a loan is not in any export and is most of net worth for many people.

      Files src/networth.py

    3. No interpolation. A missing month should be visible, since it means the data is incomplete.

      Files src/charts.py

    done when · tick each as it passes
    watch out
    • Interpolating a net worth line across a period with no data draws a smooth curve through a gap, which looks like information and is invention. Leave gaps visible, because a missing month is something you want to notice.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more personal finance.