DEV Community

Jukka Blomberg
Jukka Blomberg

Posted on

Our bitcoin stocks index never deletes a mistake. Here is the file that proves it.

Most index files you can download show you one number per day. If a number was wrong, it quietly changes, and you never find out.

The file behind the CoinBucha Listed Bitcoin Index works the other way round. It has 23 rows for 16 trading days. 16 of them are live readings. 5 are readings that were later replaced. 2 were withdrawn outright. All 23 are still in the file, each marked for what it is.

The rule

From the methodology, in plain words:

  • A recorded reading is never edited.
  • A correction is a new row. It names the row it replaces and says why.
  • Every row carries a hash of its inputs, the hash of the row before it, and its own hash. Change any old row and the chain breaks.

What the "mistakes" look like

Two rows dated 29 and 30 September were withdrawn because only 1 of the 32 companies (Metaplanet, on the Tokyo exchange) had actually closed on that date; the other 31 prices were carried over from earlier days. A reading built on 31 stale prices is not a reading, so it was voided, and the reason is written in the row.

On 9 October the index changed its rules (methodology 1.7 to 2.0: 32 companies down to 28, a new divisor). The old 9 October reading (104.84) stays in the file, marked superseded. The new one starts again at 100. Under the new rules, the latest live reading is 87.12 on 16 October.

Check it yourself

The chain is a few lines of Python against the public CSV:

import pandas as pd

url = "https://coinbucha.com/data/listed-bitcoin-series.csv"
s = pd.read_csv(url)

# every row points at the one before it
links = (s.prev_sha256.iloc[1:].values == s.row_sha256.iloc[:-1].values)
print(f"{links.sum()} of {len(links)} links hold")   # 22 of 22 on 2026-10-19

# what was replaced or withdrawn, and why
print(s.status.value_counts())
print(s[s.status != "live"][["date", "status", "level", "reason"]])

# the series you would chart: one live reading per date
live = s[s.status == "live"].drop_duplicates("date", keep="last")
print(live[["date", "level", "methodology_version", "constituent_count"]].tail())
Enter fullscreen mode Exit fullscreen mode

When we ran it on 19 October: 22 of 22 links hold; 16 live, 5 superseded, 2 void.

Why bother

An index is only worth citing if the past can't move under you. A file that keeps its own corrections lets anyone check that the past didn't move: who changed what, when, and why.

If you want what changed each month in one email, there is a free monthly brief on the index page. Information, not financial advice.

Top comments (0)