"""Reproduce Litecoin.watch snapshot and block-interval statistics from CSV. Usage: python reproduce.py observations.csv blocks.csv Download matching periods from /api/research.php; Unix timestamps are UTC. No network requests or third-party dependencies. Source: Blockchair observations. """ import csv,sys,json def quantile(values,q): values=sorted(values) if not values:return None i=(len(values)-1)*q;a=int(i);b=min(a+1,len(values)-1) return values[a]+(values[b]-values[a])*(i-a) with open(sys.argv[1],newline='',encoding='utf-8-sig') as f:observations=list(csv.DictReader(f)) with open(sys.argv[2],newline='',encoding='utf-8-sig') as f:blocks=sorted(csv.DictReader(f),key=lambda b:int(b['height'])) gaps=[];missing=negative=0 for a,b in zip(blocks,blocks[1:]): if int(b['height'])!=int(a['height'])+1: missing+=int(b['height'])-int(a['height'])-1;continue gap=int(b['block_time'])-int(a['block_time']) if gap<0:negative+=1 else:gaps.append(gap) print(json.dumps({'snapshots':len(observations),'mempool_median':quantile([float(x['mempool']) for x in observations if x['mempool']],.5),'blocks':len(blocks),'valid_intervals':len(gaps),'interval_median_seconds':quantile(gaps,.5),'interval_p90_seconds':quantile(gaps,.9),'intervals_over_10_minutes':sum(x>600 for x in gaps),'missing_heights':missing,'negative_intervals_excluded':negative},indent=2))