Skip to content

Repository files navigation

Budgeting Tools

Personal budgeting system built on Actual Budget (self-hosted) with automated transaction imports from Canadian banks and Splitwise integration.

Setup

  1. Install Docker and Node.js (v20+)
  2. Clone this repo and install dependencies:
    npm install
  3. Start Actual Budget:
    ./start.sh
  4. Open http://localhost:5006, create a budget and your accounts
  5. Copy the config template and fill in your values:
    cp config/accounts.example.yml config/accounts.yml
  6. Fill in the config — see Finding Account UUIDs below

Finding Account UUIDs

The config needs Actual Budget account UUIDs (not names). To find them:

node src/cli.js balances

This prints all accounts with their UUIDs. Copy the UUIDs into config/accounts.yml.

You need at minimum the actual.serverURL, actual.password, and actual.budgetId filled in before running this. The budget ID (Sync ID) is found in Actual Budget UI under Settings > Advanced.

Importing Transactions

All banks are imported through the CLI. Drop files into imports/ and run:

# Import everything (CSV + OFX)
node src/cli.js import ./imports/

# Preview without importing
node src/cli.js import --dry-run ./imports/

# Import a specific file
node src/cli.js import ./imports/td_chequing-2026-03.ofx

# Force a specific account (overrides auto-detection)
node src/cli.js import --account ws_chequing file.csv

Safe to re-import the same file — duplicates are skipped automatically.

File naming

The CLI resolves the target account in order: --account flag → CSV header detection → filename match.

CSV files (EQ Bank, Wealthsimple) are auto-detected by their headers — no special naming needed. OFX files need the account key (from config/accounts.yml) somewhere in the filename:

Bank Account Key Example Filename
TD Chequing td_chequing td_chequing-2026-03.ofx
TD Visa td_visa td_visa-2026-03.ofx
CIBC Chequing cibc_chequing cibc_chequing-march.ofx
Amex amex amex-2026-03.ofx
EQ Savings eq_savings (auto-detected by CSV header)
WS Chequing ws_chequing (auto-detected by CSV header)
WS Credit ws_credit (auto-detected by CSV header)

Supported formats

Bank Format Detection
TD / CIBC / Amex OFX File contains OFXHEADER or has .ofx/.qfx extension
EQ Bank CSV: Transfer date, Description, Amount, Balance Header: "Transfer date"
Wealthsimple Chequing CSV: date, transaction, description, amount, balance, currency Header: "transaction" + "balance"
Wealthsimple Credit Card CSV: transaction_date, post_date, type, details, amount, currency Header: "post_date" + "type"

Wealthsimple Activity PDFs

Wealthsimple desktop PDF exports can be converted into review CSVs for chequing and credit card activity. The converter reads PDF text/layout data only; phone screenshots and image-only PDFs are not supported because they require OCR.

Create a chequing PDF:

  1. Open Wealthsimple in a desktop browser
  2. Go to Activity
  3. Filter Account to Chequing
  4. Set the desired timeframe
  5. In Safari/macOS, choose File -> Export as PDF from the menu bar, or use your browser's print/save PDF equivalent
  6. Save the PDF into imports/

Create a credit card PDF:

  1. Open Wealthsimple in a desktop browser
  2. Open the Wealthsimple credit card page, or Activity filtered to Wealthsimple credit card
  3. Set the desired timeframe if the page offers one
  4. Export/save the page as a PDF
  5. Save the PDF into imports/

Convert the PDF to a review CSV, inspect it, then import separately:

# Chequing
node src/cli.js ws-pdf ./imports/ws-chequing.pdf --account ws_chequing --out ./imports/ws_chequing-review.csv
node src/cli.js import --dry-run ./imports/ws_chequing-review.csv

# Credit card
node src/cli.js ws-pdf ./imports/ws-credit.pdf --account ws_credit --out ./imports/ws_credit-review.csv
node src/cli.js import --dry-run ./imports/ws_credit-review.csv

The PDF converter does not connect to Actual Budget and does not import anything. It writes only the CSV columns expected by the existing Wealthsimple import parser. If rows are skipped, the CLI prints counts and up to five privacy-safe examples with reason and page position. Use --force to overwrite an existing review CSV, and --report ./imports/ws-report.json for a privacy-minimal diagnostic summary.

Investment Holdings / Net Worth Tracking

Track Wealthsimple investment account balances (RRSP, TFSA, FHSA, Non-registered) in Actual Budget using holdings report CSVs. USD positions are converted to CAD using the live Bank of Canada exchange rate.

Setup

  1. Create tracking accounts in Actual Budget for each investment account (off-budget)
  2. Find their UUIDs: node src/cli.js balances
  3. Add to config/accounts.yml:
    accounts:
      ws_rrsp: "uuid"
      ws_tfsa: "uuid"
      ws_fhsa: "uuid"
      ws_non_registered: "uuid"

Usage

  1. Download the holdings report CSV from Wealthsimple (Accounts → Download → Holdings report)
  2. Drop it in imports/
  3. Run:
    # Preview
    node src/cli.js holdings --dry-run ./imports/holdings-report-2026-04-10.csv
    
    # Update balances
    node src/cli.js holdings ./imports/holdings-report-2026-04-10.csv

Repeat monthly to track net worth over time. Each run creates a balance adjustment transaction per account to match the current market value.

Splitwise Integration

Sync shared expenses from Splitwise into Actual Budget. Handles split transactions so your budget reflects your actual share, not the full payment.

Setup

  1. Register an app at https://secure.splitwise.com/apps and get your API key
  2. Create one on-budget account in Actual Budget: "Splitwise Clearing"
  3. Create or choose a category for temporary review, e.g. "Splitwise"
  4. Find the account/category UUIDs:
    node src/cli.js balances
    node src/cli.js categories
  5. Add to config/accounts.yml:
    splitwise:
      apiKey: "your-api-key"
    accounts:
      splitwise_clearing: "uuid"
    categories:
      splitwise_review: "uuid"

Commands

# List recent Splitwise expenses
node src/cli.js splitwise expenses

# Show who owes whom
node src/cli.js splitwise balances

# Sync: match expenses to bank transactions, show proposed splits, confirm
node src/cli.js splitwise sync

# Sync with custom date range
node src/cli.js splitwise sync --since 2026-01-01

Run splitwise sync from an interactive terminal when you want it to write to Actual. Non-interactive runs only print the proposed actions and exit without applying changes.

How sync works

  1. Import bank statements first (OFX/CSV)
  2. Run splitwise sync — fetches last 60 days of Splitwise expenses
  3. For each expense, it shows what it wants to do:
    • You paid: split the bank transaction into your share + transfer to Splitwise Clearing
    • They paid: create a categorized expense in Splitwise Clearing
    • Settlement: match the bank-side settlement transaction and convert it to a transfer to/from Splitwise Clearing
  4. You confirm before any changes are applied
  5. Already-processed expenses are skipped on re-runs

If a bank transaction has not been imported yet, interactive sync can create an uncleared placeholder in the bank/card account you choose. Later statement imports can match that placeholder by account, date, and amount.

Settlement matching ignores transactions that are already Actual transfers. If you already converted a settlement manually, leave that Splitwise item skipped or mark it manually in config/splitwise-state.json after verifying it is truly handled.

Migrating from receivable/payable accounts

Older Splitwise sync runs used separate off-budget receivable/payable accounts. You can leave that history in place and use the new Splitwise Clearing workflow going forward. Legacy applied IDs are still respected, so old items should not import again, but changed or deleted legacy Splitwise expenses do not have enough saved metadata for automatic repair. Review those manually if the old off-budget balances look wrong, then close or adjust the old accounts once you are comfortable with the clearing account balance.

Remote Access and Sharing

Use Tailscale (free) to access Actual Budget from your phone or share with others.

Setup

  1. Install Tailscale on the server machine, your phone, and any other devices
  2. All devices join the same Tailscale network (tailnet)
  3. Access Actual Budget at http://<server-tailscale-hostname>:5006 from any device

Sharing with another person

  1. They create their own Tailscale account (free)
  2. Share your server device with them via Tailscale admin console
  3. Optionally use ACLs to restrict access to port 5006 only
  4. They access the same URL in their browser — same password, same budget

Actual Budget supports simultaneous access from multiple devices. Changes sync automatically.

Running CLI from a different machine

If the server runs on one laptop and you run CLI tools from another:

  1. Both machines need Tailscale
  2. Update config/accounts.yml on the CLI machine:
    actual:
      serverURL: "http://<server-tailscale-hostname>:5006"
  3. Everything else works the same — the CLI talks to the server over Tailscale

Auto-Start on Boot (macOS with Colima)

If your server is a Mac running Colima for Docker, create a LaunchAgent so Actual Budget starts automatically on login. If you also run Home Assistant, combine them into one plist so Colima only starts once.

Install

cat <<'EOF' > ~/Library/LaunchAgents/com.server.colima.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.server.colima</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>-c</string>
        <string>colima start --cpu 4 --memory 4 &amp;&amp; docker start actual_budget &amp;&amp; docker start homeassistant</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/tmp/colima-server.out.log</string>
    <key>StandardErrorPath</key>
    <string>/tmp/colima-server.err.log</string>
</dict>
</plist>
EOF

launchctl load ~/Library/LaunchAgents/com.server.colima.plist

Remove the docker start homeassistant line if you don't run Home Assistant.

Note: If colima is installed via MacPorts, use the full path (e.g., /opt/local/bin/colima). Check with which colima.

Uninstall

launchctl unload ~/Library/LaunchAgents/com.server.colima.plist
rm ~/Library/LaunchAgents/com.server.colima.plist

Check logs

cat /tmp/colima-server.out.log
cat /tmp/colima-server.err.log

Prevent macOS sleep (server machine)

The server needs to stay awake. No third-party apps needed:

# Keep awake on AC power, screen off after 1 minute
sudo pmset -c sleep 0 displaysleep 1 disksleep 0

# Revert to defaults
sudo pmset -c sleep 1 displaysleep 10 disksleep 10

Upgrading Actual Budget

Server and API versions must match. To upgrade:

  1. Check latest version at https://actualbudget.org/docs/releases/
  2. Update VERSION in start.sh
  3. Update image tag in docker-compose.yml
  4. Run npm install @actual-app/api@<same-version>
  5. Restart: ./start.sh

If you see out-of-sync-migrations errors, the versions are mismatched.

Project Structure

budgeting/
├── start.sh                    # Start/restart Actual Budget container
├── docker-compose.yml          # Alternative to start.sh (for docker compose)
├── src/
│   ├── cli.js                  # CLI entry point
│   ├── config.js               # Loads config/accounts.yml
│   ├── actual.js               # Actual Budget API wrapper
│   ├── holdings.js             # Wealthsimple holdings report parser
│   ├── parsers/
│   │   ├── index.js            # Auto-detection + filename-based account resolution
│   │   ├── ofx.js              # OFX parser (TD, CIBC, Amex)
│   │   ├── eqbank.js           # EQ Bank CSV parser
│   │   ├── wealthsimple.js     # Wealthsimple CSV parser (chequing + credit card)
│   │   └── utils.js            # Shared utilities (imported_id generation)
│   └── splitwise/
│       ├── client.js           # Splitwise API client
│       ├── sync.js             # Sync orchestrator (match, propose, apply)
│       ├── matcher.js          # Match Splitwise expenses to bank transactions
│       └── state.js            # Track processed expense IDs
├── config/
│   └── accounts.example.yml    # Config template
├── imports/                    # Drop CSV/OFX files here (gitignored)
└── data/                       # Actual Budget server data (gitignored)

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages