How To Verify The Signal Record
The record does not ask for trust. Every settled play date is hashed, each hash is chained to the one before it, and the chain is published where we cannot quietly rewrite its history.
What a hash chain does, and what it does not
It cannot stop us editing a result. It makes an edit impossible to hide. Change one graded play in a sealed day and that day's hash changes, every link after it stops reproducing, and the break points at the exact date. That is the entire claim, and it is worth making because the alternative is asking you to believe a solo operator did not delete his losers.
Late corrections are real, so they are handled openly rather than pretended away. A box score that arrives after a day is sealed produces a new link at a higher revision with the earlier link left in place. An amendment is legitimate. An invisible one is not.
Current state
Ruleset v1.0 ·
chain starts 2026-07-25 ·
14 links ·
status INTACT
Tip: 91510ec97bf65c00ab9b534ccd2e7ed95cc6b615b15aca319ee15dc3db1fab16
The exact format
One line per settled play, ordered by play id. Each line is a compact JSON array with these fields in this order, and nothing else:
id, play_date, flagged_at, kind, sport, market, side, line, book, odds, score, tier, ruleset_version, result, units, closing_fair_prob, clv_pp
Real numbers are rendered as fixed four-decimal strings and whole numbers stay whole, so a
verifier written in any language reproduces the same bytes rather than inheriting one
language's float formatting. A missing value is null, never an empty string,
because a missing price and a zero price are different facts. Lines are joined with newlines
and the text ends with a newline.
rows_hash = sha256(that text)
chain_hash = sha256(prev_hash + "\n" + rows_hash), with prev_hash
empty for the first link.
The endpoints
/api/signal-log-hashesgives the whole chain./api/signal-log-rows?date=YYYY-MM-DDgives the exact text one link was hashed over, plus our own hash of it so you can check our arithmetic before you check the chain.
Both are public and unauthenticated. A verification mechanism only we can run is not a verification mechanism.
Check it yourself
Standard library only, no packages to install. Python 3.
import hashlib, json, urllib.request
BASE = "https://theoddsgap.com"
def get(path):
with urllib.request.urlopen(BASE + path) as r:
return json.load(r)
chain = get("/api/signal-log-hashes")
prev = ""
for link in chain["links"]:
day = get("/api/signal-log-rows?date=" + link["play_date"])
rows_hash = hashlib.sha256(day["text"].encode()).hexdigest()
chain_hash = hashlib.sha256((prev + "\n" + link["rows_hash"]).encode()).hexdigest()
latest = not link["superseded"]
ok_rows = (rows_hash == link["rows_hash"]) or not latest
ok_link = chain_hash == link["chain_hash"]
print(link["play_date"], "rev", link["revision"],
"rows", "OK" if ok_rows else "MISMATCH",
"link", "OK" if ok_link else "MISMATCH")
prev = link["chain_hash"]
A superseded revision is expected not to match the rows as they stand today, because it describes the ledger as it was before an amendment. That is exactly what it is published for.
Every link
| Play date | Rev | Plays | Rows hash | Chain hash | Sealed | State |
|---|---|---|---|---|---|---|
| 2026-07-25 | 1 | 513 | 77d37c229028a005 |
e94e282785b4bbdb |
2026-08-10 12:20:01 | verified |
| 2026-07-26 | 1 | 505 | c9de83bebe8080ca |
baf8bbcd4e3b08e3 |
2026-08-10 12:20:01 | verified |
| 2026-07-27 | 1 | 561 | 6ec7b721121f622b |
61e8d60fa9cbd727 |
2026-08-10 12:20:01 | verified |
| 2026-07-28 | 1 | 1182 | eeedfe0921ffbd07 |
b929e1af7bbb74df |
2026-08-10 12:20:01 | verified |
| 2026-07-29 | 1 | 508 | c11ff8a3d3394c43 |
6e58ee7d40907d97 |
2026-08-10 12:20:01 | verified |
| 2026-07-30 | 1 | 1107 | bdbd766efd3b101c |
15ac8379cb8c7f1f |
2026-08-10 12:20:01 | verified |
| 2026-07-31 | 1 | 1160 | 60a13ce13c924ab7 |
8e5dcdce18af6662 |
2026-08-10 12:20:01 | verified |
| 2026-08-01 | 1 | 1132 | 89577a23b021564b |
2c66f3b9a026ad9d |
2026-08-10 12:20:01 | verified |
| 2026-08-02 | 1 | 1121 | 329345b40ef702c7 |
f4a4769adc4c19e7 |
2026-08-10 12:20:01 | verified |
| 2026-08-03 | 1 | 1166 | a1d015919e5d801b |
87e644251b5cda93 |
2026-08-10 12:20:01 | verified |
| 2026-08-04 | 1 | 1152 | 41953ee6d1e20a69 |
cc30aed2e378f758 |
2026-08-10 12:20:01 | verified |
| 2026-08-05 | 1 | 1170 | 07ae786b9b9f33a9 |
b2c63b9940bb51ae |
2026-08-10 12:20:01 | verified |
| 2026-08-06 | 1 | 1124 | d4d8ebff988634a1 |
1befc32d5b27bec7 |
2026-08-10 12:20:01 | verified |
| 2026-08-07 | 1 | 1129 | 42a0cc5771e6d437 |
91510ec97bf65c00 |
2026-08-10 12:20:01 | verified |
The record does not ask for trust.
Back To The Record