"""Independently verify CoinNudge's sector-research v2 public release. Download all files in this directory and run ``python3 reproduce.py``. No CoinNudge server code, database or third-party package is required. """ import csv import hashlib import json import statistics from datetime import date, timedelta from pathlib import Path HERE = Path(__file__).resolve().parent def read_json(name): return json.loads((HERE / name).read_text()) def read_csv(name): with (HERE / name).open(newline="") as handle: return list(csv.DictReader(handle)) def close(actual, expected, tolerance=0.000002): assert abs(float(actual) - float(expected)) <= tolerance, (actual, expected) def dates(start, count): first = date.fromisoformat(start) return [(first + timedelta(days=i)).isoformat() for i in range(count)] def max_drawdown(prices): peak = prices[0] result = 0.0 for price in prices: peak = max(peak, price) result = min(result, price / peak - 1) return result def main(): manifest = read_json("manifest.json") files = {p.name for p in HERE.iterdir() if p.is_file() and p.name != "manifest.json"} assert set(manifest["files"]) == files for name, expected in manifest["files"].items(): assert hashlib.sha256((HERE / name).read_bytes()).hexdigest() == expected, name study = read_json("sector-research.json") rows = read_csv("sector-observations.csv") candidates = read_csv("sector-candidates.csv") summaries = read_csv("sector-summary.csv") assert len(rows) == len(study["observations"]) == study["audit"]["eligible_assignment_n"] assert len(candidates) == study["audit"]["candidate_assignment_n"] assert len(summaries) == len(study["selected_sectors"]) == 14 assert {r["sector"] for r in rows} == set(study["selected_sectors"]) assert any(r["sector"] == "Meme" for r in rows) assert sum(r["status"] == "eligible" for r in candidates) == len(rows) bars = {} for row in read_csv("daily-inputs.csv"): bars.setdefault(row["symbol"], {})[row["utc_day"]] = row days = dates("2026-06-26", 91) assert days[-1] == "2026-09-24" btc = bars["BTC"] btc_return = float(btc[days[-1]]["open_usdt"]) / float(btc[days[0]]["open_usdt"]) - 1 close(btc_return, study["btc_return_90d"]) for published, row in zip(rows, study["observations"]): assert (published["sector"], published["symbol"]) == (row["sector"], row["symbol"]) prices = [float(bars[row["symbol"]][day]["open_usdt"]) for day in days] volume = statistics.median(float(bars[row["symbol"]][day]["quote_volume_usdt"]) for day in days[-30:]) assert volume >= study["liquidity_floor_usdt"] close(prices[-1] / prices[-8] - 1, row["return_7d"]) close(prices[-1] / prices[-31] - 1, row["return_30d"]) close(prices[-1] / prices[0] - 1, row["return_90d"]) close(max_drawdown(prices), row["max_drawdown_90d"]) close(volume, row["quote_volume_median_30d_usdt"], .01) close(100 * (prices[-1] / prices[0] - 1 - btc_return), row["btc_relative_90d_pp"], .00006) for summary in study["sector_summaries"]: group = [r for r in study["observations"] if r["sector"] == summary["sector"]] assert summary["eligible_n"] == len(group) assert summary["positive_90d_n"] == sum(r["return_90d"] > 0 for r in group) assert summary["above_btc_90d_n"] == sum(r["btc_relative_90d_pp"] > 0 for r in group) for window in (7, 30, 90): close(statistics.median(r[f"return_{window}d"] for r in group), summary[f"median_return_{window}d"]) print(f"PASS: {len(candidates)} candidates, {len(rows)} included assignments, 14 sector summaries, Meme included") if __name__ == "__main__": main()