diff --git a/wizard-work/ledger/2026-11-04-cryptos-top20.csv b/wizard-work/ledger/2026-11-04-cryptos-top20.csv new file mode 100644 index 0000000..5717eb1 --- /dev/null +++ b/wizard-work/ledger/2026-11-04-cryptos-top20.csv @@ -0,0 +1,21 @@ +rank,name,symbol,price_usd,market_cap_usd,market_cap_change_24h_pct,volume_24h_usd +1,Bitcoin,btc,78382,1575893146452,1.49,31492703674 +2,Ethereum,eth,2316.73,283079436237,1.72,11509848251 +3,Tether,usdt,0.998969,178189851682,-0.39,49857500751 +4,BNB,bnb,699.49,91991216509,0.06,972490046 +5,XRP,xrp,1.28,81138041353,0.16,2367963367 +6,USDC,usdc,0.999448,70550799494,-0.41,13815706242 +7,Solana,sol,92.72,54727794168,-0.1,2778173808 +8,TRON,trx,0.318963,30309543786,-0.83,538095594 +9,Figure Heloc,figr_heloc,1.03,26844879741,-0.03,481114715 +10,Zcash,zec,1288.02,21925269847,6.86,1147674146 +11,Hyperliquid,hype,84.94,20335597447,0.32,1348530812 +12,Monero,xmr,641.9,12084828023,2.94,193676029 +13,Dogecoin,doge,0.074743,11704959032,-0.3,483661183 +14,USDS,usds,1.0,9576191536,-0.17,210065027 +15,WhiteBIT Coin,wbt,75.28,8934911929,1.31,133655260 +16,LEO Token,leo,9.32,8576829551,0.61,100555 +17,Chainlink,link,11.13,8531870409,-0.48,304608346 +18,Cardano,ada,0.189974,7138766905,-0.16,235438100 +19,Stellar,xlm,0.163071,5761475891,-1.36,135925538 +20,Bitcoin Cash,bch,274.04,5511420220,-0.88,146355359 diff --git a/wizard-work/ledger/2026-11-04-cryptos-top20.md b/wizard-work/ledger/2026-11-04-cryptos-top20.md new file mode 100644 index 0000000..ab6be89 --- /dev/null +++ b/wizard-work/ledger/2026-11-04-cryptos-top20.md @@ -0,0 +1,28 @@ +# Top 20 Cryptocurrencies by Market Cap (USD) + +Source: CoinGecko API `coins/markets` (2026-11-04), `vs_currency=usd`, `per_page=20`, sorted by `market_cap` desc. + +| Rank | Name | Symbol | Price (USD) | Market Cap (USD) | 24h % | 24h Vol (USD) | +|---|---|---|---|---|---|---| +| 1 | Bitcoin | btc | 78,382.0000 | 1,575,893,146,452 | +1.49% | 31,492,703,674 | +| 2 | Ethereum | eth | 2,316.7300 | 283,079,436,237 | +1.72% | 11,509,848,251 | +| 3 | Tether | usdt | 0.9990 | 178,189,851,682 | -0.39% | 49,857,500,751 | +| 4 | BNB | bnb | 699.4900 | 91,991,216,509 | +0.06% | 972,490,046 | +| 5 | XRP | xrp | 1.2800 | 81,138,041,353 | +0.16% | 2,367,963,367 | +| 6 | USDC | usdc | 0.9994 | 70,550,799,494 | -0.41% | 13,815,706,242 | +| 7 | Solana | sol | 92.7200 | 54,727,794,168 | -0.10% | 2,778,173,808 | +| 8 | TRON | trx | 0.3190 | 30,309,543,786 | -0.83% | 538,095,594 | +| 9 | Figure Heloc | figr_heloc | 1.0300 | 26,844,879,741 | -0.03% | 481,114,715 | +| 10 | Zcash | zec | 1,288.0200 | 21,925,269,847 | +6.86% | 1,147,674,146 | +| 11 | Hyperliquid | hype | 84.9400 | 20,335,597,447 | +0.32% | 1,348,530,812 | +| 12 | Monero | xmr | 641.9000 | 12,084,828,023 | +2.94% | 193,676,029 | +| 13 | Dogecoin | doge | 0.0747 | 11,704,959,032 | -0.30% | 483,661,183 | +| 14 | USDS | usds | 1.0000 | 9,576,191,536 | -0.17% | 210,065,027 | +| 15 | WhiteBIT Coin | wbt | 75.2800 | 8,934,911,929 | +1.31% | 133,655,260 | +| 16 | LEO Token | leo | 9.3200 | 8,576,829,551 | +0.61% | 100,555 | +| 17 | Chainlink | link | 11.1300 | 8,531,870,409 | -0.48% | 304,608,346 | +| 18 | Cardano | ada | 0.1900 | 7,138,766,905 | -0.16% | 235,438,100 | +| 19 | Stellar | xlm | 0.1631 | 5,761,475,891 | -1.36% | 135,925,538 | +| 20 | Bitcoin Cash | bch | 274.0400 | 5,511,420,220 | -0.88% | 146,355,359 | + +*Full raw values in `crypto-top20-fresh.json`; machine-readable table in `2026-11-04-cryptos-top20.csv`.* diff --git a/wizard-work/ledger/CHECK-2026-11-04-cryptos-top20.py b/wizard-work/ledger/CHECK-2026-11-04-cryptos-top20.py new file mode 100644 index 0000000..8d06bb1 --- /dev/null +++ b/wizard-work/ledger/CHECK-2026-11-04-cryptos-top20.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +"""CHECK -- ledger: fresh CoinGecko top-20 table extraction, 2026-11-04. + +Recomputes the expected table from crypto-top20-fresh.json and compares it +cell-by-cell against the committed 2026-11-04-cryptos-top20.csv and the +2026-11-04-cryptos-top20.md. + +Exit code = verdict (0 pass, 1 fault, 2 ungradable): + 0 committed CSV + MD match the JSON source. + 1 mismatch. + 2 source unreadable / malformed. +""" +import csv +import json +import os +import sys + +HERE = os.path.dirname(os.path.abspath(__file__)) +SRC = os.path.join(HERE, "crypto-top20-fresh.json") +CSV_PATH = os.path.join(HERE, "2026-11-04-cryptos-top20.csv") +MD_PATH = os.path.join(HERE, "2026-11-04-cryptos-top20.md") + +FAILS = [] + + +def fail(msg): + FAILS.append(msg) + + +def close(a, b, eps=1e-6): + return abs(float(a) - float(b)) <= eps + + +def main(): + # --- load source --- + try: + with open(SRC, encoding="utf-8") as f: + source = json.load(f) + except Exception as exc: + print("UNGRADABLE: cannot read source JSON: %r" % exc) + return 2 + if not isinstance(source, list) or len(source) != 20: + print("UNGRADABLE: source not a 20-row list") + return 2 + + # --- load committed CSV --- + try: + with open(CSV_PATH, newline="", encoding="utf-8") as f: + reader = csv.DictReader(f) + csv_rows = list(reader) + except Exception as exc: + print("UNGRADABLE: cannot read CSV: %r" % exc) + return 2 + + expected = [] + for r in source: + expected.append({ + "rank": int(r["rank"]), + "name": r["name"], + "symbol": r["symbol"], + "price": float(r["price_usd"]), + "market_cap": int(r["market_cap_usd"]), + "change": float(r["market_cap_change_24h_pct"]), + "volume": int(r["volume_24h_usd"]), + }) + + if len(csv_rows) != len(expected): + print("FAIL: CSV row count %d != expected %d" % (len(csv_rows), len(expected))) + return 1 + + for i, (a, exp) in enumerate(zip(csv_rows, expected)): + got = { + "rank": int(a["rank"]), + "name": a["name"], + "symbol": a["symbol"], + "price": float(a["price_usd"]), + "market_cap": int(a["market_cap_usd"]), + "change": float(a["market_cap_change_24h_pct"]), + "volume": int(a["volume_24h_usd"]), + } + for col in expected[i]: + if col == "name" or col == "symbol": + if got[col] != exp[col]: + fail("row %d %s: %r != %r" % (i, col, got[col], exp[col])) + elif col == "rank": + if got[col] != exp[col]: + fail("row %d rank: %r != %r" % (i, got[col], exp[col])) + else: + if not close(got[col], exp[col]): + fail("row %d %s: %r != %r" % (i, col, got[col], exp[col])) + + if FAILS: + print("FAIL (%d cell mismatch(es)):" % len(FAILS)) + for f in FAILS: + print(" -", f) + return 1 + + # --- verify markdown body --- + try: + with open(MD_PATH, encoding="utf-8") as f: + md_lines = [ln for ln in f.read().splitlines() if ln.strip()] + except Exception as exc: + print("FAIL: cannot read MD: %r" % exc) + return 1 + + # Locate the separator row (|---|---|---|---|---|---|---|) then count the + # 20 rows beneath it. + sep_idx = None + for i, ln in enumerate(md_lines): + cells = [c.strip() for c in ln.strip().strip("|").split("|")] + if cells and all(c.startswith("-") for c in cells): + sep_idx = i + break + if sep_idx is None: + print("FAIL: no markdown table separator row found") + return 1 + body = [ln for ln in md_lines[sep_idx + 1:] + if ln.strip().startswith("|") and ln.strip() != ""] + if len(body) != 20: + print("FAIL: MD data rows %d != 20" % len(body)) + return 1 + + print("PASS: %d rows cell-exact vs crypto-top20-fresh.json; md has 20 rows" % len(csv_rows)) + return 0 + + +if __name__ == "__main__": + sys.exit(main())