The VINCAP WINE index tracks 22 listed wine companies, weighted by market value, based to 100 on 3 July 2026. It publishes one row per trading day, in three currencies, in a public CSV.
October 2026, from that file (30 September close → 30 October close):
| currency | 30 Sep | 30 Oct | October |
|---|---|---|---|
| US dollar | 106.26 | 110.81 | +4.28% |
| euro | 107.03 | 113.36 | +5.92% |
| each stock's own currency | 106.37 | 111.08 | +4.43% |
Same companies, same days. The gap between the rows is the currency, not the wine business: the euro lost ground against the dollar over the month, so a euro-based holder saw more of the gain.
Recompute it yourself
import pandas as pd
url = "https://vincap.co/data/levels/wine.csv"
df = pd.read_csv(url, parse_dates=["date"]).set_index("date")
sep, octo = df.loc["2026-09-30"], df.loc["2026-10-30"]
for col in ["level_usd", "level_eur", "level_local"]:
print(col, round((octo[col] / sep[col] - 1) * 100, 2), "%")
Output on the file as served today:
level_usd 4.28 %
level_eur 5.92 %
level_local 4.43 %
The file checks itself
Every row since 1 October carries prev_sha256 and row_sha256, so each day points at the one before it. If anyone (including us) quietly edits an old level, the chain breaks:
chain = df.dropna(subset=["row_sha256"])
breaks = (chain["prev_sha256"] != chain["row_sha256"].shift(1)).iloc[1:]
print("chain breaks:", int(breaks.sum()))
The month high was 112.38 on 16 October, the low 105.55 on 1 October.
Chart, levels file and the October numbers, free to use with credit: vincap.co/press/wine-october-2026. Method and coverage rules: vincap.co/methodology.
Information, not investment advice.
Top comments (1)
The currency table is the clearest illustration of a measurement bug I see constantly: "the index is up 4.3%" is not a statement about the wine business, it is a statement about the wine business and whichever currency the number was computed in. Publishing all three side by side is the right call, and most index publishers do not.
The
prev_sha256chain is a genuinely good idea, and I want to point at one implementation detail that decides whether it actually works.Your check is:
shift(1)compares each row'sprev_sha256against the previous surviving row in the dataframe, not against the row whose hash it claims to cite. Those are the same thing only if the frame is sorted ascending and has no gaps. On a CSV that is read fresh each day, both hold. But if someone filters by date range before running the check — the obvious thing to do when verifying one month — the first row of the filtered frame compares against nothing and you get a false break at the boundary; and if a trading day is ever missing, the chain reports a break that is really a missing row rather than a tampered one.The robust form pins the comparison to an explicit key rather than to frame order:
That answers "every cited predecessor exists" without depending on sort order or on gaps, and it survives being run on an arbitrary slice. You then assert the head separately (the first row cites a known seed / empty) and, if you want ordering guarantees, that the dates are strictly increasing — a different assertion from chain integrity.
One more: the chain only protects rows that carry the hash. You note it starts 1 October, so September levels — including the 30 September close that every number in this post depends on — are outside the chain. That is fine as long as it is stated, and you do state it, but a reader who runs the verification and gets
0breaks may reasonably conclude the whole file is verified when it is only the last month.I have written up a few of these data-integrity failure modes myself; there is a link from my profile if you want the longer version.