# popular-strategies engine: 5m bars, 8 popular strategies (PREREG), regime labels. Reuses buyer-method-audit lib/lib_trail (sim, costs, option panels). import sys; sys.path.insert(0,'/workspace/research/buyer-method-audit/scripts') from lib_trail import * L='/workspace/research/popular-strategies' def bars5(a): o,h,l,c,v=a; n=72; B=np.full((5,n),np.nan) for k in range(n): s=slice(5*k,5*k+5); cc=c[s] if np.all(np.isnan(cc)): continue B[0,k]=o[5*k] if not np.isnan(o[5*k]) else np.nanmin(o[s]) if not np.all(np.isnan(o[s])) else np.nan B[1,k]=np.nanmax(h[s]); B[2,k]=np.nanmin(l[s]); idx=np.where(~np.isnan(cc))[0]; B[3,k]=cc[idx[-1]]; B[4,k]=5*k+idx[-1] return B def km(hm): return (hm2i(hm)-4)//5 # 5m bar index whose last minute is hm def wilder(x,n): out=np.full(len(x),np.nan); s=None; cnt=0; acc=0 for i,v in enumerate(x): if np.isnan(v): out[i]=s if s is not None else np.nan; continue if s is None: acc+=v; cnt+=1 if cnt==n: s=acc/n else: s=(s*(n-1)+v)/n out[i]=s if s is not None else np.nan return out def ema(x,n): out=np.full(len(x),np.nan); a=2/(n+1); s=None for i,v in enumerate(x): if np.isnan(v): out[i]=s if s is not None else np.nan; continue s=v if s is None else a*v+(1-a)*s; out[i]=s return out def continuous(S): days=sorted(S); rows=[] for d in days: B=bars5(S[d]) for k in range(72): if not np.isnan(B[3,k]): rows.append((d,k,B[0,k],B[1,k],B[2,k],B[3,k],int(B[4,k]))) F=pd.DataFrame(rows,columns=['date','k','o','h','l','c','m']) c=F.c.values; h=F.h.values; l=F.l.values; pc=np.r_[np.nan,c[:-1]] tr=np.nanmax(np.vstack([h-l,np.abs(h-pc),np.abs(l-pc)]),axis=0); atr=wilder(tr,10) hl2=(h+l)/2; ub=hl2+3*atr; lb=hl2-3*atr; fub=np.full(len(c),np.nan); flb=np.full(len(c),np.nan); st=np.full(len(c),np.nan); dirn=np.zeros(len(c)) for i in range(len(c)): if np.isnan(atr[i]): continue if i==0 or np.isnan(fub[i-1]): fub[i]=ub[i]; flb[i]=lb[i]; dirn[i]=1; st[i]=flb[i]; continue fub[i]=ub[i] if (ub[i]fub[i-1]) else fub[i-1] flb[i]=lb[i] if (lb[i]>flb[i-1] or c[i-1]fub[i] else -1 st[i]=flb[i] if dirn[i]==1 else fub[i] F['st']=st; F['stdir']=dirn; F['e9']=ema(c,9); F['e21']=ema(c,21) d_=np.diff(c,prepend=np.nan); up=np.where(d_>0,d_,0.0); dn=np.where(d_<0,-d_,0.0); up[0]=dn[0]=np.nan au=wilder(up,14); ad=wilder(dn,14); F['rsi']=100-100/(1+au/np.where(ad==0,np.nan,ad)); F.loc[(ad==0)&(au>0),'rsi']=100 return F def daily_table(S,PC): rows=[] for d in sorted(S): o,h,l,c,v=S[d] if np.isnan(o[0]): continue rows.append(dict(date=d,open=o[0],high=np.nanmax(h[:360]),low=np.nanmin(l[:360]),prev_close=PC.get(d,np.nan))) return pd.DataFrame(rows).set_index('date') def signals(S,PC): F=continuous(S); F['pos']=np.arange(len(F)); G={d:g.set_index('k') for d,g in F.groupby('date')} D=daily_table(S,PC); days=list(D.index) # prior-day H/L/C (official close) for PDH/PDL and CPR; prior-day high/low from 1m bars 09:15-15:14 (+official close) W=[]; out=[] for j,d in enumerate(days): a=S[d]; o,h,l,c,v=a; vw=vwap(a); g=G.get(d) if g is None or j==0: continue pd_=days[j-1]; PH,PL,PCl=D.high[pd_],D.low[pd_],D.prev_close[d] P=(PH+PL+PCl)/3; BC=(PH+PL)/2; TC=2*P-BC; wid=abs(TC-BC)/P; W.append(wid) def first(cond_fn,k0,k1): for k in range(k0,k1+1): if k not in g.index: continue r=cond_fn(k,g.loc[k]) if r: return k,r return None,None def add(name,k,dd,stop): m=int(g.loc[k,'m']); out.append(dict(cand=name,date=d,m=m,dir=dd,entry=c[m],stop=stop)) # P1/P2 ORB for name,N,k1 in (('P1',15,km('11:59')),('P2',5,km('10:59'))): orh=np.nanmax(h[:N]); orl=np.nanmin(l[:N]) k,r=first(lambda k,b:(1 if b.c>orh else -1 if b.c0 else orh) # P3 PDH/PDL if PLPH else -1 if b.c0 else (g.loc[k,'h']+5)) # P4 narrow CPR hist=W[-251:-1] if len(hist)>=120 and widtop and b.c>vw[int(b.m)]) else -1 if (b.c0 else top) # P5 Supertrend flip + VWAP idx=list(g.index); def prevrow(k): i=int(g.loc[k,'pos']); return F.iloc[i-1] if i>0 else None for k in range(km('09:44'),km('14:29')+1): if k not in g.index: continue b=g.loc[k]; pr=prevrow(k) if pr is None: continue if b.stdir==1 and pr.stdir==-1 and b.c>vw[int(b.m)]: add('P5',k,1,b.st); break if b.stdir==-1 and pr.stdir==1 and b.cb.e21 and pr.e9<=pr.e21 and b.c>vw[int(b.m)]: add('P6',k,1,last6.l.min()-5); break if b.e9=pr.e21 and b.c70>=b.rsi: add('P7',k,-1,last6.h.max()+5); break # P8 gap fade gap=(o[0]-PCl)/PCl if PCl==PCl else np.nan if gap==gap and 0.003<=abs(gap)<=0.015: orh=np.nanmax(h[:15]); orl=np.nanmin(l[:15]); dd=-1 if gap>0 else 1 k,r=first(lambda k,b:(dd if ((dd<0 and b.c0 and b.c>orh)) else 0),3,km('10:59')) if k is not None: add('P8',k,r,orh if r<0 else orl) X=pd.DataFrame(out) X[['stop','R']]=[clipstop(e,s,dd) for e,s,dd in zip(X.entry,X.stop,X.dir)] return X