Aller au contenu principal
Version: v5

Release notes

This page carries the hand-written account of each release, opening at 5.0 and never looking further back: the headline changes, and the heads-up items that actually matter when you upgrade. The exhaustive, commit-level list — every dependency bump included — is generated by Release Please in CHANGELOG.md.

The version 4 history is not merged in here. It stays with the version 4 documentation, where the releases it announces can still be run.

5.1.2 — Prices in the share you hold​

Yahoo serves its closes adjusted to today's share; your ledger holds the quantity you traded. Multiplied together, any line bought before a split was valued in two units at once — three shares of a symbol that reverse-split 1-for-1000 were carried at a thousand times their worth, for the whole of their pre-split history. A free-share attribution moves the same figures the other way, a few per cent at a time: Yahoo records one share per ten as a split too, so a line you merely hold accumulated the error. The dashboard curve and the account performance are distorted in either direction: a year can show a loss on a portfolio that grew, or a gain it never made. The XIRR beside them was right all along, which is how the two came to disagree on the same card.

Before you upgrade​

Your stored price history is dropped at the first start and bought back from Yahoo, one window per symbol per cycle. It could not be corrected in place: a point written on the day it happened stands in that day's share, one backfilled after a split does not, and nothing in the store says which. Expect a shorter curve on the dashboard for a few hours. Your ledger and the prices shown today are untouched.

5.1 — Taxation, investment rhythm, MCP server​

A feature release on top of 5.0. Same store, same mounts: pull the image and restart the container.

Projected tax per account​

Declare how an account is taxed. Its panel then shows what you would owe if you sold everything in it today, the latent gain that figure applies to, and the rate.

Five kinds of model: nothing owed, a flat rate on the realised gain, a rate that changes with the account's age, a bracket table, and a withholding on income. Two presets fill their own parameters — PEA (5 years from the first payment) and assurance-vie (8 years from the opening). One model can serve several accounts.

When the model counts age from the opening, the form asks for that date and pre-fills it with your earliest payment. Correct it if the account is older than your ledger, as it is after a transfer from another broker.

Declare no model and the app shows nothing rather than a zero.

A projection, not a tax return

You enter the kind and the rates, and the app applies them. It ignores allowances, loss carry-forward, your household situation and any actual return. Nothing has been sold.

Investment rhythm​

The dashboard shows what you bought over the last twelve months: the amount a typical month carries, how many of the twelve had a purchase, and one bar per month.

It reads the buys already in your ledger. There is nothing to configure.

MCP server​

The app speaks MCP on /mcp, on the same port as everything else. An agent can read your positions, accounts, history, ledger and rhythm.

It reads only. No tool writes, and none deletes.

→ Connect your own agent

Sector, industry and country​

Each symbol carries its sector, industry and country as Yahoo! Finance classifies it, on the shares payload and over MCP.

Funds and crypto carry none: Yahoo gives a fund a breakdown across sectors rather than one sector.

The account type is gone​

Creating an account no longer asks for a type. It decided nothing the app computed — the taxation model answers that question now.

The store also tracks its own schema generation, and migrates itself when it opens.

Fixes​

  • Reads no longer wait behind a background write. They used to stall 7 to 15 seconds, sometimes never arriving; they now answer in about 0,3 s.
  • The shares table stops painting every held line green. Only the latent gain takes a colour.
  • An account shows one performance figure instead of two that disagree.
  • A failed exchange-rate fetch retries instead of caching no rate for the whole window.
  • A symbol stays in the rotation when its rescheduling fails.
  • A failed history write no longer moves the anchor past the year it missed, and the rebuild stops asking Yahoo about weekends.
  • A refusal — file too large, wrong method — returns its own error type instead of unexpected error.
  • An opening date in the future raises a notification.

Before you upgrade​

The migration only goes forward. 5.1 drops account.type and adds the three classification columns. 5.0 cannot open the result, so copy /data first if you want the option of going back.

Classification fills in over the next few minutes. The history rebuild writes it one symbol at a time, so the shares page can show blank sectors just after the upgrade.

/mcp has no authentication, like the rest of the port. Put the same thing in front of it that you put in front of the interface.

Recording an event took about 17 seconds in 5.1 as released on a ledger of a few hundred events going back to 2019. Every write replays the whole ledger and rebuilds the performance series before it answers, and that cost grew with your history, not with what you changed. It now takes about 0,9 s: the replay is still synchronous, so a 200 still means the figures behind it are current, but the series is written as one block rather than one statement a row. See #972.

5.0 — One embedded store, one interface​

Breaking release

Version 5 changes where your data lives, and there is no upgrade path from version 4. Crossing is a deliberate move you make once, described end to end in Coming from version 4 — including the reconciliation of the figures you were reading, and how to go back.

Your portfolio is a dated event ledger, and only that​

What you bought, sold, received and paid in is the whole of what you declare. Positions, prices, the weighted average cost, the performance series — everything else is derived from that ledger and recomputed from it. There is no second place where a figure is stated, and no file to keep in step with another.

A first event can simply be typed. A broker export is handed over from the page or by curl, read once, and never looked at again: the file is a gesture, not a mount, so a row that came out of a spreadsheet is corrected and deleted like any other, and nothing anywhere asks a row where it came from.

→ Import your events

One embedded store replaces InfluxDB and Grafana​

Version 4 ran three containers and asked you to hold a token that two of them had to agree on. Version 5 runs one container with one process, and everything it knows lives in a single embedded DuckDB file under /data.

There is no datastore to provision, no token to rotate, no datasource to point at anything, and no query language between you and your figures.

The app has its own interface​

Grafana is gone, and with it the dashboards you were expected to import. The five sections — Dashboard, Shares, Accounts, Ledger and Settings — are served by the app itself on one address, and they explain their own conventions: the weighted average cost, the carrying price, and the four terms the gain is made of are defined on the figure rather than in a page you have to go and find.

There is one interface and one socket. The Prometheus exporter and its port are gone: /api is the front's interface rather than a contract held for anybody else.

→ Read your figures

Settings live in the store, and are answered in the app​

settings.yaml and config.yaml are not read any more, and no environment variable declares a portfolio. Accounts are created in the app, the reporting currency is asked once, and the answers are stored beside the events they apply to.

→ Settings

It runs without Docker​

The app no longer forks: the web interface and the scheduler share one process that is started directly. That is what makes uv run python -m application.boot a supported way to run it — on Linux and on macOS alike, where the previous architecture segfaulted the moment a price was fetched.

→ Install without Docker

Heads-up when upgrading​

Nothing is migrated, and nothing needs to be. Version 4's InfluxDB history stays in InfluxDB; version 5 starts from an empty store and rebuilds the price history from Yahoo! Finance back to your first purchase. What you carry across is your events, and the reconciliation table says which figures are expected to coincide afterwards and which are not.

Both containers keep running until you stop them. Version 5 is a different image with different mounts and a different port; it does not touch version 4's volumes, and version 4 goes on working while you look around. Removing the old volumes is a separate, deliberate step, and Coming from version 4 holds the commands.

There is one mount, and it is /data. The config directory, the events directory and the read-only bind mounts of version 4 have no equivalent: a file is handed to the app through the import, not through the filesystem.

Nothing listens on 3000 or 8181 any more. One port is published, and it serves the interface, the API and the health probe. A reverse proxy, a firewall rule or a bookmark written against Grafana or InfluxDB has nothing behind it.

An external Prometheus scrape stops working. /metrics is gone rather than deprecated. If you were alerting on sb_share_price or sb_price_staleness, those series end here.

The restart policy belongs to your docker run command. Nothing inside the container respawns anything, so a process that dies takes the container with it and --restart unless-stopped is what decides what happens next.

The ledger refuses what it cannot compute with. A quantity, a price, an amount or a fee that is not a finite number is refused at the door, and so is a negative fee on a granted line or a dividend — a fee is money leaving, and a negative one conjures cash out of nothing and a negative cost basis with it. The refusal names the row and the column, so an event file that another tool accepted may be refused here, and correcting the cell is the whole of the fix.

That refusal reads the whole ledger, not only the new line. The check runs over every stored event each time the app rebuilds its picture, which is what makes it a property of the ledger rather than of the import — so a store written by an earlier build of version 5, holding a value this release refuses, stops at startup instead of serving figures computed from it. Only a preview install can be in that state: 5.0 is the first release of this major. Export the events, correct the offending cell, and import them into a fresh store.