#!/usr/bin/env python3
"""DC Auditor: Lane F (DC2.1) rerun from A2's own published files. Read only.
Inputs (download into the working dir). Audited version: features md5 5df1bc67809bf2e11d2caa53f4181c92 (609 trig rows);
A2 republished it later (md5 457c718daa01488cac70b403ed37c01a, 612 trig rows); the script runs on both.
  http://159.223.88.168/hx-c9ois/dc/a2/dc-a2-1-features.csv.gz   (md5 5df1bc67809bf2e11d2caa53f4181c92)
  http://159.223.88.168/hx-c9ois/dc/a2/raw/sw.csv.gz, coin.csv.gz, btime.csv.gz
Steps:
  1. block time: ts = btime.t[b//1000] + (b%1000)*btime.spb[b//1000]
  2. trigger row = features cp=='trig'; tc = trigger time (A2's own column)
  3. ch0  = last swap px with ts<=tc  / max px with ts<=tc        (price AT the trigger)
     ch30 = last swap px with ts<=tc+30 / max px with ts<=tc      (== A2 close_high on 608 of 609 rows)
  4. entry px = last swap px with ts<=tc+delay
     hit = any sell (side -1) of $100+ with px >= 5*entry, after entry, before coin.pull_b block time
  5. flat book: hit +$1,940, else -$500 ($500 ticket); $/day = sum/7 per half
  6. random: same n drawn from the same half's 609 trigger coins, 4,000 draws
Output: dc-audit-lanef-trades.csv (one row per coin, all flags) and the summary lines below.
"""
import pandas as pd, numpy as np
f=pd.read_csv('dc-a2-1-features.csv.gz'); t=f[f.cp=='trig'].copy()
t['half']=np.where(t.day<='2026-09-27','A','B')  # A2 prereg halves: A 21-27 Sep, B 28 Sep-4 Oct
if 'pnl' not in t: t['pnl']=np.nan
sw=pd.read_csv('sw.csv.gz'); bt=pd.read_csv('btime.csv.gz').set_index('k'); coin=pd.read_csv('coin.csv.gz').set_index('token')
k=sw.b//1000; sw['ts']=bt.t.reindex(k).values+(sw.b%1000)*bt.spb.reindex(k).values
sw=sw[sw.token.isin(t.tok)].sort_values(['token','b']); g=dict(tuple(sw.groupby('token')))
def bts(b): k=int(b//1000); return bt.t[k]+(b%1000)*bt.spb[k]
def px_at(s,tt): x=s[s.ts<=tt]; return x.px.iloc[-1] if len(x) else np.nan
rows=[]
for _,r in t.iterrows():
    s=g[r.tok]; hi=s[s.ts<=r.tc].px.max()
    pb=coin.pull_b.get(r.tok,np.nan); tp=bts(pb) if pd.notna(pb) else 1e12
    out={'tok':r.tok,'half':r.half,'day':r.day,'crew':r.crew,'cr':r.cr,'alumni_share':r.alumni_share,
         'a2_close_high':r.close_high,'ch0':px_at(s,r.tc)/hi,'ch30':px_at(s,r.tc+30)/hi,'a2_Y5':r.Y5,'a2_pnl':r.pnl}
    for d in (0,30,50):
        pe=px_at(s,r.tc+d); x=s[(s.ts>r.tc+d)&(s.ts<tp)&(s.side==-1)&(s.eth*r.ethusd>=100)&(s.px>=5*pe)]
        out['hit%d'%d]=len(x)>0
    # A2 v2 entry: FIRST trade at or after T1 + 30 s
    a=s[s.ts>=r.tc+30]
    if len(a):
        pe=a.px.iloc[0]; te=a.ts.iloc[0]; x=s[(s.ts>te)&(s.ts<tp)&(s.side==-1)&(s.eth*r.ethusd>=100)&(s.px>=5*pe)]
        out['hit30n']=len(x)>0; out['entry_gap_s']=te-r.tc
    else: out['hit30n']=False; out['entry_gap_s']=np.nan
    rows.append(out)
d=pd.DataFrame(rows); d.to_csv('dc-audit-lanef-trades.csv',index=False)
rng=np.random.default_rng(7); al=d.alumni_share>=0.053
def rep(name,mask,col):
    o=[]
    for h in 'AB':
        pp=np.where(d[d.half==h][col],1940.0,-500.0); m=mask[d.half==h].values; obs=pp[m].sum()
        r=np.array([rng.choice(pp,m.sum(),replace=False).sum() for _ in range(4000)])
        o.append(f'{h}: n {m.sum()} hits {int(d[d.half==h][col][m].sum())} $/day {obs/7:+.0f} beats random {(r<obs).mean():.3f}')
    print(f'{name:60s}',' | '.join(o))
print('check: hit30 == A2 Y5 on all rows:', bool((d.hit30==(d.a2_Y5==1)).all()), '| ch30 == A2 close_high (within 1e-6):', int(((d.ch30-d.a2_close_high).abs()<1e-6).sum()), 'of', len(d))
rep('A2 as published: close/high at +30 s, fill +30 s',(d.ch30<=0.784)&al,'hit30')
rep('close/high AT T1, fill +30 s',(d.ch0<=0.784)&al,'hit30')
rep('close/high at +30 s, fill +50 s',(d.ch30<=0.784)&al,'hit50')
rep('close/high AT T1, fill at T1',(d.ch0<=0.784)&al,'hit0')
rep('close/high AT T1, fill FIRST trade >= T1+30 s (A2 v2)',(d.ch0<=0.784)&al,'hit30n')
print('hit30n == A2 Y5 on all rows:', bool((d.hit30n==(d.a2_Y5==1)).all()), '| median entry gap s', d.entry_gap_s.median())
