"""Part B — geometry grid over the seven crown policies (pre-reg 2026-09-09).""" import itertools, numpy as np, pandas as pd, warnings, importlib.util, sys from scipy import stats warnings.filterwarnings("ignore") spec=importlib.util.spec_from_file_location("cp","crown_policies.py"); cp=importlib.util.module_from_spec(spec) # import without executing the run at module bottom: exec only the definitions src=open("crown_policies.py").read().split("\ndf=run()")[0]; exec(src, cp.__dict__) FP=cp.FP; d=cp.load_nq(); QUAD=cp.QUAD GRID=list(itertools.product([1.0,1.5,2.0],[60,120,240],[1.5,2.0,3.0])) def walk(rth, sig_min, side, f5, i5, rr, cap, mult): stop_d=max(cp.STOP_FLOOR, mult*cp.atr3(f5,i5)); nxt=rth[rth["min"]>=sig_min] if nxt.empty: return None entry=nxt.iloc[0]["open"]+(cp.SLIP if side>0 else -cp.SLIP); fill_min=int(nxt.iloc[0]["min"]); stop=entry-side*stop_d; target=entry+side*rr*stop_d lows=nxt["low"].values; highs=nxt["high"].values; closes=nxt["close"].values; mins=nxt["min"].values.astype(int) for k in range(len(nxt)): m=mins[k]; hs = lows[k]<=stop if side>0 else highs[k]>=stop; ht = highs[k]>=target if side>0 else lows[k]<=target if hs: return -1.0-cp.SLIP/stop_d if ht: return rr if m-fill_min>=cap or m>=959: return (side*(closes[k]-entry)-cp.SLIP)/stop_d return (side*(closes[-1]-entry)-cp.SLIP)/stop_d # reuse Part A's signal detection by re-running the session loop with all geometries per signal rows=[]; prior=None; rth_ranges=[]; prev_day=None for sday,g in d.groupby("sday"): rth=g[(g["min"]>=570)&(g["min"]<960)].reset_index(drop=True); on=g[(g["min"]>=1080)|(g["min"]<570)] if len(rth)<300: continue rng=rth["high"].max()-rth["low"].min() ok = prior is not None and prev_day is not None and (sday-prev_day).days<=4 and len(rth_ranges)>=20 and sday not in QUAD and sday>=pd.Timestamp("2012-01-03").date() and len(on)>=60 if ok: atr20=float(np.mean(rth_ranges[-20:])); pc,vah,val,poc=prior o=rth.iloc[0]["open"]; gap=(o-pc)/atr20; onh=on["high"].max(); onl=on["low"].min(); onq=sum(1 for e in cp.ON_EDGES if (onh-onl)/atr20>e); onlast=on.iloc[-1]["close"] accept = 1 if onlast>vah else (-1 if onlastonh else (-1 if c[i]vah and val<=c[i]<=vah: return -1 if c[i-1]0 and c[i]>vah and any(c[k]0 and c[i]val for k in range(0,i))) else 0 return 0 sigs={} sigs["P5_on_break"]=cp.first_signal(f5,brk); sigs["P1_expansion_break"]=sigs["P5_on_break"] if onq>=3 else (None,0) sigs["P2_contained_fade"]=cp.first_signal(f5,fade) if onq<=1 else (None,0) sigs["P3_gap_fade"]=cp.first_signal(f5,lambda i: 1 if c[i]>pc else 0) if gap<=cp.GAP_LO else (cp.first_signal(f5,lambda i: -1 if c[i]=cp.GAP_HI else (None,0)) sigs["P4_value_cont"]=cp.first_signal(f5,cont) if accept!=0 else (None,0) sigs["P6_orb15"]=cp.first_signal(f5,lambda i: (1 if (f5.endmin[i]>585 and c[i]>orb_h) else (-1 if (f5.endmin[i]>585 and c[i]=3 else (sigs["P2_contained_fade"] if onq<=1 else (None,0)) for pol,(i5,side) in sigs.items(): if i5 is None or not side: continue for rr,cap,mult in GRID: r=walk(rth,int(f5.endmin[i5]),side,f5,i5,rr,cap,mult) if r is not None: rows.append((sday,pol,rr,cap,mult,side,r)) rth_ranges.append(rng); va=cp.value_area(rth); prior=(rth.iloc[-1]["close"],va[1],va[2],va[0]); prev_day=sday df=pd.DataFrame(rows,columns=["date","pol","rr","cap","mult","side","r"]); df.to_parquet("data/crown_geometry.parquet") half=pd.Timestamp("2019-01-01").date(); ALPHA=0.05/189 L=[f"# Crown-play geometry grid — results · {pd.Timestamp.now():%Y-%m-%d %H:%M}\n\n{len(df):,} walks = triggers × 27 geometries; α = {ALPHA:.5f}.\n"] L.append("| policy | target R | cap min | stop ×ATR3 | n | hit% | R/trig | H1 | H2 | +yrs | p vs 0 | verdict |\n|---|---|---|---|---|---|---|---|---|---|---|---|") out=[] for (pol,rr,cap,mult),g in df.groupby(["pol","rr","cap","mult"]): h1=g[g.date=half].r; yrs=g.groupby(pd.to_datetime(g.date).dt.year).r.mean(); pos=int((yrs>0).sum()) t=stats.ttest_1samp(g.r,0).pvalue if len(g)>2 else 1 ok= len(g)>=100 and h1.mean()>=0.10 and h2.mean()>=0.10 and pos>=10 and t0 v="VALIDATED" if ok else ("suggestive" if (len(g)>=100 and h1.mean()>=0.05 and h2.mean()>=0.05) else "refuted") out.append((g.r.mean(),pol,rr,cap,mult,len(g),(g.r>=rr-1e-9).mean()*100,h1.mean(),h2.mean(),pos,t,v)) out.sort(reverse=True) for m,pol,rr,cap,mult,n,hit,h1,h2,pos,t,v in out[:25]: L.append(f"| {pol} | {rr} | {cap} | {mult} | {n} | {hit:.0f} | {m:+.3f} | {h1:+.3f} | {h2:+.3f} | {pos}/15 | {t:.4f} | {v} |") L.append(f"\n(top 25 of 189 cells by mean R; full grid in data/crown_geometry.parquet)\n\n## Survivors\n"+ (", ".join(f"{o[1]} {o[2]}R/{o[3]}m/{o[4]}×" for o in out if o[11]=="VALIDATED") or "none") + "\n\n## Suggestive\n" + (", ".join(f"{o[1]} {o[2]}R/{o[3]}m/{o[4]}× ({o[0]:+.3f})" for o in out if o[11]=="suggestive") or "none")) L.append("\n## Geometry marginals (mean R/trigger across all policies)\n"+df.groupby("rr").r.mean().round(3).to_string()+"\n"+df.groupby("cap").r.mean().round(3).to_string()+"\n"+df.groupby("mult").r.mean().round(3).to_string()) res="\n".join(L); open(f"{FP}/studies/2026-09-09-crown-geometry-results.md","w").write(res); print(res)