#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""三重奏 BTS — Python信号计算 (与TDX公式逐位一致)"""
import numpy as np
import pandas as pd

def _ema(X, N):
    out = np.empty_like(X, dtype=np.float64); out[0] = X[0]; m = 2.0/(N+1)
    for i in range(1, len(X)): out[i] = X[i]*m + out[i-1]*(1-m)
    return out

def _sma(X, N, M):
    out = np.empty_like(X, dtype=np.float64); out[0] = X[0]
    for i in range(1, len(X)): out[i] = (X[i]*M + out[i-1]*(N-M))/N
    return out

def _rmin(X, N): return pd.Series(X).rolling(N, min_periods=1).min().values
def _rmax(X, N): return pd.Series(X).rolling(N, min_periods=1).max().values

def _cross_up(A, B):
    out = np.zeros(len(A), dtype=bool); out[1:] = (A[1:]>=B[1:]) & (A[:-1]<B[:-1]); return out

def _cross_dn(A, B):
    out = np.zeros(len(A), dtype=bool); out[1:] = (A[1:]<=B[1:]) & (A[:-1]>B[:-1]); return out

def _barslast(x):
    out = np.full(len(x), 999999.0); last = -1
    for i in range(len(x)):
        if x[i]: last = i
        if last >= 0: out[i] = i - last
    return out

def _ref(X, k):
    out = X.copy()
    if k > 0: out[k:] = X[:-k]; out[:k] = X[0]
    return out

def compute_bts(C, H, L, O):
    """三重奏信号: 返回 dict(buy, tp_warn, sell, C7, C9, low_vol)"""
    n = len(C)
    GATE=3.5; HHUP=0.5; HHDN=0.0
    UP_T=GATE+HHUP; DN_T=GATE-HHDN; SB=120; OFF=4.5

    # C2
    B1=np.minimum(C,O); B2=np.where(B1>0,(B1-L)/L*100,0)
    ar=np.arange(n)
    B3=np.where(ar>0,(C-_ref(C,1))/_ref(C,1)*100,0)
    B4=np.where(ar>0,(O-_ref(C,1))/_ref(C,1)*100,0)
    B5=np.where(O>0,(C-O)/O*100,0); B6=np.maximum(C,O)
    B7=np.where(B6>0,(H-B6)/B6*100,0)
    B8=B5+B7; B9=B5+B2; C1=B7+B2
    C2=np.maximum(np.maximum(np.maximum(np.maximum(B3,B4),B5),np.maximum(B7,B2)),
                  np.maximum(C1,np.maximum(B8,B9)))*1.2

    # Av
    cs=np.cumsum(C2)
    avg_raw=np.where(ar<20, cs/(ar+1), pd.Series(C2).rolling(20,min_periods=1).mean().values)
    # AVGVOL
    AVGVOL=np.where(ar<20, cs/(ar+1), _ema(C2,20) if False else pd.Series(C2).rolling(20,min_periods=1).mean().values)

    # 档位
    UP=AVGVOL>UP_T; DN=AVGVOL<DN_T
    BU=_barslast(UP); BD=_barslast(DN)
    st_low=BU>=BD; st_high=~st_low
    nost_low=AVGVOL<GATE; nost_high=~nost_low
    enough=ar>=SB
    low_vol=np.where(enough, st_low, nost_low)
    high_vol=np.where(enough, st_high, nost_high)

    # 吸货线
    pL=np.roll(L,1); pL[0]=L[0]
    A2=_sma(np.abs(L-pL),3,1); A3=_sma(np.maximum(L-pL,0),3,1)
    A4=np.where(A3>0.000001,A2/A3*100,100); A5=_ema(A4*10,3)
    A6=np.where(low_vol,_rmin(L,26),_rmin(L,21))
    A7=np.where(low_vol,_rmax(A5,26),_rmax(A5,21))
    A8=np.where(L<=A6,(A5+A7*2)/2,0)
    xihuo_line=np.minimum(_ema(A8,3)/618.0,25)

    # 波动线
    C5=(2*C+H+L)/4
    RLL=_rmin(L,34); RHL=_rmax(H,34); RGL=RHL-RLL
    RSVL=np.where(RGL>0.000001,(C5-RLL)/np.where(RGL<=0,1,RGL)*100,50)
    C7L0=_ema(RSVL,13); C7L=np.where(RGL>0.000001,C7L0,50)
    RL2=_rmin(L,21); RH2=_rmax(H,21); RG2=RH2-RL2
    RSVH=np.where(RG2>0.000001,(C5-RL2)/np.where(RG2<=0,1,RG2)*100,50)
    C7H0=_ema(RSVH,11); C7H=np.where(RG2>0.000001,C7H0,50)
    C7=np.where(low_vol,C7L,C7H)
    C9=_ema(0.667*_ref(C7,1)+0.333*C7,2)

    # 黄金止盈位
    D1=_rmin(L,20); D2=_rmax(H,20)
    D3=np.where(D2>D1,(C-D1)/(D2-D1),0)
    golden_tp=D3>=np.where(low_vol,0.618,0.5)

    # 买点
    xih_active=(xihuo_line>0.001)|(_ref(xihuo_line,1)>0.001)|(_ref(xihuo_line,2)>0.001)
    trend_turn=_cross_up(C7,C9)
    VT=np.where(low_vol,2.5,3.5)
    vol_pass=C2>=VT
    buy=xih_active&trend_turn&vol_pass

    # 止盈T
    H13=_rmax(H,13); H11=_rmax(H,11)
    C13=_rmax(C7,13); C11=_rmax(C7,11)
    pnh=np.where(low_vol,(H==H13)&(_ref(H,1)<_ref(H13,1)),(H==H11)&(_ref(H,1)<_ref(H,1)))
    inh=np.where(low_vol,C7<_ref(C13,1),C7<_ref(C11,1))
    top_div=pnh&inh&(C7>65)
    tp_warn=(golden_tp&top_div)|((C7>np.where(low_vol,75,78))&(_ref(C7,1)>C7))

    # 终卖S
    sell=_cross_dn(C7,C9)&(C7>np.where(low_vol,55,50))

    return dict(buy=buy, tp_warn=tp_warn, sell=sell, C7=C7, C9=C9, low_vol=low_vol, C2=C2)
