if 1:
    from   oneclass.onedb   import Onedb
    from   oneclass.onedata import Onedata
    import os,math,sys,time,re,json,datetime,requests,warnings,logging,xlrd,traceback
    import numpy as np
    import pandas as pd
    import tushare as ts
    from   sqlalchemy import create_engine
    import sqlalchemy  #初始化数据库连接，使用pymysql模块
    import pymysql
    import multiprocessing as mp

# 全局共享计数器
shared_counter = None

# 初始化函数，用于在每个子进程中设置共享计数器
def init_counter(counter):
    global shared_counter
    shared_counter = counter
if 1:
    ts.set_token('8f6e67ec8a48922bbb531b95687625d99ee263940a07561ae95f4648')  # Tushare Pro
    pd.set_option('mode.chained_assignment', None)
    warnings.filterwarnings("ignore")
    durtime,durting1,FocusPeriod,uihandle,plock,showtype,havelistseq=0.001,0.5,0,0,0,1,1
    #stockcode,mysqlhost="300027.SS","119.45.95.223"
    stockcode,mysqlhost="300027.SS","60.204.129.214"
    xgresult,qsresult,allstocklist=pd.DataFrame(),pd.DataFrame(),pd.DataFrame()
class Oneqs():
    '''K线类 技术分析评分机制'''
    def __init__(self,stockcode,parent=None):
        super().__init__()                  #没有父类,是否需要
        self.allstocklist=pd.DataFrame()    #不需要被重复更新的数据
        self.init_parameter()               #需要被重复更新的数据
        self.onedb=Onedb()
        self.onedata=Onedata(1)
        '''自动运行'''
    def init_parameter(self):
        '''定义参数'''
        # 计算不同股票时需要重新初始化的变量
        # ??? 但是常用的趋势或结构参数应该不用每次都初始化
        self.hp=0.0
        self.ttlup=0.0
        self.ttldown=0.0
        self.mstatus=0
        self.period  = ['month', 'week', 'day', 'm30', 'm5', 'm1']
        self.periodab  = ['m', 'w', 'd', 't', 'f', 'o']
        self.p2ab={'month':'m','week':'w','day':'d','m30':'t','m5':'f','m1':'o'}
        self.QSKJ    = [100, 50, 20, 10, 5, 2]  # 可以从config.ini设置
        self.diffbyp ={"month":0.08,"week":  0.04,"day":0.02   ,"m30":0.01   ,"m5":0.005   ,"m1":0.0025 }
        self.space   ={"month": 1,"week":0.2,"day":0.08,"m30":0.04,"m5":0.02,"m1":0.01}
        self.QS      ={"month":"","week":"","day":"","m30":"","m5":"","m1":""}
        self.JG      ={"month":"","week":"","day":"","m30":"","m5":"","m1":""}
        self.keypoint={"QS":0,"NDG":0,"NKL":0,"DD":0,"Kai":0,"GG":0,"Luo":0,"CHL1":0,"DDCGorGGCD1":0,"CHL":0,"DDCGorGGCD":0,"DDCGorGGCD1ma30":0,"DDCGorGGCDma30":0,"GGday":0,"DDday":0}
        self.kdata_m = pd.DataFrame()  # K线数据库 
        self.kdata_w = pd.DataFrame()
        self.kdata_d = pd.DataFrame()
        self.kdata_t = pd.DataFrame()
        self.kdata_f = pd.DataFrame()
        self.kdata_o = pd.DataFrame()
        #趋势结果 - to Mysql
        self.QS_m = {}
        self.QS_w = {}
        self.QS_d = {}
        self.QS_t = {}
        self.QS_f = {}
        self.QS_o = {}
        #结构结果 - to Mysql
        self.JG_m = {}
        self.JG_w = {}
        self.JG_d = {}
        self.JG_t = {}
        self.JG_f = {}
        self.JG_o = {}
        #???
        self.all_YLZC = {}
        self.PnS = {} 
        #???
        self.pns=pd.DataFrame()
        self.pnsttl=pd.DataFrame()
        self.newpnsttl=pd.DataFrame()
        self.refreshK_timeinter=10000
        self.stockcode = stockcode  # 股票代码 300027.S
        self.sig=1                  # 信号
        self.havelist=pd.DataFrame()
        self.xgresult=pd.DataFrame()
        self.qsresult=pd.DataFrame() 
    '''
    def run(self):
        self.whole_process()
    def whole_process(self):
        self.get_div()          #取除权数据
        self.get_K_all()        #下载所有周期K线数据
        self.cal_ma_all()       #计算所有周期ma
        self.cal_qs_all()       #计算所有周期的趋势和结构数据
    '''

    def get_div_fromdb(self):
        #从onedb取除权数据 并 存放到 ./csv/divdata.csv
        #每天早上执行一次,当天就不用重复执行
        #是否需要重新索引和重新排序???
        #应该是在读取SPLIT.PWR并存放到db前,做索引和排序的工作
        #print("start get_divdata_fromdb")
        onedb=Onedb()
        mydiv = onedb.From_table("select * from divdata")
        mydiv.to_csv(rf"./csv/divdata.csv",encoding="utf_8_sig",index=False)
        #print("end get_divdata_fromdb")

    def xg_all_mp(self):
        #计算所有股票
        starttime=time.time()
        self.xgresult=pd.DataFrame()    #存放选股结果数据,每支股票一条记录
        self.qsresult=pd.DataFrame()    #存放趋势数据,每支股票最多6条记录
        # 创建共享计数器
        counter = mp.Value('i', 0)
        corelist=[0]
        
        try:
            pro = ts.pro_api()
            stocklist = pro.query('stock_basic', exchange='', list_status='L')
            stocklist = pd.concat([stocklist, pd.DataFrame([{'ts_code': "000001.SH", 'name': "上证综指"}])], ignore_index=True)
            stocklist = pd.concat([stocklist, pd.DataFrame([{'ts_code': "399006.SZ", 'name': "创业板指"}])], ignore_index=True)
            stocklist = pd.concat([stocklist, pd.DataFrame([{'ts_code': "399001.SZ", 'name': "深圳成指"}])], ignore_index=True)
            stocklist.to_csv(rf"./csv/tusharestocklist.csv",encoding="utf_8_sig",index=True)
        except:
            stocklist = pd.read_csv(r"./csv/tusharestocklist.csv")
            #stocklist=stocklist.drop_duplicates(['stockcode'])

        self.allstocklist=stocklist
        #print(self.allstocklist)

        stockqty=self.allstocklist.shape[0]
        qtyperjob=100
        coreqty=math.ceil(stockqty/qtyperjob)
        for i in range(1,coreqty):
            corelist.append(i*100)
        print("XXXX corelist",corelist)
        threadqty=16
        pool=mp.Pool(threadqty, initializer=init_counter, initargs=(counter,))
        qtyperthread=math.ceil(stockqty/threadqty)
        print("qtyperthread",qtyperthread)
        result=pool.map(self.xg_all_for_mp,corelist)
        a,b=zip(*result)
        for i in range(len(a)):
            self.xgresult=pd.concat([self.xgresult, a[i]], ignore_index=True)
            self.qsresult=pd.concat([self.qsresult, b[i]], ignore_index=True)
        self.xgresult = self.xgresult.replace([np.inf, -np.inf], 0)
        self.qsresult = self.qsresult.replace([np.inf, -np.inf], 0)
        onedb=Onedb()
        onedb.del_table("xgallnew")
        onedb.del_table("qsallnew")
        onedb.into_table(self.xgresult,"xgallnew","stockcode",9)
        onedb.into_table(self.qsresult,"qsallnew","stockcode",9)
        print("TTL time:", time.time()-starttime)
    def xg_all_for_mp(self,n):
        xgresult=pd.DataFrame()
        qsresult=pd.DataFrame()
        stocklist = self.allstocklist
        stocklist=stocklist[n:n+100]
        print("stock qty =", stocklist.shape[0])
        for index,row in stocklist.iterrows():
            self.stockcode=row['ts_code']
            # 使用全局共享计数器，加锁以确保线程安全
            global shared_counter
            with shared_counter.get_lock():
                shared_counter.value += 1
                current_count = shared_counter.value
            print(current_count,self.stockcode)
            if self.stockcode[-3:]==".BJ":
                self.onedata=Onedata(0)
            else:
                self.onedata=Onedata(1)
                #continue
            try:
                self.kdata_o,self.kdata_f,self.kdata_t,self.kdata_d,self.kdata_w,self.kdata_m=self.onedata.k(self.stockcode,"all")
            except:
                continue

            #hhh=self.kdata_d['high'].max() #.round(2)
            #lll=self.kdata_d['low'].min()  #.round(2)
            hhh=self.kdata_m['high'].max() #.round(2)
            lll=self.kdata_m['low'].min()  #.round(2)
            ttlk=self.kdata_d.shape[0]
            cp=self.kdata_d['close'][ttlk-1] #.round(2)
            try:
                #把历史最高价与历史最低价的差等分10份,现在处在第几份
                self.hp=(cp-lll)/((hhh-lll)/100)
                self.hp=self.hp.round(1)
            except:
                print("calculate self.hp error")

            #only cal ma
            self.cal_ma_all()
            #only cal QS
            self.kdata_m,self.QS_m,self.JG_m = self.cal_qs(self.kdata_m, self.stockcode, 'month')
            self.kdata_w,self.QS_w,self.JG_w = self.cal_qs(self.kdata_w, self.stockcode, 'week')
            self.kdata_d,self.QS_d,self.JG_d = self.cal_qs(self.kdata_d, self.stockcode, 'day')
            self.kdata_t,self.QS_t,self.JG_t = self.cal_qs(self.kdata_t, self.stockcode, 'm30')
            self.kdata_f,self.QS_f,self.JG_f = self.cal_qs(self.kdata_f, self.stockcode, 'm5')
            self.kdata_o,self.QS_o,self.JG_o = self.cal_qs(self.kdata_o, self.stockcode, 'm1')

            self.kdata_d['ma250'] = self.kdata_d['close'].rolling(250).mean().round(3)
            self.kdata_d= self.kdata_d.fillna(method='bfill')  # bfill由后向前填充 ffill由前向后填充
            yearup=0
            yearvalue = 0
            try:
                if self.kdata_d['ma250'][-2]<=self.kdata_d['ma250'][-1]:
                    yearup = 1
                else:
                    yearup=0
                    yearvalue=self.kdata_d['ma250'][-1]
            except:
                yearup=0
                yearvalue = 999

            try:
                yearvaluep=(self.kdata_d['close'][-1] - self.kdata_d['ma250'][-1])/self.kdata_d['close'][-1] * 100
                yearvaluep=yearvaluep.round(2)
            except:
                yearvaluep=-999
            try:
                yearvs30p=(self.kdata_d['ma30'][-1] - self.kdata_d['ma250'][-1])/self.kdata_d['ma30'][-1] * 100
                yearvs30p=yearvs30p.round(2)
            except:
                yearvs30p=-999

            try:
                tmpkdata=self.kdata_d[-30:]
                d30upqty = len(tmpkdata[tmpkdata['ma250'] < tmpkdata['close']])
            except:
                d30upqty=0

            try:
                tmpkdata=self.kdata_d[-250:]
                yearupqty = len(tmpkdata[tmpkdata['ma250'] < tmpkdata['close']])
            except:
                yearupqty = 0

            print(self.QS['month'],self.QS['week'],self.QS['day'],self.QS['m30'],self.QS['m5'],self.QS['m1'])
            if 1: #try:
                tmp={'scode':self.stockcode[0:6],'stockcode':self.stockcode,'stockname':row['name'],'time':self.QS_t['reviseTime'],'c':self.QS_t['C'],\
                     'QSm':self.QS['month'],'QSw':self.QS['week'],'QSd':self.QS['day'],'QSt':self.QS['m30'],'QSf':self.QS['m5'],'QSo':self.QS['m1'],\
                     'ma30m':self.QS_m['ma30'],'ma30w':self.QS_w['ma30'],'ma30d':self.QS_d['ma30'],'ma30t':self.QS_t['ma30'],'ma30f':self.QS_f['ma30'],'ma30o':self.QS_o['ma30'],\
                     'ma10m':self.QS_m['ma10'],'ma10w':self.QS_w['ma10'],'ma10d':self.QS_d['ma10'],'ma10t':self.QS_t['ma10'],'ma10f':self.QS_f['ma10'],'ma10o':self.QS_o['ma10'],\
                     'spacePm':self.QS_m['spaceP'],'profitPm':self.QS_m['profitP'],'riskPm':self.QS_m['riskP'],\
                     'NDGm':self.QS_m['NDG'],'NKLm':self.QS_m['NKL'],'DDm':self.QS_m['DD'],'Kaim':self.QS_m['Kai'],'GGm':self.QS_m['GG'],'Luom':self.QS_m['Luo'],\
                     'CHL1m':self.QS_m['CHL1'],'DDCGorGGCD1m':self.QS_m['DDCGorGGCD1'], 'CHLm':self.QS_m['CHL'],'DDCGorGGCDm':self.QS_m['DDCGorGGCD'],
                     'spacePw':self.QS_w['spaceP'],'profitPw':self.QS_w['profitP'],'riskPw':self.QS_w['riskP'],\
                     'NDGw':self.QS_w['NDG'],'NKLw':self.QS_w['NKL'],'DDw':self.QS_w['DD'],'Kaiw':self.QS_w['Kai'],'GGw':self.QS_w['GG'],'Luow':self.QS_w['Luo'],\
                     'CHL1w':self.QS_w['CHL1'],'DDCGorGGCD1w':self.QS_w['DDCGorGGCD1'], 'CHLw':self.QS_w['CHL'],'DDCGorGGCDw':self.QS_m['DDCGorGGCD'],
                     'spacePd':self.QS_d['spaceP'],'profitPd':self.QS_d['profitP'],'riskPd':self.QS_d['riskP'],\
                     'NDGd':self.QS_d['NDG'],'NKLd':self.QS_d['NKL'],'DDd':self.QS_d['DD'],'Kaid':self.QS_d['Kai'],'GGd':self.QS_d['GG'],'Luod':self.QS_d['Luo'],\
                     'CHL1d':self.QS_d['CHL1'],'DDCGorGGCD1d':self.QS_d['DDCGorGGCD1'], 'CHLd':self.QS_d['CHL'],'DDCGorGGCDd':self.QS_d['DDCGorGGCD'],
                     'spacePt':self.QS_t['spaceP'],'profitPt':self.QS_t['profitP'],'riskPt':self.QS_t['riskP'],\
                     'NDGt':self.QS_t['NDG'],'NKLt':self.QS_t['NKL'],'DDt':self.QS_t['DD'],'Kait':self.QS_t['Kai'],'GGt':self.QS_t['GG'],'Luot':self.QS_t['Luo'],\
                     'CHL1t':self.QS_t['CHL1'],'DDCGorGGCD1t':self.QS_t['DDCGorGGCD1'], 'CHLt':self.QS_t['CHL'],'DDCGorGGCDt':self.QS_t['DDCGorGGCD'],
                     'spacePf':self.QS_f['spaceP'],'profitPf':self.QS_f['profitP'],'riskPf':self.QS_f['riskP'],\
                     'NDGf':self.QS_f['NDG'],'NKLf':self.QS_f['NKL'],'DDf':self.QS_f['DD'],'Kaif':self.QS_f['Kai'],'GGf':self.QS_f['GG'],'Luof':self.QS_f['Luo'],\
                     'CHL1f':self.QS_f['CHL1'],'DDCGorGGCD1f':self.QS_f['DDCGorGGCD1'], 'CHLf':self.QS_f['CHL'],'DDCGorGGCDf':self.QS_f['DDCGorGGCD'],
                     'spacePo':self.QS_o['spaceP'],'profitPo':self.QS_o['profitP'],'riskPo':self.QS_o['riskP'],\
                     'NDGo':self.QS_o['NDG'],'NKLo':self.QS_o['NKL'],'DDo':self.QS_o['DD'],'Kaio':self.QS_o['Kai'],'GGo':self.QS_o['GG'],'Luoo':self.QS_o['Luo'],\
                     'CHL1o':self.QS_o['CHL1'],'DDCGorGGCD1o':self.QS_o['DDCGorGGCD1'], 'CHLo':self.QS_o['CHL'],'DDCGorGGCDo':self.QS_o['DDCGorGGCD'],\
                     'ConfirmedJGm':self.JG_m['ConfirmedJG'],'CurrentJGm':self.JG_m['CurrentJG'],'JGdonem':self.JG_m['JGdone'],'QXm':self.JG_m['QX'],'JGQRm':self.JG_m['JGQR'],\
                     'ConfirmedJGw':self.JG_w['ConfirmedJG'],'CurrentJGw':self.JG_w['CurrentJG'],'JGdonew':self.JG_w['JGdone'],'QXw':self.JG_w['QX'],'JGQRw':self.JG_w['JGQR'],\
                     'ConfirmedJGd':self.JG_d['ConfirmedJG'],'CurrentJGd':self.JG_d['CurrentJG'],'JGdoned':self.JG_d['JGdone'],'QXd':self.JG_d['QX'],'JGQRd':self.JG_d['JGQR'],\
                     'ConfirmedJGt':self.JG_t['ConfirmedJG'],'CurrentJGt':self.JG_t['CurrentJG'],'JGdonet':self.JG_t['JGdone'],'QXt':self.JG_t['QX'],'JGQRt':self.JG_t['JGQR'],\
                     'ConfirmedJGf':self.JG_f['ConfirmedJG'],'CurrentJGf':self.JG_f['CurrentJG'],'JGdonef':self.JG_f['JGdone'],'QXf':self.JG_f['QX'],'JGQRf':self.JG_f['JGQR'],\
                     'ConfirmedJGo':self.JG_o['ConfirmedJG'],'CurrentJGo':self.JG_o['CurrentJG'],'JGdoneo':self.JG_o['JGdone'],'QXo':self.JG_o['QX'],'JGQRo':self.JG_o['JGQR'],\
                     'hp':self.hp,'ttlup':self.ttlup,'ttldown':self.ttldown,'mstatus':self.mstatus,'yearup':yearup,
                     'yearvalue':yearvalue,'yearvaluep':yearvaluep,'yearvs30p':yearvs30p,'yearupqty':yearupqty,'d30upqty':d30upqty
                    }
                for key,value in tmp.items():
                    try:
                        if value>1000:
                            value=round(value)
                            tmp[key]=value
                    except:
                        #print("error!!!!!!!!!!!!!! line 277")
                        pass
                xgresult=pd.concat([xgresult, pd.DataFrame([tmp])], ignore_index=True)
            #xgresult=xgresult.append(tmp,ignore_index=True)
            # 将字典转换为DataFrame后再拼接
            qsresult=pd.concat([qsresult, pd.DataFrame([self.QS_m])], ignore_index=True)
            qsresult=pd.concat([qsresult, pd.DataFrame([self.QS_w])], ignore_index=True)
            qsresult=pd.concat([qsresult, pd.DataFrame([self.QS_d])], ignore_index=True)
            qsresult=pd.concat([qsresult, pd.DataFrame([self.QS_t])], ignore_index=True)
            qsresult=pd.concat([qsresult, pd.DataFrame([self.QS_f])], ignore_index=True)
            qsresult=pd.concat([qsresult, pd.DataFrame([self.QS_o])], ignore_index=True)
            self.init_parameter()
        return xgresult,qsresult

    def xg_2022_mp(self): 
        #计算2022中的股票
        starttime=time.time()
        self.xgresult=pd.DataFrame()
        self.qsresult=pd.DataFrame()
        #result=pd.DataFrame()
        # 创建共享计数器
        counter = mp.Value('i', 0)
        stocklist = pd.read_csv(r"./csv/2022.csv")
        print("read 2022.csv 300",stocklist)
        #print(f"former qty = {stocklist.shape[0]}")
        stocklist=stocklist.drop_duplicates(['stockcode'])
        print(f"former qty = {stocklist.shape[0]}")
        stockqty=stocklist.shape[0]
        threadqty=6
        pool=mp.Pool(threadqty, initializer=init_counter, initargs=(counter,))
        qtyperthread=math.ceil(stockqty/threadqty)
        print("qtyperthread",qtyperthread)
        #corelist=[0,10,20,30,40,50]
        corelist=[0,50,100,150,200,250,300,350]
        print(corelist)
        #self.xgresult=self.xgresult.append(pool.map(self.xg_2022_for_mp,corelist))
        result=pool.map(self.xg_2022_for_mp,corelist)
        a,b=zip(*result)
        for i in range(len(a)):
            # 检查a[i]和b[i]的类型，确保正确处理
            if isinstance(a[i], dict):
                self.xgresult=pd.concat([self.xgresult, pd.DataFrame([a[i]])], ignore_index=True)
            else:
                self.xgresult=pd.concat([self.xgresult, a[i]], ignore_index=True)
            
            if isinstance(b[i], dict):
                self.qsresult=pd.concat([self.qsresult, pd.DataFrame([b[i]])], ignore_index=True)
            else:
                self.qsresult=pd.concat([self.qsresult, b[i]], ignore_index=True)
        self.xgresult = self.xgresult.replace([np.inf, -np.inf], 0)
        self.qsresult = self.qsresult.replace([np.inf, -np.inf], 0)
        onedb=Onedb()
        
        onedb.del_table("xg2022new")
        onedb.del_table("qs2022new")

        onedb.into_table(self.xgresult,"xg2022new","stockcode",9)
        onedb.into_table(self.qsresult,"qs2022new","stockcode",9)

        print("???",self.xgresult);
        print("???",self.qsresult);

        onedb.execute_sql("delete `xgallnew` from `xgallnew`,`xg2022new` where `xgallnew`.`stockcode`=`xg2022new`.`stockcode`")
        onedb.into_table(self.xgresult,"xgallnew","stockcode",9)
        
        #onedb.execute_sql("select * from xgall a left join xg2022 b on a.stockcode=b.stockcode where b.stockcode is NULL")
        #onedb.into_table(self.xgresult,"xgall","stockcode",9)
        #self.xgresult.to_csv(rf"./tmpout/xg2022.csv",encoding="utf_8_sig",index=True)
        #self.qsresult.to_csv(rf"./tmpout/qs2022.csv",encoding="utf_8_sig",index=True)
        #_=input("export here")
        #self.xg_2022_after_mp()
        print("TTL time:", time.time()-starttime)
    def xg_2022_for_mp(self,n):
        xgresult=pd.DataFrame()
        qsresult=pd.DataFrame()
        stocklist = pd.read_csv(r"./csv/2022.csv")
        print("read 2022.csv 353",stocklist)
        #print(f"former qty = {stocklist.shape[0]}")
        stocklist=stocklist.drop_duplicates(['stockcode'])
        stocklist=stocklist[n:n+30]
        print("stock qty =", stocklist.shape[0])
        for index,stock in stocklist.iterrows():
            if len(stock['stockcode'])<9:
                if stock['stockcode'][0:1]=="3" or stock['stockcode'][0:1]=="0":
                    stock['stockcode']=stock['stockcode'][0:6]+".SZ"
                elif stock['stockcode'][0:1]=="6":
                    stock['stockcode']=stock['stockcode'][0:6]+".SH"
                elif stock['stockcode'][0:1]=="8" or stock['stockcode'][0:1]=="4":
                    stock['stockcode']=stock['stockcode'][0:6]+".BJ"
            else:
                #stock['stockcode']=stock['stockcode'][1:10]
                #stock['stockcode']=stock['stockcode'][1:10]
                pass
        for index,row in stocklist.iterrows():
            self.stockcode=row['stockcode']
            # 使用全局共享计数器，加锁以确保线程安全
            global shared_counter
            with shared_counter.get_lock():
                shared_counter.value += 1
                current_count = shared_counter.value
            print(current_count,self.stockcode)
            if self.stockcode[-3:]==".BJ":
                self.onedata=Onedata(0)
            else:
                self.onedata=Onedata(1)
            try:
                self.kdata_o,self.kdata_f,self.kdata_t,self.kdata_d,self.kdata_w,self.kdata_m=self.onedata.k(self.stockcode,"all")
            except:
                continue
            #only cal ma

            #hhh=self.kdata_d['high'].max() #.round(2)
            #lll=self.kdata_d['low'].min()  #.round(2)
            hhh=self.kdata_m['high'].max() #.round(2)
            lll=self.kdata_m['low'].min()  #.round(2)
            ttlk=self.kdata_d.shape[0]
            cp=self.kdata_d['close'][ttlk-1] #.round(2)
            try:
                #把历史最高价与历史最低价的差等分10份,现在处在第几份
                self.hp=(cp-lll)/((hhh-lll)/100)
                self.hp=self.hp.round(1)
            except:
                print("calculate self.hp error")

            self.cal_ma_all()
            #only cal QS
            self.kdata_m,self.QS_m,self.JG_m = self.cal_qs(self.kdata_m, self.stockcode, 'month')
            self.kdata_w,self.QS_w,self.JG_w = self.cal_qs(self.kdata_w, self.stockcode, 'week')
            self.kdata_d,self.QS_d,self.JG_d = self.cal_qs(self.kdata_d, self.stockcode, 'day')
            self.kdata_t,self.QS_t,self.JG_t = self.cal_qs(self.kdata_t, self.stockcode, 'm30')
            self.kdata_f,self.QS_f,self.JG_f = self.cal_qs(self.kdata_f, self.stockcode, 'm5')
            self.kdata_o,self.QS_o,self.JG_o = self.cal_qs(self.kdata_o, self.stockcode, 'm1')

            self.kdata_d['ma250'] = self.kdata_d['close'].rolling(250).mean().round(3)
            self.kdata_d= self.kdata_d.fillna(method='bfill')  # bfill由后向前填充 ffill由前向后填充
            '''
            if self.kdata_d['ma250'][-2]<=self.kdata_d['ma250'][-1]:
                yearup = 1
            else:
                yearup=0
            yearvalue=self.kdata_d['ma250'][-1]
            '''
            yearup=0
            yearvalue = 0
            try:
                if self.kdata_d['ma250'][-2]<=self.kdata_d['ma250'][-1]:
                    yearup = 1
                else:
                    yearup=0
                    yearvalue=self.kdata_d['ma250'][-1]
            except:
                yearup=0
                yearvalue = 999

            try:
                yearvaluep=(self.kdata_d['close'][-1] - self.kdata_d['ma250'][-1])/self.kdata_d['close'][-1] * 100
                yearvaluep=yearvaluep.round(2)
            except:
                yearvaluep=-999
            try:
                yearvs30p=(self.kdata_d['ma30'][-1] - self.kdata_d['ma250'][-1])/self.kdata_d['ma30'][-1] * 100
                yearvs30p=yearvs30p.round(2)
            except:
                yearvs30p=-999

            try:
                tmpkdata=self.kdata_d[-30:]
                d30upqty = len(tmpkdata[tmpkdata['ma250'] < tmpkdata['close']])
            except:
                d30upqty=0

            try:
                tmpkdata=self.kdata_d[-250:]
                yearupqty = len(tmpkdata[tmpkdata['ma250'] < tmpkdata['close']])
            except:
                yearupqty = 0
            
            print(self.QS['month'],self.QS['week'],self.QS['day'],self.QS['m30'],self.QS['m5'],self.QS['m1'])
            try:
                #[ttl:93]代码/名称/修改时间/趋势信号[6mwdtfo]
                #/趋势数据[84spaceP/profitP/riskP/NDG/NKL/DD/Kai/GG/Luo/CHL1/DDCGorGGCD/CHL/DDCGorGGCD/C[mwdtfo]
                tmp={'scode':self.stockcode[0:6],'stockcode':self.stockcode,'stockname':row['name'],'time':self.QS_t['reviseTime'],'c':self.QS_t['C'],\
                     'QSm':self.QS['month'],'QSw':self.QS['week'],'QSd':self.QS['day'],'QSt':self.QS['m30'],'QSf':self.QS['m5'],'QSo':self.QS['m1'],\
                     'ma30m':self.QS_m['ma30'],'ma30w':self.QS_w['ma30'],'ma30d':self.QS_d['ma30'],'ma30t':self.QS_t['ma30'],'ma30f':self.QS_f['ma30'],'ma30o':self.QS_o['ma30'],\
                     'ma10m':self.QS_m['ma10'],'ma10w':self.QS_w['ma10'],'ma10d':self.QS_d['ma10'],'ma10t':self.QS_t['ma10'],'ma10f':self.QS_f['ma10'],'ma10o':self.QS_o['ma10'],\
                     'spacePm':self.QS_m['spaceP'],'profitPm':self.QS_m['profitP'],'riskPm':self.QS_m['riskP'],\
                     'NDGm':self.QS_m['NDG'],'NKLm':self.QS_m['NKL'],'DDm':self.QS_m['DD'],'Kaim':self.QS_m['Kai'],'GGm':self.QS_m['GG'],'Luom':self.QS_m['Luo'],\
                     'CHL1m':self.QS_m['CHL1'],'DDCGorGGCD1m':self.QS_m['DDCGorGGCD1'], 'CHLm':self.QS_m['CHL'],'DDCGorGGCDm':self.QS_m['DDCGorGGCD'],
                     'spacePw':self.QS_w['spaceP'],'profitPw':self.QS_w['profitP'],'riskPw':self.QS_w['riskP'],\
                     'NDGw':self.QS_w['NDG'],'NKLw':self.QS_w['NKL'],'DDw':self.QS_w['DD'],'Kaiw':self.QS_w['Kai'],'GGw':self.QS_w['GG'],'Luow':self.QS_w['Luo'],\
                     'CHL1w':self.QS_w['CHL1'],'DDCGorGGCD1w':self.QS_w['DDCGorGGCD1'], 'CHLw':self.QS_w['CHL'],'DDCGorGGCDw':self.QS_m['DDCGorGGCD'],
                     'spacePd':self.QS_d['spaceP'],'profitPd':self.QS_d['profitP'],'riskPd':self.QS_d['riskP'],\
                     'NDGd':self.QS_d['NDG'],'NKLd':self.QS_d['NKL'],'DDd':self.QS_d['DD'],'Kaid':self.QS_d['Kai'],'GGd':self.QS_d['GG'],'Luod':self.QS_d['Luo'],\
                     'CHL1d':self.QS_d['CHL1'],'DDCGorGGCD1d':self.QS_d['DDCGorGGCD1'], 'CHLd':self.QS_d['CHL'],'DDCGorGGCDd':self.QS_d['DDCGorGGCD'],
                     'spacePt':self.QS_t['spaceP'],'profitPt':self.QS_t['profitP'],'riskPt':self.QS_t['riskP'],\
                     'NDGt':self.QS_t['NDG'],'NKLt':self.QS_t['NKL'],'DDt':self.QS_t['DD'],'Kait':self.QS_t['Kai'],'GGt':self.QS_t['GG'],'Luot':self.QS_t['Luo'],\
                     'CHL1t':self.QS_t['CHL1'],'DDCGorGGCD1t':self.QS_t['DDCGorGGCD1'], 'CHLt':self.QS_t['CHL'],'DDCGorGGCDt':self.QS_t['DDCGorGGCD'],
                     'spacePf':self.QS_f['spaceP'],'profitPf':self.QS_f['profitP'],'riskPf':self.QS_f['riskP'],\
                     'NDGf':self.QS_f['NDG'],'NKLf':self.QS_f['NKL'],'DDf':self.QS_f['DD'],'Kaif':self.QS_f['Kai'],'GGf':self.QS_f['GG'],'Luof':self.QS_f['Luo'],\
                     'CHL1f':self.QS_f['CHL1'],'DDCGorGGCD1f':self.QS_f['DDCGorGGCD1'], 'CHLf':self.QS_f['CHL'],'DDCGorGGCDf':self.QS_f['DDCGorGGCD'],
                     'spacePo':self.QS_o['spaceP'],'profitPo':self.QS_o['profitP'],'riskPo':self.QS_o['riskP'],\
                     'NDGo':self.QS_o['NDG'],'NKLo':self.QS_o['NKL'],'DDo':self.QS_o['DD'],'Kaio':self.QS_o['Kai'],'GGo':self.QS_o['GG'],'Luoo':self.QS_o['Luo'],\
                     'CHL1o':self.QS_o['CHL1'],'DDCGorGGCD1o':self.QS_o['DDCGorGGCD1'], 'CHLo':self.QS_o['CHL'],'DDCGorGGCDo':self.QS_o['DDCGorGGCD'],\
                     'ConfirmedJGm':self.JG_m['ConfirmedJG'],'CurrentJGm':self.JG_m['CurrentJG'],'JGdonem':self.JG_m['JGdone'],'QXm':self.JG_m['QX'],'JGQRm':self.JG_m['JGQR'],\
                     'ConfirmedJGw':self.JG_w['ConfirmedJG'],'CurrentJGw':self.JG_w['CurrentJG'],'JGdonew':self.JG_w['JGdone'],'QXw':self.JG_w['QX'],'JGQRw':self.JG_w['JGQR'],\
                     'ConfirmedJGd':self.JG_d['ConfirmedJG'],'CurrentJGd':self.JG_d['CurrentJG'],'JGdoned':self.JG_d['JGdone'],'QXd':self.JG_d['QX'],'JGQRd':self.JG_d['JGQR'],\
                     'ConfirmedJGt':self.JG_t['ConfirmedJG'],'CurrentJGt':self.JG_t['CurrentJG'],'JGdonet':self.JG_t['JGdone'],'QXt':self.JG_t['QX'],'JGQRt':self.JG_t['JGQR'],\
                     'ConfirmedJGf':self.JG_f['ConfirmedJG'],'CurrentJGf':self.JG_f['CurrentJG'],'JGdonef':self.JG_f['JGdone'],'QXf':self.JG_f['QX'],'JGQRf':self.JG_f['JGQR'],\
                     'ConfirmedJGo':self.JG_o['ConfirmedJG'],'CurrentJGo':self.JG_o['CurrentJG'],'JGdoneo':self.JG_o['JGdone'],'QXo':self.JG_o['QX'],'JGQRo':self.JG_o['JGQR'],\
                     'hp':self.hp,'ttlup':self.ttlup,'ttldown':self.ttldown,'mstatus':self.mstatus,'yearup':yearup,
                     'yearvalue':yearvalue,'yearvaluep':yearvaluep,'yearvs30p':yearvs30p,'yearupqty':yearupqty,'d30upqty':d30upqty
                    }
                for key,value in tmp.items():
                    try:
                        if value>1000:
                            value=round(value)
                            tmp[key]=value
                    except:
                        #print("error!!!!!!!!!!!!!! line 490")
                        pass
                #print("???tmp",tmp)
                #xgresult=pd.concat([xgresult, tmp], ignore_index=True)
                xgresult=pd.concat([xgresult, pd.DataFrame([tmp])], ignore_index=True)
                #print("???xgresult",tmp)
            except:
                print("error!!!!!!!!!!!!!! line 497")
                pass
            # 将字典转换为DataFrame后再拼接
            qsresult=pd.concat([qsresult, pd.DataFrame([self.QS_m])], ignore_index=True)
            qsresult=pd.concat([qsresult, pd.DataFrame([self.QS_w])], ignore_index=True)
            qsresult=pd.concat([qsresult, pd.DataFrame([self.QS_d])], ignore_index=True)
            qsresult=pd.concat([qsresult, pd.DataFrame([self.QS_t])], ignore_index=True)
            qsresult=pd.concat([qsresult, pd.DataFrame([self.QS_f])], ignore_index=True)
            qsresult=pd.concat([qsresult, pd.DataFrame([self.QS_o])], ignore_index=True)
            self.init_parameter()
        return xgresult,qsresult

    def xg_2022_after_mp(self):
        #print("self.xgresult after mp",self.xgresult)
        newlist=pd.DataFrame()
        newlistt=pd.DataFrame()
        for key,stock in self.xgresult.iterrows():
            if "-1abc" in stock.day or "-1abc" in stock.m30:
                newlist=pd.concat([newlist, pd.DataFrame([stock])], ignore_index=True)
            if "-1a" in stock.m30 or "-2" in stock.m30:
                newlistt=pd.concat([newlistt, pd.DataFrame([stock])], ignore_index=True)
        print("day -1abc and m30 -1abc")
        print(newlist)
        print("m30 -1a or m30 -2:")
        print(newlistt)
        
    def cal_ma_all(self):
        self.kdata_m = self.cal_ma_K(self.kdata_m)
        self.kdata_w = self.cal_ma_K(self.kdata_w)
        self.kdata_d = self.cal_ma_K(self.kdata_d)
        self.kdata_t = self.cal_ma_K(self.kdata_t)
        self.kdata_f = self.cal_ma_K(self.kdata_f)
        self.kdata_o = self.cal_ma_K(self.kdata_o)
        #如果月K线多于60根, 且股价在月线ma30上以的时间超过80%,则可以认为是有价值投资的股票
        ttlkm=self.kdata_m.shape[0]
        if ttlkm>60:
            df=self.kdata_m
            #print("month_kdata:",self.kdata_m[:10])
            self.ttlup  =int(df[df['low'] >=df['ma30']].shape[0]/ttlkm*100)
            self.ttldown=int(df[(df['high']+df['low'])/2<df['ma30']].shape[0]/ttlkm*100)
            if df['high'][ttlkm-5] >=df['ma30'][ttlkm-5] or df['high'][ttlkm-1] >=df['ma30'][ttlkm-1]: 
                self.mstatus=1
            else:
                self.mstatus=2
                '''
                if df['high'][ttlkm-1]<=df['ma30'][ttlkm-1]: 
                    self.mstatus=3
                if df['low'][ttlkm-1]<df['ma30'][ttlkm-1] and df['high'][ttlkm-1]>df['ma30'][ttlkm-1]: 
                    self.mstatus=2
                '''
        
    def cal_ma_K(self,kdata):
        kdata['ma5']  = kdata['close'].rolling(5).mean().round(3)
        kdata['ma10'] = kdata['close'].rolling(10).mean().round(3)
        kdata['ma30'] = kdata['close'].rolling(30).mean().round(3)
        kdata           = kdata.fillna(method='bfill')  # bfill由后向前填充 ffill由前向后填充
        return kdata

    def __Merge(self, dict1, dict2):
        res = {**dict1, **dict2}
        return res 

    def cal_qs_KaiLuo(self,kdata,ttlk):
        #找到可能的Kai 和 Luo两种信号
        # 从第31根K线开始，到ttlk-10,计算MA30上的起涨点和起跌点 +1代表起跌点 -1代表起涨点
        kdata['kkll']=0
        kdata['kkllx']=0
        kdata['kkllv']=0.0
        kdata['kkllxv']=0.0
        for i in range(30, ttlk - 10):  #
            bcheckttl = 0;
            # 如果是V型，即可能的起涨点
            if kdata['ma30'][i] <= kdata['ma30'][i - 1] and kdata['ma30'][i] < kdata['ma30'][i + 1]:
                # 如果后面9根K线中，有6根K线的重心（H+L）/2在MA30以上，并且MA30持续向上，则确定为一个起涨点
                for k in range(9):
                    if (kdata['high'][i + k] + kdata['low'][i + k]) / 2 > kdata['ma30'][i + k] \
                            and kdata['ma30'][i + k] < kdata['ma30'][i + k + 1]:
                        bcheckttl = bcheckttl + 1
                if bcheckttl >= 6: kdata['kkll'][i] = -1  # 起涨点 V 型

                tmpk=kdata[i+4:i+9]
                if tmpk[(tmpk["low"]>tmpk["ma30"])].shape[0]>=4:
                    kdata['kkll'][i]=-1 
            # 如果是A型，即可能的起跌点
            elif kdata['ma30'][i] >= kdata['ma30'][i - 1] and kdata['ma30'][i] > kdata['ma30'][i + 1]:
                # 如果后面9根K线中，有6极K线的重心在MA30以下，并且MA30持续向下，则确定为一个起跌点
                for k in range(9):
                    if (kdata['high'][i + k] + kdata['low'][i + k]) / 2 < kdata['ma30'][i + k] \
                            and kdata['ma30'][i + k] > kdata['ma30'][i + k + 1]:
                        bcheckttl = bcheckttl + 1
                if bcheckttl >= 6: kdata['kkll'][i] = 1  # 起跌点  A 型
                tmpk=kdata[i+4:i+9]
                if tmpk[(tmpk["high"]<tmpk["ma30"])].shape[0]>=4:
                    kdata['kkll'][i]=1 
                #data_df = data_df[(data_df["risk"] >=0) & (data_df["time"] <60)]
        # 最后10根K线，从后向前找，只要找到最近的第一次 MA30出现V型 或是 A型 就直接标注并退出
        for i in range(2, 10):  # 算最后的几根K线，看有没有趋势
            j = ttlk - i
            if (kdata['ma30'][j] <= kdata['ma30'][j - 1] and kdata['ma30'][j] < kdata['ma30'][j + 1] and kdata['high'][j + 1] >kdata['ma30'][j + 1]):
                kdata['kkll'][j] = -1
                break
            elif (kdata['ma30'][j] >= kdata['ma30'][j - 1] and kdata['ma30'][j] > kdata['ma30'][j + 1] and kdata['low'][j + 1] <kdata['ma30'][j + 1]):
                kdata['kkll'][j] = 1
                break
        # =================================================================================
        # 从前向后找连续重复的 +1 或 -1 信号，
        # 连续 + 1信号，保留“最高价”最大的那个
        # 连续 - 1信号，保留“最低价”最小的那个
        for i in range(29, ttlk):
            if kdata['kkll'][i] == 1:
                for j in range(i + 1, ttlk):
                    if kdata['kkll'][j] == -1:
                        i = j - 1  # !!!!!!i=j-1
                        break
                    if kdata['kkll'][j] == 1:
                        if kdata['high'][j] > kdata['high'][i]:  # 起跌点找重复1中的H的最高值
                            kdata['kkll'][i] = 0
                            i = j
                        else:
                            kdata['kkll'][j] = 0
            elif kdata['kkll'][i] == -1:  # !!!!!!!
                for j in range(i + 1, ttlk):
                    if kdata['kkll'][j] == 1:
                        i = j - 1  # !!!!!
                        break
                    if kdata['kkll'][j] == -1:
                        if kdata['low'][j] < kdata['low'][i]:  # 起涨点找重复-1中L的最低值
                            kdata['kkll'][i] = 0
                            i = j
                        else:
                            kdata['kkll'][j] = 0
        return kdata
    def cal_qs_GGDD(self,kdata,ttlk):
        # =================================================================================
        # 从后向前找，如果碰到-1，则找出与下一个+1之间的最低点，作为-2（最低价）
        #          如果碰到+1，则找出与下一个-1之间的最高点，作为+2（最高价）
        for i in range(ttlk - 1, 30, -1):
            if kdata['kkll'][i] == -1:  # 如果碰到-1
                LL = kdata['low'][i]
                disLL = 0
                # logging.info(data.iloc[i].name)
                for j in range(i - 1, -1, -1):
                    # logging.info(data.iloc[j].name,j)
                    if kdata['kkll'][j] == 1 or j == 0:  # 定义找-2的区间，从-1到（要么找到下一个+1，要么找到第一根K线）
                        for k in range(i - 1, j, -1):
                            if kdata['low'][k] <= LL:
                                disLL = i - k
                                LL = kdata['low'][k]
                        kdata['kkllx'][i - disLL] = -2
                        kdata['kkllxv'][i - disLL] = LL
                        break
            if kdata['kkll'][i] == 1:  # 如果碰到+1
                HH = kdata['high'][i]
                disHH = 0
                for j in range(i - 1, -1, -1):
                    if kdata['kkll'][j] == -1 or j == 0:  # 定义找+2的区间，从+1到（要么找到下一个-1，要么找到第一根K线）
                        for k in range(i - 1, j, -1):
                            if kdata['high'][k] >= HH:
                                disHH = i - k
                                HH = kdata['high'][k]
                        kdata['kkllx'][i - disHH] = 2
                        kdata['kkllxv'][i - disHH] = HH
                        break
        return kdata
    def cal_qs_kkllv(self,kdata,ttlk):
        # range可以带三个参数，但是这个地方只带了一个？？？
        for i in range(ttlk):  ### 计算平均价 kai/luo 1和-1这两个位置的，因为大智慧计算太慢 ###
            if kdata['kkllx'][i] == 2:
                TTLa = 0
                TTLv = 0
                for j in range(i, ttlk):
                    #TTLa= TTLa+ kdata['amt'][j] #kdata['close'][j] * kdata['vol'][j]
                    TTLa = TTLa + kdata['close'][j] * kdata['vol'][j]
                    TTLv = TTLv + kdata['vol'][j]
                    if kdata['kkll'][j] == 1:
                        i = j
                        if TTLv == 0:  # 如果TTLv==0，则直接提示并将kkllv置零，避免/0错误！！！
                            #logging.info(stockcode, period, "TTLv=0")
                            kdata['kkllv'][j] = 0
                        else:
                            kdata['kkllv'][j] = TTLa / TTLv
                        break
            if kdata['kkllx'][i] == -2:
                TTLa = 0
                TTLv = 0
                for j in range(i, ttlk):
                    #TTLa = TTLa+ kdata['amt'][j] #kdata['close'][j] * kdata['vol'][j]
                    TTLa = TTLa + kdata['close'][j] * kdata['vol'][j]
                    TTLv = TTLv + kdata['vol'][j]
                    if kdata['kkll'][j] == -1:
                        i = j
                        if TTLv == 0:
                            #logging.info(stockcode, period, "TTLv=0")
                            kdata['kkllv'][j] = 0
                        else:
                            kdata['kkllv'][j] = TTLa / TTLv
                        break
        kdata['kkll'] = kdata['kkll'].round(0)
        kdata['kkllx'] = kdata['kkllx'].round(0)
        kdata['kkllv'] = kdata['kkllv'].round(3)
        kdata['kkllxv'] = kdata['kkllxv'].round(3)
        '''
        if kdata['kkllv']<3:
            kdata['kkllv'] = kdata['kkllv'].round(3)
            kdata['kkllxv'] = kdata['kkllxv'].round(3)
        else:
            kdata['kkllv'] = kdata['kkllv'].round(2)
            kdata['kkllxv'] = kdata['kkllxv'].round(2)
        '''
        return kdata
    def cal_qs_QSmodule_NDGNKL(self,kdata,ttlk):
        #计算11点趋势模型中的NKL和NDG
        NDG=NKL=0
        DDday=GGday=0
        for i in range(ttlk - 1, -1, -1):
        # 即最近一个最值点如果是-2（下跌趋势中），则NKL=NLuo NDG=NGG
            if kdata['kkllx'][i] == -2:  # DD
                DDday = i  # !!!!!
                for j in range(i - 1, -1, -1):
                    if kdata['kkll'][j] == -1:
                        NKL = kdata['kkllv'][j]
                    if kdata['kkllx'][j] == -2:
                        NDG = kdata['kkllxv'][j]
                        break
                break
            elif kdata['kkllx'][i] == 2:  # GG
                    GGday = i  # !!!!!
                    for j in range(i - 1, -1, -1):
                        if kdata['kkll'][j] == 1:
                            NKL = kdata['kkllv'][j]
                        if kdata['kkllx'][j] == 2:
                            NDG = kdata['kkllxv'][j]
                            break
                    break
        self.keypoint['NDG']=NDG
        self.keypoint['NKL']=NKL
        self.keypoint['GGday']=GGday
        self.keypoint['DDday']=DDday
        return kdata
    def cal_qs_QSmodule_QSDDKaiGGLuo(self,kdata,ttlk):
        DD=Kai=GG=Luo=Kaiday=Luoday=0
        for i in range(ttlk - 1, -1, -1):
            if kdata['kkllx'][i] == -2:  # DD
                #kdata.insert(12, 'DD', kdata['kkllxv'][i])  # 所有的DD都设置为最后一个DD
                kdata['DD']=kdata['kkllxv'][i]  # 所有的DD都设置为最后一个DD
                DD = kdata['kkllxv'][i]
                break  # 只找最后一个
        added = kdata.shape[1]  # 如果没有DD，则 加一列 DD
        if added == 12:  
            #kdata.insert(12, 'DD', 0)  # 如果趋势不全，找不到-2，则所有DD为0 XXXXXXXX
            kdata['DD']=0  # 如果趋势不全，找不到-2，则所有DD为0 XXXXXXXX

        for i in range(ttlk - 1, -1, -1):
            if kdata['kkll'][i] == -1:  # Kai
                #kdata.insert(13, 'Kai', kdata['kkllv'][i])
                kdata['Kai']=kdata['kkllv'][i]
                Kai = kdata['kkllv'][i]
                break
        Kaiday = ttlk - i  # 和Luoday比较大小，Kaiday小，是上涨趋势，Kaiday大，是下跌趋势
        added = kdata.shape[1]
        if added == 13:  
            #kdata.insert(13, 'Kai', 0)  # 如果趋势不全，找不到-1
            kdata['Kai']=0

        for i in range(ttlk - 1, -1, -1):
            if kdata['kkllx'][i] == 2:  # GG
                #kdata.insert(14, 'GG', kdata['kkllxv'][i])
                kdata['GG']=kdata['kkllxv'][i]
                GG = kdata['kkllxv'][i]
                break
        added = kdata.shape[1]
        if added == 14:  
            #kdata.insert(14, 'GG', 0)
            kdata['GG']=0

        for i in range(ttlk - 1, -1, -1):
            if kdata['kkll'][i] == 1:  # Luo
                #kdata.insert(15, 'Luo', kdata['kkllv'][i])
                kdata['Luo']=kdata['kkllv'][i]
                Luo = kdata['kkllv'][i]
                break
        Luoday = ttlk - i
        added = kdata.shape[1]
        if added == 15:  
            #kdata.insert(15, 'Luo', 0)
            kdata['Luo']=0
        if Kaiday >= Luoday:
            QS = -1
        else:
            QS = 1
        self.keypoint['QS']=QS
        self.keypoint['DD']=DD
        self.keypoint['Kai']=Kai
        self.keypoint['GG']=GG
        self.keypoint['Luo']=Luo
        return kdata
    def cal_qs_QSmodule_CHL1andCHL(self,stockcode,kdata,ttlk,period):
        NDG=self.keypoint['NDG']
        NKL=self.keypoint['NKL']
        DD=self.keypoint['DD']
        Kai=self.keypoint['Kai']
        GG=self.keypoint['GG']
        Luo=self.keypoint['Luo']
        QS=self.keypoint['QS']

        # 周期 方向 指导建议 空间[起涨 起跌]    [收益 % 风险 % 益险 %][最低  最高]    当前极值
        periodsuggestion = " "  # 操盘建议
        profitP = 0             # 收益%        LUO/C            ()
        riskP = 0               # 风险%        C/Kai            (if Kai<>0, C/Kai else 100)
        PLP = 0                 # 收益风险率    profitP/riskP    ()
        CHL = 0.0  
        CHLP = 0                #
        DDCGorGGCD = 0.0  
        DDCGorGGCDma30 = 0.0
        closeprice = kdata['close'][-1]

        CHL1=0
        DDCGorGGCD1=0
        DDCGorGGCD1ma30=0
        DDCGorGGCD1location=0
        CHL1location=0

        HHHlocation = 0
        LLLlocation = 0
        # 从后向前找趋势极值
        #2-28-2022
        #WWW服务器只抓数据,不运算,是否会加快计算进度[每天收盘做一次]
        #DDCGorGGCD1是CHL向[ 前 ]再找一个HHH或LLL碰Ma30的位置
        #CHL1在DDCGorGGCD[ 前 ]与CHL的性质相同的最高价或最低价
        #CHL与CHL1相比较,用来判断有没有创新高或创新低
        #QStask1: 完成趋势空间        [CHL完成? CHL1完成?]
        #QStask2: 完成趋势结构        [如果CHL与CHL1之前,有一个H或L碰到Ma30,则完成趋势结构]
        #QStask3: 创新,创新低或创新高 [+1 创新高 -1创新低 还是1就代表创新]
        #QStask4: 趋势转折点          [本趋势转折点,即下一个MA30会让生命线变红]
        #QStask5: 趋势启动            [L-2级别连续放量大涨,还是只针对日线级别]
        for i in range(ttlk - 1, -1, -1):
            if i == ttlk - 1:
                HHH = kdata['high'][i]
                LLL = kdata['low'][i]
                HHHlocation = i
                LLLlocation = i
            if kdata['high'][i] > HHH:  #如果==则记录后一个高点
                HHH = kdata['high'][i]
                HHHlocation = i
            if kdata['low'][i] < LLL:   #如果==则记录后一个低点
                LLL = kdata['low'][i]
                LLLlocation = i

            if kdata['kkll'][i] == 1:  # Luo    =>[[[[下跌趋势中...]]]]
                CHL = LLL  # 下跌趋势中CHL取最低价
                CHLlocation=LLLlocation
                #1 Luo的位置 [Luo | CHL1 DDCGorGGCD1 CHL DDCGorGGCD C] 
                Luolocation=i         
                if Kai != 0:
                    CHLP = (CHL / Kai * 100 - 100).round(1)
                    # 判断下跌趋势极值离完成任务还有多远，负数代表超额完成下跌任务
                else:
                    CHLP = 0  # 999代表趋势不全，即Kai=0, 直接除会程序报错
                
                #1 [Luo] DDCGorGGCD1 and DDCGorGGCD1ma30
                for k in range(CHLlocation-1,Luolocation,-1):     #从CHL向前找一个[完成止跌结构]的"伪"高点,这个高点不是DDCGorGGCD1
                    if kdata['high'][k]>kdata['ma30'][k]:       #如果找到,再向前到Luo后,找CHL1
                        CHL1=kdata['low'][k]                    #如果没有找到,就不用DDCGorGGCD1和CHL这两个参考点
                        CHL1location=k
                        for m in range(k,Luolocation,-1):       #即没有完成止跌结构,就不用比是否创新低
                            if kdata['low'][m]<CHL1:
                                CHL1=kdata['low'][m]
                                CHL1location=m
                        DDCGorGGCD1=kdata['high'][CHL1location]
                        for m in range(CHL1location,CHLlocation,1):
                            if kdata['high'][m]>DDCGorGGCD1:
                                DDCGorGGCD1=kdata['high'][m]
                                DDCGorGGCD1ma30=kdata['ma30'][m]
                                DDCGorGGCD1location=m
                        break #只要找到一个high>ma30的伪"高点"就向前找最低点CHL1,再向CHL找DDCGorGGCD1

                for k in range(ttlk - 1, CHLlocation, -1):  # 如果找到的是下跌趋势中的最低值，则找C与趋势最低值之间的最高价
                    if k == ttlk - 1:
                        DDCGorGGCD = kdata['high'][k]
                        DDCGorGGCDma30 = kdata['ma30'][k]
                    if kdata['high'][k] > DDCGorGGCD:
                        DDCGorGGCD = kdata['high'][k]
                        DDCGorGGCDma30 = kdata['ma30'][k]
                if DDCGorGGCD == 0:
                    DDCGorGGCD = kdata['high'][ttlk - 1]
                    DDCGorGGCDma30 = kdata['ma30'][ttlk - 1]
                break

            if kdata['kkll'][i] == -1:  # Kai   =>[[[[...上涨趋势中...]]]
                CHL = HHH  # 上涨趋势中CHL取最高价
                CHLlocation=HHHlocation
                #1 Kai的位置
                Kailocation=i         
                #1 end
                CHLP = (Luo / CHL * 100 - 100).round(1)
                # 判断上涨趋势极值离完成任务还有多远，负数代表超额完成上涨任务
                #1 [如果创新低后再一次形成DDCGorGGCD]找DDCGorDDCG1
                # 上涨趋势中形成的最高CHL向前找DDCGorGGCD1的前提是
                # 下跌趋势中形成的最低CHL向前找DDCGorGGCD1的前提是向上碰Ma30(完成止跌结构,否则没有DDCGorGGCD1也就没有CHL

                for k in range(CHLlocation-1,Kailocation,-1):   #从CHL(最高)向前找一个[完成止跌结构]的"伪"低点,这个低点不是DDCGorGGCD1
                    if kdata['low'][k]<kdata['ma30'][k]:        #如果找到,再向前到Kai后,找CHL1
                        CHL1=kdata['high'][k]                   #如果没有找到,就不用DDCGorGGCD1和CHL1这两个参考点
                        CHL1location=k
                        for m in range(k,Kailocation,-1):       #即没有完成止跌结构,就不用比是否创新低
                            if kdata['high'][m]>CHL1:
                                CHL1=kdata['high'][m]
                                CHL1location=m
                        DDCGorGGCD1=kdata['low'][CHL1location]
                        for m in range(CHL1location,CHLlocation,1):
                            if kdata['low'][m]<DDCGorGGCD1:
                                DDCGorGGCD1=kdata['low'][m]
                                DDCGorGGCD1ma30=kdata['ma30'][m]
                                DDCGorGGCD1location=m
                        break

                for k in range(ttlk - 1, HHHlocation, -1):  # 如果找到的是上涨趋势中的最高值，则找C与趋势最高值之间的最低价
                    if k == ttlk - 1:
                        DDCGorGGCD = kdata['low'][k]
                        DDCGorGGCDma30 = kdata['ma30'][k]
                    if kdata['low'][k] < DDCGorGGCD:
                        DDCGorGGCD = kdata['low'][k]
                        DDCGorGGCDma30 = kdata['ma30'][k]
                if DDCGorGGCD == 0:
                    DDCGorGGCD = kdata['low'][ttlk - 1]
                    DDCGorGGCDma30 = kdata['ma30'][ttlk - 1]
                break
                # 趋势极值为正数，代表还有%多少能完成任务，如果为负数，则代表超额完成上涨或下跌任务的%
        profitP = (Luo / closeprice * 100 - 100).round(1)
        if Kai != 0:
            riskP = (closeprice / Kai * 100 - 100).round(1)
        else:
            riskP = 999.0
        PLP = ((profitP + 100) / (riskP + 100) * 100 - 100).round(1)

        if closeprice > GG * 1.01 or closeprice < DD * 0.99:
            outofrange = 1
        else:
            outofrange = 0

        if GG == 0 or DD == 0 or Kai == 0 or Luo == 0:
            QSabnormal = 1
        else:
            QSabnormal = 0

        if Kai != 0:
            spaceP = (Luo / Kai * 100 - 100).round(1)
        else:
            spaceP = 0

        revisetime = datetime.datetime.strftime(datetime.datetime.now(), '%m-%d %H:%M')

        try:
            CHL=CHL.round(2)
            CHL1=CHL1.round(2)
        except:
            #print("error!!!!!!!!!!!!!! line 953")
            pass

        periodQS = {'code':         stockcode + period,
                    'stockcode':    stockcode,
                    'period':       period,
                    'C':            closeprice,  # data['close'][-1],
                    'QS':           QS,
                    'DD':           DD,
                    'Kai':          Kai,
                    'GG':           GG,
                    'Luo':          Luo,
                    'profitP':      profitP,
                    'riskP':        riskP,
                    'PLP':          PLP,
                    'CHL':          CHL,
                    'CHLP':         CHLP,
                    'spaceP':       spaceP,
                    'outofrange':   outofrange,
                    'QSabnormal':   DDCGorGGCDma30,  # QSabnormal,
                    'ma5':          kdata['ma5'][-1].round(3),
                    'ma10':         kdata['ma10'][-1].round(3),
                    'ma30':         kdata['ma30'][-1].round(3),
                    'ma5P':         (closeprice / kdata['ma5'][-1] * 100 - 100).round(2),
                    'ma10P':        (closeprice / kdata['ma10'][-1] * 100 - 100).round(2),
                    'ma30P':        (closeprice / kdata['ma30'][-1] * 100 - 100).round(2),
                    'periodSugg':   periodsuggestion,
                    'DDCGorGGCD':   DDCGorGGCD,
                    'reviseTime':   revisetime,
                    'NDG'	:       NDG,
                    'NKL'	    :   NKL,
                    'CHL1'      :   CHL1,
                    'DDCGorGGCD1':  DDCGorGGCD1
                    }

        periodQStime={
                    'code'          :stockcode+period,
                    'stockcode'     :stockcode,
                    'period'        :period,
                    #'DDtime'        : DDtime,
                    #'Kaitime'       : Kaitime,
                    #'GGtime'        : GGtime,
                    #'Luotime'       : Luotime,
                    #'CHLtime'       : CHLtime,
                    #'DDCGorGGCDtime': DDCGorGGCDtime,
                    #'NDGtime'	    : NDGtime,
                    #'NKLtime'	    : NKLtime
                    'reviseTime': revisetime
                    }
        self.keypoint['CHL1']=CHL1
        self.keypoint['DDCGorGGCD1']=DDCGorGGCD1
        self.keypoint['CHL']=CHL
        self.keypoint['DDCGorGGCD']=DDCGorGGCD
        self.keypoint['DDCGorGGCD1ma30']=DDCGorGGCD1ma30
        self.keypoint['DDCGorGGCDma30']=DDCGorGGCDma30
        return kdata,periodQS #,GGday,DDday
    def cal_qs_QSmessage(self,kdata,ttlk,period):
        NDG=self.keypoint['NDG']
        NKL=self.keypoint['NKL']
        DD=self.keypoint['DD']
        Kai=self.keypoint['Kai']
        GG=self.keypoint['GG']
        Luo=self.keypoint['Luo']
        QS=self.keypoint['QS']
        CHL1=self.keypoint['CHL1']
        CHL=self.keypoint['CHL']
        DDCGorGGCD1=self.keypoint['DDCGorGGCD1']
        DDCGorGGCD1ma30=self.keypoint['DDCGorGGCD1ma30']
        DDCGorGGCD=self.keypoint['DDCGorGGCD']
        DDCGorGGCDma30=self.keypoint['DDCGorGGCDma30']
        #如果趋势空间合理 + 收盘价在趋势空间[DD,GG]以内,当前趋势就有价值
        closeprice=kdata['close'][ttlk-1]
        goodspace=False
        insidespace=False

        self.QS[period]=""  #avoid nan error
        #self.space={"month": 1,"week":0.2,"day":0.08,"m30":0.04,"m5":0.02,"m1":0.01}
        #趋势空间 月线100% 周线20% 日线8% 30分钟4% 5分钟2% 1分钟1%
        #self.diffbyp ={"month":0.08,"week":  0.04,"day":0.02   ,"m30":0.01   ,"m5":0.005   ,"m1":0.0025 }
        #容错率   月线  8% 周线 4% 日线2% 30分钟1% 5分钟0.5% 1分钟0.25%
        if Kai!=0:
            if Luo/Kai>(1+self.space[period]):
                goodspace=True
                #下跌趋势中，如果CHL和CHL1中有一个是0，那么minchl就是0，则趋势就自动变为-2，因为minchl不可能大于DD
                #上涨趋势中，不存在这个问题
                minchl=min(CHL,CHL1)
                maxchl=max(CHL,CHL1)
                if CHL==0 or CHL1==0: minchl=maxchl

                #if closeprice>DD*(1-self.diffbyp[period]) and closeprice<GG*(1+self.diffbyp[period]):
                if minchl>DD*(1-self.diffbyp[period]) and maxchl<GG*(1+self.diffbyp[period]):
                    insidespace=True
                    #self.diffbyp ={"month":0.08,"week":  0.04,"day":0.02   ,"m30":0.01   ,"m5":0.005   ,"m1":0.0025 }
                    if QS==-1:
                        self.QS[period]+="-1"       #正常下跌
                    else:
                        self.QS[period]+="+1"
                else:   
                    if QS==-1:
                        self.QS[period]+="-2"       #破位下跌
                    else:
                        self.QS[period]+="+2"
            else:
                #超过盘整GG +0, 小于盘整DD -0, 位于盘整中间 =0
                if closeprice>GG*(1+self.diffbyp[period]):
                    self.QS[period]+="+0"             #盘整
                else:
                    if closeprice<DD*(1-self.diffbyp[period]):
                        self.QS[period]+="-0"
                    else:
                        self.QS[period]+="=0"

        if QS==-1:    #下跌趋势中
            #计算QStask1   
            #CHL或CHL1中小的那个完成了趋势空间,就行
            if min(CHL,CHL1)<Kai*(1+self.diffbyp[period]):
                self.QS[period]+="a"
            #计算QStask2
            #如果DDCGorGGCD  上碰Ma30,完成止跌结构                #如果DDCGorGGCD1 上碰Ma30,完成止跌结构
            DoGfinishJG=False
            DoG1finishJG=False
            if DDCGorGGCD>DDCGorGGCDma30 and DDCGorGGCD*DDCGorGGCDma30!=0: 
                DoGfinishJG=True
            if DDCGorGGCD1>DDCGorGGCD1ma30 and DDCGorGGCD1*DDCGorGGCD1ma30!=0: 
                DoG1finishJG=True
            if DoGfinishJG or DoG1finishJG:
                self.QS[period]+="b"
            #1 计算QStask3
            #下跌趋势中是完成趋势任务后创新低
            if CHL1>0 and CHL<CHL1 and CHL1<Kai*(1+self.diffbyp[period]):
                self.QS[period]+="c"
            #下跌趋势中的趋势突破是下一根K线让生命线向上    
            tmpk=kdata[-29:]
            newma30=tmpk['close'].sum()+kdata['close'][ttlk-1]
            newma30=round(newma30/30,4)
            #print(f"{period} newma30:{newma30} vs {tmpk['ma30'][28]}")
            try:
                if newma30>=tmpk['ma30'][28]:
                    self.QS[period]+="d"
            except:
                print("error!!!!!!!!!!!!!! line 1093")
                pass
        elif QS==1:       #上涨趋势
            #计算本级别趋势是否有意义[上涨趋势]
            #如果趋势空间合理 + 收盘价在趋势空间[DD,GG]以内,当前趋势就有价值
            #计算QStask1   
            #CHL或CHL1中小的那个完成了趋势空间,就行
            if min(CHL,CHL1)>Luo*(1-self.diffbyp[period]):
                self.QS[period]+="a"
            #计算QStask2
            #如果DDCGorGGCD  上碰Ma30,完成止跌结构
            #如果DDCGorGGCD1 上碰Ma30,完成止跌结构
            DoGfinishJG=False
            DoG1finishJG=False
            if DDCGorGGCD<DDCGorGGCDma30: 
                DoGfinishJG=True
            if DDCGorGGCD1<DDCGorGGCD1ma30: 
                DoG1finishJG=True
            if DoGfinishJG or DoG1finishJG:
                self.QS[period]+="b"
            #1 计算QStask3
            #下跌趋势中是完成趋势任务后创新低
            if CHL1>0 and CHL>CHL1 and CHL1>Luo*(1-self.diffbyp[period]):
                #print(period,"CHL,CHL1",CHL1,CHL)
                self.QS[period]+="c"
            #下跌趋势中的趋势突破是下一根K线让生命线向上    
            tmpk=kdata[-29:]
            newma30=tmpk['close'].sum()+kdata['close'][ttlk-1]
            newma30=round(newma30/30,4)
            try:
                if newma30<=tmpk['ma30'][28]:
                    self.QS[period]+="d"
            except:
                print("error!!!!!!!!!!!!!! line 1126")
                pass
        #如果30天内有连续两个涨停,则e
        df=self.kdata_d[-30:]
        #df['zdf']=(df['close']-df['close'].shift(1))/df['close'].shift(1)
        try:
            for i in range(2,30,1): #连续两天上涨超过9%则为+e,连续两天下跌超过9%则为-e, 同时出现+e和-e看先后顺序
                if df.close[i]/df.close[i-1]>1.08 and df.close[i-1]/df.close[i-2]>1.08:
                    self.QS[period]+="+e"
                    break
            for i in range(2,30,1): #连续两天上涨超过9%则为+e,连续两天下跌超过9%则为-e, 同时出现+e和-e看先后顺序
                if df.close[i]/df.close[i-1]<0.92 and df.close[i-1]/df.close[i-2]<0.92:
                    self.QS[period]+="-e"
                    break
        except:
            print("error!!!!!!!!!!!!!! line 1141")
            pass
    def cal_jg_JGmodule(self,kdata,ttlk,period,periodQS):
        # 计算结构
        # ====================================================
        '''
        DD -2
        Kai -1
        GG 2
        Luo 1
        从后向前找，如果第一个找到-2，即当前在上涨趋势中
        1，下跌趋势中找底分型，
        2，两个底分型之间找最高点（或碰MA10或MA30的点）
        '''
        CHL  =periodQS['CHL']
        Kai  =periodQS['Kai']
        Luo  =periodQS['Luo']
        QS  =periodQS['QS']
        GGday=self.keypoint['GGday']
        DDday=self.keypoint['DDday']
        revisetime  =periodQS['reviseTime']

        '''
        kdata.insert(16, 'JGVFX', 0.00)  # 结构中的V分型
        kdata.insert(17, 'JGAFX', 0.00)  # 结构中的A分型
        kdata.insert(18, 'JG', ' ')  # 结构
        kdata.insert(19, 'JGV', 0.00)  # 结构特征价格
        '''
        kdata['JGVFX']=0.00  # 结构中的V分型
        kdata['JGAFX']=0.00  # 结构中的A分型
        kdata['JG']=' '  # 结构
        kdata['JGV']=0.00  # 结构特征价格

        JGdone      = 0  # 完成止跌结构     #JGdone
        KJdone      = 0  # 完成下跌空间     #KJdone
        JGQR        = 0  # 结构强弱         #JGQR 1代表强(创新高/不创新低) -1代表弱(不创新高/创新低)
        QX          = 0  # 情绪, 代替以前的KongHuang和KangFen两个指标,完成止跌结构后创新低制造恐慌
        ConfirmedJG =   ' '     #当前能确认的结构???
        CurrentJG   =   ' '     #最后一根K线可能的结构
        CurrentJGV  =   0       #当前结构value

        minlpercent =0
        minllocation = 0
        maxhpercent=0
        maxhlocation=0

        for i in range(ttlk - 1, -1, -1):  # 0<=i<=ttlk-1
            # 下跌趋势中上结构判断
            if kdata['kkllx'][i] == 2:
                # 从最高点开始找L离MA30最远的那根K线，开始找第一个底
                minlpercent = kdata['low'][i] /kdata['ma30'][i]
                minllocation = i

                for j in range(i ,ttlk -1 ,1):
                    minlpercentnew =kdata['low'][j] /kdata['ma30'][j]
                    if minlpercentnew <minlpercent:
                        minllocation = j
                        minlpercent =minlpercentnew

                kdata['JGVFX'][minllocation ] =-1  # 最远点为底分型
                kdata['JG'][minllocation] = "6"  # 最远点为结构6
                kdata['JGV'][minllocation] = kdata['low'][minllocation]
                if minllocation == ttlk - 1: break  # 如果6是最后一根K线，则不计算其它结构只有6

                # 不同的结构算法，可能的计算起点不同，但是下面的计算都是一致的
                # 从6+1开始找下跌趋势中的V分型
                for j in range(minllocation +1, ttlk -1 ,1):
                    if kdata['low'][j] <kdata['low'][j -1]:
                        lowhigh =kdata['high'][j]
                        lowlow =kdata['low'][j]
                        for k in range(j +1 ,ttlk -1 ,1):
                            if lowlow >kdata['low'][k]:
                                j=k  # 如果在找到顶分型找到更低的底，则
                                break
                            if lowhigh<kdata['high'][k]:
                                kdata['JGVFX'][j] = -1
                                j=k
                                break
                                # 找到一个高顶
                # 从6开始找下跌趋势中的A分型
                for j in range(minllocation,ttlk-1,1):
                    if kdata ['JGVFX'][j]==-1:
                        currentHigh=kdata['high'][j]
                        currentHighLocation=j
                        k=0
                        for k in range(j+1,ttlk-1,1):
                            if kdata['high'][k]>currentHigh:
                                currentHigh=kdata['high'][k]
                                currentHighLocation=k
                            if kdata['JGVFX'][k]==-1:
                                j=k
                                break
                        if k!=ttlk-1:
                            kdata['JGAFX'][currentHighLocation]=1

                # 删除下跌趋势中无效A分型信号和左右两边较高的那个V分型信号 # 如果要删除的那个V是起始的6，则必须保留(当前是保留 因为其比例意义）
                # （或者换6到新的底）
                # 如果移动6，则minllocation要跟着一起改

                #删除碰不到MA10或MA30的A分型信号
                for j in range(minllocation,ttlk-1,1):
                    if kdata['JGAFX'][j]==1:
                        high2ma10 =abs(kdata['ma10'][j]-kdata['high'][j])
                        high2ma5=abs(kdata['ma5'][j]-kdata['high' ] [j])
                        if kdata['high'][j]< kdata['ma10'][j] \
                                and high2ma5/2<high2ma10:   #？？？high超过ma10或high离ma5的距离是到ma10距离的2倍
                            #如果下跌过程中的A分型不满足79的结构条件，则删除这个A分型
                            kdata['JGAFX'][j] = 0
                                # 2 for debug
                            #找A分型的 下一个 V分型 （可能没有）
                            comp_OK=0
                            for k in  range(j,ttlk-1,1):
                                if kdata ['JGVFX'][k]==-1:
                                    nextVFX=k   #如果后面没有V分型，则nextVFX=?
                                    comp_OK=comp_OK+1   #后面有个V
                                    break
                            #找A分型的 上一个 V分型 （必定有，至少有6）
                            for k in range(j,-1,-1):
                                if kdata['JGVFX'][k]==-1:
                                    lastVFX=k
                                    comp_OK=comp_OK+1   #前面有个V
                                    break

                            if comp_OK==2:  #确保有两个V分型，再比较，否则程序报错
                                if kdata['low'][nextVFX]<=kdata['low'][lastVFX]:    #如果下一个low更低
                                    if kdata['JGVFX'][lastVFX]!="6":                  #上一个不是6，就把上一个V分型信号删除
                                        try:                                        #任何一个A分型前都能找到至少一个V分型信号（6）
                                            kdata['JGVFX '][lastVFX]=0#这个地方应该不会出错 lastVFX下标一定能找到
                                        except:
                                            # print("error!!!!!!!!!!!!!! line 1270")
                                            pass
                                    else:
                                        kdata['JGVFX'][nextVFX] =0                  #???A后是否必定有nextVFX
                                else:
                                    kdata['JGVFX'][nextVFX]=0                       #???删除下一个V分型信号

                # 计算其它结构
                for j in range(minllocation+1,ttlk-1,1):
                    if kdata['JGAFX'][j]!=0:
                        high2ma30 = abs(kdata ['ma30'][j] - kdata['high'][j])
                        high2ma10 = abs(kdata['ma10'][j] - kdata['high'][j])
                        high2ma5 = abs(kdata['ma5'][j] - kdata['high'][j])
                        if kdata['high'][j]>kdata['ma30'][j] or high2ma30<high2ma10/2:
                            kdata['JG'][j]='9'
                            JGdone=1
                            if CHL<Kai*1.1: # and spaceP>normalspaceP[period]!!!
                                KJdone=1  # 需要参考对应级别的正常趋势空间  ZDJG=1  # 完成止跌结构
                                #10%以内都算完成趋势空间XXX CHL<Kai+normalspaceP[period]*10%
                            kdata['JGV'][j] = kdata['high'][j] #如果9与A重合，则JGV中只能保存一个值
                            for k in range(j,ttlk-1,1):     #有了9，后面的V分型就是0
                                if kdata['JGVFX'][k]==-1:
                                    kdata['JG'][k]='0'
                                    kdata['JGV'][k] = kdata['low'][k]
                                    break
                        else:
                            if kdata['high'][j]>kdata['ma10'][j] or high2ma10<high2ma5/2:
                                kdata['JG'][j]='7'
                                kdata['JGV'] [j] = kdata['high'][j]
                                for k in range(j,ttlk-1,1):
                                    if kdata['JGVFX'][k] ==-1: # logging.info("6+")
                                        kdata['JG'][k] = '8'
                                        kdata['JGV'][k] = kdata['low'][k]
                                        break

                for j in range(minllocation, ttlk - 1, 1):
                    if kdata['JGVFX'][j]==-1:
                        for k in range(j+1, ttlk-1,1):
                            if kdata['JGAFX'][k]==1:
                                break
                            if kdata['JGVFX'][k]==-1:
                                if kdata['JG'][j]!='6':
                                    if kdata['low'][j]<kdata['low'][k]:
                                        kdata['JGVFX'][k]=0
                                    else:
                                        kdata['JGVFX'][j]=0
                                else:
                                    kdata['JGVFX'][k] = 0

                # 定义最后一根K线的结构
                for j in range(ttlk-1, minllocation-1, -1):
                    lastFXis=kdata['JG'][j]
                    if lastFXis!=' ':
                        logging.info(f"-i am here {period} {lastFXis}")
                        if lastFXis == '6':
                            if kdata['high'][ttlk-1]>kdata['high'][j]:
                                CurrentJG = '7'
                                CurrentJGV = kdata['high'][ttlk - 1]
                            else:
                                CurrentJG ='6'
                                CurrentJGV=kdata['low'][ttlk-1]
                        if lastFXis == '7':
                            if kdata['low'][ttlk-1]<kdata['low'][j]:
                                CurrentJG = '8'
                                CurrentJGV = kdata['low'][ttlk - 1]
                            else:
                                CurrentJG ='7'
                                CurrentJGV=kdata['high'][ttlk-1]
                        if lastFXis == '8':
                            if kdata['high'][ttlk-1]>kdata['high'][j]:
                                CurrentJG = '9'
                                CurrentJGV = kdata['high'][ttlk - 1]
                            else:
                                CurrentJG ='8'
                                CurrentJGV=kdata['low'][ttlk-1]
                        if lastFXis == '9':
                            if kdata['low'][ttlk-1]<kdata['low'][j]:
                                CurrentJG = '0'
                                CurrentJGV = kdata['low'][ttlk - 1]
                            else:
                                CurrentJG ='9'
                                CurrentJGV=kdata['high'][ttlk-1]
                        if lastFXis == '0':
                            if kdata['high'][ttlk-1]>kdata['high'][j]:
                                CurrentJG ='01'  #'1?'
                                CurrentJGV = kdata['high'][ttlk - 1]
                            else:
                                CurrentJG ='0'
                                CurrentJGV=kdata['low'][ttlk-1]
                        kdata['JG'][ttlk-1]=CurrentJG
                        kdata['JGV'][ttlk-1] = CurrentJGV
                        ConfirmedJG=lastFXis
                        break

                #完成结构9后是否创新低 //KongHuang?
                for j in range(GGday,ttlk-1,1):
                    if kdata['JG'][j]=='9':
                        lowbefore   =kdata.iloc[GGday:j, 3].min()   # 3 'low'
                        lowafter    = kdata.iloc[j:ttlk-1, 3].min()  # 2 high 1  c losee 0 open
                        if lowafter<lowbefore: QX=1

                #比较下跌结构强弱       //JGQR
                for j in range(ttlk-2,minllocation-1,-1):   #从后向前找
                    if kdata['JG'][j] == '6' or kdata['JG'][j] == '8' or kdata[ 'JG'][j] == '0':
                        for k in range(j-1 , minllocation-1 , -1 ):
                            if kdata['JG'][k] == '6' or kdata['JG'][k]=='8' or kdata['JG'][k]=='0':
                                if kdata['low'][j]>kdata ['low'][k]:
                                    JGQR=1  # 结构强弱  //后面一个V比前面一个高, 则下跌结构强
                                    break
                                else:
                                    JGQR=-1 #后面一个V比前面一个低,则下跌结构弱
                                    break
                        break  # 只找最近的两个下跌的结构，比较强弱
                break  # 计算完下跌趋势的结构后退出程序（不用再向前找了）
            # 上涨趋势中上结构判断
            if kdata['kkllx'][i] == -2:
                # 从最高点开始找L离MA30最远的那根K线，开始找第一个底
                #logging.info(f"{period}是下跌趋势")
                maxhpercent = kdata['high'][i] /kdata['ma30'][i]
                maxhlocation = i

                for j in range(i ,ttlk -1 ,1):
                    maxhpercentnew =kdata['high'][j] /kdata['ma30'][j]
                    if maxhpercentnew > maxhpercent:
                        maxhlocation = j
                        maxhpercent =maxhpercentnew

                kdata['JGAFX'][maxhlocation ] =1  # 最远点为顶分型
                kdata['JG'][maxhlocation] = "1"  # 最远点为结构1
                kdata['JGV'][maxhlocation] = kdata['high'][maxhlocation]
                if maxhlocation == ttlk - 1: break  # 如果6是最后一根K线，则不计算其它结构只有6

                # 不同的结构算法，可能的计算起点不同，但是下面的计算都是一致的
                # 从1+1开始找下跌趋势中的A分型
                for j in range(maxhlocation +1, ttlk -1 ,1):
                    if kdata['high'][j] >kdata['high'][j -1]:
                        highlow =kdata['low'][j]
                        highhigh =kdata['high'][j]
                        for k in range(j +1 ,ttlk -1 ,1):
                            if highhigh <kdata['high'][k]:
                                j=k  # 如果在找到顶分型找到更低的底，则
                                break
                            if highlow>kdata['low'][k]:
                                kdata['JGAFX'][j] = 1
                                j=k
                                break
                                # 找到一个高顶
                # 1开始找上涨趋势中的V分型
                k=0
                for j in range(maxhlocation,ttlk-1,1):
                    if kdata ['JGAFX'][j]==1:
                        currentLow=kdata['low'][j]
                        currentLowLocation=j
                        for k in range(j+1,ttlk-1,1):
                            if kdata['low'][k]<currentLow:
                                currentLow=kdata['low'][k]
                                currentLowLocation=k
                            if kdata['JGAFX'][k]==1:
                                j=k
                                break
                        if k!=ttlk-1:
                            kdata['JGVFX'][currentLowLocation]=-1

                # 删除下跌趋势中无效A分型信号和左右两边较高的那个V分型信号 # 如果要删除的那个V是起始的6，则必须保留(当前是保留 因为其比例意义）
                # （或者换6到新的底）
                # 如果移动6，则minllocation要跟着一起改

                #logging.info(f"period:{period}")
                #logging.info(f"kdata:{kdata}")
                #删除碰不到MA10或MA30的V分型信号

                for j in range(maxhlocation,ttlk-1,1):
                    if kdata['JGVFX'][j]==-1:
                        low2ma10 =abs(kdata['ma10'][j]-kdata['low'][j])
                        low2ma5=abs(kdata['ma5'][j]-kdata['low' ] [j])
                        if kdata['low'][j]> kdata['ma10'][j] \
                                and low2ma5/2>low2ma10:   #？？？high超过ma10或high离ma5的距离是到ma10距离的2倍
                            #如果下跌过程中的A分型不满足79的结构条件，则删除这个A分型
                            kdata['JGVFX'][j] = 0
                                # 2 for debug
                            #找A分型的 下一个 A分型 （可能没有）
                            comp_OK=0
                            for k in  range(j,ttlk-1,1):
                                if kdata ['JGAFX'][k]==1:
                                    nextAFX=k   #如果后面没有V分型，则nextVFX=?
                                    comp_OK=comp_OK+1   #后面有个V
                                    break
                            #找A分型的 上一个 A分型 （必定有，至少有6）
                            for k in range(j,-1,-1):
                                if kdata['JGAFX'][k]==1:
                                    lastAFX=k
                                    comp_OK=comp_OK+1   #前面有个V
                                    break

                            if comp_OK==2:  #确保有两个V分型，再比较，否则程序报错
                                logging.info(f"lastAFX is {lastAFX} ||| nextAFX is {nextAFX}")
                                if kdata['high'][nextAFX]>=kdata['high'][lastAFX]:    #如果下一个low更低
                                    if kdata['JG'][lastAFX]!="1":                  #上一个不是6，就把上一个V分型信号删除
                                        #try:                                        #任何一个A分型前都能找到至少一个V分型信号（6）
                                        kdata['JGAFX'][lastAFX]=0              #这个地方应该不会出错 lastVFX下标一定能找到
                                    else:
                                        kdata['JGAFX'][nextAFX] =0                  #???A后是否必定有nextVFX
                                else:
                                    kdata['JGAFX'][nextAFX]=0                       #???删除下一个V分型信号

                # 计算其它结构
                # have 7  have8
                # lastFXis='6'
                # lastVFXis='6'
                # lastAFXis='0'
                # 从6后面的第一根K线开始找分型

                # 判断 9 和 是否完成下跌空间指标 XDKJ
                for j in range(maxhlocation+1,ttlk-1,1):
                    if kdata['JGVFX'][j]!=0:
                        low2ma30 = abs(kdata ['ma30'][j] - kdata['low'][j])
                        low2ma10 = abs(kdata['ma10'][j] - kdata['low'][j])
                        low2ma5 = abs(kdata['ma5'][j] - kdata['low'][j])
                        if kdata['low'][j]<kdata['ma30'][j] or low2ma30<low2ma10/2:
                            kdata['JG'][j]='4'
                            JGdone=1
                            if CHL>Luo/1.1: # and spaceP>normalspaceP[period]!!!
                                XDKJ=1  # 需要参考对应级别的正常趋势空间  ZDJG=1  # 完成止跌结构
                                #10%以内都算完成趋势空间XXX CHL<Kai+normalspaceP[period]*10%
                            kdata['JGV'][j] = kdata['low'][j] #如果9与A重合，则JGV中只能保存一个值
                            for k in range(j,ttlk-1,1):     #有了9，后面的V分型就是0
                                if kdata['JGAFX'][k]==1:
                                    kdata['JG'][k]='5'
                                    kdata['JGV'][k] = kdata['high'][k]
                                    break
                        else:
                            if kdata['low'][j]<kdata['ma10'][j] or low2ma10<low2ma5/2:
                                kdata['JG'][j]='2'
                                kdata['JGV'][j] = kdata['low'][j]
                                for k in range(j,ttlk-1,1):
                                    if kdata['JGAFX'][k] ==1: # logging.info("6+")
                                        kdata['JG'][k] = '3'
                                        kdata['JGV'][k] = kdata['high'][k]
                                        break

                for j in range(maxhlocation, ttlk - 1, 1):
                    if kdata['JGAFX'][j]==1:
                        for k in range(j+1, ttlk-1,1):
                            if kdata['JGVFX'][k]==-1:
                                break
                            if kdata['JGAFX'][k]==1:
                                if kdata['JG'][j]!='1':
                                    if kdata['high'][j]>kdata['high'][k]:
                                        kdata['JGAFX'][k]=0
                                    else:
                                        kdata['JGAFX'][j]=0
                                else:
                                    kdata['JGAFX'][k] = 0

                # 定义最后一根K线的结构
                #logging.info(f"{period},{kdata}")

                for j in range(ttlk-1, maxhlocation-1, -1):
                    lastFXis=kdata['JG'][j]
                    if lastFXis!=' ':
                        if lastFXis == '1':
                            if kdata['low'][ttlk-1]<kdata['low'][j]:
                                CurrentJG = '2'
                                CurrentJGV = kdata['low'][ttlk - 1]
                            else:
                                CurrentJG ='1'
                                CurrentJGV=kdata['high'][ttlk-1]
                        if lastFXis == '2':
                            if kdata['high'][ttlk-1]>kdata['high'][j]:
                                CurrentJG = '3'
                                CurrentJGV = kdata['high'][ttlk - 1]
                            else:
                                CurrentJG ='2'
                                CurrentJGV=kdata['low'][ttlk-1]
                        if lastFXis == '3':
                            if kdata['low'][ttlk-1]<kdata['low'][j]:
                                CurrentJG = '4'
                                CurrentJGV = kdata['low'][ttlk - 1]
                            else:
                                CurrentJG ='3'
                                CurrentJGV=kdata['high'][ttlk-1]
                        if lastFXis == '4':
                            if kdata['high'][ttlk-1]>kdata['high'][j]:
                                CurrentJG = '5'
                                CurrentJGV = kdata['low'][ttlk - 1]
                            else:
                                CurrentJG ='4'
                                CurrentJGV=kdata['low'][ttlk-1]
                        if lastFXis == '5':
                            if kdata['low'][ttlk-1]<kdata['low'][j]:
                                CurrentJG = '06' #'6?'
                                CurrentJGV = kdata['low'][ttlk - 1]
                            else:
                                CurrentJG ='5'
                                CurrentJGV=kdata['high'][ttlk-1]
                        kdata['JG'][ttlk-1]=CurrentJG
                        kdata['JGV'][ttlk-1] = CurrentJGV
                        ConfirmedJG=lastFXis
                        #logging.info(rf"Confirmed JG  now:{ConfirmedJG}")
                        break

                #完成结构4后是否创新高
                for j in range(DDday,ttlk-1,1):
                    if kdata['JG'][j]=='4':
                        highbefore   =kdata.iloc[DDday:j, 2].max()   # 3 'low'
                        highafter    = kdata.iloc[j:ttlk-1, 2].max()  # 2 high 1  c losee 0 open
                        if highafter>highbefore: QX=1

                #比较下跌结构强弱
                for j in range(ttlk-2,maxhlocation-1,-1):
                    if kdata['JG'][j] == '1' or kdata['JG'][j] == '3' or kdata[ 'JG'][j] == '5':
                        for k in range(j-1 , maxhlocation-1 , -1 ):
                            if kdata['JG'][k] == '1' or kdata['JG'][k]=='3' or kdata['JG'][k]=='5':
                                if kdata['high'][j]<kdata ['high'][k]:
                                    JGQR=-1  # 结构强弱
                                    #logging.info(rf"{period}结构强 {kdata['low'][k]} < {kdata['low' ] [j]}")
                                    break
                                else:
                                    JGQR=1
                                    #logging.info(rf"{period}结构弱 {kdata['low'][k]} > {kdata['low'][j]}")
                                    break
                        break  # 只找最近的两个下跌的结构，比较强弱
                break  # 计算完下跌趋势的结构后退出程序（不用再向前找了）
        periodJG={
            #'code'          :   stockcode+period,
            #'stockcode'     :   stockcode,
            #'period'        :   period,
            #'QS'            :   QS,         # 趋势方向
            'ConfirmedJG'   :   ConfirmedJG,# 确认的结构
            'CurrentJG'     :   CurrentJG,  # 当前（可能）结构'XDKJ'          :   XDKJ,  # 完成下跌空间？
            'JGdone'        :   JGdone,     #完成结构 有9或4且又没有大幅创新高或创新低 与ma30的相对距离判断
            #'KJDone'        :   KJdone,     #完成空间   #这个应该是在趋势计算中判断
            #'ZDJG'          :   ZDJG,       # 形成止跌结构？
            'QX'     :   QX,  # 制造恐慌？
            'JGQR'          :   JGQR,       # 结构强弱
            #'XDKJ'          :   XDKJ,       # 完成下跌空间
            #'reviseTime'    :   revisetime
        }
        #kdata.to_csv(rf"./tmpout/{period}.csv", encoding="utf_8_sig", index=True)
        return kdata, periodJG
    def topns(self,module,pns,reason,price,period):
        tmppns={'module':module,'pns':pns,'reason':reason,'price':price,'per':0,'period':period}
        self.pns=pd.concat([self.pns, pd.DataFrame([tmppns])], ignore_index=True)
    def cal_qsjg_pns(self,periodQS,periodJG,period):
        self.topns("QS","P","GG",periodQS['GG'],self.p2ab[period])
        self.topns("QS","P","Luo",periodQS['Luo'],self.p2ab[period])
        self.topns("QS","S","Kai",periodQS['Kai'],self.p2ab[period])
        self.topns("QS","S","DD",periodQS['DD'],self.p2ab[period])
        tmpqs=self.QS[period]
        #print(f"period:{period}")
        if ("-1" in tmpqs or "-2" in tmpqs or "0" in tmpqs) and period!="m1":
            self.topns("JG","P","ma30",periodQS['ma30'],self.p2ab[period])
            if int(periodJG['CurrentJG'])<=7 and periodJG['CurrentJG']!="0" and period!="m1" and period!="m5":
                self.topns("JG","P","ma10",periodQS['ma10'],self.p2ab[period])
        if ("+1" in tmpqs or "+2" in tmpqs) and period!="m1":
            self.topns("JG","S","ma30",periodQS['ma30'],self.p2ab[period])
            if int(periodJG['CurrentJG'])<=2 and period!="m1" and period!="m5":
                self.topns("JG","S","ma10",periodQS['ma10'],self.p2ab[period])

    def cal_qs_pnsbyperiod(self):  
        #数据 - > 趋势分析 -> 结构分析 ->pns -> BSZC
        #print("by period self.QS_m:",self.QS_m)
        #print("by period self.JG_m:",self.JG_m)
        '''
        for i in self.periodab:
            #print(i)
            print("error!!!!!!!!!!!!!! line 1635")
            pass
        '''
        pass

    def cal_qs(self,kdata, stockcode, period):
        try:
            closeprice=kdata['close'][-1]
        except:
            pass
            print("error!!!!!!!!!!!!!! line 1646")
        periodQS = {'code':         stockcode + period,
                    'stockcode':    stockcode,
                    'period':       period,
                    'C':            99,#closeprice,  # data['close'][-1],
                    'QS':           99,
                    'DD':           99,
                    'Kai':          99,
                    'GG':           99,
                    'Luo':          99,
                    'profitP':      99,
                    'riskP':        99,
                    'PLP':          99,
                    'CHL':          99,
                    'CHLP':         99,
                    'spaceP':       99,
                    'outofrange':   99,
                    'QSabnormal':   99,  # QSabnormal,
                    'ma5':          99,#kdata['ma5'][-1].round(2),
                    'ma10':         99,#kdata['ma10'][-1].round(2),
                    'ma30':         99,#kdata['ma30'][-1].round(2),
                    'ma5P':         99,#(closeprice / kdata['ma5'][-1] * 100 - 100).round(2),
                    'ma10P':        99,#(closeprice / kdata['ma10'][-1] * 100 - 100).round(2),
                    'ma30P':        99,#(closeprice / kdata['ma30'][-1] * 100 - 100).round(2),
                    'periodSugg':   99,
                    'DDCGorGGCD':   99,
                    'reviseTime':   99,#"nodata"+period,
                    'NDG'	:   99,
                    'NKL'       :   99,
                    'CHL1'      :   99,
                    'DDCGorGGCD1':  99
                    }
        periodJG={
             'ConfirmedJG'   :   99,# 确认的结构
             'CurrentJG'     :   99,  # 当前（可能）结构'XDKJ'          :   XDKJ,  # 完成下跌空间？
             'JGdone'        :   99,     #完成结构 有9或4且又没有大幅创新高或创新低 与ma30的相对距离判断
             'QX'            :   99,  # 制造恐慌？
             'JGQR'          :   99       # 结构强弱
         }
        ttlk = kdata.shape[0]
        if ttlk<50: return kdata,periodQS,periodJG
        kdata=self.cal_qs_KaiLuo(kdata,ttlk)                        #+1 -1 [Luo 和 Kai]
        kdata=self.cal_qs_GGDD(kdata,ttlk)                          #+2 -2 [GG 和 DD]
        kdata=self.cal_qs_kkllv(kdata,ttlk)                         #计算+1 和 -1的数值
        #趋势模型
        kdata=self.cal_qs_QSmodule_NDGNKL(kdata,ttlk)
        kdata=self.cal_qs_QSmodule_QSDDKaiGGLuo(kdata,ttlk)
        kdata,periodQS=self.cal_qs_QSmodule_CHL1andCHL(stockcode,kdata,ttlk,period)
        #趋势信号
        self.cal_qs_QSmessage(kdata,ttlk,period)
        #结构模型
        kdata,periodJG=self.cal_jg_JGmodule(kdata,ttlk,period,periodQS)
        self.cal_qsjg_pns(periodQS,periodJG,period)
        return kdata, periodQS, periodJG
