"""A light re-implementation of PrimeAcademyStrategy's plain bracket on the hourly EURUSD file (no broker replay, no account rules):
entry at the close of the first bar of the day whose open is in [7,10) UTC, stop = max(beta*sigma_KF(prev day), floor) pips, target same distance,
barriers on bar high/low, stop first on a tie, one entry a day and none on the exit day.  Returns one record per trade.
Used for: pace at a given stop rule, mean of 1/stop (Jensen), swap nights, and the strict daily-loss reading."""
import numpy as np, pandas as pd
PIP=1e-4
def load():
    d=pd.read_parquet('/root/Projects/trading-framework-prime/historical_data/EURUSD/ohlcv_3600/eurusd_1h.parquet')
    ts=(d.ts_event.astype('int64')//10**9).to_numpy()
    return ts,d.open.to_numpy(),d.high.to_numpy(),d.low.to_numpy(),d.close.to_numpy()
def sigma_by_day(ts,c,rho=0.0025,seed_n=30):
    day=ts//86400
    ud,first=np.unique(day,return_index=True)
    last=np.r_[first[1:]-1,len(c)-1]
    dc=c[last]; r=np.r_[0.0,np.diff(dc)]
    sig=np.full(len(ud),np.nan); v=None; P=1.0; ssum=0.0
    # filter value after day k's close update = sigma used on day k+1 (known at the first bar of day k+1)
    for k in range(len(ud)):
        r2=r[k]**2
        if k<seed_n:
            ssum+=r2; mean=ssum/(k+1)
            if k+1>=seed_n: v=max(mean,1e-8)
            sig[k]=np.sqrt(max(mean,1e-12))
        else:
            Pm=P+rho; K=Pm/(Pm+1); v=v+K*(r2-v); P=(1-K)*Pm; sig[k]=np.sqrt(max(v,1e-12))
    return ud,sig/PIP,first   # sig[k]: sigma after day k's close
def trades(beta=0.75,floor=30.0,seed=1,start_h=7,end_h=10,rr=1.0,side=None):
    # rr: target distance = rr * stop distance (rr=1 reproduces the deployed bracket)
    ts,o,h,l,c=load()
    rng=np.random.default_rng(seed)
    ud,sig,first=sigma_by_day(ts,c)
    dayidx=np.searchsorted(ud,ts//86400)
    hour=(ts%86400)//3600
    n=len(ts); out=[]
    i=0; last_exit_day=-1
    nd=len(ud)
    while i<n:
        d=dayidx[i]
        if d<=last_exit_day or d<31 or not (start_h<=hour[i]<end_h):
            i+=1; continue
        stop_pips=max(beta*sig[d-1],floor)     # yesterday's sigma
        stop=stop_pips*PIP
        s=1.0 if (rng.random()<0.5 if side is None else side>0) else -1.0
        e=c[i]; res=None
        j=i+1
        bnd=[]  # open profit (in stop units) at each 21:00 UTC rollover crossed, using the close of the bar ending at 21:00
        while j<n:
            adverse=(e-l[j]) if s>0 else (h[j]-e)
            fav=(h[j]-e) if s>0 else (e-l[j])
            if adverse>=stop: res=-1; break
            if fav>=stop*rr: res=1; break
            if hour[j]==20:   # bar 20:00-21:00 closes at the 21:00 UTC rollover
                bnd.append(s*(c[j]-e)/stop)
            j+=1
        if res is None: break
        hold=(ts[j]+3600-ts[i]-3600)/86400.0
        # swap nights: rollovers at 21:00 UTC crossed while open; Wednesday counts 3, none Sat/Sun
        t0=pd.Timestamp(ts[i]+3600,unit='s'); t1=pd.Timestamp(ts[j]+3600,unit='s')
        nn=0; dd=t0.normalize()
        while dd<=t1.normalize():
            roll=dd+pd.Timedelta(hours=21)
            if t0<roll<=t1: nn+=3 if roll.dayofweek==2 else (0 if roll.dayofweek>=5 else 1)
            dd+=pd.Timedelta(days=1)
        out.append(dict(i=i,j=j,t_in=ts[i]+3600,t_out=ts[j]+3600,stop=stop_pips,res=res,hold=hold,nights=nn,bnd=bnd,nbnd=len(bnd)))
        last_exit_day=dayidx[j]
        i=j+1 if dayidx[min(j+1,n-1)]>last_exit_day else j+1
    return pd.DataFrame(out)
