Beispiel #1
0
def sell(remc, share, date):
    '''
    :returns: tuple, (sold rem, new rem)
        sold rem is the positions being sold while new rem is the positions being held
    '''
    rem = copy(remc)
    share = myround(share)
    date = convert_date(date)
    totposition = sum([pos[1] for pos in rem])  # the remaining shares
    if totposition == 0:
        return ([], [])
    if (date - rem[-1][0]).days < 0:
        raise Exception(_errmsg)
    if share > totposition:
        share = totposition  # not raise error when you sell more than you buy
    soldrem = []
    newrem = []
    for i, pos in enumerate(rem):
        if share > myround(sum([rem[j][1] for j in range(i + 1)])):
            soldrem.append(rem[i])
        elif share == myround(sum([rem[j][1] for j in range(i + 1)])):
            soldrem.append(rem[i])
        elif share < myround(sum([rem[j][1] for j in range(i + 1)])):
            if share > sum([rem[j][1] for j in range(i)]):
                soldrem.append([rem[i][0], share - sum([rem[j][1] for j in range(i)])])
                newrem.append([rem[i][0], sum([rem[j][1] for j in range(i + 1)]) - share])
            elif share <= sum([rem[j][1] for j in range(i)]):
                newrem.append(rem[i])
    return (soldrem, newrem)
Beispiel #2
0
    def status_gen(self, date):
        # 过滤交易日这一需求,交给各个类自由裁量,这里网格类就需要过掉非交易日干扰,
        # 而定投类中则不过掉,遇到非交易日顺延定投更合理些
        if date.strftime("%Y-%m-%d") not in opendate:
            return 0

        if date == self.start:
            if self.buypercent[0] == 0:
                self.pos += 1
                return myround(self.totmoney / self.division)
            else:
                return 0
        value = self.price[self.price["date"] <= date].iloc[-1].loc["netvalue"]
        valueb = self.price[
            self.price["date"] <= date].iloc[-2].loc["netvalue"]
        action = 0
        for i, buypt in enumerate(self.buypts):
            if (value - buypt) <= 0 and (valueb - buypt) > 0 and self.pos <= i:
                self.pos += 1
                action += myround(self.totmoney / self.division)
        for j, sellpt in enumerate(self.sellpts):
            if (value - sellpt) >= 0 and (valueb -
                                          sellpt) < 0 and self.pos > j:
                action += -1 / self.pos
                self.pos += -1
        return action
Beispiel #3
0
    def dailyreport(self, date=yesterdayobj()):
        date = convert_date(date)
        partcftb = self.cftable[self.cftable["date"] <= date]
        value = self.get_netvalue(date)

        if len(partcftb) == 0:
            reportdict = {
                "基金名称": [self.name],
                "基金代码": [self.code],
                "当日净值": [value],
                "持有份额": [0],
                "基金现值": [0],
                "基金总申购": [0],
                "历史最大占用": [0],
                "基金分红与赎回": [0],
                "基金收益总额": [0],
            }
            df = pd.DataFrame(reportdict, columns=reportdict.keys())
            return df
        # totinput = myround(-sum(partcftb.loc[:,'cash']))
        totinput = myround(-sum(
            [row["cash"]
             for _, row in partcftb.iterrows() if row["cash"] < 0]))
        totoutput = myround(
            sum([
                row["cash"] for _, row in partcftb.iterrows()
                if row["cash"] > 0
            ]))

        currentshare = myround(sum(partcftb.loc[:, "share"]))
        currentcash = myround(currentshare * value)
        btnk = bottleneck(partcftb)
        turnover = turnoverrate(partcftb, date)
        ereturn = myround(currentcash + totoutput - totinput)
        if currentshare == 0:
            unitcost = 0
        else:
            unitcost = round((totinput - totoutput) / currentshare, 4)
        if btnk == 0:
            returnrate = 0
        else:
            returnrate = round((ereturn / btnk) * 100, 4)

        reportdict = {
            "基金名称": [self.name],
            "基金代码": [self.code],
            "当日净值": [value],
            "单位成本": [unitcost],
            "持有份额": [currentshare],
            "基金现值": [currentcash],
            "基金总申购": [totinput],
            "历史最大占用": [btnk],
            "基金持有成本": [totinput - totoutput],
            "基金分红与赎回": [totoutput],
            "换手率": [turnover],
            "基金收益总额": [ereturn],
            "投资收益率": [returnrate],
        }
        df = pd.DataFrame(reportdict, columns=reportdict.keys())
        return df
Beispiel #4
0
    def status_gen(self, date):
        if date.strftime("%Y-%m-%d") not in opendate:
            return 0
        rows = self.price[self.price["date"] <= date]
        if len(rows) == 1:
            return 0
        value = rows.iloc[-1].loc[self.col]
        valueb = rows.iloc[-2].loc[self.col]
        action = 0
        if self.buylow is True:
            judge = 1
        else:
            judge = -1
        for i, term in enumerate(self.buy):
            if (judge * (value - term[0]) <= 0 < judge * (valueb - term[0])
                    and self.pos + sum([it[1] for it in self.buy[i:]]) <= 1):
                self.pos += term[1]
                action += myround(self.totmoney * term[1])
                self.selllevel = 0
        if self.sell is not None:
            for i, term in enumerate(self.sell):
                if (judge * (value - term[0]) >= 0 > judge * (valueb - term[0])
                        and self.pos > 0 and self.selllevel <= i):
                    deltaaction = myround(term[1] /
                                          sum([it[1] for it in self.sell[i:]]))
                    action -= (1 + action) * deltaaction  # 需考虑一日卖出多仓的情形
                    self.pos = (1 - deltaaction) * self.pos
                    self.selllevel = i + 1

        return action
Beispiel #5
0
    def shuhui(self, share, date, rem):
        """
        give the cashout considering redemption rates as zero.
        if the date is not a trade date, then the purchase would happen on the next trade day, if the date is
        in the furture, then the trade date is taken as yesterday.

        :param share: float or int, number of shares to be sold
        :param date: string or object of date
        :returns: three elements tuple, the first is dateobj
            the second is a positive float for cashout,
            the third is a negative float for share decrease
        """
        date = convert_date(date)
        tots = sum([remitem[1] for remitem in rem if remitem[0] <= date])
        if share > tots:
            sh = tots
        else:
            sh = share
        partprice = self.price[self.price["date"] >= date]
        if len(partprice) == 0:
            row = self.price[self.price["date"] < date].iloc[-1]
        else:
            row = partprice.iloc[0]
        value = myround(sh * row.netvalue)
        return (row.date, value, -myround(sh))
Beispiel #6
0
def _shengoucal(sg, sgf, value, label):
    """
    Infer the share of buying fund by money input, the rate of fee in the unit of %,
        and netvalue of fund

    :param sg: positive float, 申购金额
    :param sgf: positive float, 申购费,以%为单位,如 0.15 表示 0.15%
    :param value: positive float, 对应产品的单位净值
    :param label: integer, 1 代表份额正常进行四舍五入, 2 代表份额直接舍去小数点两位之后。金额部分都是四舍五入
    :returns: tuple of two positive float, 净申购金额和申购份额
    """
    jsg = myround(sg / (1 + sgf * 1e-2))
    share = myround(jsg / value, label)
    return (jsg, share)
Beispiel #7
0
 def _shuhui_by_share(self, share, date, rem):
     date = convert_date(date)
     tots = sum([remitem[1] for remitem in rem if remitem[0] <= date])
     if share > tots:
         sh = tots
     else:
         sh = share
     partprice = self.price[self.price["date"] >= date]
     if len(partprice) == 0:
         row = self.price[self.price["date"] < date].iloc[-1]
     else:
         row = partprice.iloc[0]
     value = myround(sh * row.netvalue)
     return (
         row.date,
         value,
         -myround(sh),
     )  # TODO: 这里 myround 是否也和 round_label 有关,有待考证
Beispiel #8
0
def bottleneck(cftable):
    """
    find the max total input in the history given cftable with cash column

    :param cftable: pd.DataFrame of cftable
    """
    if len(cftable) == 0:
        return 0
    # cftable = cftable.reset_index(drop=True) # unnecessary as iloc use natural rows instead of default index
    inputl = [-sum(cftable.iloc[:i].cash) for i in range(1, len(cftable) + 1)]
    return myround(max(inputl))
Beispiel #9
0
    def shuhui(self, share, date, rem):
        """
        give the cashout based on rem term considering redemption rates

        :returns: three elements tuple, the first is dateobj
            the second is a positive float for cashout,
            the third is a negative float for share decrease
        """
        # 		 value = myround(share*self.price[self.price['date']==date].iloc[0].netvalue)
        date = convert_date(date)
        partprice = self.price[self.price["date"] >= date]
        if len(partprice) == 0:
            row = self.price[self.price["date"] < date].iloc[-1]
        else:
            row = partprice.iloc[0]
        soldrem, _ = rm.sell(rem, share, row.date)
        value = 0
        sh = myround(sum([item[1] for item in soldrem]))
        for d, s in soldrem:
            value += myround(s * row.netvalue * (1 - self.feedecision(
                (row.date - d).days) * 1e-2))
        return (row.date, value, -sh)
Beispiel #10
0
    def briefdailyreport(self, date=yesterdayobj()):
        """
        quick summary of highly used attrs for trade

        :param date: string or object of datetime
        :returns: dict with several attrs: date, unitvalue, currentshare, currentvalue
        """
        date = convert_date(date)
        partcftb = self.cftable[self.cftable["date"] <= date]
        if len(partcftb) == 0:
            return {}

        unitvalue = self.get_netvalue(date)
        currentshare = myround(sum(partcftb.loc[:, "share"]))
        currentvalue = myround(currentshare * unitvalue)

        return {
            "date": date,
            "unitvalue": unitvalue,
            "currentshare": currentshare,
            "currentvalue": currentvalue,
        }
Beispiel #11
0
    def briefdailyreport(self, date=yesterdayobj()):
        '''
        quick summary of highly used attrs for trade

        :param date: string or object of datetime
        :returns: dict with several attrs: date, unitvalue, currentshare, currentvalue
        '''
        date = convert_date(date)
        partcftb = self.cftable[self.cftable['date'] <= date]
        if len(partcftb) == 0:
            return {}

        unitvalue = self.aim.price[
            self.aim.price['date'] <= date].iloc[-1].netvalue
        currentshare = myround(sum(partcftb.loc[:, 'share']))
        currentvalue = myround(currentshare * unitvalue)

        return {
            'date': date,
            'unitvalue': unitvalue,
            'currentshare': currentshare,
            'currentvalue': currentvalue
        }
Beispiel #12
0
 def _vcash(totmoney, totcftable, cashobj):
     '''
     return a virtue status table with a mf(cash) column based on the given tot money and cftable
     '''
     cashl = []
     cashl.append(totmoney + totcftable.iloc[0].cash)
     for i in range(len(totcftable) - 1):
         date = totcftable.iloc[i + 1].date
         delta = totcftable.iloc[i + 1].cash
         if delta < 0:
             cashl.append(myround(delta / cashobj.price[cashobj.price['date'] <= date].iloc[-1].netvalue))
         else:
             cashl.append(delta)
     datadict = {'date': totcftable.loc[:, 'date'], 'mf': cashl}
     return pd.DataFrame(data=datadict)
Beispiel #13
0
    def shengou(self, value, date):
        """
        give the realdate deltacash deltashare tuple based on purchase date and purchase amount
        if the date is not a trade date, then the purchase would happen on the next trade day, if the date is
        in the furture, then the trade date is taken as yesterday.

        :param value: the money for purchase
        :param date: string or object of date
        :returns: three elements tuple, the first is the actual dateobj of commit
            the second is a negative float for cashin,
            the third is a positive float for share increase
        """
        row = self.price[self.price["date"] >= date].iloc[0]
        share = _shengoucal(value, self.rate, row.netvalue, label=self.label)[1]
        return (row.date, -myround(value), share)
Beispiel #14
0
def trans(remc, coef, date):
    '''
    在基金份额折算时,将之前持有的仓位按现值折算,相当于前复权

    :param coef: the factor shown in comment column of fundinfo().price, but with positive value
    :param date: string in date form or datetime obj
    :returns: new rem after converting
    '''
    rem = copy(remc)
    date = convert_date(date)
    if len(rem) == 0:
        return []
    if (date - rem[-1][0]).days <= 0:
        raise Exception(_errmsg)
    newrem = [[item[0], myround(item[1] * coef)] for item in rem]
    return newrem
Beispiel #15
0
    def unitcost(self, date=yesterdayobj()):
        """
        give the unitcost of fund positions

        :param date: string or object of datetime
        :returns: float number of unitcost
        """
        partcftb = self.cftable[self.cftable["date"] <= date]
        if len(partcftb) == 0:
            return 0
        totnetinput = myround(-sum(partcftb.loc[:, "cash"]))
        currentshare = self.briefdailyreport(date).get("currentshare", 0)
        # totnetinput
        if currentshare > 0:
            unitcost = totnetinput / currentshare
        else:
            unitcost = 0
        return unitcost
Beispiel #16
0
def buy(remc, share, date):
    '''
    :param remc: array of two-elements arrays, eg [[pd.Timestamp(), 50],[pd.Timestamp(), 30]
        the first element in tuple is pandas.Timestamp object for date while the second
        element is positive float for remaining shares, tuples in rem MUST be time ordered.
    :param share: positive float, only 2 decimal is meaningful.
    :param date: string in the date form or datetime object
    :returns: new rem after the buying
    '''
    rem = copy(remc)
    share = myround(share)
    date = convert_date(date)
    if len(rem) == 0:
        return [[date, share]]
    elif (date - rem[-1][0]).days > 0:
        rem.append([date, share])
        return rem
    elif (date - rem[-1][0]).days == 0:
        rem[-1][1] = rem[-1][1] + share
        return rem
    else:
        raise Exception(_errmsg)
Beispiel #17
0
    def dailyreport(self, date=yesterdayobj()):
        """
        breif report dict of certain date status on the fund investment

        :param date: string or obj of date, show info of the date given
        :returns: dict of various data on the trade positions
        """
        date = convert_date(date)
        partcftb = self.cftable[self.cftable["date"] <= date]
        print(partcftb)
        value = self.aim.price[
            self.aim.price["date"] <= date].iloc[-1].netvalue

        if len(partcftb) == 0:
            reportdict = {
                "基金名称": [self.aim.name],
                "基金代码": [self.aim.code],
                "当日净值": [value],
                "持有份额": [0],
                "基金现值": [0],
                "基金总申购": [0],
                "历史最大占用": [0],
                "基金分红与赎回": [0],
                "基金收益总额": [0],
            }
            df = pd.DataFrame(reportdict, columns=reportdict.keys())
            return df
        # totinput = myround(-sum(partcftb.loc[:,'cash']))
        totinput = myround(-sum(
            [row["cash"]
             for _, row in partcftb.iterrows() if row["cash"] < 0]))
        totoutput = myround(
            sum([
                row["cash"] for _, row in partcftb.iterrows()
                if row["cash"] > 0
            ]))

        currentshare = myround(sum(partcftb.loc[:, "share"]))
        currentcash = myround(currentshare * value)
        btnk = bottleneck(partcftb)
        turnover = turnoverrate(partcftb, date)
        ereturn = myround(currentcash + totoutput - totinput)
        if currentshare == 0:
            unitcost = 0
        else:
            unitcost = round((totinput - totoutput) / currentshare, 4)
        if btnk == 0:
            returnrate = 0
        else:
            returnrate = round((ereturn / btnk) * 100, 4)

        reportdict = {
            "基金名称": [self.aim.name],
            "基金代码": [self.aim.code],
            "当日净值": [value],
            "单位成本": [unitcost],
            "持有份额": [currentshare],
            "基金现值": [currentcash],
            "基金总申购": [totinput],
            "历史最大占用": [btnk],
            "基金持有成本": [totinput - totoutput],
            "基金分红与赎回": [totoutput],
            "换手率": [turnover],
            "基金收益总额": [ereturn],
            "投资收益率": [returnrate],
        }
        df = pd.DataFrame(reportdict, columns=reportdict.keys())
        return df
Beispiel #18
0
    def dailyreport(self, date=yesterdayobj()):
        '''
        breif report dict of certain date status on the fund investment

        :param date: string or obj of date, show info of the date given
        :returns: dict of various data on the trade positions
        '''
        date = convert_date(date)
        partcftb = self.cftable[self.cftable['date'] <= date]
        value = self.aim.price[
            self.aim.price['date'] <= date].iloc[-1].netvalue

        if len(partcftb) == 0:
            reportdict = {
                '基金名称': [self.aim.name],
                '基金代码': [self.aim.code],
                '当日净值': [value],
                '持有份额': [0],
                '基金现值': [0],
                '基金总申购': [0],
                '历史最大占用': [0],
                '基金分红与赎回': [0],
                '基金收益总额': [0]
            }
            df = pd.DataFrame(reportdict, columns=reportdict.keys())
            return df
        # totinput = myround(-sum(partcftb.loc[:,'cash']))
        totinput = myround(-sum(
            [row['cash']
             for _, row in partcftb.iterrows() if row['cash'] < 0]))
        totoutput = myround(
            sum([
                row['cash'] for _, row in partcftb.iterrows()
                if row['cash'] > 0
            ]))

        currentshare = myround(sum(partcftb.loc[:, 'share']))
        currentcash = myround(currentshare * value)
        btnk = bottleneck(partcftb)
        turnover = turnoverrate(partcftb, date)
        ereturn = myround(currentcash + totoutput - totinput)
        if currentshare == 0:
            unitcost = 0
        else:
            unitcost = round((totinput - totoutput) / currentshare, 4)
        if btnk == 0:
            returnrate = 0
        else:
            returnrate = round((ereturn / btnk) * 100, 4)

        reportdict = {
            '基金名称': [self.aim.name],
            '基金代码': [self.aim.code],
            '当日净值': [value],
            '单位成本': [unitcost],
            '持有份额': [currentshare],
            '基金现值': [currentcash],
            '基金总申购': [totinput],
            '历史最大占用': [btnk],
            '基金持有成本': [totinput - totoutput],
            '基金分红与赎回': [totoutput],
            '换手率': [turnover],
            '基金收益总额': [ereturn],
            '投资收益率': [returnrate]
        }
        df = pd.DataFrame(reportdict, columns=reportdict.keys())
        return df
Beispiel #19
0
    def _addrow(self):
        """
        Return cashflow table with one more line or raise an exception if there is no more line to add
        The same logic also applies to rem table
        关于对于一个基金多个操作存在于同一交易日的说明:无法处理历史买入第一笔同时是分红日的情形, 事实上也不存在这种情形。无法处理一日多笔买卖的情形。
        同一日既有卖也有买不现实,多笔买入只能在 csv 上合并记录,由此可能引起份额计算 0.01 的误差。可以处理分红日买入卖出的情形。
        分级份额折算日封闭无法买入,所以程序直接忽略当天的买卖。因此不会出现多个操作共存的情形。
        """
        # the design on data remtable is disaster, it is very dangerous though works now

        code = self.aim.code
        if len(self.cftable) == 0:
            if len(self.status[self.status[code] != 0]) == 0:
                raise Exception("no other info to be add into cashflow table")
            i = 0
            while self.status.iloc[i].loc[code] == 0:
                i += 1
            value = self.status.iloc[i].loc[code]
            date = self.status.iloc[i].date
            if value > 0:
                rdate, cash, share = self.aim.shengou(value, date)
                rem = rm.buy([], share, rdate)
            else:
                raise TradeBehaviorError("You cannot sell first when you never buy")
        elif len(self.cftable) > 0:
            recorddate = list(self.status.date)
            lastdate = self.cftable.iloc[-1].date + pd.Timedelta(1, unit="d")
            while (lastdate not in self.aim.specialdate) and (
                (lastdate not in recorddate)
                or (
                    (lastdate in recorddate)
                    and (
                        self.status[self.status["date"] == lastdate].loc[:, code].any()
                        == 0
                    )
                )
            ):
                lastdate += pd.Timedelta(1, unit="d")
                if (lastdate - yesterdayobj()).days >= 1:
                    raise Exception("no other info to be add into cashflow table")
            date = lastdate
            label = self.aim.dividend_label  # 现金分红 0, 红利再投 1
            cash = 0
            share = 0
            rem = self.remtable.iloc[-1].rem
            rdate = date

            if (date in recorddate) and (date not in self.aim.zhesuandate):
                # deal with buy and sell and label the fenhongzaitouru, namely one label a 0.05 in the original table to label fenhongzaitouru
                value = self.status[self.status["date"] == date].iloc[0].loc[code]
                fenhongmark = round(10 * value - int(10 * value), 1)
                if fenhongmark == 0.5 and label == 0:
                    label = 1  # fenhong reinvest
                    value = round(value, 1)
                elif fenhongmark == 0.5 and label == 1:
                    label = 0
                    value = round(value, 1)

                if value > 0:  # value stands for purchase money
                    rdate, dcash, dshare = self.aim.shengou(value, date)
                    rem = rm.buy(rem, dshare, rdate)

                elif value < -0.005:  # value stands for redemp share
                    rdate, dcash, dshare = self.aim.shuhui(
                        -value, date, self.remtable.iloc[-1].rem
                    )
                    _, rem = rm.sell(rem, -dshare, rdate)
                elif value >= -0.005 and value < 0:
                    # value now stands for the ratio to be sold in terms of remain positions, -0.005 stand for sell 100%
                    remainshare = sum(self.cftable.loc[:, "share"])
                    ratio = -value / 0.005
                    rdate, dcash, dshare = self.aim.shuhui(
                        remainshare * ratio, date, self.remtable.iloc[-1].rem
                    )
                    _, rem = rm.sell(rem, -dshare, rdate)
                else:  # in case value=0, when specialday is in record day
                    rdate, dcash, dshare = date, 0, 0

                cash += dcash
                share += dshare
            if date in self.aim.specialdate:  # deal with fenhong and xiazhe
                comment = (
                    self.aim.price[self.aim.price["date"] == date]
                    .iloc[0]
                    .loc["comment"]
                )
                if isinstance(comment, float):
                    if comment < 0:
                        dcash2, dshare2 = (
                            0,
                            sum([myround(sh * (-comment - 1)) for _, sh in rem]),
                        )  # xiazhe are seperately carried out based on different purchase date
                        rem = rm.trans(rem, -comment, date)
                        # myround(sum(cftable.loc[:,'share'])*(-comment-1))
                    elif comment > 0 and label == 0:
                        dcash2, dshare2 = (
                            myround(sum(self.cftable.loc[:, "share"]) * comment),
                            0,
                        )
                        rem = rm.copy(rem)

                    elif comment > 0 and label == 1:
                        dcash2, dshare2 = (
                            0,
                            myround(
                                sum(self.cftable.loc[:, "share"])
                                * (
                                    comment
                                    / self.aim.price[self.aim.price["date"] == date]
                                    .iloc[0]
                                    .netvalue
                                )
                            ),
                        )
                        rem = rm.buy(rem, dshare2, date)

                    cash += dcash2
                    share += dshare2
                else:
                    raise ParserFailure("comments not recoginized")

        self.cftable = self.cftable.append(
            pd.DataFrame([[rdate, cash, share]], columns=["date", "cash", "share"]),
            ignore_index=True,
        )
        self.remtable = self.remtable.append(
            pd.DataFrame([[rdate, rem]], columns=["date", "rem"]), ignore_index=True
        )
Beispiel #20
0
    def _addrow(self):
        """
        Return cashflow table with one more line or raise an exception if there is no more line to add
        The same logic also applies to rem table
        关于对于一个基金多个操作存在于同一交易日的说明:无法处理历史买入第一笔同时是分红日的情形, 事实上也不存在这种情形。无法处理一日多笔买卖的情形。
        同一日既有卖也有买不现实,多笔买入只能在 csv 上合并记录,由此可能引起份额计算 0.01 的误差。可以处理分红日买入卖出的情形。
        分级份额折算日封闭无法买入,所以程序直接忽略当天的买卖。因此不会出现多个操作共存的情形。
        """
        # the design on data remtable is disaster, it is very dangerous though works now
        # possibly failing cases include:
        # 买卖日记录是节假日,而顺延的日期恰好是折算日(理论上无法申赎)或分红日(可能由于 date 和 rdate 的错位而没有考虑到),
        # 又比如周日申购记录,周一申购记录,那么周日记录会现金流记在周一,继续现金流标更新将从周二开始,周一数据被丢弃
        code = self.aim.code
        if len(self.cftable) == 0:
            if len(self.status[self.status[code] != 0]) == 0:
                raise Exception("no other info to be add into cashflow table")
            i = 0
            while self.status.iloc[i].loc[code] == 0:
                i += 1
            value = self.status.iloc[i].loc[code]
            date = self.status.iloc[i].date
            self.lastdate = date
            if len(self.price[self.price["date"] >= date]) > 0:
                date = self.price[self.price["date"] >= date].iloc[0]["date"]
            else:
                date = self.price[self.price["date"] <= date].iloc[-1]["date"]

            # 这里没有像下边部分一样仔细处理单独的 lastdate,hopefully 不会出现其他奇怪的问题,有 case 再说
            # https://github.com/refraction-ray/xalpha/issues/47
            # 凭直觉这个地方的处理很可能还有其他 issue

            if value > 0:
                feelabel = 100 * value - int(100 * value)
                if round(feelabel, 1) == 0.5:
                    # binary encoding, 10000.005 is actually 10000.0050...1, see issue #59
                    feelabel = feelabel - 0.5
                    if abs(feelabel) < 1e-4:
                        feelabel = 0
                    else:
                        feelabel *= 100
                else:
                    feelabel = None
                value = int(value * 100) / 100
                assert feelabel is None or feelabel >= 0.0, "自定义申购费必须为正值"
                rdate, cash, share = self.aim.shengou(value,
                                                      date,
                                                      fee=feelabel)
                rem = rm.buy([], share, rdate)
            else:
                raise TradeBehaviorError(
                    "You cannot sell first when you never buy")
        elif len(self.cftable) > 0:
            # recorddate = list(self.status.date)
            if not getattr(self, "lastdate", None):
                lastdate = self.cftable.iloc[-1].date + pd.Timedelta(1,
                                                                     unit="d")
            else:
                lastdate = self.lastdate + pd.Timedelta(1, unit="d")
            while (lastdate not in self.aim.specialdate) and (
                (lastdate not in self.recorddate_set) or
                ((lastdate in self.recorddate_set) and
                 (self.status[self.status["date"] ==
                              lastdate].loc[:, code].any() == 0))):
                lastdate += pd.Timedelta(1, unit="d")
                if (lastdate - yesterdayobj()).days >= 1:
                    raise Exception(
                        "no other info to be add into cashflow table")
            if (lastdate - yesterdayobj()).days >= 1:
                raise Exception("no other info to be add into cashflow table")
            date = lastdate
            # 无净值日优先后移,无法后移则前移
            # 还是建议日期记录准确,不然可能有无法完美兼容的错误出现
            if len(self.price[self.price["date"] >= date]) > 0:
                date = self.price[self.price["date"] >= date].iloc[0]["date"]
            else:
                date = self.price[self.price["date"] <= date].iloc[-1]["date"]
            if date != lastdate and date in list(self.status.date):
                # 日期平移到了其他记录日,很可能出现问题!
                logger.warning(
                    "账单日期 %s 非 %s 的净值记录日期,日期智能平移后 %s 与账单其他日期重合!交易处理极可能出现问题!! "
                    "靠后日期的记录被覆盖" % (lastdate, self.code, date))
            self.lastdate = lastdate
            if date > lastdate:
                self.lastdate = date
            # see https://github.com/refraction-ray/xalpha/issues/27, begin new date from last one in df is not reliable
            label = self.aim.dividend_label  # 现金分红 0, 红利再投 1
            cash = 0
            share = 0
            rem = self.remtable.iloc[-1].rem
            rdate = date
            if (lastdate
                    in self.recorddate_set) and (date
                                                 not in self.aim.zhesuandate):
                # deal with buy and sell and label the fenhongzaitouru, namely one label a 0.05 in the original table to label fenhongzaitouru
                value = self.status[
                    self.status["date"] <= lastdate].iloc[-1].loc[code]
                if date in self.aim.fenhongdate:  # 0.05 的分红行为标记,只有分红日才有效
                    fenhongmark = round(10 * value - int(10 * value), 1)
                    if fenhongmark == 0.5 and label == 0:
                        label = 1  # fenhong reinvest
                        value = value - math.copysign(0.05, value)
                    elif fenhongmark == 0.5 and label == 1:
                        label = 0
                        value = value - math.copysign(0.05, value)

                if value > 0:  # value stands for purchase money
                    feelabel = 100 * value - int(100 * value)

                    if int(10 * feelabel) == 5:
                        feelabel = (feelabel - 0.5) * 100
                    else:
                        feelabel = None
                    value = int(value * 100) / 100
                    rdate, dcash, dshare = self.aim.shengou(
                        value, date, fee=feelabel
                    )  # shengou fee is in the unit of percent, different than shuhui case
                    rem = rm.buy(rem, dshare, rdate)

                elif value < -0.005:  # value stands for redemp share
                    feelabel = int(100 * value) - 100 * value
                    if int(10 * feelabel) == 5:
                        feelabel = feelabel - 0.5
                    else:
                        feelabel = None
                    value = int(value * 100) / 100
                    rdate, dcash, dshare = self.aim.shuhui(
                        -value, date, self.remtable.iloc[-1].rem, fee=feelabel)
                    _, rem = rm.sell(rem, -dshare, rdate)
                elif value >= -0.005 and value < 0:
                    # value now stands for the ratio to be sold in terms of remain positions, -0.005 stand for sell 100%
                    remainshare = sum(self.cftable[
                        self.cftable["date"] <= date].loc[:, "share"])
                    ratio = -value / 0.005
                    rdate, dcash, dshare = self.aim.shuhui(
                        remainshare * ratio, date, self.remtable.iloc[-1].rem,
                        0)
                    _, rem = rm.sell(rem, -dshare, rdate)
                else:  # in case value=0, when specialday is in record day
                    rdate, dcash, dshare = date, 0, 0

                cash += dcash
                share += dshare
            if date in self.aim.specialdate:  # deal with fenhong and xiazhe
                comment = self.price[self.price["date"] ==
                                     date].iloc[0].loc["comment"]
                if isinstance(comment, float):
                    if comment < 0:
                        dcash2, dshare2 = (
                            0,
                            sum([
                                myround(sh * (-comment - 1)) for _, sh in rem
                            ]),
                        )  # xiazhe are seperately carried out based on different purchase date
                        rem = rm.trans(rem, -comment, date)
                        # myround(sum(cftable.loc[:,'share'])*(-comment-1))
                    elif comment > 0 and label == 0:
                        dcash2, dshare2 = (
                            myround(
                                sum(self.cftable.loc[:, "share"]) * comment),
                            0,
                        )
                        rem = rm.copy(rem)

                    elif comment > 0 and label == 1:
                        dcash2, dshare2 = (
                            0,
                            myround(
                                sum(self.cftable.loc[:, "share"]) *
                                (comment / self.price[self.price["date"] ==
                                                      date].iloc[0].netvalue)),
                        )
                        rem = rm.buy(rem, dshare2, date)

                    cash += dcash2
                    share += dshare2
                else:
                    raise ParserFailure("comments not recognized")

        self.cftable = self.cftable.append(
            pd.DataFrame([[rdate, cash, share]],
                         columns=["date", "cash", "share"]),
            ignore_index=True,
        )
        self.remtable = self.remtable.append(pd.DataFrame(
            [[rdate, rem]], columns=["date", "rem"]),
                                             ignore_index=True)