import math
def ema(xs,n):
    k=2/(n+1); out=[]; e=None
    for x in xs:
        e=x if e is None else e+k*(x-e); out.append(e)
    return out
def stage_series(closes, W=90, BOT=60, BRK=30, EMA=20):
    """closes: list of positive floats (one per bar). returns list of (stage, info) per bar"""
    L=[math.log(c) for c in closes]; e=ema(L,EMA); res=[]
    for i in range(len(L)):
        if i<max(EMA,25): res.append(("WARMUP",{})); continue
        lo_w=L[max(0,i-W+1):i+1]; lo=min(lo_w); lo_i=max(0,i-W+1)+lo_w.index(lo); hi=max(lo_w)
        age=i-lo_i; up=math.exp(L[i]-lo)-1
        rng=hi-lo; pos=(L[i]-lo)/rng if rng>0 else 0.5
        above=L[i]>e[i]; rising=e[i]>e[i-5]
        prior=L[max(0,i-BRK):i]; brk=L[i]>=max(prior) if prior else False
        m=L[max(0,i-19):i+1]; mu=sum(m)/len(m); sd=(sum((x-mu)**2 for x in m)/len(m))**0.5; z=(L[i]-mu)/sd if sd>0 else 0
        info=dict(age=age,up=up,pos=pos,above=above,rising=rising,brk=brk,z=z,dd=math.exp(L[i]-hi)-1)
        if age<=2: st="FALLING"
        elif z>=2 and pos>0.8: st="HOT"
        elif age<=BOT and brk and above and rising: st="CONFIRMED UP"
        elif age<=BOT and age>=4 and above and rising: st="TURNING UP"
        elif age<=BOT and not (above and rising): st="BOTTOMING"
        elif above and rising: st="UPTREND"
        else: st="DRIFTING DOWN"
        res.append((st,info))
    return res
