예제 #1
0
    def __init__(self, *codes, start="20200101", end=yesterday(), col="close"):
        """

        :param codes: Union[str, tuple], 格式与 :func:`xalpha.universal.get_daily` 相同,若需要汇率转换,需要用 tuple,第二个元素形如 "USD"
        :param start: %Y%m%d
        :param end: %Y%m%d, default yesterday
        """
        totdf = pd.DataFrame()
        codelist = []
        for c in codes:
            if isinstance(c, tuple):
                code = c[0]
                currency = c[1]
            else:
                code = c
                currency = "CNY"  # 标的不做汇率调整
            codelist.append(code)
            df = xu.get_daily(code, start=start, end=end)
            df = df[df.date.isin(opendate)]
            currency_code = _get_currency_code(currency)
            if currency_code:
                cdf = xu.get_daily(currency_code, start=start, end=end)
                cdf = cdf[cdf["date"].isin(opendate)]
                df = df.merge(right=cdf, on="date", suffixes=("_x", "_y"))
                df[col] = df[col + "_x"] * df[col + "_y"]
            df[code] = df[col] / df.iloc[0][col]
            df = df.reset_index()
            df = df[["date", code]]
            if "date" not in totdf.columns:
                totdf = df
            else:
                totdf = totdf.merge(on="date", right=df)
        self.totdf = totdf
        self.codes = codelist
예제 #2
0
def daily_increment(code, date, lastday=None, _check=False):
    """
    单一标的 date 日(若 date 日无数据则取之前的最晚有数据日,但该日必须大于 _check 对应的日期)较上一日或 lastday 的倍数,
    lastday 支持不完整,且不能离 date 太远

    :param code:
    :param date:
    :param lastday: 如果用默认 None,则表示和前一日的涨跌
    :param _check: 数据必须已更新到 date 日,除非之前每天都是节假日
    :return:
    """
    try:
        tds = xu.get_daily(code=code, end=date, prev=30)
    except Exception as e:  # 只能笼统 catch 了,因为抓取失败的异常是什么都能遇到。。。
        code = get_alt(code)
        if code:
            tds = xu.get_daily(code=code, end=date, prev=30)
        else:
            raise e
    tds = tds[tds["date"] <= date]
    if _check:
        date = date.replace("-", "").replace("/", "")
        date_obj = dt.datetime.strptime(date, "%Y%m%d")

        while tds.iloc[-1]["date"] < date_obj:
            # in case data is not up to date
            # 但是存在日本市场休市时间不一致的情况,估计美股也存在
            if not is_on(
                    date_obj.strftime("%Y%m%d"),
                    get_market(code),
                    no_trading_days=no_trading_days,
            ) or (date_obj.strftime("%Y-%m-%d") in gap_info.get(code, [])):
                print("%s is closed on %s" % (code, date))
                if not lastday:
                    return 1  # 当日没有涨跌,这里暂时为考虑 _check 和 lastday 相同的的情形
                date_obj -= dt.timedelta(days=1)
            else:
                raise DateMismatch(
                    code,
                    reason="%s has no data newer than %s" %
                    (code, date_obj.strftime("%Y-%m-%d")),
                )
    if not lastday:
        ratio = tds.iloc[-1]["close"] / tds.iloc[-2]["close"]
    else:
        tds2 = tds[tds["date"] <= lastday]
        # 未考虑连 lastday 的数据数据源都没更新的情形,这种可能极小
        ratio = tds.iloc[-1]["close"] / tds2.iloc[-1]["close"]
    return ratio
예제 #3
0
    def __init__(self, code, status, name=None):
        """

        :param code: str. 代码格式与 :func:`xalpha.universal.get_daily` 要求相同
        :param status: 记账单或 irecord 类。
        :param name: Optional[str]. 可提供标的名称。
        """
        self.code = code
        if isinstance(status, irecord):
            self.status = status.filter(code)
        else:
            self.status = status[status.code == code]
        # self.cftable = pd.DataFrame([], columns=["date", "cash", "share"])
        try:
            self.price = xu.get_daily(
                self.code,
                start=self.status.iloc[0]["date"].strftime("%Y-%m-%d"))
            self.price["netvalue"] = self.price["close"]
        except Exception as e:
            logger.warning("%s when trade trying to get daily price of %s" %
                           (e, self.code))
            self.price = None
        self._arrange()
        if not name:
            try:
                self.name = get_rt(code)["name"]
            except:
                self.name = code
        self.type_ = None
예제 #4
0
파일: trade.py 프로젝트: zofuthan/xalpha
def xirrcal(cftable, trades, date, guess=0.1):
    """
    calculate the xirr rate

    :param cftable: cftable (pd.Dateframe) with date and cash column
    :param trades: list [trade1, ...], every item is an trade object,
        whose shares would be sold out virtually
    :param date: string of date or datetime object,
        the date when virtually all holding positions being sold
    :param guess: floating number, a guess at the xirr rate solution to be used
        as a starting point for the numerical solution
    :returns: the IRR as a single floating number
    """
    date = convert_date(date)
    partcftb = cftable[cftable["date"] <= date]
    if len(partcftb) == 0:
        return 0
    cashflow = [(row["date"], row["cash"]) for i, row in partcftb.iterrows()]
    rede = 0
    for fund in trades:
        if not isinstance(fund, itrade):
            rede += fund.aim.shuhui(
                fund.briefdailyreport(date).get("currentshare", 0),
                date,
                fund.remtable[fund.remtable["date"] <= date].iloc[-1].rem,
            )[1]
        else:  # 场内交易
            pricedf = get_daily(fund.code, end=date.strftime("%Y%m%d"), prev=20)
            price = pricedf[pricedf.date <= date].iloc[-1]["close"]
            rede += fund.cftable.share.sum() * price
    cashflow.append((date, rede))
    return xirr(cashflow, guess)
예제 #5
0
    def __init__(self, code, start=None, end=None):
        """

        :param code: str. 形式可以是 399971.XSHE 或者 SH000931
        :param start: Optional[str]. %Y-%m-%d, 估值历史计算的起始日。
        :param end: Dont use, only for debug
        """
        yesterday_str = (dt.datetime.now() -
                         dt.timedelta(days=1)).strftime("%Y-%m-%d")
        if len(code.split(".")) == 2:
            self.code = code
            self.scode = _convert_code(code)
        else:
            self.scode = code
            self.code = _inverse_convert_code(self.scode)
        if self.code in self.indexs:
            self.name = self.indexs[self.code][0]
            if not start:
                start = self.indexs[self.code][1]
        else:
            try:
                self.name = get_rt(self.scode)["name"]
            except:
                self.name = self.scode
            if not start:
                start = "2012-01-01"  # 可能会出问题,对应指数还未有数据
        self.start = start
        if not end:
            end = yesterday_str
        self.df = xu.get_daily("peb-" + self.scode, start=self.start, end=end)
        self.ratio = None
        self.title = "指数"
        self._gen_percentile()
예제 #6
0
    def __init__(self, code, start=None, end=None, prev=None):
        """

        :param code: str. eg SH501018, SZ160416
        :param start: date range format is the same as xa.get_daily
        :param end:
        :param prev:
        """
        self.code = code
        df1 = xu.get_daily("F" + self.code[2:],
                           start=start,
                           end=end,
                           prev=prev)
        df2 = xu.get_daily(self.code, start=start, end=end, prev=prev)
        df1 = df1.merge(df2, on="date", suffixes=("_F", "_" + code[:2]))
        df1["diff_rate"] = ((df1["close_" + code[:2]] - df1["close_F"]) /
                            df1["close_F"] * 100)
        self.df = df1
예제 #7
0
 def fluctuation(self):
     if not self.ratio:
         d = self.df.iloc[-1]["date"]
         oprice = xu.get_daily(code=self.scode,
                               end=d.strftime("%Y%m%d"),
                               prev=20).iloc[-1]["close"]
         nprice = get_rt(self.scode)["current"]
         self.ratio = nprice / oprice
     return self.ratio
예제 #8
0
파일: multiple.py 프로젝트: stwendy/xalpha
    def get_portfolio(self, date=yesterdayobj()):
        """
        获取基金组合底层资产大类配置的具体值

        :param date:
        :return: Dict[str, float]. stock,bond,cash 对应总值的字典
        """

        d = {"stock": 0, "bond": 0, "cash": 0}
        date = convert_date(date)
        for f in self.fundtradeobj:
            value = f.briefdailyreport(date).get("currentvalue", 0)
            if value > 0:
                if isinstance(f, itrade):
                    if f.get_type() == "股票":
                        d["stock"] += value
                        continue
                    elif f.get_type() in ["可转债", "债券"]:
                        d["bond"] += value
                        continue
                    elif f.get_type() == "货币基金":
                        d["cash"] += value
                        continue
                    elif f.get_type() == "场内基金":
                        code = f.code[2:]
                    else:
                        continue
                else:
                    code = f.code
                if code == "mf":
                    d["cash"] += value
                    continue
                if get_fund_type(code) == "货币基金":
                    d["cash"] += value
                    continue
                df = xu.get_daily("pt-F" + code, end=date.strftime("%Y%m%d"))
                if df is None or len(df) == 0:
                    logger.warning("empty portfolio info for %s" % code)
                row = df.iloc[-1]
                if row["bond_ratio"] + row["stock_ratio"] < 10:  # 联接基金
                    d["stock"] += (
                        (100 - row["bond_ratio"] - row["cash_ratio"]) * value / 100
                    )
                    d["bond"] += row["bond_ratio"] * value / 100
                    d["cash"] += row["cash_ratio"] * value / 100
                else:
                    d["stock"] += row["stock_ratio"] * value / 100
                    d["bond"] += row["bond_ratio"] * value / 100
                    d["cash"] += row["cash_ratio"] * value / 100
        return d
예제 #9
0
 def __init__(self, code, start=None, end=None):
     self.code = code
     self.scode = code
     if not end:
         end = (dt.datetime.now() -
                dt.timedelta(days=1)).strftime("%Y-%m-%d")
     if not start:
         start = "2016-01-01"  # 基金历史通常比较短
     self.start = start
     self.df = xu.get_daily("peb-" + code, start=start, end=end)
     self.name = get_rt(code)["name"]
     self.title = "基金"
     self.ratio = None
     self._gen_percentile()
예제 #10
0
 def get_t0(self, return_date=True, percent=False):
     last_value, last_date = self.get_t1()
     last_date_obj = dt.datetime.strptime(last_date, "%Y-%m-%d")
     cday = last_onday(self.today)
     while last_date_obj < cday:  # 昨天净值数据还没更新
         # 是否存在部分部分基金可能有 gap?
         if cday.strftime("%Y-%m-%d") not in gap_info[self.fcode]:
             self.t1_type = "昨日未出"
             raise DateMismatch(
                 self.code,
                 reason="%s netvalue has not been updated to yesterday" %
                 self.code,
             )
         else:
             cday = last_onday(cday)
         # 经过这个没报错,就表示数据源是最新的
     if last_date_obj >= self.today:  # 今天数据已出,不需要再预测了
         print("no need to predict net value since it has been out for %s" %
               self.code)
         self.t1_type = "今日已出"
         if not return_date:
             return last_value
         else:
             return last_value, last_date
     t = 0
     n = 0
     today_str = self.today.strftime("%Y%m%d")
     for k, v in self.t0dict.items():
         w = v
         t += w
         r = get_rt(k)
         # k should support get_rt, investing pid doesn't support this!
         if percent:
             c = w / 100 * (1 + r["percent"] / 100)  # 直接取标的当日涨跌幅
         else:
             df = xu.get_daily(k)
             basev = df[df["date"] <= last_date].iloc[-1]["close"]
             c = w / 100 * r["current"] / basev
         currency_code = get_currency_code(k)
         if currency_code:
             c = c * daily_increment(currency_code, today_str)
         n += c
     n += (100 - t) / 100
     t0value = n * last_value
     self.t0_delta = n
     if not return_date:
         return t0value
     else:
         return t0value, self.today.strftime("%Y-%m-%d")
예제 #11
0
 def _base_value(self, code, shift):
     if not shift:
         funddf = xu.get_daily(code)  ## 获取股指现货日线
         return funddf[funddf["date"] < self.today.strftime(
             "%Y-%m-%d")].iloc[-1]["close"]  # 日期是按当地时间
     # TODO: check it is indeed date of last_on(today)
     else:
         if code not in self.bar_cache:
             funddf = get_bar(code, prev=48, interval="3600")  ## 获取小时线
             if self.now.hour > 6:  # 昨日美国市场收盘才正常,才缓存参考小时线
                 self.bar_cache[code] = funddf
         else:
             funddf = self.bar_cache[code]
         return funddf[funddf["date"] <= self.today +
                       dt.timedelta(hours=shift)].iloc[-1][
                           "close"]  # 时间是按北京时间, 小时线只能手动缓存,日线不需要是因为自带透明缓存器
예제 #12
0
    def __init__(self, code, start=None, end=None):
        """

        :param code: str. 指数代码,eg. SH000016
        :param start:
        :param end:
        """
        df = xu.get_daily("teb-" + code, start=start, end=end)
        df["e"] = pd.to_numeric(df["e"])
        df["b"] = pd.to_numeric(df["b"])
        df["lnb"] = df["b"].apply(lambda s: np.log(s))
        df["lne"] = df["e"].apply(lambda s: np.log(s))
        df["roe"] = df["e"] / df["b"] * 100
        df["date_count"] = (df["date"] -
                            df["date"].iloc[0]).apply(lambda s: int(s.days))
        self.df = df
        self.fit(verbose=False)
예제 #13
0
 def get_netvalue(self, date=yesterdayobj()):
     # 若使用请务必配合带 precached 的缓存策略!!
     if not getattr(self, "fetchonly", None):
         self.fetchonly = False
     prestart = self.status.iloc[0]["date"]
     df = xu.get_daily(
         self.code,
         end=date.strftime("%Y%m%d"),
         prev=20,
         fetchonly=self.fetchonly,
         precached=prestart.strftime("%Y%m%d"),
     )
     self.fetchonly = True  # 这么做防止数据空白时期的反复抓取
     if len(df) > 0:
         return df.iloc[-1].close
     else:
         return 0
예제 #14
0
 def _base_value(self, code, shift):
     if not shift:
         funddf = xu.get_daily(code)  ## 获取股指现货日线
         return funddf[funddf["date"] <= last_onday(self.today)].iloc[-1][
             "close"]  # 日期是按当地时间
     # TODO: check it is indeed date of last_on(today)
     else:
         if code not in self.bar_cache:
             funddf = get_bar(code, prev=168, interval="3600")  ## 获取小时线
             ## 注意对于国内超长假期,prev 可能还不够
             if self.now.hour > 6:  # 昨日美国市场收盘才正常,才缓存参考小时线
                 self.bar_cache[code] = funddf
         else:
             funddf = self.bar_cache[code]
         refdate = last_onday(self.today) + dt.timedelta(days=1)  # 按北京时间校准
         return funddf[funddf["date"] <= refdate +
                       dt.timedelta(hours=shift)].iloc[-1][
                           "close"]  # 时间是按北京时间, 小时线只能手动缓存,日线不需要是因为自带透明缓存器
예제 #15
0
    def __init__(self, code, start=None, end=None):
        """

        :param code: 801180 申万行业指数
        :param start:
        :param end:
        """
        self.code = code
        self.scode = code
        if not end:
            end = (dt.datetime.now() -
                   dt.timedelta(days=1)).strftime("%Y-%m-%d")
        if not start:
            start = "2012-01-01"
        self.start = start
        self.df = xu.get_daily("sw-" + code, start=start, end=end)
        self.name = self.df.iloc[0]["name"]
        self.ratio = 1
        self.title = "申万行业指数"
        self._gen_percentile()
예제 #16
0
    def _arrange(self):
        d = {"date": [], "cash": [], "share": []}
        for _, r in self.status.iterrows():
            d["date"].append(r.date)

            if r.share == 0:
                d["cash"].append(-r.value)
                d["share"].append(0)
            else:
                if r.value < 0:
                    r.value = xu.get_daily(
                        self.code, end=r.date.strftime("%Y-%m-%d"), prev=15
                    ).iloc[-1]["close"]
                if r.value == 0:
                    d["cash"].append(0)
                    d["share"].append(r.share)  # 直接记录总的应增加+或减少的份额数
                else:
                    d["cash"].append(-r.value * r.share - abs(r.fee))  # 手续费总是正的,和买入同号
                    d["share"].append(r.share)
        self.cftable = pd.DataFrame(d)
예제 #17
0
파일: trade.py 프로젝트: zofuthan/xalpha
 def get_netvalue(self, date=yesterdayobj()):
     return get_daily(self.code, end=date.strftime("%Y%m%d"), prev=20).iloc[-1].close
예제 #18
0
def _is_on(code, date):
    df = xu.get_daily(code, prev=20, end=date)
    if len(df[df["date"] == date]) == 0:
        return False
    return True
예제 #19
0
    def process_byday(self, date):
        if not date:
            self.date_obj = dt.datetime.today()
        else:
            self.date_obj = dt.datetime.strptime(
                date.replace("-", "").replace("/", ""), "%Y%m%d")
        if not date:
            rt = get_rt(self.code)
            self.name = rt["name"]
            self.cbp = rt["current"]  # 转债价
            self.stockp = get_rt(self.scode)["current"]  # 股票价
        else:
            try:
                if not self.name:
                    rt = get_rt(self.code)
                    self.name = rt["name"]
            except:
                self.name = "unknown"
            df = xu.get_daily(self.code,
                              prev=100,
                              end=self.date_obj.strftime("%Y%m%d"))
            self.cbp = df.iloc[-1]["close"]
            df = xu.get_daily(self.scode,
                              prev=100,
                              end=self.date_obj.strftime("%Y%m%d"))
            self.stockp = df.iloc[-1]["close"]

        df = xu.get_daily(self.scode,
                          prev=360,
                          end=self.date_obj.strftime("%Y%m%d"))
        self.history_volatility = np.std(
            np.log(df["close"] / df.shift(1)["close"])) * np.sqrt(244)
        if not self.refvolatility:
            self.volatility = 0.17
            if self.rating in ["A", "A+", "AA-"]:
                self.volatility = 0.19
            elif self.rating in ["AA"]:
                self.volatility = 0.18
            if self.history_volatility < 0.25:
                self.volatility -= 0.01
            elif self.history_volatility > 0.65:
                self.volatility += 0.02
            elif self.history_volatility > 0.45:
                self.volatility += 0.01
        self.years = len(self.rlist) - 1
        syear = int(self.enddate.split("-")[0]) - self.years
        self.issuedate = str(syear) + self.enddate[4:]
        self.days = (dt.datetime.strptime(self.enddate, "%Y-%m-%d") -
                     self.date_obj).days
        if not self.refbondrate:
            ratestable = get_bond_rates(self.rating,
                                        self.date_obj.strftime("%Y-%m-%d"))
            if self.rating in ["A", "A+", "AA-"]:
                ## AA 到 AA- 似乎是利率跳高的一个坎
                cutoff = 2
            else:
                cutoff = 4
            if self.days / 365 > cutoff:
                # 过长久期的到期收益率,容易造成估值偏离,虽然理论上是对的
                # 考虑到国内可转债市场信用风险较低,不应过分低估低信用债的债券价值
                self.bondrate = (
                    ratestable[ratestable["year"] <= cutoff].iloc[-1]["rate"] /
                    100)
            else:
                self.bondrate = (ratestable[ratestable["year"] >= self.days /
                                            365].iloc[0]["rate"] / 100)
        if not self.refriskfreerate:
            ratestable = get_bond_rates("N",
                                        self.date_obj.strftime("%Y-%m-%d"))
            if self.days / 365 > 5:
                self.riskfreerate = (
                    ratestable[ratestable["year"] <= 5].iloc[-1]["rate"] / 100)
            else:
                self.riskfreerate = (
                    ratestable[ratestable["year"] >= self.days /
                               365].iloc[0]["rate"] / 100)
예제 #20
0
    def get_t1(self, date=None, return_date=True):
        """
        预测 date 日的净值,基于 date-1 日的净值和 date 日的外盘数据,数据自动缓存,不会重复计算

        :param date: str. %Y-%m-%d. 注意若是 date 日为昨天,即今日预测昨日的净值,date 取默认值 None。
        :param return_date: bool, default True. return tuple, the second one is date in the format %Y%m%d
        :return: float, (str).
        :raises NonAccurate: 由于外盘数据还未及时更新,而 raise,可在调用程序中用 except 捕获再处理。
        """
        if date is None:
            yesterday = last_onday(self.today)
            datekey = yesterday.strftime("%Y%m%d")
        else:
            datekey = date.replace("/", "").replace("-", "")
        if datekey not in self.t1value_cache:

            if self.positions:
                current_pos = self.get_position(datekey, return_date=False)
                hdict = scale_dict(self.t1dict.copy(), aim=current_pos * 100)
            else:
                hdict = self.t1dict.copy()

            if date is None:  # 此时预测上个交易日净值
                yesterday_str = datekey
                last_value, last_date = self.get_t2()
                last_date_obj = dt.datetime.strptime(last_date, "%Y-%m-%d")
                cday = last_onday(last_onday(self.today))
                while last_date_obj < cday:  # 前天净值数据还没更新
                    # 是否存在部分 QDII 在 A 股交易日,美股休市日不更新净值的情形?
                    if (cday.strftime("%Y-%m-%d")
                            not in gap_info[self.fcode]) and is_on(
                                cday, "US", no_trading_days):
                        # 这里检查比较宽松,只要当天美股休市,就可以认为确实基金数据不存在而非未更新
                        self.t1_type = "前日未出"
                        raise DateMismatch(
                            self.code,
                            reason=
                            "%s netvalue has not been updated to the day before yesterday"
                            % self.code,
                        )
                    else:
                        cday = last_onday(cday)
                    # 经过这个没报错,就表示数据源是最新的
                if last_date_obj >= last_onday(self.today):  # 昨天数据已出,不需要再预测了
                    print(
                        "no need to predict t-1 value since it has been out for %s"
                        % self.code)
                    self.t1_type = "昨日已出"
                    self.t1value_cache = {
                        last_date.replace("-", ""): last_value
                    }
                    if not return_date:
                        return last_value
                    else:
                        return last_value, last_date
            else:
                yesterday_str = datekey
                fund_price = xu.get_daily(self.fcode)  # 获取国内基金净值
                fund_last = fund_price[fund_price["date"] < date].iloc[-1]
                # 注意实时更新应用 date=None 传入,否则此处无法保证此数据是前天的而不是大前天的,因为没做校验
                # 事实上这里计算的预测是针对 date 之前的最晚数据和之前一日的预测
                last_value = fund_last["close"]
                last_date = fund_last["date"].strftime("%Y-%m-%d")
            self.t1_delta = (1 + evaluate_fluctuation(
                hdict, yesterday_str, lastday=last_date, _check=True) / 100)
            net = last_value * self.t1_delta
            self.t1value_cache[datekey] = net
            self.t1_type = "已计算"
        if not return_date:
            return self.t1value_cache[datekey]
        else:
            return (
                self.t1value_cache[datekey],
                datekey[:4] + "-" + datekey[4:6] + "-" + datekey[6:8],
            )