Пример #1
0
    def update(self):
        """
        function to incrementally update the pricetable after fetch the old one
        """
        lastdate = self.price.iloc[-1].date
        diffdays = (yesterdayobj() - lastdate).days
        if (
                diffdays == 0
        ):  ## for some QDII, this value is 1, anyways, trying update is compatible (d+2 update)
            return None
        elif diffdays <= 10:
            self._updateurl = (
                "http://fund.eastmoney.com/f10/F10DataApi.aspx?type=lsjz&code="
                + self.code + "&page=1&per=" + str(diffdays))
            con = _download(self._updateurl)
            soup = BeautifulSoup(con.text, "lxml")
            items = soup.findAll("td")
        elif (
                diffdays > 10
        ):  ## there is a 20 item per page limit in the API, so to be safe, we query each page by 10 items only
            items = []
            for pg in range(1, int(diffdays / 10) + 2):
                self._updateurl = (
                    "http://fund.eastmoney.com/f10/F10DataApi.aspx?type=lsjz&code="
                    + self.code + "&page=" + str(pg) + "&per=10")
                con = _download(self._updateurl)
                soup = BeautifulSoup(con.text, "lxml")
                items.extend(soup.findAll("td"))
        else:
            raise TradeBehaviorError(
                "Weird incremental update: the saved copy has future records")

        date = []
        netvalue = []
        totvalue = []
        comment = []
        for i in range(int(len(items) / 7)):
            ts = pd.Timestamp(items[7 * i].string)
            if (ts - lastdate).days > 0:
                date.append(ts)
                netvalue.append(float(items[7 * i + 1].string))
                totvalue.append(float(items[7 * i + 2].string))
                comment.append(_nfloat(items[7 * i + 6].string))
            else:
                break
        df = pd.DataFrame({
            "date": date,
            "netvalue": netvalue,
            "totvalue": totvalue,
            "comment": comment,
        })
        df = df.iloc[::-1]  ## reverse the time order
        df = df[df["date"].isin(opendate)]
        df = df.reset_index(drop=True)
        df = df[df["date"] <= yesterdayobj()]
        if len(df) != 0:
            self.price = self.price.append(df, ignore_index=True, sort=True)
            return df
Пример #2
0
 def __init__(self, *fundtradeobj, status=None, fetch=False, save=False, path='', form='csv', totmoney=100000,
              cashobj=None):
     super().__init__(*fundtradeobj, status=status, fetch=fetch, save=save, path=path, form=form)
     if cashobj is None:
         cashobj = cashinfo()
     self.totmoney = totmoney
     nst = mulfix._vcash(totmoney, self.totcftable, cashobj)
     cashtrade = trade(cashobj, nst)
     #		 super().__init__(*self.fundtradeobj, cashtrade)
     self.fundtradeobj = list(self.fundtradeobj)
     self.fundtradeobj.append(cashtrade)
     self.fundtradeobj = tuple(self.fundtradeobj)
     btnk = bottleneck(self.totcftable)
     if btnk > totmoney:
         raise TradeBehaviorError('the initial total cash is too low')
     self.totcftable = pd.DataFrame(data={'date': [nst.iloc[0].date], 'cash': [-totmoney]})
Пример #3
0
    def sell(self, code, share, date, is_value=False):
        """

        :param code:
        :param share:
        :param date: datetime obj
        :param is_value: bool, default False. 货币基金可按照金额赎回
        :return:
        """

        share = abs(share)
        if self.verbose:
            print(f"sell {share} of {code} on {date.strftime('%Y-%m-%d')}")
        if code not in self.trades:
            raise TradeBehaviorError(
                "You are selling something that you don't have")
        df = self.trades[code].status
        cftable = self.trades[code].cftable
        cftable = cftable[cftable["date"] <= self.lastdates[code]]
        remtable = self.trades[code].remtable
        remtable = remtable[remtable["date"] <= self.lastdates[code]]
        self.lastdates[code] = date
        self.lastdates[code] = date
        df2 = pd.DataFrame([[date, -share]],
                           columns=["date", self.get_code(code)])
        df = df.append(df2)
        if is_value:
            self.set_fund(code, value_label=1)
        self.trades[code] = trade(
            self.infos[code],
            df,
            cftable=cftable,
            remtable=remtable,
        )
        if is_value:
            self.set_fund(code, value_label=0)
Пример #4
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
        )
Пример #5
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)
Пример #6
0
    def update(self):
        """
        function to incrementally update the pricetable after fetch the old one
        """
        lastdate = self.price.iloc[-1].date
        startvalue = self.price.iloc[-1].totvalue
        diffdays = (yesterdayobj() - lastdate).days
        if diffdays == 0:
            return None
        self._updateurl = (
            "http://fund.eastmoney.com/f10/F10DataApi.aspx?type=lsjz&code=" +
            self.code + "&page=1&per=1")
        con = rget(self._updateurl)
        soup = BeautifulSoup(con.text, "lxml")
        items = soup.findAll("td")
        if dt.datetime.strptime(str(items[0].string), "%Y-%m-%d") == today():
            diffdays += 1
        if diffdays <= 10:
            # caution: there may be today data!! then a day gap will be in table
            self._updateurl = (
                "http://fund.eastmoney.com/f10/F10DataApi.aspx?type=lsjz&code="
                + self.code + "&page=1&per=" + str(diffdays))
            con = rget(self._updateurl)
            soup = BeautifulSoup(con.text, "lxml")
            items = soup.findAll("td")
        elif (
                diffdays > 10
        ):  ## there is a 20 item per page limit in the API, so to be safe, we query each page by 10 items only
            items = []
            for pg in range(1, int(diffdays / 10) + 2):
                self._updateurl = (
                    "http://fund.eastmoney.com/f10/F10DataApi.aspx?type=lsjz&code="
                    + self.code + "&page=" + str(pg) + "&per=10")
                con = rget(self._updateurl)
                soup = BeautifulSoup(con.text, "lxml")
                items.extend(soup.findAll("td"))
        else:
            raise TradeBehaviorError(
                "Weird incremental update: the saved copy has future records")

        date = []
        earnrate = []
        comment = []
        for i in range(int(len(items) / 6)):
            ts = pd.Timestamp(str(items[6 * i].string))
            if (ts - lastdate).days > 0:
                date.append(ts)
                earnrate.append(float(items[6 * i + 1].string) * 1e-4)
                comment.append(_nfloat(items[6 * i + 5].string))
        date = date[::-1]
        earnrate = earnrate[::-1]
        comment = comment[::-1]
        netvalue = [startvalue]
        for earn in earnrate:
            netvalue.append(netvalue[-1] * (1 + earn))
        netvalue.remove(startvalue)

        df = pd.DataFrame({
            "date": date,
            "netvalue": netvalue,
            "totvalue": netvalue,
            "comment": comment,
        })
        df = df[df["date"].isin(opendate)]
        df = df.reset_index(drop=True)
        df = df[df["date"] <= yesterdayobj()]
        if len(df) != 0:
            self.price = self.price.append(df, ignore_index=True, sort=True)
            return df