if 1:
    import sys,math
    import multiprocessing as mp
    import os
    import numpy as np
    import time
    import pandas as pd
    import warnings
    import xlrd
    import re
    import threading
    import tushare as ts
    import 	requests
    import json
    import logging
    import datetime
    import traceback
    import numpy as np
    import pandas as pd
    import tushare as ts
    import datetime
    from 	sqlalchemy import create_engine
    import 	sqlalchemy  #初始化数据库连接，使用pymysql模块
    import pymysql
ts.set_token('8f6e67ec8a48922bbb531b95687625d99ee263940a07561ae95f4648')  # Tushare Pro
pd.set_option('mode.chained_assignment', None)
warnings.filterwarnings("ignore")
durtime=0.001    #执行时间
durtime1=0.5
FocusPeriod=0       #当前”关注“的周期
uihandle=0          #主程序句柄 （包含ui界面中的元素 ui.ui 和 主程序中的变量 ui.name
plock=0
showtype=1
havelistseq=1
stockcode="300027.SZ"
mysqlhost="119.45.95.223"
xgresult=pd.DataFrame()

class Kdata():
    '''K线类 技术分析评分机制'''
    def __init__(self,stockcode,showtype,havelistseq,parent=None):
        super().__init__()
        self.refreshK_timeinter=10000
        self.init_parameter()
        self.stockcode = stockcode  # 股票代码 300027.S
        self.showtype=showtype      # 显示什么内容
        self.havelistseq=havelistseq
        self.sig=1                  # 信号
        self.havelist=pd.DataFrame()
        '''自 动运行'''
    def run(self):
        if 1: #i in range(0,100):
            self.whole_process()
            #self.show()
            #self.export()   #输出结果到CSV文件和mysql数据库
            #self.show_log() #
            #QThread.msleep(self.refreshK_timeinter)
    def init_parameter(self):
        '''定义参数'''
        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()
    def thread_it(self, func, *args):  # '''将函数放入线程中执行''' #有两个参数 1，函数名称 func 2,函数变量 **args
        task = threading.Thread(target=func, args=args)  # 创建线程
        task.setDaemon(True)  # 守护线程
        task.start()
        #task.wait()
    def whole_process(self):
        self.get_div()          #取除权数据
        self.get_K_all()        #下载所有周期K线数据
        self.cal_ma_all()       #计算所有周期ma
        self.cal_qs_all()       #计算所有周期的趋势和结构数据
        #self.signal.emit(1)

    def xg(self,stockcode):
        global xgresult
        starttime=time.time()
        pro = ts.pro_api()
        stocklist = pro.query('stock_basic', exchange='', list_status='L')
        stocklist=stocklist[(stocklist['list_date']<"20180101")]
        print(stocklist.shape[0])
        i=1
        for index,row in stocklist.iterrows():
            self.stockcode=row['ts_code']
            print(i,self.stockcode)

            #self.stockcode=stockcode
            self.get_div()
            #only get WMD kdata
            try:
                self.get_K_D()
            except:
                continue
            if self.kdata_d.shape[0]==0: continue
            self.kdata_d=self.wash_K_div(self.kdata_d)
            self.get_K_MW()
            self.kdata_d=self.kdata_d[-300:]            #缩减日K线数量以加速计算
            #only cal WMD ma
            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)
            #only cal WMD 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')
            print(self.QS['month'],self.QS['week'],self.QS['day'])
            tmp={'stockcode':self.stockcode,'month':self.QS['month'],'week':self.QS['week'],'day':self.QS['day']}
            xgresult=xgresult.append(tmp,ignore_index=True)
            i+=1
            self.init_parameter()
        xgresult.to_csv(rf"./xgresult.csv",encoding="utf_8_sig",index=True)
        print("TTL time:", time.time()-starttime)

    def getdk_mp(self):
        starttime=time.time()
        corelist=[0]
        
        pro = ts.pro_api()
        stocklist = pro.query('stock_basic', exchange='', list_status='L')
        #stocklist=stocklist[(stocklist['list_date']<"20180101")]
        self.allstocklist=stocklist#[-1600:]
        print(self.allstocklist)

        stockqty=self.allstocklist.shape[0]
        qtyperjob=80
        coreqty=math.ceil(stockqty/qtyperjob)
        for i in range(1,coreqty):
            corelist.append(i*80)
        print("XXXX corelist",corelist)
        threadqty=128
        pool=mp.Pool(threadqty)
        qtyperthread=math.ceil(stockqty/threadqty)
        print("qtyperthread",qtyperthread)
        pool.map(self.getdk_for_mp,corelist)
        print("TTL time:", time.time()-starttime)
    def getdk_for_mp(self,n):
        stocklist=self.allstocklist[n:n+80]
        print(stocklist.shape[0])
        i=1
        for index,row in stocklist.iterrows():
            self.stockcode=row['ts_code']
            print(i,self.stockcode)
            try:
                self.get_K_D()
                self.kdata_t=self.get_K_TFO("m30")
                self.kdata_f=self.get_K_TFO("m5")
                self.kdata_o=self.get_K_TFO("m1")
                i+=1
            except:
                continue
            if self.kdata_d.shape[0]==0: continue
            self.kdata_d.to_csv(rf"./kdata/{self.stockcode[:6]}_d.csv",encoding="utf_8_sig",index=True)
            self.kdata_t.to_csv(rf"./kdata/{self.stockcode[:6]}_t.csv",encoding="utf_8_sig",index=True)
            self.kdata_f.to_csv(rf"./kdata/{self.stockcode[:6]}_f.csv",encoding="utf_8_sig",index=True)
            self.kdata_o.to_csv(rf"./kdata/{self.stockcode[:6]}_o.csv",encoding="utf_8_sig",index=True)
    def getdk(self,stockcode):
        starttime=time.time()
        ttlkdata=pd.DataFrame()
        pro = ts.pro_api()
        stocklist = pro.query('stock_basic', exchange='', list_status='L')
        stocklist=stocklist[(stocklist['list_date']<"20180101")]
        print(stocklist.shape[0])
        i=1
        for index,row in stocklist.iterrows():
            self.stockcode=row['ts_code']
            print(i,self.stockcode)
            try:
                self.get_K_D()
                i+=1
            except:
                continue
            if self.kdata_d.shape[0]==0: continue
            self.kdata_d.to_csv(rf"./tmpout/{self.stockcode[:6]}.csv",encoding="utf_8_sig",index=True)
            print(ttlkdata.shape[0])
        print("TTL time:", time.time()-starttime)

    def From_table(self,sql="select username,loged from oneus er"):
        global mysqlhost
        db = pymysql.connect(host=mysqlhost, user="root", password="Cfintttt3321", database="one", charset="utf8")
        df = pd.read_sql(sql, db)  # , index_col=index_column)
        # print(df)
        return df
    def export(self):
        self.kdata_m.to_csv(rf"./tmpout/kdata_m.csv", encoding="utf_8_sig", index=True)
        self.kdata_w.to_csv(rf"./tmpout/kdata_w.csv", encoding="utf_8_sig", index=True)
        self.kdata_d.to_csv(rf"./tmpout/kdata_d.csv", encoding="utf_8_sig", index=True)
        self.kdata_t.to_csv(rf"./tmpout/kdata_t.csv", encoding="utf_8_sig", index=True)
        self.kdata_f.to_csv(rf"./tmpout/kdata_f.csv", encoding="utf_8_sig", index=True)
        self.kdata_o.to_csv(rf"./tmpout/kdata_o.csv", encoding="utf_8_sig", index=True)
        logging.info("fake export K data done!")
    def show_log(self):
        logging.info("fake show_log done!")
        '''
        logging.info("month k",    self.kdata_m)
        logging.info("week k",     self.kdata_w)
        logging.info("day K",      self.kdata_d)
        logging.info("m30 k",      self.kdata_t)
        logging.info("m5 k",       self.kdata_f)
        logging.info("m1 k",       self.kdata_o)

        logging.info("month QS",   self.QS_m)
        logging.info("week QS",    self.QS_w)
        logging.info("day QS",     self.QS_d)
        logging.info("30 QS",      self.QS_t)
        logging.info("5 QS",       self.QS_f)
        logging.info("1 QS",       self.QS_o)
        '''
    def get_div(self):
        self.divpd = pd.read_csv(r"./csv/divdata.csv")
        self.divpd = self.divpd[self.divpd['code'] == self.stockcode]
        self.divpd['date'] = pd.to_datetime(self.divpd['date'])
        self.divpd = self.divpd.set_index('date')
        self.divpd = self.divpd.sort_index(ascending=False)
    def wash_K_div(self,kdata):
        #self.divpd['date'] = pd.to_datetime(self.divpd['date'])
        #self.divpd = self.divpd.set_index('date')
        #self.divpd = self.divpd.sort_index(ascending=False)
        for key, val in self.divpd.iterrows():
            # logging.info("key:",key)
            date = key - datetime.timedelta(minutes=1)
            if date < kdata.index[0]: break
            for field in kdata.columns.values:
                if field != 'volume' and field != 'amount':  # open	close	high	low	    vol
                    kdata.loc[:date, field] -= val.bonus / 10
                    kdata.loc[:date, field] += val.price * (val.rationed / 10)
                    kdata.loc[:date:, field] /= 1 + val.present / 10 + val.rationed / 10
        return kdata
    def get_K_all(self):
        #多线程一次性获取6个周期的K线数据，并存放到6个dataframe中
        self.get_K_D()
        self.kdata_d=self.wash_K_div(self.kdata_d)
        self.get_K_MW()
        self.kdata_d=self.kdata_d[-300:]            #缩减日K线数量以加速计算

        self.kdata_t=self.get_K_TFO('m30')          #得到30分钟数据
        self.kdata_t=self.wash_K_div(self.kdata_t)
        self.kdata_f=self.get_K_TFO('m5')           #得到5分钟数据
        self.kdata_f=self.wash_K_div(self.kdata_f)
        self.kdata_o=self.get_K_TFO('m1')           #得到1分钟数据
        self.kdata_o=self.wash_K_div(self.kdata_o)
    def get_K_D(self):
        urlstockcode = self.stockcode[-2:].lower() + self.stockcode[:6]
        # http://web.ifzq.gtimg.cn/appstock/app/fqkline/get?param=sz300027,month,1990-12-01,,10240,qfq
        url = rf'http://web.ifzq.gtimg.cn/appstock/app/fqkline/get?param={urlstockcode},day,1990-12-01,,10240,bfq'
        originaltxt = self.gethtml(url)
        goodtxt = re.sub(',{(.+?)}]', ']', originaltxt)

        my_json = json.loads(goodtxt)
        my_k = my_json['data'][urlstockcode]['day']
        arrayk = np.array(my_k)
        self.kdata_d = pd.DataFrame(arrayk, columns=['date', 'open', 'close', 'high', 'low', 'vol'])  # ???ul1
        self.kdata_d[['open', 'close', 'high', 'low']] = self.kdata_d[['open', 'close', 'high', 'low']].apply(pd.to_numeric).round(2)
        self.kdata_d['vol'] = self.kdata_d['vol'].apply(pd.to_numeric).astype(int)  # vol 转换为 整型
        self.kdata_d['date'] = pd.to_datetime(self.kdata_d['date'])
        self.kdata_d.set_index(["date"], inplace=True)  # set date as index
        self.kdata_d['vol'] = self.kdata_d['vol'].map(lambda x: x / 1000).round(3)  # 成交量/1000,避免溢出？？？有没有问题
    def get_K_MW(self):
        mvol    = self.kdata_d['vol'].resample('M').sum().round(2)
        mopen   = self.kdata_d['open'].resample('M').first().round(2)
        mclose  = self.kdata_d['close'].resample('M').last().round(2)
        mhigh   = self.kdata_d['high'].resample('M').max().round(2)
        mlow    = self.kdata_d['low'].resample('M').min().round(2)
        self.kdata_m = pd.concat([mopen, mclose, mhigh, mlow, mvol], axis=1)
        self.kdata_m = self.kdata_m.dropna(axis=0, how='any')

        wvol    = self.kdata_d['vol'].resample('W-FRI').sum().round(2)
        wopen   = self.kdata_d['open'].resample('W-FRI').first().round(2)
        wclose  = self.kdata_d['close'].resample('W-FRI').last().round(2)
        whigh   = self.kdata_d['high'].resample('W-FRI').max().round(2)
        wlow    = self.kdata_d['low'].resample('W-FRI').min().round(2)
        self.kdata_w = pd.concat([wopen, wclose, whigh, wlow, wvol], axis=1)
        self.kdata_w = self.kdata_w.dropna(axis=0, how='any')
    def gethtml(self,url):
        ''' 
        最多读取10次，每次等待3秒钟
        如果超过3次没有抓到K线数据，就把 i+url 输出到htmlerror.txt文件中
        每次程序开始运行的时候，清空 htmlerror.txt 文件
        '''
        i = 0
        while i < 10:
            try:
                html = requests.get(url, timeout=3).text
                return html
            except requests.exceptions.RequestException:
                i += 1
        if i==10:   #10次都没有取得数据
            print(f"尝试过{i}次后，依然没有通过{url}得到数据")
        if i > 1 and i!=10:
            #self.string2txt(1, str(i) + url)
            print(f"{i} 次通过{url} 没有得到数据")
    def get_K_TFO(self,period):
        urlstockcode = self.stockcode[-2:].lower() + self.stockcode[:6]

        url = rf'http://ifzq.gtimg.cn/appstock/app/kline/mkline?param={urlstockcode},{period},,320&_var=m1_today&r=0.260880015116'
            # vol后面多了两列 {} 和 类似成交量的一个数字, 用 un1 和 un2替代后，再删除
        originaltxt = self.gethtml(url)
        goodtxt = re.sub(',{}(.+?)]', ']', originaltxt)
        goodtxt = goodtxt[9:]

        #logging.info(goodtxt)

        my_json = json.loads(goodtxt)
        my_k = my_json['data'][urlstockcode][period]
        arrayk = np.array(my_k)
        kdata = pd.DataFrame(arrayk, columns=['date', 'open', 'close', 'high', 'low', 'vol'])  # ???ul1
        kdata[['open', 'close', 'high', 'low']] = kdata[['open', 'close', 'high', 'low']].apply(
            pd.to_numeric).round(2)
        kdata['vol'] = kdata['vol'].apply(pd.to_numeric).astype(int)  # vol 转换为 整型
        kdata['date'] = pd.to_datetime(kdata['date'])
        kdata.set_index(["date"], inplace=True)  # set date as index
        kdata['vol'] = kdata['vol'].map(lambda x: x / 1000).round(3)  # 成交量/1000,避免溢出？？？有没有问题
        #logging.info("kdata",kdata)
        return(kdata)
    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)
    def cal_ma_K(self,kdata):
        ma5             = kdata["close"].rolling(5).mean()
        ma10            = kdata["close"].rolling(10).mean()
        ma30            = kdata["close"].rolling(30).mean()
        kdata.insert(5, 'ma5', ma5)
        kdata.insert(6, 'ma10', ma10)
        kdata.insert(7, 'ma30', ma30)
        kdata           = kdata.fillna(method='bfill')  # bfill由后向前填充 ffill由前向后填充
        kdata['ma5']    = kdata['ma5'].round(3)  # 保留4位是为了计算的准确性？？？
        kdata['ma10']   = kdata['ma10'].round(3)  # 最大的计算是除权
        kdata['ma30']   = kdata['ma30'].round(3)  # 除权与均线无关，且是除权后算均线
        return kdata
    def Merge(self, dict1, dict2):
        res = {**dict1, **dict2}
        return res 
        
    def get_havelist(self):
        self.havelist = self.From_table("select * from havelist")
        self.havelist.index = self.havelist.index + 1
        self.havelist=self.havelist[['代码','成本','数量','名称']]
        self.havelist.columns=['code','cost','qty','name']
        self.havelist['qty']=self.havelist['qty']/100
        self.havelist['qty']=self.havelist['qty'].round(0)
        insertx=3
        self.havelist.insert(insertx,"%",0.00)
        self.havelist.insert(insertx+1,"S1Q",0)
        self.havelist.insert(insertx+2, "S1", 0.00)
        self.havelist.insert(insertx+3, "B1", 0.00)
        self.havelist.insert(insertx+4, "B1Q", 0)
        self.havelist.insert(insertx+5, "amt", self.havelist['cost']*self.havelist['qty']/100)
        self.havelist['amt']=self.havelist['amt'].round(2)
        #self.havelist=self.havelist.set_index['amt']
        self.havelist=self.havelist.sort_values(axis=0,by='amt',ascending=False,ignore_index=True)
        self.havelist.index=self.havelist.index+1
        #self.havelist=self.havelist.reset_index(drop=True)
        self.havelist.insert(insertx+6,'Q',0.00)
        self.havelist.insert(insertx+7,'+',0.00)
        self.havelist.insert(insertx+8,'-',0.00)
        self.havelist.insert(insertx+9,'sn',self.havelist.index)
        self.get_hq()
    def get_hq(self):
        seq="http://qt.gtimg.cn/q="
        for key,have in self.havelist.iterrows():
            if have.code[:1]=="6":
                seq=seq+"sh"+have.code+","
            else:
                seq=seq+"sz"+have.code+","
        html=requests.get(seq,timeout=3).text
        sp=html.split(";")
        for spn in sp:
            if len(spn)>10:
                spnn=spn.split("~")
                #print("spnn",i,spnn[3],spnn[2],spnn[20],spnn[19],spnn[9],spnn[10],spnn[32])
                ttl=self.havelist.shape[0]
                for j in range(1,ttl+1):
                    if self.havelist['code'][j]==spnn[2]:
                        self.havelist['%'][j]=spnn[32]
                        self.havelist['S1Q'][j]=int(spnn[20])
                        self.havelist['S1'][j]=spnn[19]
                        self.havelist['B1'][j]=spnn[9]
                        self.havelist['B1Q'][j]=int(spnn[10])
                        self.havelist['Q'][j]=spnn[49]
                        self.havelist['+'][j]=spnn[47]
                        self.havelist['-'][j]=spnn[48]
    def show(self):
        #os.system("clear")
        if self.showtype==1:
            self.get_havelist()
            #1os.system("clear")
            print(self.havelist)
            #print(self.havelist['code'],self.havelist['%'])
        if self.showtype==2:    #select stock
            self.get_havelist()
            self.stockcode=self.havelist.iloc[havelistseq-1,0]
            if self.stockcode[:1]=='6':
                self.stockcode=self.stockcode+".SH"
            else:
                self.stockcode = self.stockcode + ".SZ"
            self.run()
            #1os.system("clear")
            print("\033[0;31;40m\tstockcode:\033[0m", self.stockcode)
            print("seq stockcode",self.stockcode)
            #print(self.PnSpd)
        if self.showtype==3:    #keyin stock
            self.run()
            #1os.system("clear")
            #print(self.PnSpd)

    def cal_qs_KaiLuo(self,kdata,ttlk):
        #找到可能的Kai 和 Luo两种信号
        # 从第31根K线开始，到ttlk-10,计算MA30上的起涨点和起跌点 +1代表起跌点 -1代表起涨点
        kdata.insert(8, 'kkll', 0)  # -1起涨点 +1起跌点
        kdata.insert(9, 'kkllv', 0.00)  # 起涨价和起跌价
        kdata.insert(10, 'kkllx', 0)  # 最高价点+2 和 最低价点-2
        kdata.insert(11, 'kkllxv', 0.00)  # 最高价 和 最低价
        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['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['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(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
                DD = kdata['kkllxv'][i]
                break  # 只找最后一个
        added = kdata.shape[1]  # 如果没有DD，则 加一列 DD
        if added == 12:  kdata.insert(12, '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])
                Kai = kdata['kkllv'][i]
                break
        Kaiday = ttlk - i  # 和Luoday比较大小，Kaiday小，是上涨趋势，Kaiday大，是下跌趋势
        added = kdata.shape[1]
        if added == 13:  kdata.insert(13, 'Kai', 0)  # 如果趋势不全，找不到-1

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

        for i in range(ttlk - 1, -1, -1):
            if kdata['kkll'][i] == 1:  # Luo
                kdata.insert(15, 'Luo', kdata['kkllv'][i])
                Luo = kdata['kkllv'][i]
                break
        Luoday = ttlk - i
        added = kdata.shape[1]
        if added == 15:  kdata.insert(15, '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,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')

        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(2),
                    'ma10':         kdata['ma10'][-1].round(2),
                    'ma30':         kdata['ma30'][-1].round(2),
                    '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.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
                if closeprice>DD*(1-self.diffbyp[period]) and closeprice<GG*(1+self.diffbyp[period]):
                    insidespace=True
                    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]}")
            if newma30>=tmpk['ma30'][28]:
                self.QS[period]+="+d"
        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)
            #print(f"{period} newma30:{newma30} vs {tmpk['ma30'][28]}")
            if newma30<=tmpk['ma30'][28]:
                self.QS[period]+="-d"
    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)  # 结构特征价格

        ZDJG        = 0  # 完成止跌结构
        XDKJ        = 0  # 完成下跌空间
        JGQR        = 0  # 结构强弱
        KongHuang   = 0  # 完成止跌结构后创新低制造恐慌
        ConfirmedJG =   ' '
        CurrentJG   =   ' '
        CurrentJGV  =   0

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

        KangFen = 1
        # logging.info("before JG loop")
        # kdata.to_csv(rf"./tmpout/b_{period}.csv",encoding="utf_8_sig", index=True )
        #logging.info(f"current period is: {period}")
        for i in range(ttlk - 1, -1, -1):  # 0<=i<=ttlk-1
            # logging.info(f"i={i},ttlk={ttlk-1}")
            # 下跌趋势中上结构判断
            if kdata['kkllx'][i] == 2:
                # 从最高点开始找L离MA30最远的那根K线，开始找第一个底
                #logging.info(f"{period}是下跌趋势")
                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要跟着一起改

                #logging.info(f"period:{period}")
                #logging.info(f"kdata:{kdata}")
                #删除碰不到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分型，再比较，否则程序报错
                                logging.info(f"lastVFX is {lastVFX} ||| nextVFX is {nextVFX}")
                                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:
                                            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'
                            if CHL<Kai*1.1: # and spaceP>normalspaceP[period]!!!
                                XDKJ=1  # 需要参考对应级别的正常趋势空间  ZDJG=1  # 完成止跌结构
                                #10%以内都算完成趋势空间XXX CHL<Kai+normalspaceP[period]*10%
                            kdata['JGV'][j] = kdata['high'][j] #如果9与A重合，则JGV中只能保存一个值
                            # logging.info("minllocation:",minllocation)
                            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]
                                    # logging.info("8")
                                    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
                        #logging.info(rf"Confirmed JG  now:{ConfirmedJG}")
                        break

                #完成结构9后是否创新低
                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: KongHuang=1

                #比较下跌结构强弱
                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  # 结构强弱
                                    #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  # 计算完下跌趋势的结构后退出程序（不用再向前找了）
            # 上涨趋势中上结构判断
            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'
                            if CHL<Kai*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中只能保存一个值
                            # logging.info("minllocation:",minllocation)
                            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]
                                    # logging.info("8")
                                    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: KangFen=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,  # 完成下跌空间？
            'ZDJG'          :   ZDJG,       # 形成止跌结构？
            'KongHuang'     :   KongHuang,  # 制造恐慌？
            '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=self.pns.append(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_pnsall(self):
        if self.QS_m['Kai']!=0:
            if self.QS_m['Luo']/self.QS_m['Kai']-1>=self.space['month']:
                self.PnS=\
                    {'mGG': self.QS_m['GG'],'mLuo':self.QS_m['Luo'],'mDD': self.QS_m['DD'],'mKai':self.QS_m['Kai']\
                ,'mma5':self.QS_m['ma5'],'mma10':self.QS_m['ma10'],'mma30':self.QS_m['ma30']
                     }

        if self.QS_w['Kai']!=0:
            if self.QS_w['Luo']/self.QS_w['Kai']-1>=self.space['week']:
                self.PnS=self.Merge(self.PnS,\
                    {'wGG': self.QS_w['GG'],'wLuo':self.QS_w['Luo'],'wDD': self.QS_w['DD'],'wKai':self.QS_w['Kai']\
                ,'wma5':self.QS_w['ma5'],'wma10':self.QS_w['ma10'],'wma30':self.QS_w['ma30']
                     })

        if self.QS_d['Kai']!=0:
            if self.QS_d['Luo']/self.QS_d['Kai']-1>=self.space['day']:
                self.PnS=self.Merge(self.PnS,\
                    {'dGG': self.QS_d['GG'],'dLuo':self.QS_d['Luo'],'dDD': self.QS_d['DD'],'dKai':self.QS_d['Kai']\
                ,'dma5':self.QS_d['ma5'],'dma10':self.QS_d['ma10'],'dma30':self.QS_d['ma30']
                     })

        if self.QS_t['Kai']!=0:
            if self.QS_t['Luo']/self.QS_t['Kai']-1>=self.space['m30']:
                self.PnS=self.Merge(self.PnS,\
                    {'tGG': self.QS_t['GG'],'tLuo':self.QS_t['Luo'],'tDD': self.QS_t['DD'],'tKai':self.QS_t['Kai']\
                ,'tma5':self.QS_t['ma5'],'tma10':self.QS_t['ma10'],'tma30':self.QS_t['ma30']
                     })

        if self.QS_f['Kai']!=0:
            if self.QS_f['Luo']/self.QS_f['Kai']-1>=self.space['m5']:
                self.PnS=self.Merge(self.PnS,\
                    {'fGG': self.QS_f['GG'],'fLuo':self.QS_f['Luo'],'fDD': self.QS_f['DD'],'fKai':self.QS_f['Kai']\
                ,'fma5':self.QS_f['ma5'],'fma10':self.QS_f['ma10'],'fma30':self.QS_f['ma30']
                     })

        if self.QS_o['Kai']!=0:
            if self.QS_o['Luo']/self.QS_o['Kai']-1>=self.space['m1']:
                self.PnS=self.Merge(self.PnS,\
                    {'oGG': self.QS_o['GG'],'oLuo':self.QS_o['Luo'],'oDD': self.QS_o['DD'],'oKai':self.QS_o['Kai']\
                ,'oma5':self.QS_o['ma5'],'oma10':self.QS_o['ma10'],'oma30':self.QS_o['ma30']
                     })
        
        self.PnS = self.Merge(self.PnS, \
                              {'C':self.QS_m['C'],'H':self.kdata_d['high'][(self.kdata_d.shape[0]-1)],'L':self.kdata_d['low'][(self.kdata_d.shape[0]-1)]
                               })

        tmppd=pd.DataFrame.from_dict(self.PnS,orient='index',columns=['value'])
        tmppd=tmppd.reset_index().rename(columns={'index':'name'})
        tmppd.insert(2, 'pct', 0.00)  # -1起涨点 +1起跌点
        tmppd['pct'] = tmppd['value'].map(lambda x: (x / self.QS_m['C']-1)*100)
        tmppd['pct']=tmppd['pct'].round(2)
        tmppd=tmppd.sort_values(by="pct", ascending=False)
        tmppd=tmppd.reset_index(drop=True)
        tmppd.index=tmppd.index+1
        self.PnSpd=tmppd
    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)
            pass
        pass
    def cal_pns(self):
        newpns=self.pns
        #月线pns
        if "0" in self.QS['month']:newpns=newpns[(newpns['period']!="m")]
        if "0" in self.QS['week']:newpns=newpns[(newpns['period']!="w")]
        if "0" in self.QS['day']:newpns=newpns[(newpns['period']!="d")]
        if "0" in self.QS['m30']:newpns=newpns[(newpns['period']!="t")]
        if "0" in self.QS['m5']:newpns=newpns[(newpns['period']!="f")]
        if "0" in self.QS['m1']:newpns=newpns[(newpns['period']!="o")]
        newhavelist=self.havelist[(self.havelist['code']==self.stockcode[:6])]
        newhavelist=newhavelist.reset_index(drop=True)
        maxprice=newhavelist['+'][0]
        minprice=newhavelist['-'][0]
        newpns=newpns[(newpns['price']<maxprice) & (newpns['price']>minprice)]
        newpns=newpns.reset_index(drop=True)
        print(newpns)
        #self.pns=newpns

        pnsm=self.pns[(self.pns['period']=="m") | (self.pns['module']=="")]
        pnsm=pnsm[['reason','price','per']]
        pnsm=pnsm.reset_index(drop=True)
        #周线pns
        try: 
            pnsw=self.pns[(self.pns['period'] =="m") | (self.pns['period']=="w")  | (self.pns['module']=="")]
            pnsw=pnsw.reset_index(drop=True)
            try:
                startx=pnsw[pnsw['reason']=="GG_w"].index[0]
            except:
                startx=100
            startx1=pnsw[pnsw['reason']=="H_"].index[0]
            startx=min(startx,startx1)

            try:
                endx=pnsw[pnsw['reason']=="DD_w"].index[0]
            except:
                endx=0
            endx1=pnsw[pnsw['reason']=="L_"].index[0]
            endx=max(endx,endx1)
            pnsw=pnsw[startx:endx+1]
            pnsw=pnsw[['reason','price','per']]
            pnsw=pnsw.reset_index(drop=True)
        except:
            pnsw=pd.DataFrame()

        #日线pns
        try: 
            pnsd=self.pns[(self.pns['period'] =="m") | (self.pns['period']=="w") | (self.pns['period']=="d")  | (self.pns['module']=="")]
            pnsd=pnsd.reset_index(drop=True)
            try:
                startx=pnsd[pnsd['reason']=="GG_d"].index[0]
            except:
                startx=100
            startx1=pnsd[pnsd['reason']=="H_"].index[0]
            startx=min(startx,startx1)

            try:
                endx=pnsd[pnsd['reason']=="DD_d"].index[0]
            except:
                endx=0
            endx1=pnsd[pnsd['reason']=="L_"].index[0]
            endx=max(endx,endx1)

            pnsd=pnsd[startx:endx+1]
            pnsd=pnsd[['reason','price','per']]
            pnsd=pnsd.reset_index(drop=True)
        except:
            pnsd=pd.DataFrame()

        #30分钟pns
        try: 
            pnst=self.pns[(self.pns['period'] =="m") | (self.pns['period']=="w") | (self.pns['period']=="d") | (self.pns['period']=="t")  | (self.pns['module']=="")]
            pnst=pnst.reset_index(drop=True)
            try:
                startx=pnst[pnst['reason']=="GG_t"].index[0]
            except:
                startx=100
            startx1=pnst[pnst['reason']=="H_"].index[0]
            startx=min(startx,startx1)
            
            try:
                endx=pnst[pnst['reason']=="DD_t"].index[0]
            except:
                endx=0
            endx1=pnst[pnst['reason']=="L_"].index[0]
            endx=max(endx,endx1)

            pnst=pnst[startx:endx+1]
            pnst=pnst[['reason','price','per']]
            pnst=pnst.reset_index(drop=True)
        except:
            pnst=pd.DataFrame()

        #5分钟pns
        try:
            pnsf=self.pns[(self.pns['period'] =="m") | (self.pns['period']=="w") | (self.pns['period']=="d") | (self.pns['period']=="t") | (self.pns['period']=="f")   | (self.pns['module']=="")]
            pnsf=pnsf.reset_index(drop=True)
            try:
                startx=pnsf[pnsf['reason']=="GG_f"].index[0]
            except:
                startx=100
            startx1=pnsf[pnsf['reason']=="H_"].index[0]
            startx=min(startx,startx1)
                
            try:
                endx=pnsf[pnsf['reason']=="DD_f"].index[0]
            except:
                endx=0
            endx1=pnsf[pnsf['reason']=="L_"].index[0]
            endx=max(endx,endx1)

            pnsf=pnsf[startx:endx+1]
            pnsf=pnsf[['reason','price','per']]
            pnsf=pnsf.reset_index(drop=True)
        except:
            pass

        #1分钟pns
        try:
            pnso=self.pns[(self.pns['period'] =="m") | (self.pns['period']=="w") | (self.pns['period']=="d") | (self.pns['period']=="t") | (self.pns['period']=="f") | (self.pns['period']=="o")  | (self.pns['module']=="")]
            pnso=pnso.reset_index(drop=True)
            try:
                startx=pnso[pnso['reason']=="GG_o"].index[0]
            except:
                startx=100
            startx1=pnso[pnso['reason']=="H_"].index[0]
            startx=min(startx,startx1)
                
            try:
                endx=pnso[pnso['reason']=="DD_o"].index[0]
            except:
                endx=0
            endx1=pnso[pnso['reason']=="L_"].index[0]
            endx=max(endx,endx1)

            pnso=pnso[startx:endx+1]
            pnso=pnso[['reason','price','per']]
            pnso=pnso.reset_index(drop=True)
        except:
            pass
        pnsttl=pd.concat([pnsm,pnsw,pnsd,pnst,pnsf,pnso],axis=1)
        pnsttl=pnsttl.fillna("")

        
        newpnsm=pnsm[(pnsm['reason']=="fake")]
        newpnsw=newpnsm
        newpnsd=newpnsm
        newpnst=newpnsm
        newpnsf=newpnsm
        newpnso=newpnsm

        if "0" not in self.QS['month']: newpnsm=pnsm[(pnsm['price']<maxprice) & (pnsm['price']>minprice)]
        if "0" not in self.QS['week']:  newpnsw=pnsw[(pnsw['price']<maxprice) & (pnsw['price']>minprice)]
        if "0" not in self.QS['day']:   newpnsd=pnsd[(pnsd['price']<maxprice) & (pnsd['price']>minprice)]
        if "0" not in self.QS['m30']:   newpnst=pnst[(pnst['price']<maxprice) & (pnst['price']>minprice)]
        if "0" not in self.QS['m5']:    newpnsf=pnsf[(pnsf['price']<maxprice) & (pnsf['price']>minprice)]
        if "0" not in self.QS['m1']:    newpnso=pnso[(pnso['price']<maxprice) & (pnso['price']>minprice)]
        newpnsttl=pd.concat([newpnsm,newpnsw,newpnsd,newpnst,newpnsf,newpnso],axis=1)
        newpnsttl=newpnsttl.fillna("")

        self.get_havelist()
        #os.system("clear")
        print(self.havelist)
        #print(self.QS)
        tmpstr=f"--M:{self.QS['month']}-------------W:{self.QS['week']}-------------D:{self.QS['day']}-------------T:{self.QS['m30']}--------------F:{self.QS['m5']}-------------O:{self.QS['m1']}----------"
        #print(f"-------------------M:{self.QS['month']}-------------W:{self.QS['week']}-------------D:{self.QS['day']}-------------T:{self.QS['m30']}--------------F:{self.QS['m5']}-------------O:{self.QS['m1']}----------")
        print(f"\033[0;31;40m\t {tmpstr} \033[0m")
        print(pnsttl)
        print(f"\033[0;31;40m\t {tmpstr} \033[0m")
        print(newpnsttl)
        print(f"\033[0;31;40m\t {tmpstr} \033[0m")
        #print(f"-------------------M:{self.QS['month']}-------------W:{self.QS['week']}-------------D:{self.QS['day']}-------------T:{self.QS['m30']}--------------F:{self.QS['m5']}-------------O:{self.QS['m1']}----------")
        #print("-------------------M------------------W------------------D------------------T-------------------F------------------O----------")

    def cal_qs(self,kdata, stockcode, period):
        periodQS={}
        periodJG={}
        ttlk = kdata.shape[0]
        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(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
    def cal_qs_all(self):

        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.cal_qs_pnsall()        #所有级别的ps放在一起
        self.cal_qs_pnsbyperiod()   #单独计算每个级别的ps

        self.topns("","","H",self.kdata_d['high'][(self.kdata_d.shape[0]-1)],"")
        self.topns("","","L",self.kdata_d['low'][(self.kdata_d.shape[0]-1)],"")
        self.topns("","","C",self.QS_m['C'],"")
        self.pns['per']=round((self.pns['price']/self.QS_m['C']-1)*100,1)
        #print("all",self.pns)
        pns1=self.pns[(self.pns['per']>0) & (self.pns['pns']=="S")]
        self.pns=self.pns.append(pns1).drop_duplicates(keep=False)
        pns1=self.pns[(self.pns['per']<0) & (self.pns['pns']=="P")]
        self.pns=self.pns.append(pns1).drop_duplicates(keep=False)
        self.pns=self.pns.sort_values(by=['per'],ascending=False)
        self.pns['reason']=self.pns['reason']+"_"+self.pns['period']
        #print("good",self.pns)
        self.cal_pns()

def format_code(scode):
    if scode[0:1]=="6":
        return scode+".SH"
    else:
        return scode+".SZ"
def main():
    global stockcode,showtype,havelistseq
    while True:
        k=Kdata(stockcode,showtype,havelistseq)
        k.show()
        old_stockcode=stockcode
        stockcode=input("New stockcode:")
        #"Q" = Quit
        if stockcode=="q":
            print("\033[0;31;40m\t ~see u!~ \033[0m")
            break
        #"A" = all List
        if stockcode=='a':
            showtype = 1
        #"NN" = show selected stock
        try:
            if int(stockcode)>=1 and int(stockcode)<=300:
                showtype=2
                havelistseq=int(stockcode)
        except:
            pass
        #"XXXXXX.SZ" = show keyin stock
        if len(stockcode)==6:
            showtype=3
        if len(stockcode)==9:
            old_stockcode=stockcode
            showtype=3
        if showtype==3 and len(stockcode)==0:
            stockcode=old_stockcode
        #"s;1;3.79;5000" = sell stock by SeqNo.
        #"b;1;3.55;5000" = sell stock by SeqNo.
        #"s;300027;3.79;5000"=sell stock by stockcode
        if stockcode[0:1]=="s" or stockcode[0:1]=="S":
            sinfo=stockcode.split(";")
            scode=k.havelist['code'][int(sinfo[1])]
            sname=k.havelist['name'][int(sinfo[1])]
            sprice=float(sinfo[2])
            sqty=float(sinfo[3])
            samt=sprice*sqty/10000
            samt=round(samt,2)
            sconfirm=input(f"[---SSS---{samt}W---]:{scode}:{sname}:{sprice}:{sqty} (Y/N)")
            if sconfirm=="Y":
                print("[---SSS---]")
            stockcode=old_stockcode

if __name__=="__main__":
    #main()
    k=Kdata(stockcode,showtype,havelistseq)
    k.getdk_mp()
