AJ973 downloadsLog spending as tags in your daily notes, then turn them into pace-aware budget, holiday, and savings dashboards. Capture via Apple Shortcuts or a quick-add modal.
Log spending as tags in your daily notes. Get budgets, trips, savings goals and forecasts back.
Your markdown notes stay the source of truth. There is no database and no sync — uninstall the plugin tomorrow and you still have every transaction, in plain text, in notes you can grep, link and edit by hand.
![]()
## Finance
- [ ] #log/spending 16.20
- $12.00 #log/spending/food/restaurants
- Nobu
- $4.20 #log/spending/food/snacks
That is the whole data format. Everything below is built from it.
Runs on desktop and mobile (Obsidian 1.0+).
Network use is opt-in and named. Three features can reach the internet, and none of them does until you ask:
No telemetry, no bank connections, nothing phoning home.
MIT licensed — see LICENSE.
Recent changes: CHANGELOG.md.
Support the project: Buy Me a Coffee
On a fresh install the setup wizard opens automatically (or run the Set up finance notes command / the button at the top of settings any time — it never overwrites existing notes). It asks where your daily notes live, where finance notes should be stored, your currency, and what to name the recurring payments note (changeable later in settings) — then creates the starter notes from templates embedded in the plugin:
![]()
💸 Budgets.md — the budget table📊 Finance Dashboard.md — weekly/monthly dashboards, net worth, forecast, a sample query🔁 Recurring Payments.md — the bill management page🎯 Goals.md — the goals overview with one-tap contributionsThe manual path, if you prefer:
Install & enable. Copy this plugin to .obsidian/plugins/finance-tracker/,
then Settings → Community plugins → enable Finance Tracker. (On mobile, turn
on community plugins first.)
Set two things in the plugin's settings tab (Settings → Finance Tracker → Capture):
AUD, USD).Journal/Periodics/1. Daily; the plugin routes captures to <folder>/YYYY-MM-DD.md
(or <folder>/YYYY/MM/YYYY-MM-DD.md if you already use that structure).![]()
Open the hub. Click the wallet ribbon icon (or run Open finance hub); the coin icon opens the smaller Daily budget sidebar. This creates, on first run:
Utility/Budgets/💸 Budgets.md — a starter budget table you can edit.Utility/Finance/Inbox/ — the capture folder Shortcuts write to.Log your first transaction. Run command Quick add transaction, type
12 coffee snacks, press Enter. Today's note is created with a ## Finance
section and the entry; the panel and status bar update.
Set your budgets. Open 💸 Budgets.md and edit the table rows to your own
categories, limits and periods. Keep an all row for the overall bar + Left/Day.
Add a dashboard (optional). Put a ```finance-dashboard block (see
Dashboards) in any note for charts and pace bars.
Phone capture (optional but the point). Pick the method that matches your sync
(see How logging works): on Obsidian Sync, use a Shortcut
that opens obsidian://finance-capture?… (no special storage needed); on an iCloud
Drive / Mac-local vault you can instead drop capture files into your configured
inbox folder. Step-by-step Shortcut recipes are in How logging works below.
Nothing else is required — daily notes, the budget note, and the inbox folder are all created for you on demand.
Command: Open finance hub · ribbon wallet icon · Hub in the Daily budget sidebar
One view for everything beyond logging, in tabs:
| Tab | What's there |
|---|---|
| Today | The Daily budget panel (today, this period, left per day, budgets, bills due), goal and trip prompts, and your shares' move today. |
| Inbox | Bills that are overdue or due soon (with Mark paid), payments that look recurring but belong to no bill, every uncategorised entry grouped by merchant, and captures that failed. The tab's badge counts all of them. |
| Budgets | This week or month (whichever periods your budgets use): budget bars, uncategorised callout, daily spend and the category donut. |
| Bills | The full bills list: overdue, due soon, later, the payment calendar and suggestions. |
| Goals & trips | Every goal with Contribute, New goal, trip mode, and each trip's dates. |
| Portfolio | Holdings with sparklines, dividends to log, your watchlist, and a page for each ticker. |
| Reviews | A week, month, quarter or year with every dashboard section. Step back and forward with ‹ ›; Copy as text copies a frozen review to paste into a note. |
Every tab has its own command (Open finance hub: bills, and so on), so any of them can have a hotkey. The hub remembers its tab. It sits alongside the Daily budget sidebar rather than replacing it, and every code block keeps working inside notes.
Everything stays current. When a daily note, budget, goal, bill or the portfolio note changes — typed here, synced from your phone, or written by the plugin — the hub, the sidebar and every finance block open in a note refresh once, half a second later. A block you're typing into waits until you click away.
Every transaction is a bullet under a ## Finance heading in a daily note
(…/YYYY/MM/YYYY-MM-DD.md). The plugin maintains the checkbox total automatically:
## Finance
- [ ] #log/spending 16.20
- $12.00 #log/spending/food/restaurants
- Nobu
- $4.20 #log/spending/food/snacks
The same thing rendered in a real daily note:
![]()
You rarely type this by hand. Six ways to log:
| Method | Opens Obsidian? | Use it for |
|---|---|---|
obsidian://finance-capture URL |
yes, briefly | phone capture with any sync (incl. Obsidian Sync) |
Batched queue (?lines=) |
once per flush | high-volume phone capture — queue all day, flush once |
| GitHub gist | no | silent phone capture on any sync; the only method that works from an Apple Watch |
| Capture inbox file | no | Files-writable vaults only (iCloud Drive / Mac-local) |
| Quick add modal (command or status bar) | — | logging while you're in Obsidian |
| Type the bullet yourself | — | edge cases; the total self-heals on note open |
The first four are capture methods, and each has its own toggle in Settings → Capture methods. They are designed to run at the same time — see Running several methods at once for how the plugin stops that turning into double-logged transactions. Quick add and hand-typed bullets always work and have no toggle.
Which phone method? It depends on how the vault syncs, and on how much you spend:
A Shortcut opens obsidian://finance-capture?amount=12.5&merchant=Coles&category=food/groceries&source=manual.
The plugin logs it to today's note. This briefly foregrounds Obsidian but needs no filesystem access.
The URL method also works as an automation — no Save File step needed on an Obsidian Sync vault.
This is the actual automation used against an ANZ Plus card (source=anz): a Transaction
trigger set to Run Immediately —
![]()
— followed by URL-encoding the merchant, building the obsidian://finance-capture URL, and opening it:
![]()
Shortcut D — "Log + confirm in Obsidian": build an Open URL action —
obsidian://finance-capture?vault=<vault>&amount=12.5&category=food/groceries&merchant=Coles&source=manual —
and run it from the Home Screen, Lock Screen, or Action Button. This is the only phone method that
works on an Obsidian Sync vault; use it whenever you want the dashboard to update immediately.
The cost of the URL method is that every tap-to-pay yanks you into Obsidian. The batched queue removes that: the automation appends each transaction to a list on the phone and does nothing else, and a second Shortcut flushes the whole list in one app switch.
Capture a queue of lines into lines=, newline-separated, in the same one-line format the
capture inbox uses:
obsidian://finance-capture?lines=amount%3D12.50%20%7C%20merchant%3DColes%20%7C%20date%3D2026-07-30%0Aamount%3D4.20%20%7C%20merchant%3DBoost%20Juice%20%7C%20date%3D2026-07-30
Shortcut E — "Queue spend" (automation): Shortcuts → Automation → Transaction
trigger, Run Immediately. Build one Text line —
amount=[Transaction Amount] | merchant=[Transaction Merchant] | date=[Current Date · yyyy-MM-dd] | source=anz | id=[Current Date · yyyy-MM-dd'T'HHmmss]-[Random 1000–9999]
— then Add to Variable → FinanceQueue, or append it to a Data Jar
value if you want the queue to survive between Shortcut runs (it does not otherwise).
Shortcut F — "Flush spend queue": Get Value from Data Jar → Combine Text with
New Lines → URL Encode → Text = obsidian://finance-capture?lines=[URL Encoded Text]
→ Open URLs → Delete Value from Data Jar. Put it on an Automation for a time of day,
or on the Home Screen. Obsidian opens once, logs everything, and reports
logged 7, skipped 1 duplicate.
Each line is parsed independently, so one malformed line never costs you the rest of the
batch — bad lines land in Inbox/_failed/ with the reason, exactly like a bad inbox file.
Including id= is worth the extra action: it means a re-run of the flush can never
double-log.
The only method that captures without opening Obsidian and still works on an Obsidian
Sync vault. The phone appends a line to a private gist; the plugin polls the gist with
Obsidian's own network layer (which works on iOS and Android), logs whatever is waiting,
then clears it. It is also the only method that works from an Apple Watch, where
obsidian:// cannot open anything.
Setup:
finance-capture.txt and any placeholder content (a # queue comment line is ideal — comment
lines are ignored). Copy the gist ID from its URL: the long hex string after your username.On the token. It is stored in this vault's
data.jsonin plain text, which is how every Obsidian plugin stores settings. A Gists-only token can read and write your gists and nothing else, but treat it like a password: don't commitdata.json, and revoke the token on GitHub if the vault is ever shared or synced somewhere you don't control.
Shortcut G — "Log spend (silent)": Get Contents of URL →
https://api.github.com/gists/<gist-id>, Method GET, Headers
Authorization: Bearer <token> → Get Dictionary Value files → finance-capture.txt →
content. Then Text = the existing content, a new line, and your capture line. Finally
Get Contents of URL → same URL, Method PATCH, same header, Request Body → JSON:
{"files": {"finance-capture.txt": {"content": "[Text]"}}}.
That is a read-modify-write, so keep the token in the Shortcut (never in the vault) and don't run two flushes at the same second. The plugin's own drain is safe against this: it re-reads the gist before clearing and preserves anything that arrived mid-sync, and if the file was rewritten underneath it, it leaves the gist alone and says so rather than destroying captures.
The trade-off versus the URL methods: a gist write needs a connection. Shortcuts will fail
the action offline, where opening obsidian:// would still have worked. If you're often
offline, add an Otherwise branch to the Shortcut that falls back to the URL method.
A Shortcut (or Mac script, or the bank-CSV reconcile) drops a one-line file into the
inbox folder (default Utility/Finance/Inbox/); the plugin drains it into the right daily
note on arrival and on launch. Capture line format — one line, key=value pairs separated by |:
amount=12 | cat=food/restaurants | merchant=Nobu | date=2026-06-10 | cur=AUD | source=apple-pay
| Key | Meaning |
|---|---|
amount (amt, total) |
the amount to log — the only required key |
cat (category) |
category path, e.g. food/groceries. Omit to log as uncategorized and categorise later. |
merchant (payee, name) |
shown under the entry; also used to auto-guess the category from the merchant map |
date |
YYYY-MM-DD; defaults to today. Set it for retroactive logging — the plugin routes to that day's note. |
cur (currency) |
currency code; defaults to your vault default |
origamt / origcur |
foreign amount + currency (travel); the holiday dashboard converts it |
source |
free text, e.g. apple-pay, wise, manual |
id (wiseid, ref) |
external id for de-duplication (used by Wise sync / CSV reconcile) |
Bad files are moved to Inbox/_failed/ with the error, never dropped.
Shortcut A — "Log Spend" (manual, everyday) — put it on the Home Screen, Lock Screen, Action Button, and "Hey Siri, log spend":
amount=[Provided Input] | cat=[Menu Result] | merchant=[Text] | date=[Current Date · yyyy-MM-dd] | source=manual
(leave cat= empty if "Skip" was chosen).[Current Date · yyyy-MM-dd'T'HHmmss]-[Random 4 chars].txt.Shortcut B — "Log Apple Pay" (automation, the important one) — for a card you don't sync via
Wise: Shortcuts → Automation → Transaction trigger, filtered to that card, Run Immediately.
Build the same Text line from Transaction Amount / Transaction Merchant, leave cat= empty
(it auto-guesses from the merchant map or lands as uncategorized), add source=anz, then Save
File to the inbox as above. Every tap-to-pay now auto-logs with zero interaction. Don't also make
an Apple Pay automation for a card that's covered by Wise sync (below) — that would double-log it.
Shortcut C — "Log Spend (pick date)" — same as Shortcut A plus an Ask for Input → Date step
feeding date=, for backfilling a day you forgot.
These two are set up for the author's own accounts. The pattern generalises to any bank that exports a CSV or exposes an API, but nothing here is required — the URL shortcut and quick-add above cover everything.
Wise personal API tokens support balance statements, so a scheduled Shortcut can pull every Wise transaction — card, online, transfers — including the real exchange rate for foreign spends. Keep the token in the Shortcut, never in the vault (generate it in Wise → Settings → API tokens; rotate or revoke it there if needed).
GET https://api.wise.com/v1/profiles, header Authorization: Bearer <token> → profileId.GET /v4/profiles/{profileId}/balances?types=STANDARD → balanceId per currency.GET /v1/profiles/{profileId}/balance-statements/{balanceId}/statement.json?currency=AUD&intervalStart=<lastSync>&intervalEnd=<now>&type=COMPACT.amount=<spend> | merchant=<details> | date=<YYYY-MM-DD> | cur=AUD | source=wise | id=<referenceNumber> (add origamt=/origcur= for foreign spends).lastSync and schedule the Shortcut nightly. The id= field means re-runs never double-log.The Apple Pay automation only sees in-person taps — not online, direct debits, or BPAY. Monthly, export the bank CSV and run Reconcile a bank CSV; unmatched charges can be sent to the capture inbox. Matching is by date+amount, so already-logged taps aren't duplicated.
Every capture method has its own toggle in Settings → Capture methods, and they are meant to be combined — a bank automation feeding the queue, a gist for the Watch, quick add when you're at the desk. The risk in combining them is the same purchase arriving twice.
How duplicates are detected. The plugin keeps a rolling record of what each method has
captured (the last 400, in data.json). A new capture is compared against it on amount +
date + merchant, and is treated as a duplicate only when the earlier copy came in by a
different method or from a different source=. That distinction is the whole point:
A missing merchant on one side still matches (bank feeds often carry a merchant the manual capture lacks, and vice versa), and the duplicate window — 1 day by default — allows for a feed that settles the day after the tap.
| Setting | Effect |
|---|---|
| Skip the second one (default) | The duplicate is not logged. A notice names the method that got there first. |
| Log it, but tell me | Both are logged, with a notice. Use this while you're still tuning which methods you want. |
| Log everything | Detection off. |
Quick add is exempt: it does its own "already logged today" check before you submit, so pressing the button always logs. A deliberate human action is never silently dropped.
Finding overlap. Settings → Capture methods shows a live Overlap check, and the Check capture methods for overlap command opens the full report: which pairs of methods have been capturing the same transactions, how often, and the most recent example. If one pair dominates, you're maintaining a method you could switch off. This is measured from what actually happened rather than guessed from what each Shortcut is supposed to cover.
Nothing in the plugin is Apple-specific — the capture formats are a URL and an HTTP request, and Obsidian's mobile app handles both identically on Android. Only the automation app changes:
| iOS | Android equivalent |
|---|---|
| Shortcuts (manual run) | HTTP Shortcuts (gist), or any launcher shortcut to an obsidian:// URL |
| Shortcuts Automation (Transaction trigger) | Tasker or MacroDroid, triggered on a notification from your banking app |
| Data Jar (queue storage) | Tasker variables or a local file |
| Action Button / Back Tap | home-screen widget, or Tasker's Quick Settings tile |
Two Android-specific notes:
Transaction trigger. The usual substitute is a notification
trigger on your banking app, parsing the amount and merchant out of the notification text
with a regex. That is inherently bank-specific and breaks when the bank rewords its
notifications, so treat it as best-effort and keep the monthly CSV reconcile as the backstop.Untested. The author develops on iOS and has no Android device, so the Android column above is reasoning from each app's documented capabilities rather than something that has been run. The plugin side is identical either way — if you use it on Android, corrections are welcome.
At current rates, Buy Me a Coffee needs to be clicked roughly 150 times before it becomes Buy Me A Second-Hand Pixel, at which point this section gets promoted from "reasoned about" to "actually tested". The finance tracker has, of course, already logged this as
#log/spending/hardware/researchand flagged it as over budget.
Capture is instant and dumb; categorisation is a quick review later. Anything logged without a category, or guessed wrong, waits in the hub's Inbox — see Categorising.
Command Quick add transaction (bind a hotkey) or click the status bar. One field, natural language, live preview:
12 nobu restaurants $8 #transport Lime 4.50 coffee snacks @yesterday
First number = amount · a cat/sub, #tag or known category word = category ·
@date (ISO, yesterday, weekday, or anything the Natural Language Dates plugin parses) ·
the rest = merchant.
![]()
Foreign currency, without storing a rate. Put a currency code next to the amount you were charged, then the amount it actually cost in your home currency. Both are recorded; only the home amount counts toward budgets and totals.
58 USD : 83.64 obsidian sync #subscriptions/yearly/obsidian-sync
writes:
- $58.00 USD : $83.64 AUD #log/spending/subscriptions/yearly/obsidian-sync
- obsidian sync
The code can go on either side of the number and the separator is optional, so
58usd 83.64, USD58 83.64 and $58 USD = $83.64 AUD all parse the same. The
preview shows the implied rate (@ 1.4421) so you can see you typed it right —
nothing stores it, and nothing converts anything for you. If you give only the
foreign amount, quick add says so rather than guessing a rate.
Only real currency codes count, so 58 oak table is still $58 at "oak table".
Duplicate guard. If the same amount and merchant is already logged on that date, the modal says so before you add a second one — a double-tapped Shortcut is far more common than genuinely buying the same thing twice in a day.
Autocomplete — as you type, suggestions appear for categories,
merchants, and people (owed=…), all derived from what you have
actually logged plus your budget table — there is no category list to maintain.
Arrow keys cycle, Tab or Enter accepts the highlighted suggestion, Esc
dismisses. Once no suggestions are showing, Enter submits the entry — so
typing a full line is just Enter-Enter-Enter through each token, finishing on
a plain Enter with the popup closed. Accepting a known merchant also fills in
its remembered category.
Date from the open note — with the Quick add uses the open daily note's date setting on (off by default), opening quick add while a daily note is active pre-fills that note's date, so backfilling an old day is frictionless.
| Kind | Tag | Example bullet |
|---|---|---|
| Spending | #log/spending/<category>[/<sub>] |
- $12 #log/spending/food/restaurants |
| Income | #log/income/<key> |
- $2400 #log/income/salary |
| Dividend | #log/income/dividend/<ticker> |
- $8.20 #log/income/dividend/vas-ax |
| Savings contribution | #log/income/<goalKey> |
- $500 #log/income/japanmidyear |
| Savings withdrawal | #log/spending/goal/<goalKey>/<category> |
- $90 #log/spending/goal/rainy-day/medical |
| Holiday spend | #log/spending/<year>/<key>/<category> |
- $40 #log/spending/26/japanmidyear/food |
| Recurring bill | #log/spending/subscriptions/<cadence>/<name> |
- $12.99 #log/spending/subscriptions/monthly/spotify |
| Owed share (child line) | #log/owed/<person> |
- owes: Sam $8.00 #log/owed/sam |
| Balance snapshot | #log/balance/<account> |
- $5,230.00 #log/balance/anz-plus |
The amount is a $-prefixed number on the bullet; a child line is the merchant, a
second child is a note. Categories are slash paths (food/restaurants).
Because every entry is just a tag, Obsidian's own tag pane doubles as a category browser — nested paths become a real tree, with a live count per level:
![]()
Command: Open finance hub: inbox · the Inbox tab · Open inbox in the sidebar
The inbox holds every uncategorised entry, whatever its date, grouped by merchant so twelve rows of "SQ * Rode Fresh" are one decision. Each group suggests a category and says where the suggestion came from (a rule, or how the same shop was filed before). Apply to N files them all; the category picker has search, recent categories and + New. Captures that failed to parse are listed too, with Retry and Dismiss, and bills that are due or look recurring sit at the top.
Suggestions come from, in order: a merchant rule, a rule matching a chunk of the merchant ("woolworths" covers "Woolworths/cnr Brisbane H"), the same shop's past entries, then a shop with the same root. Quick add's preview shows the guess and why. Bill payments are never learned as a shop's everyday category.
Merchant rules live in plugin settings, not a note. Add or edit them in
Settings → Categories (there's a box to test what a descriptor would be filed as),
or tick Remember this merchant when you categorise something. Switching off
Learn categories from past notes leaves only the rules. A Merchant Map.md
note from an older version is read once and folded in; your own rules win, and
Import merchant map note re-runs it.
Editing an entry (tap it in the inbox, the sidebar or a dashboard) changes the amount, category, merchant or date; a new date moves it to that day's note. It offers to apply the same category to other uncategorised entries from the shop.
Rename or split a category rewrites a category everywhere it appears — daily
notes, the budgets table and merchant rules — or splits it by merchant (bare
transport into public transport, rideshare and scooter). It previews every
change first, touches only notes that still match, and names File recovery and
Sync history as the undo.
Budgets are a markdown table in Utility/Budgets/💸 Budgets.md:
| Name | Category | Limit | Period | Currency |
| ------------ | ---------- | ----: | ------ | -------- |
| Groceries | groceries | 140 | week | AUD |
| Shopping | shopping | 200 | month | AUD |
| All Spending | all | 250 | week | AUD |
A real budgets note, with the free-form Notes section people tend to add below the table:
![]()
food) or a path (food/restaurants); all
budgets everything.day, week, fortnight, month, bimonth, quarter, year.
Limits scale by calendar days when shown over a different range (a weekly $140
shows as ~$600 on a monthly dashboard).$/day for the days left.all budget also drives the Left / Day card and the daily-spend trend line.- $40 #log/spending/26/japan/shopping is a withdrawal from that trip's
savings goal: it shows on the holiday dashboard and reduces the goal, but is
excluded from regular budgets, dashboards, the sidebar totals, and the
forecast. Use type: all in a finance-query block for a report that
includes trip entries.Command: Open finance hub: bills · Log due bills · Insert a finance block → Recurring payments
Every bill is a note in Utility/Budgets/Bills/, with everything about it in
its properties, so a bill is edited like any other note, including on a phone:
---
bill_id: spotify
bill_name: Spotify
aliases: [Spotify P0123ABC]
cadence: monthly
due_rule: day-of-month: 14
amount: 12.99
amount_model: fixed
reminder_days: 3
active: true
auto_log: false
next_amount:
change_date:
end_date:
payments_left:
next_due:
skipped: []
currency: AUD
---
Payments are the entries tagged #log/spending/subscriptions/<cadence>/<bill_id>,
plus any entry whose merchant matches an alias. When a capture from a known bill
arrives, it's filed to that bill automatically (with an Undo on the notice).
Due dates follow the bill's rule: after the last payment (the default), a
day of the month, or an nth weekday (nth-weekday: 1 monday, -1 friday
for the last). Schedules don't drift: paying a few days late doesn't push every
later due date out. next_due overrides one cycle and clears itself once paid.
The Bills tab (and the finance-recurring block) groups bills as Overdue,
Due soon (each bill's own reminder_days), Later this month and Later.
Each row has Mark paid, and ⋯ for Push a week, Skip this cycle, Edit, Merge
into another bill, Open note, Pause and End now. A skip is recorded on the bill;
no $0 line is written. Above the list: cost per month and year, and due in the
next 30 days. Below it: a payment calendar (1 month, 3 months, or a 12-month
heat grid), Looks recurring payments that no bill claims yet (Track, Add
to a bill, Ignore), and ended or paused bills.
Price changes go in next_amount and change_date. The edit dialog lays out
the next eight cycles at the price each will actually be charged, and every total
and projection follows it. amount_model: variable projects from the average of
recent payments. end_date or payments_left retires a bill on its own.
Converting from the old model. Before 0.9, bills were detected from tags and
tuned in a registry table in 🔁 Recurring Payments.md. That still works until you
run Convert recurring payments to bill notes, which makes one note per bill,
merges duplicate wordings into aliases, keeps cancelled bills as ended, and shows a
preview first. Tidy up recurring payments removes same-day duplicate charges,
old $0 skip lines and leftover registry rows, also behind a preview.
Command: Insert a finance block → Runway · Settings → Runway
How much do I need to keep available to be safe for the next month?
Runway answers that, and — once you choose the account it comes out of — whether you have it. It is a read-only figure: there is no envelope to fund and no bookkeeping. Pick a period and what counts, and it reads your bills:
![]()
Keep $395.27 available for the next 1 month Recurring bills only, between today and 2026-08-29 · 11 bills due
Three settings, in Settings → Bills and runway:
| Setting | Options |
|---|---|
| Runway period | 1 week · 2 weeks · 1 month · 2 months · 3 months · 6 months |
| What counts | Bills + usual spending (default), or recurring bills only |
| Account | The account it comes out of (from your balance snapshots), or none |
Against your balance. With an account chosen (or account: anz-plus in the
block), runway compares itself with that account's latest balance snapshot:
Covered, with $412.00 to spare — enough for about 41 days of outgoings, or
Short by $180.00 — which runs out around 26 Sep. Days are walked through the
window, so a bill counts on the day it lands. A snapshot more than two weeks old
is flagged, since the answer is only as current as the balance.
The settings page shows the resulting figure live, so choosing a period is not abstract.
The figure walks your actual schedule. It is the sum of every bill occurrence that lands inside the window, not a monthly average scaled up. A $900 annual insurance renewal is worth nothing while it is eleven months away and worth all $900 the moment it enters the window — because that is the month you need the money. "Bills + usual spending" adds your average discretionary spend over the last 90 days, scaled to the window.
Every bill making up the total is listed with its date, soonest first, so the number is never a black box. There is also a per week and per day breakdown. The same card also appears at the bottom of the Bills tab.
If you tracked a bill reserve in 0.6, those #log/income/billreserve bullets
are left alone. They no longer feed a balance, but they still count as transfers
rather than income, so your reviews and forecasts are unaffected.
Commands: Create savings goal · Contribute to a goal · Withdraw from a goal · Start trip mode · End trip mode · the hub's Goals & trips tab
Savings goals and trips share one frontmatter schema. Any goal with
target_amount and due_date shows sinking-fund maths: the set-aside needed per
week, and whether you're ahead of or behind a steady pace since your first
contribution (with no contributions yet it says nothing saved yet). A trip is a
goal that also has a trip_tag, dates and a currency.
Creating one asks for a name, a target and an optional due date. The tag its
entries use is made from the name and shown as you type (Contributions are
logged as #log/income/house-deposit), kept clear of goals and income categories
already in use; Advanced lets you choose it yourself. A new trip takes its
year from the name or start date (2027/new-zealand) and asks for its currency.
---
goal_name: Roadbike
goal_key: roadbike
target_amount: 3000
starting_balance: 0
due_date: 2026-12-10
active: true
currency: AUD
---
```savings-dashboard
A trip goal adds the trip keys (and optionally `trip_currency` for trip-mode capture):
```md
---
goal_name: Japan Mid-Year
goal_key: japanmidyear
target_amount: 6000
due_date: 2026-06-18
trip_tag: 26/japanmidyear
trip_currency: JPY
start_date: 2026-06-21
end_date: 2026-07-08
total_budget: 6000
currency: AUD
exchange_rates: JPY=0.00877
---
Contributions are - $500 #log/income/roadbike bullets; withdrawals are
- $90 #log/spending/goal/roadbike/<category>, and don't count as home spending.
Contribute and Withdraw share one dialog, with the goal typed and
autocompleted rather than picked from a list. Goals are virtual envelopes: nothing
has to move between real accounts.
When a goal or trip needs a decision, a card says so at the top of the sidebar and the hub's Today and Goals & trips tabs:
| When | It offers |
|---|---|
| A goal is due within three days | Contribute, New due date |
| A goal's due date arrives or passes | Archive goal (after a confirmation), New due date |
| A goal reaches its target | Archive goal |
| A trip's start date arrives | Start trip mode |
| A trip has ended but trip mode is on | End trip mode |
| A trip is over | Archive trip |
Each can be put off until tomorrow (Not now) or dismissed. A dismissed prompt comes back if its date changes.
While trip mode is on, quick add and URL captures default to the trip's tag and
currency, converting to your home currency through the note's exchange_rates
(JPY=0.00877: one yen is worth 0.00877 of your home currency) or dated
exchange_rate_periods. The sidebar shows spent today, what's left of the trip
budget and a safe amount per day. Add trip exchange rate has Fetch current
rate, which fills in the European Central Bank's reference rate for the day;
card and cash rates are usually a little worse, so adjust it if you know yours.
Command: Insert a finance block → Goals
```finance-goals
account: short-term-savings # optional — reconcile against a real account
```
One card per goal: saved vs target, the weekly set-aside still needed,
ahead/behind pace, and a Contribute button. With account: it compares the
sum of your goal envelopes against that account's latest #log/balance/…
snapshot and shows the unallocated remainder — the piece of the lump sum
not yet promised to any goal (or a warning when you've over-allocated).
Commands: Archive finished trips · Archive completed goals · the prompts' Archive button
You can save for several holidays at once — every goal note with
active: true shows in the sidebar and counts toward forecast set-asides, and
capture routes to whichever trip's dates match.
When a trip is over — or a savings goal has hit its target — archive it.
Archiving writes a frozen Archive summary into the note: the savings steps
(every contribution, dated), withdrawals, and for trips how the money was
spent during the trip and after its end date. The note is then marked
archived: <date> and moved to the archive folder. The note keeps its full
history and its dashboards still render when you open it, but it leaves the
active set: no more capture routing, sidebar cards, or forecast set-asides.
## Archive summary (2026-07-25)
- Target: $3,500.00
- Saved: $3,500.00 (100% of target) — $500.00 starting balance + 2 contributions
### Savings steps
| Date | Amount | Note |
| ---------- | --------: | ---------------- |
| 2026-05-01 | $1,500.00 | Savings transfer |
| 2026-06-01 | $1,500.00 | Savings transfer |
### How it was spent
- Trip budget: $3,500.00
- Spent during the trip: $2,914.00 across 41 entries
- Spent after 2026-07-24: $40.00 across 1 entry
Add a fenced code block to any note; each reads transactions from your daily notes.
```finance-dashboard
period: week # day | week | fortnight | month | bimonth | quarter | year
groupBy: primary # primary | full
currency: AUD # optional override
start: 2026-06-01 # optional explicit range (needs end)
end: 2026-06-30
title: June Spending
show: defaults, merchants # optional: which sections, in this order
hide: trend # optional: sections to leave out
```
| Section | Shows |
|---|---|
summary |
Total, average per day, change vs the previous period, top category. |
income |
Income, spent, saved and savings rate = (income − home spending) ÷ income. Goal contributions, settle-ups and trip spending are transfers, so they're left out. With no income logged it says so rather than showing a rate. |
uncategorised |
How many entries (and how much) have no category, with Open inbox. Only appears when there are some. |
categories |
The category donut. |
trend |
Daily spend with your budget line (periods up to 62 days). |
changes |
The categories that moved most since the previous period, either way. |
merchants |
Top five merchants, with branches of one shop grouped together. |
largest |
The five largest transactions (your share of a split). Tap one to edit it. |
budgets |
Pace-aware budget bars. |
bills |
Bills paid this period; for a period that's still running, what's still due and what's due next period. |
trips |
Trip spending, which never counts toward home totals. |
savings |
Savings contributions this period. |
portfolio |
Trades, dividends and — when there's price history — the change in value. Never fetches prices. |
Defaults: period: week and period: month show every section. Other
periods show what dashboards always did — summary, categories, trend, budgets,
savings — plus the uncategorised callout. Sections with nothing to say stay
hidden, so a quiet week doesn't fill up with empty cards. show: lists the
sections you want in the order you want them (defaults and all expand in
place); hide: removes some. Your existing weekly and monthly notes pick up
the new sections without any edits.
"The previous period" is the previous calendar period: September is compared with August, a week with the week before.
The donut is a
three-ring category donut — inner ring is the major categories, middle
ring is each subcategory as its own colour section (a shade of its parent's
hue), and the outer ring splits each subcategory further into its own leaf
items — with a nested legend — a daily-spend sparkline with your budget line,
pace-aware budget bars, and savings activity. Export CSV from the header.
(The sidebar's mini pie stays at major categories; the donut falls back to a
flat pie when nothing has subcategories.) A category that's only two levels
deep (e.g. shopping/amazon, nothing beneath it) has no leaf level to split
into, so its outer-ring band just continues as one uninterrupted block in the
same colour rather than an arbitrary extra seam. Shades within a ring are
ranked by spend — the biggest item gets the lightest shade of that hue, the
smallest the darkest — and hovering any slice (at any of the three levels)
shows its name, amount, and share of the total.
![]()
holiday-dashboard — planned vs actual trip spend, per-day budget remaining, and a
trip calendar. Reads a goal note that has a trip_tag (see Goals),
plus its Planned/Allocated tables. Once the trip has ended (past end_date,
or the note is archived) the same block automatically becomes a trip
reflection: total and after-trip spend, how it landed against the budget,
average per day, biggest and quietest days, a per-category table (total, avg/day,
share, and the biggest single expense with its merchant), a spend-by-day chart,
and planned-vs-paid. Force either mode with view: live or view: reflection.
![]()
savings-dashboard — per-goal progress, contributions, sinking-fund set-aside and
pace. Reads a goal note (frontmatter goal_key, target_amount, due_date, …).
![]()
Every chart shares one hue-family colour system: each major category gets a base hue, its subcategories render as lighter/darker shades of that hue, and pies and legends rank by major-group total with subgroups nested beneath.
Chart grouping and the budget period are in Settings → Budgets and reviews; the week start is in General.
Commands: Insert weekly review · Insert monthly review · Insert a finance block → Weekly review / Monthly review / Quarter in review / Year in review · Copy as text in the hub's Reviews tab
A review is not a live block. It works the numbers out once and inserts
finished markdown at your cursor, so the note still says the same thing a year
from now. The period is the note's own: a review inserted into your W19 note
covers week 19, and one inserted into 2026-07.md covers July, whenever you
get round to writing it up. Outside a periodic note it covers the current
period.
A weekly or monthly review has what you spent (and the change from the period before), income, what you saved and your savings rate, uncategorised entries, bills paid, trip spending, a table of where the money went with each category's change, top merchants, the largest transactions, and any transfers:
## Finance review: week of 7 Sep 2026
- Period: 2026-09-07 to 2026-09-13
- Spent: $801.73 (▲ $661.73 (+473%) vs previous week)
- Income: $2,050.00
- Saved: $1,248.27 (61% savings rate)
- Uncategorised: 1 entry, $33.23
- Bills paid: 1 ($66.50)
- Trip spending, not counted above: Japan $210.00
### Where it went
| Category | Spent | Share | vs previous week |
| --- | ---: | ---: | ---: |
| Medical | $480.00 | 60% | ▲ $480.00 |
| Food | $210.00 | 26% | ▲ $110.00 |
### Top merchants
| Merchant | Spent | Visits |
| --- | ---: | ---: |
| The Good Group Cli | $480.00 | 1 |
| Woolworths | $150.00 | 2 |
### Largest transactions
- 2026-09-09 · The Good Group Cli · Medical · $480.00
A quarter or year review has total spent and income, the best and worst month by spend, the top spending categories with their share, and a transfers summary (savings contributions and withdrawals, settled split repayments, runway contributions):
## 2026 Year in Review
- Period: 2026-01-01 to 2026-12-31
- Total spent: $18,240.55
- Total income: $64,000.00
- Best month (lowest spend): 2026-02 — $980.10
- Worst month (highest spend): 2026-07 — $4,011.45
### Top spending categories
| Category | Total | % of spend |
| --- | ---: | ---: |
| Food | $5,120.30 | 28% |
| Subscriptions | $2,890.00 | 16% |
### Transfers
- Savings contributions: $3,000.00 (4)
- Savings withdrawals: $250.00 (1)
- Settled repayments received: $120.00 (2)
- Runway contributions: $400.00 (4)
Running one again later gives a fresh snapshot with whatever you've logged since.
Commands: Snapshot balances · Open portfolio · Log a trade · Refresh share prices · the hub's Portfolio tab
Everything here is arithmetic on your own records. It is not financial or tax advice, and the capital-gains flag is a reminder to check, not a ruling.
Snapshot balances logs one bullet per account into today's daily note. Accounts
you've snapshotted before are pre-filled, and the account box suggests the ones
you've used — including the source= names your captures carry, like anz:
![]()
- $5,230.00 #log/balance/anz-plus
- $812.40 #log/balance/wise
Shares and ETFs live in a note, Utility/Finance/📈 Portfolio.md, as a table of
trades — hand-editable, and the only record the plugin needs. Log a trade
writes a row for you.
| Date | Type | Ticker | Units | Price | Fees | Currency | AUD cost | Account | Note |
| --- | --- | --- | ---: | ---: | ---: | --- | ---: | --- | --- |
| 2026-03-02 | buy | VAS.AX | 50 | 98.20 | 9.50 | AUD | | Pearler | |
| 2026-06-10 | buy | AAPL | 5 | 190.00 | 2.00 | USD | 1455.20 | Stake | |
| 2026-08-01 | split | AAPL | 2 | | | | | | 2-for-1 |
buy, sell, drp (dividend reinvestment) or split. For a
split, Units is the ratio: 2 for a 2-for-1, 0.5 for a 1-for-2
consolidation..AX; US tickers are written plain.- $12.40 #log/income/dividend/vas-ax. With prices from Yahoo you rarely
type these yourself — see Dividends to log below.The finance-portfolio block (already in the note, and the hub's Portfolio
tab) shows:
A holding's page. Tap a holding, or a watchlist ticker, for its price over 1M, 3M, 6M, 1Y or 2Y with your buys and sells marked as dots, your average cost as a dashed line and ex-dividend dates as ticks underneath; then your units, value, gain, parcels (flagging those held twelve months), recent dividends, Log trade already filled in with the ticker, and Open on Yahoo Finance. It draws from prices already fetched, so opening it never makes a request.
Watchlist. Follow a share or ETF without owning it: Add to watchlist (it
searches as you type) saves it in the note's watchlist property, e.g.
watchlist: [VGS.AX, NDQ.AX], which you can also edit by hand. Each ticker shows
its price, today's move, the last month's change and a sparkline.
Dividends to log. Yahoo publishes each holding's dividends: the ex-date and the amount per unit. The portfolio works out how many units you held the day before each ex-date (buying on the ex-date doesn't earn that dividend), and lists any dividend in the last twelve months with nothing logged near it, and no reinvestment either, as VAS.AX · ex 2026-07-01 · 20 units × $0.49 = $9.80. Log it opens it with that estimate and a paid-on date to confirm or correct (withholding and rounding change the real figure), then logs it as income in that day's note. Dismiss stops listing it. They also appear in the hub's Inbox.
Today in the hub shows a Shares today card: the day's move in dollars and percent, the biggest mover, and any dividends to log.
Settings → Portfolio → Price source:
| Source | Network | Notes |
|---|---|---|
| Typed in (default) | none | Put prices in the note's properties: price_overrides: VAS.AX=103.42, AAPL=229.10 and fx_rates: USD=1.51. Always works. |
| A published Google Sheet | Free, reliable, and yours. Settings gives you a template to paste into a new sheet — a row per ticker, GOOGLEFINANCE formulas already written — then publish it as CSV and paste the link. |
|
| Yahoo Finance | Yahoo | Free, covers the ASX, US and London (prices in pence become pounds), and the only source with price history (the charts) and dividend history (Dividends to log). Unofficial: Yahoo only answers requests that look like a web browser's, so the plugin's requests identify as one. It could stop working without notice. |
However prices arrive, they are cached, so the portfolio still shows offline or while a source is refusing — with old prices marked as old. Yahoo has two servers, and a refusal from one is tried on the other; a source that refuses outright is backed off (2 minutes, doubling, up to six hours), and prices you type in always win. Prices are only fetched when the portfolio block is opened or you run Refresh share prices; there is no background polling.
There is no way to use the Stocks app on a Mac or iPhone: it has no API, and its prices are not available to other apps.
```networth-dashboard
```
Net worth is your account balances plus the portfolio, at the prices already held — this block never fetches prices itself. The line over time carries each forward from its own last point.
![]()
Command: Settle up split expenses · Insert a finance block → Split expenses
Quick-add and obsidian://finance-capture accept split=N (even split — your
share is amount ÷ N) and owed=Name:$X tokens:
24 nobu restaurants split=2 → you owe $12, someone owes you $12
30 dinner restaurants owed=Sam:$10 → Sam owes $10 of the $30
The full amount stays on the bullet, with a hand-editable child line per person. Only your share counts toward budgets:
- $24.00 #log/spending/food/restaurants
- Nobu
- owes: Sam $12.00 #log/owed/sam
The ```finance-splits``` block and a sidebar card sum outstanding balances
per person. Settle up logs the repayment as income
(- $12.00 #log/income/settleup/sam) and appends · settled <date> to the owed
lines.
![]()
Command: Insert a finance block → Forecast
```finance-forecast
months: 6
```
Projects recurring income, minus recurring bills, minus your trailing-90-day
average discretionary spend, forward N months — including committed goal
set-asides — as a line chart with a ~$X by <date> headline. Override any
input with income:, bills:, discretionary:, setaside:, or start:.
Command: Insert a finance block → Query
A read-only report over your entries — filter by category, tag, merchant, or date range; group by category, merchant, or month; sum or count:
```finance-query
period: month # or start: / end: dates
category: food # optional prefix filter
merchant: nobu # optional substring filter
type: spending # spending | income | all
group: category # category | category-full | merchant | month | none
op: sum # sum | count
view: table # table | categories | bars | income-expense | cumulative
```
Views: table (grouped sums), categories (ranked category table with
percentages), bars (ranked bars), income-expense (monthly income-vs-expense
bars), cumulative (cumulative balance line).
The Daily Budget sidebar (ribbon coin icon) shows today + period spend, a Left/Day card, a mini pie, compact pace-aware budget rows (tap a row for the detail), savings goals, split balances, a Recurring bills due card, and a Needs a category card counting everything uncategorised, with Open inbox. Hub opens the finance hub.
Recurring bills due lists every overdue or due-today bill, and stays until each is paid, so it can't get lost among the rest of the sidebar. Goal and trip prompts appear just under the totals.
Tap an uncategorised entry to edit it, or Open inbox to work through the lot (see Categorising).
![]()
![]()
The status bar shows 💸 Today $X · Week $Y · 📥 N (N = pending captures);
click it to quick-add.
Command Reconcile a bank CSV: paste an ANZ or Wise export. Rows are matched by date+amount (so merchant-name differences don't cause duplicates); unmatched charges can be sent to the capture inbox to log and triage.
Run any of these from the command palette (Cmd/Ctrl+P). Obsidian prefixes each
with Finance Tracker:. None has a default hotkey; every one can take yours.
Logging
| Command | What it does |
|---|---|
| Quick add transaction | One field, natural language, live preview. Also the status bar. |
| Snapshot balances | One balance bullet per account into today's note, pre-filled with each account's last balance. |
| Settle up split expenses | Outstanding split balances per person; settling logs the repayment as income. |
| Process capture inbox · Sync capture gist now | Drain those capture methods now rather than on their schedule. |
| Check capture methods for overlap | Which pairs of methods have been logging the same transactions. |
| Reconcile a bank CSV | Match an ANZ or Wise export by date and amount; send misses to the capture inbox. |
Hub and views
| Command | What it does |
|---|---|
| Open finance hub | The finance hub (ribbon wallet icon). |
| Open finance hub: today / inbox / budgets / bills / goals & trips / portfolio / reviews | The hub on that tab. |
| Open daily budget | The Daily budget sidebar (ribbon coin icon). |
| Open budgets note · Open recurring payments note · Open portfolio | Open (creating if needed) that note. |
Categories
| Command | What it does |
|---|---|
| Rename or split a category | Rename everywhere, or split by merchant. Preview first. |
| Import merchant map note | Re-read Merchant Map.md into the merchant rules. |
Bills
| Command | What it does |
|---|---|
| Log due bills | Log every bill whose due date has arrived, catching up missed cycles. |
| Convert recurring payments to bill notes | One note per bill from the old tag-and-registry model. Preview first. |
| Tidy up recurring payments | Remove same-day duplicate charges, $0 skip lines and leftover registry rows. Preview first. |
Goals and trips
| Command | What it does |
|---|---|
| Create savings goal | Name, target and optional due date; the tag is made from the name. |
| Contribute to a goal · Withdraw from a goal | Log money into or out of a goal. |
| Start trip mode · End trip mode | Point quick add and URL capture at a trip's tag and currency, and back. |
| Add trip exchange rate | Add a rate to a trip note, with Fetch current rate. |
| Archive finished trips · Archive completed goals | Write a frozen summary into each and move it to the archive folder. |
| Convert legacy trip tags | Rewrite trips filed under #log/archive/<year>/<trip>/… to the current format. Preview first. |
Portfolio
| Command | What it does |
|---|---|
| Log a trade | Add a buy, sell, reinvestment or split to the Trades table, with ticker search. |
| Refresh share prices | Fetch prices now, unless the source is backing off after refusing. |
Notes and reviews
| Command | What it does |
|---|---|
| Insert a finance block | Pick a block — dashboard, bills, goals, splits, forecast, accounts & portfolio, portfolio, query — or a frozen review, and insert it at the cursor. |
| Insert weekly review · Insert monthly review | A frozen review of the note's own week or month. |
Maintenance
| Command | What it does |
|---|---|
| Repair daily note totals | Recompute every daily note's running total in one pass. |
| Export transactions to CSV | Every transaction, with an entry_type column so income and balances aren't mistaken for spending. |
| Set up finance notes | The setup wizard; creates missing starter notes and never overwrites one. |
Settings follow the hub's areas, with a link to each at the top:
| Section | What's there |
|---|---|
| General | Currency, week start, a live line saying how many entries the plugin can read (so a wrong folder is obvious), and Open finance hub. |
| Capture | Quick add's date, opening the note after a capture, each capture method on or off, duplicate handling, the gist, and (under Advanced) the note format. |
| Categories | Learn categories from past notes, and the merchant rules: add, edit, test and remove. |
| Budgets and reviews | The sidebar's budget period, chart grouping, and the budgets note. |
| Bills and runway | The recurring tag prefix, auto-log, sort order, every running bill with Active and Auto-log, the payment calendar range, and runway's period, what counts and account. |
| Goals and trips | Goals and trips, with trip mode and archiving. |
| Portfolio | The price source, the sheet link and a template for it, and how often prices refresh. |
| Files and setup | Folder and note locations, and the setup wizard. |
The daily-note folder and date format are detected from the Journals community
plugin or the core Daily notes plugin when present. Settings are stored in the
plugin's data.json, which also holds the gist token if you use one — keep it out
of anything you share.
Worth knowing before you install:
obsidian:// URL works with any sync; the file-based capture inbox needs a
vault your Files app can write to.npm test # checks main.js is in sync with its sources, then runs node --test
npm run build:main # regenerate main.js from finance-core.js + src/
main.js is the file Obsidian loads: one file, no bundler, committed and attested
by the release workflow. It is generated — do not edit it by hand. Two sources
feed it:
finance-core.js — the pure logic, and the only file the unit tests import.
It becomes the core IIFE at the top of main.js.src/*.js — the plugin itself (settings, commands, views, modals, the
settings tab), concatenated in file-name order. Fragments are copied verbatim,
so each one is ordinary readable JavaScript.scripts/build-main.js performs both steps, and npm test fails if main.js has
drifted from its sources. The core transform is deliberately dumb: drop the
leading "use strict", indent by two, turn module.exports = { into return {,
and wrap it in the IIFE that replaces the //@@CORE@@ placeholder in
src/00-header.js.
This started as a convention maintained by hand. By 0.6.0 it had quietly drifted in four places — including a default that differed between the tested code and the shipped code — so now a script owns it.
Tests are in three files:
tests/finance-core.test.js — unit tests for individual core functions.tests/integrity.test.js — invariants across the whole write→read cycle:
everything written parses back unchanged, and the running total on a note's
root line always equals the entries beneath it. These guard the paths where a
bug silently corrupts a user's notes rather than just showing a wrong number.tests/capture-methods.test.js — a functional harness that loads the real
main.js against a stubbed obsidian module and a stub vault, so plugin-side
wiring is actually executed. It catches the classic failure where a core
function lands but its caller never calls it.