Skip to main content

Hosted Ledgers Now Resolve Managed Price Includes

Published 10 min readMike ThriftMike Thrift
Hosted Ledgers Now Resolve Managed Price Includes
On this page

Your holdings are already in the ledger. Their prices are the part you keep retyping.

That asymmetry is the oldest chore in plain-text accounting. A purchase is written once and is true forever: 120 NWRB {41.80 USD, 2024-03-12} records a quantity, a cost and a date, and nothing that happens afterwards changes any of the three. A price is the opposite — correct for one day, then quietly wrong, and wrong in a way no balance check will ever catch, because a stale price still balances perfectly. Beancount's own answer has always been to fetch them with a tool and commit the output, which works and which many people have automated. It is still a script you own, a cron entry you maintain, and a file you merge.

The beancount.io ledger engine now does that resolution itself, for ledgers hosted with us. This post is about what actually shipped, what it deliberately does not touch, and — just as importantly — what is not built yet.

What shipped

A hosted beancount.io ledger can carry an include whose target is a URL instead of a filename:

; main.bean, in a hosted beancount.io ledger.
;
; The managed line is shown commented out on purpose: upstream `include` takes a
; file glob, so this file still loads if you copy it to your own machine. Only
; the hosted engine resolves the URL form.
 
option "title" "Taxable brokerage"
option "operating_currency" "USD"
 
include "accounts.bean"
; include "https://beancount.io/prices/ACME-USD"
 
include "transactions/purchases.bean"
include "transactions/sales.bean"

Upstream Beancount's include takes a filename — "the specified path can be an absolute or a relative filename" is the whole of the specification — so a URL include is not plain Beancount and never pretends to be. It is a hosted-engine behaviour, and here is precisely what the engine does with it:

  • It materialises the feed as a read-only virtual file. The URL resolves through the engine's own include-resolution path, exactly where a local file would have landed, so every directive keeps a real source location. Your bytes are never rewritten. The file you wrote stays the file you wrote.
  • The fetched body is validated as price-only. price directives, comments, and four allowed metadata keys — price-source, price-kind, observed-at and provisional — and nothing else. Anything beyond that rejects the whole body. There is no partial ingestion, so a feed can never smuggle a transaction into your books.
  • Feeds are cached as an immutable revision plus a moving pointer. Refresh is driven by a timestamp rather than by cache expiry, which means an upstream outage cannot take your last good revision with it. A failed refresh never replaces a good revision with nothing.
  • Freshness is computed when the ledger is read, not stored: recent, stale, or unavailable, alongside the observation time the feed itself reported. A price you cannot date is a price you cannot audit.
  • Managed entries are read-only. Editing or deleting one is refused with an error naming the managed source, and they do not count against directive limits — the feed is not allowed to consume your ledger's budget.

Everything in that list runs inside the hosted ledger service. None of it changes what a price directive means: it still establishes the rate of exchange between a base commodity and a quote commodity, exactly as the language reference defines it. The engine's job is only to put correct, dated, attributable ones in front of the loader.

Your own price always wins

This is the part that decides whether a feed is usable by someone who takes their ledger seriously, so it gets stated exactly.

For the same date and the same commodity pair — and for the reciprocal pair — a price you wrote yourself wins over the managed feed, regardless of include order.

Not "usually", and not "if you put your include last". The shadowing decision is made before the price map is built, so it does not depend on where the include sits in the file. Move it to the top, move it to the bottom, split it across three files: the answer is the same.

; include "https://beancount.io/prices/ACME-USD"   ; hosted-engine form, again shown commented
 
; A price you wrote yourself, for the same date and pair.
; This one wins — above the include or below it, it makes no difference.
2026-09-16 price ACME 93.40 USD

(ACME is the fictional issuer from the example ledger further down; the number is invented, not a market observation.)

Why that rule and not the other one: a price in your own file is a decision. It might be the close your broker printed on the statement you are reconciling against, a contemporaneous quote for a thinly-traded position, or a figure your accountant asked you to use. A feed does not know any of that, and a system that silently overrides a human-authored figure has stopped being a ledger and started being an opinion. The feed fills gaps; it does not correct you.

Prices move valuation, and nothing else

The second reassurance is structural rather than a policy choice, and it is worth showing with real numbers rather than asserting. Here is the crypto example ledger — dated lots, staking, mining, DeFi positions, airdrops:

Open Crypto accounting example — dated lots, staking, DeFi and airdrops in a new tab

Take one entry from it. A governance-token airdrop arrives and is recorded as income at fair market value on the day it lands:

2024-03-20 * "Uniswap" "Receive UNI governance token airdrop"
  Assets:Crypto:Wallet:MetaMask:UNI          50.00 UNI {12.50 USD, 2024-03-20}
  Income:Crypto:Airdrops                      -625.00 USD

That $625.00 of income, and the $12.50 per-unit basis attached to the lot, are now facts about 2024-03-20. Every price directive in the ledger — managed, hand-written, or missing entirely — leaves both untouched. Prices change market value; they never change quantities, cost basis, cash flows, fees or realized gains. Which is why a price feed is a safe thing to accept help with in the first place: the worst a wrong price can do is misreport what a position is worth today, and it can never corrupt the number you will put on a tax return.

Where a wrong price model actually misleads you

The stock and ETF example ledger makes the sharper version of the same point:

Open Stock and ETF cost basis example — identifiable lots, a 4-for-1 split, specific-lot sales in a new tab

It holds a 4-for-1 share split, and the split is recorded the correct way — as a quantity change that preserves total basis, touching no income account:

2025-07-15 * "Broker" "NWRB 4-for-1 share split — quantity change, not income"
  Assets:Brokerage:NWRB                   -120 NWRB {41.80 USD, 2024-03-12}
  Assets:Brokerage:NWRB                    480 NWRB {10.45 USD, 2024-03-12}

Both sides are $5,016.00. The market value is unchanged across the split — 120 shares at $62.00 the day before, 480 shares at $15.50 the day after, $7,440.00 either way — and the acquisition date in the braces survives, which is what keeps a 2026 sale of those shares long-term.

The common mistake is to record a split as a price event instead and lean on a "split-adjusted" series to make the valuation come out right. That works only as long as every price you ever see has been adjusted the same way. The moment an unadjusted figure arrives — an old confirmation, a screenshot, a third-party series that restates nothing — the position is valued at four times its worth, and the share count in the ledger no longer matches the broker's statement, so the year-end assertion that would have caught it cannot fire.

This is the real argument for a price feed with a declared source, a declared kind, and a visible observation time: not convenience, but knowing what convention the number you just imported was computed under. The example ledger's own price file is deliberately unadjusted and says so, and its two split-straddling directives are written as a check you can verify by eye.

One honest note about clicking into either embed: the hosted ledger viewer renders account balances at cost, and offers no valuation control on the page. The ledgers above are there to show you the ledgers — the lots, the split, the specific-lot sales — not a market valuation the viewer does not currently draw. Both are public, and both can be cloned and run locally.

What is not here yet

A changelog entry is worth less than nothing if it lets you believe something that is not true, so here is the other half, plainly and with no date attached to any of it.

  • The price endpoint is not public. An anonymous request to https://beancount.io/prices/<ALIAS> is redirected to the login page. There is no public alias catalog.
  • So this is not something you can paste into your own file today. The engine resolves the include; the route it resolves is not open yet. When it is, that will be its own changelog entry.
  • The local bea CLI does not resolve URL includes. It reads files from disk, so a URL include fails locally as a file glob matching no files. Loader support in the CLI is a named follow-up.
  • There is no API surface. No REST, GraphQL or MCP field for managed prices.
  • There is no dashboard surface. No connect-a-feed screen and no freshness label in the UI; the freshness the engine computes has nowhere to be displayed yet.
  • Snapshots and export are not built, and neither is an instrument catalog or a manual refresh endpoint.

What shipped is the engine layer: the include resolution, the validation, the revision cache, the precedence rule and the freshness computation. That is the part everything else has to stand on, and it is the part that is hardest to change later, which is why it went first.

Where to look next

Both ledgers above are part of the example gallery, six worked patterns you can clone and run locally — both of these two ship static, checked-in price files on purpose, so a clone made in two years still produces the report it produces today. Everything else we ship lands on the changelog.

If you are still keeping prices current with a fetcher of your own, that remains the right answer for a local ledger, and Beancount's own price-fetching documentation plus the maintained beanprice tool are where to start.

Keep the boring part boring

The reason prices are worth automating is that they are the only part of a plain-text ledger that decays on its own. Beancount.io gives you plain-text accounting that stays yours — auditable, version-controlled, and never rewritten behind your back, which is exactly the standard a managed feed had to meet before we would ship one. Start for free and keep your books in files you can read.

Share this article

Follow this topic

Source: https://beancount.io/blog/2026/09/17/managed-price-includes-hosted-ledgers

Published: September 17, 2026