Esempio n. 1
0
 def trdlist_format(self,
                    strat=None,
                    startlinenum=0,
                    date=None,
                    tablename=None,
                    outdir=None):
     """
         生成标准交易单 ['code','name','num','multi','prc','val','tscost','inout'] ,此处tscost为金额
     """
     if date is None:
         date = dt.date.today()
     if not tablename:
         tablename = self.get_trdname(inputdate=date)
     if not strat:
         stratfilter = ''
     else:
         stratfilter = ''.join([' AND strat =\'', strat, '\''])
     with da.DatabaseAssistant(dbdir=self._trd_dbdir) as trddb:
         conn = trddb.connection
         exeline = ''.join([
             'SELECT code,name,num,multi,prc,val,tscost,inout FROM ',
             tablename, ' WHERE row_id >',
             str(startlinenum), stratfilter
         ])
         trades = pd.read_sql(exeline, conn)
         if not trades.empty:
             trades['code'] = trades['code'].map(RawHoldingStocks.addfix)
             trades['tscost'] = -np.abs(trades['val'] * trades['tscost'])
         if outdir:
             trades.to_csv(outdir, header=True, index=False)
         else:
             return trades
Esempio n. 2
0
 def list_pofval_gen(self,date = None,prctype = 'settle'):
     """ 从标准数据库中读取持仓信息并写入文件 """
     assert prctype in ('close','settle')
     assetfut = '999997' if prctype=='settle' else '999998'
     if date is None:
         date = dt.datetime.today()
     with da.DatabaseAssistant(dbdir=self._posi_db) as posidb:
         conn = posidb.connection
         stktb = '_'.join([self._pofname,'positions_stocks',date.strftime('%Y%m%d')])
         exeline = ''.join(['SELECT code,num,multi,',prctype,' AS prc FROM ',stktb])
         holdings = pd.read_sql(con=conn,sql=exeline)
         pofval = holdings.loc[holdings['code']=='999999','num'].values[0]
         if self._blog: # 有期货持仓
             futtb = '_'.join([self._pofname,'positions_futures',date.strftime('%Y%m%d')])
             exeline = ''.join(['SELECT code,num,multi,',prctype,' AS prc FROM ',futtb])
             posifut = pd.read_sql(con=conn,sql=exeline)
             holdings = holdings.append(posifut,ignore_index=True)
             pofval += holdings.loc[holdings['code']==assetfut,'num'].values[0]
         holdings = holdings[~holdings['code'].isin(gv.HOLD_FILTER)]
         holdings = holdings[holdings['num']!=0]
         holdings['code'] = holdings['code'].map(Products.addfix)
         #### 写入文件 #######
         holdings.to_csv(self._holdlst_dir,header=True,index=False)
         with open(self._pofval_dir,'w') as pof:
             pof.write(str(pofval))
         print('[+]{0} : formatted holding list produced'.format(self._pofname))
 def holdlist_format(self, titles, date=None, tablename=None, outdir=None):
     """ 从数据库提取画图所需格式的持仓信息,tablename 未提取的表格的名称
         存储为 DataFrame, 输出到 csv
         titles 应为包含 证券代码 证券名称 证券数量 最新价格 的列表
         需要返回字段 : code, name, num, multi, prc
     """
     if date is None:
         date = dt.datetime.today()
     if not tablename:
         tablename = self.get_holdname(inputdate=date)
     with da.DatabaseAssistant(self._hold_dbdir) as holddb:
         conn = holddb.connection
         exeline = ''.join(
             ['SELECT ', ','.join(titles), ' FROM ', tablename])
         holdings = pd.read_sql(exeline, conn)
         holdings.columns = ['code', 'name', 'num', 'prc']  # 因此给的title只能有4个
         # 剔除非股票持仓和零持仓代码 逆回购 理财产品等
         holdings['code'] = holdings['code'].map(RawHoldingStocks.addfix)
         holdings = holdings[~holdings['code'].isin(gv.HOLD_FILTER)]
         holdings = holdings[holdings['num'] > 0]
         if holdings.empty:
             holdings = pd.DataFrame()
         else:
             holdings['multi'] = np.ones([len(holdings), 1])
             #holdings['val'] = holdings['num']*holdings['prc']
             holdings = holdings.sort_values(by=['code'], ascending=[1])
             holdings = holdings.loc[:, [
                 'code', 'name', 'num', 'multi', 'prc'
             ]]
         if outdir:
             holdings.to_csv(outdir, header=True, index=False)
         else:
             return holdings
Esempio n. 4
0
 def get_newtables(self):
     """
     提取已存储至数据库但还未处理 的 估值表的名称(数据库标准名称格式:产品代码_产品名称_日期)
     """
     with da.DatabaseAssistant(dbdir=self._dbdir) as db:
         temp = db.get_db_tablenames()
         temp.remove('SAVED_TABLES')
         savedtbs = set(temp)
     with da.DatabaseAssistant(dbdir=self._netdbdir) as netdb:
         cursor = netdb.connection.cursor()
         netdb.create_db_table(tablename='PROCESSED_TABLES',
                               titles=['TableNames TEXT'],
                               replace=False)
         temp = cursor.execute('SELECT * FROM PROCESSED_TABLES')
         processedtbs = set([tb[0] for tb in temp])
     newtables = sorted(list(savedtbs.difference(processedtbs)))
     return newtables
Esempio n. 5
0
 def get_newtables(self):
     """
     提取已下载但还未存入数据库 的 估值表的名称,为估值表原始名称 包含文件后缀
     """
     currntbs = set(os.listdir(self._filedir))  # 文件夹中当前存储的估值表
     with da.DatabaseAssistant(dbdir=self._dbdir) as db:
         cursor = db.connection.cursor()
         db.create_db_table(tablename='SAVED_TABLES',
                            titles=['TableNames TEXT'],
                            replace=False)
         temp = cursor.execute('SELECT * FROM SAVED_TABLES').fetchall()
         savedtbs = set([tb[0] for tb in temp])
     newtables = sorted(list(currntbs.difference(savedtbs)))
     return newtables
 def get_totvalue(self,
                  titles,
                  date=None,
                  tablename=None,
                  othersource=None):
     """ 提取 客户端软件 对应的总资产 """
     if date is None:
         date = dt.datetime.today()
     if not tablename:
         tablename = self.get_holdname(inputdate=date)
     if not titles[0]:  # 客户端持仓表格没有资产信息,需要从其他源(手填)提取
         with open(othersource, 'r') as pof:
             totval = float(pof.readlines()[0].strip())
     else:
         with da.DatabaseAssistant(dbdir=self._hold_dbdir) as holddb:
             conn = holddb.connection
             exeline = ''.join([
                 'SELECT ', ','.join(titles), ' FROM ',
                 tablename + '_summary'
             ])
             values = conn.execute(exeline).fetchall()
             totval = np.sum(values[0])
     return totval
Esempio n. 7
0
 def table_to_db(self,
                 tbname_dict,
                 tabledir,
                 varstypes,
                 datemark='日期',
                 titlemark='科目代码',
                 omitchars='-%',
                 defaluttype='REAL',
                 replace=True):
     """
     将估值表excel表格写入至数据库
     tbname_dict 为估值表将在数据库中存储的名称,需在该方法外确认其是否已经更新过, 目前为包含rawname 和 tablename 的dict
     vartypes 应给为可能包含的数据类型的字典
     titlemark 需要能用做识别出标题行,同时识别出表格正文
     replace 为 True 避免在同一张表格中多次写入
     """
     hasdb = os.path.exists(self._dbdir)  # 写入前数据库必须存在,数据库的建立在方法外实现
     if not hasdb:
         print('[-]Database does NOT exist, no update!')
         return
     self.table_info_check(tabledir=tabledir, datemark='日期')  # 写入前需先检查估值表内容
     rawtitles = self.get_table_titles(tabledir=tabledir,
                                       titlemark=titlemark,
                                       omitchars=omitchars)  # 从估值表中提取初始标题
     markpos = rawtitles.index(titlemark)  # titlemark 所在的标题行的位置
     titles = da.DatabaseAssistant.gen_table_titles(
         titles=rawtitles, varstypes=varstypes,
         defaluttype=defaluttype)['typed_titles']  # 标注了字段类型的标题
     tablename = tbname_dict['tablename']
     with da.DatabaseAssistant(dbdir=self._dbdir) as db:
         db.create_db_table(tablename=tablename,
                            titles=titles,
                            replace=replace)  # 创建表格
         data = xlrd.open_workbook(tabledir)
         table = data.sheets()[0]
         # 寻找正表起始行
         startline = -1
         for dumi in range(table.nrows):
             try:
                 # 检查首个元素类型,如果能转换为数值则为应该记录的行的起始行
                 int(table.row_values(dumi)[markpos])
             except ValueError:
                 continue
             else:
                 startline = dumi
                 break
         if startline == -1:  # 读完整个文件都没找到起始行则程序报错
             raise Exception('[-]Can not find startline for table : %s' %
                             tablename)
         # 已经找到正文起始行,开始写入数据库
         cursor = db.connection.cursor()
         try:
             print('[*]Writing main body of the table...')
             demonstrator = proshow.progress_demonstrator(table.nrows -
                                                          startline)
             for dumi in range(startline, table.nrows):  # 开始从正文行写入
                 exeline = ''.join([
                     'INSERT INTO ', tablename, ' VALUES (',
                     ','.join(['?'] * len(titles)), ')'
                 ])
                 cursor.execute(exeline, tuple(table.row_values(dumi)))
                 #db.connection.commit()
                 demonstrator.progress_show(currentnum=dumi - startline + 1,
                                            title=tablename)
         except:  # 无论任何原因导致写入table失败,则都要删除未写完的table
             print('[-]Writing table %s failed !' % tablename)
             db.connection.execute(' '.join(['DROP TABLE', tablename]))
             print('[+]Failed table %s dropped !' % tablename)
             raise
         else:  # 如果表格正常完成更新,则需将其原始名称写入 SAVED_TABLES 数据表用作记录
             cursor.execute('INSERT INTO SAVED_TABLES VALUES (?)',
                            (tbname_dict['rawname'], ))  # 需要一个 tuple 作为输入
             db.connection.commit()  # 完成后再统一commit
             print('[+]Writing table %s succed !' % tablename)
Esempio n. 8
0
 def trdlist_to_db(self,
                   textvars,
                   tabledir,
                   date=None,
                   tablename=None,
                   codemark='证券代码',
                   replace=False):
     """ 将一张软件端导出的 交易/委托表格 更新至数据库
         textvars 存储数据库格式为TEXT的字段
         codemark 用于标识正表表头
         tablename 表格存储在数据库中的名称
         已经写入数据库的行将不再写入,需要确定数据表格按交易时间排序
     """
     if date is None:
         date = dt.date.today()
     if not tablename:
         tablename = self.get_trdname(inputdate=date)
     with da.DatabaseAssistant(dbdir=self._trd_dbdir) as trddb:
         conn = trddb.connection
         c = conn.cursor()
         with open(tabledir, 'r') as fl:
             rawline = fl.readline()
             foundtitle = False
             linecount = 0
             processed_lines = 0
             while rawline:
                 line = rawline.strip().split(',')
                 if not foundtitle:  # 寻找到正文开始标题后才开始写入
                     if codemark in line:  #寻找正表标题
                         titles = line
                         codepos = titles.index(codemark) + 1
                         titlecheck = da.DatabaseAssistant.gen_table_titles(
                             titles=titles, varstypes={'TEXT': textvars})
                         titletrans = ['row_id INTEGER'
                                       ] + titlecheck['typed_titles']
                         titlelen = len(titletrans)
                         # title_empty = titlecheck['empty_pos']   # 此处尤其暗藏风险,假设正表数据没有空列
                         newcreated = trddb.create_db_table(
                             tablename=tablename,
                             titles=titletrans,
                             replace=replace)
                         if not newcreated:  # 读取已经存储过的行数
                             processed_lines = c.execute(''.join([
                                 'SELECT COUNT(', codemark, ') FROM ',
                                 tablename
                             ])).fetchall()[0][0]
                         rawline = fl.readline()
                         foundtitle = True
                         continue
                 else:  # 已经找到了标题
                     linecount += 1
                     if linecount > processed_lines:  # 只在有比数据库已存储的数据行更多的行提供时才继续写入
                         line = [linecount] + line
                         exeline = ''.join([
                             'INSERT INTO ', tablename, ' VALUES (',
                             ','.join(['?'] * titlelen), ')'
                         ])
                         c.execute(exeline, line)
                         conn.commit()
                 rawline = fl.readline()
             print('%d lines updated to trddb with table %s \n' %
                   (linecount - processed_lines, tablename))
Esempio n. 9
0
 def trdlog_to_db(self, tscost, date=None, tablename=None):
     """
     从交易记录读取信息并写入数据库,同时生成标准格式也写入数据库(注:此处tscost为比率)
     写入数据库,因为数据比较少(100档才100行),因为每次都全部写入
     需要考虑包含灵活换月的情况
     """
     if date is None:
         date = dt.date.today()
     if tablename is None:
         tablename = self.get_trdname(date)
     trdlogname = '_'.join([tablename, 'trdlog'])
     fulllog = pd.DataFrame()
     stdtable = []
     for strat in self._logdir:
         stratinfo = strat.split('_')
         cttype = stratinfo[1].upper()
         montype = stratinfo[0]
         contracts = RawHoldingFutures.get_contracts_ours(date=date,
                                                          cttype=cttype)
         nvolumn = int(
             open(os.path.join(self._cwdir[strat],
                               'nVolume.txt')).readline()[0].strip())
         multi = self.multi_dict[cttype]
         logdir = os.path.join(
             self._logdir[strat], 'tradelog',
             ''.join(['tradelog_',
                      date.strftime('%Y%m%d'), '.txt']))
         if not os.path.exists(logdir):
             continue
         with open(logdir) as fl:
             line = fl.readline()
             trdlogs = []
             namefull = False
             names = ['date', 'time', 'trade_action']
             while line:
                 ############# 生成可存入数据库格式 #############
                 vars = line.strip().split()
                 monthchg = len(vars) == 16
                 if monthchg:  # 灵活换月
                     startnum = 5
                 else:
                     startnum = 3
                 trdlog = vars[:3]
                 trdlog += [v.split('=')[1] for v in vars[startnum:]]
                 if not namefull:
                     names += [v.split('=')[0] for v in vars[startnum:]]
                     namefull = True
                 if not monthchg:  # 考虑到有肯能在灵活换月当天还会有交易,需要确保每行等长(灵活换月还包含nextqhprice)
                     trdlog.insert(5, 'NaN')
                     if len(names) < 14:
                         names.insert(5, 'nextqhprice')
                 trdlogs.append(trdlog)
                 ############# 生成标准格式 #############
                 sn = int(trdlog[3])
                 prc = float(trdlog[6])
                 if '开仓' in trdlog[2]:
                     code = contracts[montype]
                     num = -nvolumn
                     val = prc * num * multi
                     inout = 'in'
                     stdtable.append([
                         strat, code, code, num, multi, prc, val, tscost,
                         inout, sn
                     ])
                 elif '平仓' in trdlog[2]:
                     code = contracts[montype]
                     num = nvolumn
                     val = prc * num * multi
                     inout = 'out'
                     stdtable.append([
                         strat, code, code, num, multi, prc, val, tscost,
                         inout, sn
                     ])
                 elif '换仓' in trdlog[2]:
                     real_contracts = RawHoldingFutures.get_contracts_real(
                         date=date, cttype=cttype)
                     code = real_contracts[montype]
                     num = nvolumn
                     val = prc * num * multi
                     inout = 'out'
                     stdtable.append([
                         strat, code, code, num, multi, prc, val, tscost,
                         inout, sn
                     ])
                     if montype == 'near1':  #远月换月还未定
                         code2 = real_contracts['near2']
                         prc2 = float(trdlog[5])
                         val2 = -num * prc2 * multi
                         stdtable.append([
                             strat, code2, code2, -num, multi, prc2, val2,
                             tscost, 'in', sn
                         ])
                 else:
                     raise Exception('Unrecognized trade_action!')
                 line = fl.readline()
         fulllog = fulllog.append(pd.DataFrame(trdlogs, columns=names),
                                  ignore_index=True)
     ############ 写入 数据库 ################
     with da.DatabaseAssistant(dbdir=self._trd_dbdir) as trddb:
         conn = trddb.connection
         fulllog.to_sql(name=trdlogname, con=conn, if_exists='replace')
         stdtable = pd.DataFrame(stdtable,
                                 columns=[
                                     'strat', 'code', 'name', 'num',
                                     'multi', 'prc', 'val', 'tscost',
                                     'inout', 'sn'
                                 ])
         stdtable['row_id'] = stdtable.index + 1
         stdtable.to_sql(name=tablename, con=conn, if_exists='replace')
Esempio n. 10
0
    def trdlist_format(self,
                       titles,
                       tscostrate,
                       startlinenum=0,
                       date=None,
                       tablename=None,
                       outdir=None):
        """ 从数据库提取画图所需格式的 交易 信息,tablename 未提取的表格的名称
            存储为 DataFrame, 输出到 csv
            titles 应为包含 的列表
            需要返回字段 : code, name, num, prc,val,tscost,inout
            做多 数量为正、金额为正, 做空为负、金额为负, 价格恒正, 交易成本恒为负
        """
        tofilter = ['131810', '204001']
        if date is None:
            date = dt.date.today()
        if not tablename:
            tablename = self.get_trdname(inputdate=date)

        def marktrans(x):
            if '买' in x:
                return 'in'
            elif '卖' in x:
                return 'out'
            else:
                raise Exception('Error in trdlist_format')

        def reverseval(x):
            if x == 'out':
                return -1
            elif x == 'in':
                return 1
            else:
                raise Exception('Error in trdlist_format')

        def tsratecalc(x):
            if x == 'out':
                return tscostrate + 1 / 1000  # 卖出会有印花税
            else:
                return -tscostrate

        with da.DatabaseAssistant(dbdir=self._trd_dbdir) as trddb:
            conn = trddb.connection
            exeline = ''.join([
                'SELECT ', ','.join(titles), ' FROM ', tablename,
                ' WHERE row_id >',
                str(startlinenum)
            ])
            trades = pd.read_sql(exeline, conn)
            trades.columns = ['code', 'name', 'num', 'prc', 'inout']
            # 剔除非股票持仓和零持仓代码
            trades = trades[~trades['code'].isin(tofilter)]
            trades['code'] = trades['code'].map(RawHoldingStocks.addfix)
            trades = trades[trades['num'] > 0]
            trades['inout'] = trades['inout'].map(marktrans)
            trades['num'] = trades['num'] * trades['inout'].map(reverseval)
            trades['val'] = trades['num'] * trades['prc']
            trades['tscost'] = trades['val'] * trades['inout'].map(tsratecalc)
            trades['multi'] = 1
            trades = trades.sort_values(by=['code'], ascending=[1])
            trades = trades.ix[:, [
                'code', 'name', 'num', 'multi', 'prc', 'val', 'tscost', 'inout'
            ]]
            if outdir:
                trades.to_csv(outdir, header=True, index=False)
            else:
                return trades
Esempio n. 11
0
 def table_to_netdb(self,
                    tablename,
                    codedict,
                    indexmark='科目代码',
                    defaultvalcols=('市值', '市值本币')):
     """
     从已经存储到数据库中的估值表中提取计算净值所需的基础元素,更新至netdb中
     更新前需检查 PROCESSED_TABLES 中是否已经拥有了该表格,已经存在的话就不能写入,报错
     tablename 为需要更新的表格名称 在估值表数据库中的名称 标准名称
     codedict 为存储必须提取的行的字典,如果估值表中没有该行的话则其值应该为零
     indexmark 为行标记,标记存储codedict需要提取的字段的列
     valcols 可能作为存储数值的列的名称通常为 [市值 、市值本币]
     """
     # 从数据库中的估值表中提取需要的字段的值,并赋予标准化名称
     if not codedict:
         print('[-]Codedict is empty can not update table : %s' % tablename)
     output_dict = {}  # 存储提取出的数值的字典
     rev_codedict = {}  # 以行名(indexmark列对应的值)为KEY
     valcols = [indexmark]
     for cd in codedict:  # 初始化输出字典
         output_dict[cd] = 0
         cditem = codedict[cd]
         if len(cditem) > 1:
             valcols.append(cditem[1])  # 把非默认的列提取出来
             rev_codedict[cditem[0]] = (cd, cditem[1])
         else:
             rev_codedict[cditem[0]] = (cd, )
     #rev_codedict = {v[0]:k for k,v in codedict.items()}   # 以行名为KEY,对应要素表列名为值的字典
     # 提取数据
     with da.DatabaseAssistant(self._dbdir) as db:
         cols = db.get_table_cols(tablename)
         # 匹配用作存储的值,同一只产品有些会是市值 、有些市值本币 所以需要逐一匹配
         valmark = None
         for col in defaultvalcols:
             if col in cols:
                 valmark = col
                 break
         if not valmark:
             raise Exception('[-]在表格 %s 中没有匹配到存储数值的列' % tablename)
         else:
             valcols.insert(1, valmark)
         cursor = db.connection.cursor()
         exeline = ''.join(
             ['SELECT ', ','.join(valcols), ' FROM ', tablename])
         rowvals = cursor.execute(exeline).fetchall()  # 提取 indexmark 列对应的值
         for row in rowvals:
             rowname = row[0].strip()
             if rowname in rev_codedict:
                 item = rev_codedict[rowname]
                 itemcol = item[1] if len(item) > 1 else valmark
                 output_dict[item[0]] = row[valcols.index(itemcol)]
     # 写入 netdb 元素表
     filedate = tablename[-8:]
     with da.DatabaseAssistant(self._netdbdir) as netdb:
         base_titles = ['date TEXT']
         base_values = [filedate]
         for cd in sorted(output_dict):
             base_titles.append(' '.join([cd, 'REAL']))
             base_values.append(output_dict[cd])
         netdb.create_db_table(tablename='Net_Values_Base',
                               titles=base_titles,
                               replace=False)
         try:  # 开始写入净值基础数据
             cursor = netdb.connection.cursor()
             exeline = ''.join([
                 'INSERT INTO Net_Values_Base VALUES (',
                 ','.join(['?'] * len(base_titles)), ')'
             ])
             cursor.execute(exeline, tuple(base_values))
             netdb.connection.commit()
         except:
             print('[-]Update Net_Values_Base with table %s failed !' %
                   tablename)
             raise
         else:
             cursor.execute('INSERT INTO PROCESSED_TABLES VALUES (?)',
                            (tablename, ))  # 需要一个 tuple 作为输入
             netdb.connection.commit()
             print('[+]Update Net_Values_Base with table %s succed !' %
                   tablename)
    def generate_netvalues(self,earnvars = ('earn')):
        """ 从头计算净值表,包括全部日期(包含非交易日) 暂时未考虑融券情况 """
        digits = self.precision
        confirmdays = self.confirmdays
        w.start()
        with da.DatabaseAssistant(self.netdbdir) as netdb:
            conn_net = netdb.connection
            basedata = pd.read_sql('SELECT * FROM Net_Values_Base',conn_net)  #提取基础数据
            sorteddata = basedata.sort_values(['date'],ascending=[1])     # 将数据根据日期排序,防止有些估值表发送较晚,更新靠后的情况
            # 检查缺失交易日期,如果有缺失则只生成至缺失交易日前一(交易)日; 有交易日就应该有估值表,反之不然
            alldates = sorteddata['date']
            firstdate = alldates.values[0]   # 基础数据起始日 须确保基础数据中没有重复项
            lastdate = alldates.values[-1]   # 基础数据结束日
            tdays = w.tdays(firstdate,lastdate).Times  # 期间交易日
            trddays = pd.DataFrame([dt.datetime.strftime(t,'%Y%m%d') for t in tdays])
            misstrd = ~trddays.isin(alldates.values)
            trdmiss = trddays[misstrd.values]  # 期间缺失的交易日
            if not trdmiss.empty:
                # 截止到第一个缺失交易日前的一个交易日
                #cutdate=dt.datetime.strftime(w.tdaysoffset(-1,trdmiss.values[0][0]).Times[0],'%Y%m%d')
                cutdate = dt.datetime.strftime(w.tdaysoffset(-1,trdmiss.values[0][0]).Times[0],'%Y%m%d')
                cutpos = alldates<=cutdate    # 需要提取的日期的位置
                sorteddata = sorteddata[cutpos]
            dates = sorteddata['date']
            datenum = dates.__len__()
            # 处理 融券的字段
            if 'secloan' not in sorteddata.columns:
                sorteddata['secloan'] = 0
            if 'fee_secloan' not in sorteddata.columns:
                sorteddata['fee_secloan'] = 0
            # 计算实际付费 出入金 -- 费用为每日计提,累计显示,因而是逐渐增加的,如果一次减小说明当日支付上一付费季度的费用
            fees = -(sorteddata.loc[:,['fee_service','fee_keep','fee_management','fee_secloan']+earnvars].diff())  #正常日计提 作差为正数,实际支付 作差为负数,取反则可以找到实际支付
            fees[fees<0] = 0   # 将正常每日费用计提设为0
            fees.iloc[0,:] = 0   # diff 导致第一行为NaN 需要填补为0
            paid=fees.sum(axis=1)  # 将每日各项费用加总,计算每日(账面)支付金额
            ################### 开始计算净值  #######################
            netreal=sorteddata['assetnet']/sorteddata['sharenum'] # 计算真实(单位)净值
            # 计算将业绩提成补回后的(累计)净值收益率
            bookearns = sorteddata.loc[:,earnvars].diff()   # 账面业绩提成,不是实际支付的 与fees['earn']  是不一样的!!!
            bookearns.iloc[0,:] = 0
            bookearns[bookearns<0] = 0
            bookearn = bookearns.sum(axis=1)
            net_addearn = (sorteddata['assetnet']+bookearn)/sorteddata['sharenum']
            cumret = net_addearn.values[1:]/netreal.values[:-1]-1
            cumret = np.row_stack([np.zeros([1,1]),np.reshape(cumret,[datenum-1,1])])
            netcum = np.cumprod(1+cumret)*netreal[0]  # 计算补回业绩提成后的(累计)单位净值,并确保初始值与单位净值初始值对齐
            # 在有申购赎回的时候需要采取平滑处理
            sharechg = sorteddata['sharenum'].diff() # 计算份额变动差异 >0 为由申购, <0 为有赎回,有可能申赎数量相同,但不影响金额
            sharechg[0] = 0
            # 和前一个数值相比, 确定 confirm date 交易确认日 (T+C) 的位置
            idxchg_TC = sharechg!=0 # type: pd.DataFrame
            chgpos_TC = idxchg_TC[idxchg_TC].index  # 变动位置的序号
            chgdate = dates[chgpos_TC].values # 变动位置的对应的日期
            opendate = [w.tdaysoffset(-confirmdays,c).Times[0].strftime('%Y%m%d') for c in chgdate]
            openidx = dates.isin(opendate)  # 开放日位置
            inout = np.zeros_like(netreal)
            inout[chgpos_TC] = np.round(netreal[openidx].values,digits)*sharechg[chgpos_TC].values  # 将单位净值round到指定位置,乘以分额变动计算出在开放日申购赎回的金额
            # 提取 开放日 到 确认日 期间的日期(TCm1)对应的位置,并在相应位置对应上申购赎回的金额
            inout2 = np.zeros_like(netreal)
            idxchg_TCm1 = np.zeros_like(netreal)
            opennum = 0
            opentot = opendate.__len__()
            for dumi in range(datenum):  # 将日期一个个与开放日比对
                if opennum>=opentot:      # 已经找到的开放日数量超过了所有 开放日数量,说明已经完成,可以退出了
                    break
                mydt = dates.values[dumi]
                if mydt>opendate[opennum] and mydt<chgdate[opennum]:
                    inout2[dumi] = inout[chgpos_TC[opennum]]
                    idxchg_TCm1[dumi] = 1
                elif mydt>=chgdate[opennum]:
                    opennum += 1

            amtchg = np.zeros_like(netreal)
            comptot = sorteddata['assettot']-sorteddata['shares_sell']-sorteddata['secloan']  # 资产总额扣除应付赎回款\融券金额,总资产不包含已经支付的费用,需要加回
            amtchg[1:] = comptot.values[1:]-comptot.values[:-1]+paid.values[1:]-inout[1:]  # 每日资金变动金额,补回费用,剔除申购赎回

            # 计算平滑后的 补回全部费用的 收益率
            rets = np.zeros_like(netreal)
            numerator = (comptot.values + paid.values + inout2)[1:]  # 分子与确认日对齐
            denominator = comptot.values[:-1]+(inout2+inout)[1:]
            rets[1:] = numerator/denominator-1

            level = 0.1  # 异常收益情况检查
            assert all(rets<level) and all(rets>-level) ,'abnormal return value, check data !'

            netvals = pd.DataFrame(np.column_stack([dates.values,netreal,netcum,np.cumprod(1+rets)*netreal[0],rets,amtchg,np.cumsum(amtchg)]),
                                 columns=['Date','NetSingle','NetCumulated','NetCompensated','CompReturns','AmtChg','AmtCumChg'])
            sql.to_sql(netvals,name='Net_Values',con=conn_net,if_exists='replace',index=False)
            print(' '.join(['%s : Netvalues updated from' %self.pname,firstdate,'to',dates.values[-1]]))
        w.close()
 def holdlist_to_db(self,
                    textvars,
                    tabledir,
                    date=None,
                    tablename=None,
                    codemark='证券代码',
                    replace=True,
                    currencymark='币种'):
     """ 将一张软件端导出的 持仓表格 更新至数据库
         textvars 存储数据库格式为TEXT的字段
         currencymark 用于识别汇总情况表头 目前只有通达信终端才有 若找不到就不建立表格
         codemark 用于标识正表表头
         tablename 表格存储在数据库中的名称
     """
     if date is None:
         date = dt.datetime.today()
     if not tablename:
         tablename = self.get_holdname(inputdate=date)
     with da.DatabaseAssistant(dbdir=self._hold_dbdir) as holddb:
         conn = holddb.connection
         c = conn.cursor()
         with open(tabledir, 'r') as fl:
             rawline = fl.readline()
             startwrite = False
             summary = False
             newtb = False
             while rawline:
                 line = rawline.strip().split(',')
                 if not startwrite:
                     if currencymark:  # 在找到详细数据之前会先查找汇总,如果不需要汇总则直接寻找标题
                         if not summary:  # 检查持仓汇总部分
                             if currencymark in line:  #寻找汇总标题
                                 stitles = line
                                 currpos = stitles.index(currencymark)
                                 stitlecheck = da.DatabaseAssistant.gen_table_titles(
                                     titles=stitles,
                                     varstypes={'TEXT': (currencymark, )})
                                 stitletrans = stitlecheck['typed_titles']
                                 stitle_empty = stitlecheck['empty_pos']
                                 stitlelen = len(stitletrans)
                                 holddb.create_db_table(
                                     tablename=tablename + '_summary',
                                     titles=stitletrans,
                                     replace=True)
                                 rawline = fl.readline()
                                 summary = True
                                 continue
                         else:
                             if line[currpos] == '人民币':  # 读取人民币对应的一行
                                 exeline = ''.join([
                                     'INSERT INTO ', tablename + '_summary',
                                     ' VALUES (',
                                     ','.join(['?'] * stitlelen), ')'
                                 ])
                                 newline = []
                                 for dumi in range(len(line)):
                                     if not stitle_empty[dumi]:
                                         newline.append(line[dumi])
                                 c.execute(exeline, newline)
                                 conn.commit()
                     #寻找正表标题
                     if codemark in line:
                         titles = line
                         titlecheck = da.DatabaseAssistant.gen_table_titles(
                             titles=titles, varstypes={'TEXT': textvars})
                         titletrans = titlecheck['typed_titles']
                         # title_empty = titlecheck['empty_pos']   # 此处尤其暗藏风险,假设正表数据没有空列
                         newtb = holddb.create_db_table(tablename=tablename,
                                                        titles=titletrans,
                                                        replace=replace)
                         rawline = fl.readline()
                         startwrite = True
                         if not newtb:  # 表格已经存在就不必再写入
                             break
                         continue
                 elif startwrite and newtb:  # 在已找到正文并且表格是新建的情况下才开始写
                     exeline = ''.join([
                         'INSERT INTO ', tablename, ' VALUES (',
                         ','.join(['?'] * len(line)), ')'
                     ])
                     c.execute(exeline, line)
                     conn.commit()
                 rawline = fl.readline()
         if startwrite and newtb:  #实现写入并退出循环
             print('Table ' + tablename + ' updated to database !')
         elif startwrite and not newtb:
             print('Table ' + tablename + ' already in the database !')
         else:  # 未能实现写入
             print('Table ' + tablename +
                   ' cannot read the main body, nothing writen !')