def get_market(code): """ 非常粗糙的通过代码获取交易市场的函数 :param code: :return: """ trans = { "USD": "US", "GBP": "UK", "HKD": "HK", "CNY": "CN", "CHF": "CH", "JPY": "JP", "EUR": "DE", "AUD": "AU", "INR": "IN", "SGD": "SG", } try: if code in market_info: return market_info[code] elif code.startswith("CNY/") or code.endswith("/CNY"): return "CM" # china money 中间价市场标记 elif code.startswith("HK") and code[2:].isdigit(): return "HK" market = get_rt(code)["market"] if market is None: market = get_currency(code) market = trans.get(market, market) except (TypeError, AttributeError, ValueError, IndexError): market = "CN" return market
def get_currency(code): """ 通过代码获取计价货币的函数 :param code: :return: """ # 强制需要自带 cache,否则在回测 table 是,info 里没有的代码将很灾难。。。 # only works for HKD JPY USD GBP CNY EUR, not very general when data source gets diverse more try: if code in currency_info: return currency_info[code] elif (code.startswith("F") or code.startswith("M")) and code[1:].isdigit(): return "CNY" currency = get_rt(code)["currency"] if currency is None: currency = "CNY" elif currency == "JPY": currency = "100JPY" except (TypeError, AttributeError, ValueError): if code.startswith("FT-") and len(code.split(":")) > 2: currency = code.split(":")[-1] else: currency = "CNY" return currency
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()
def get_currency(code): """ 通过代码获取计价货币的函数 :param code: :return: """ # 强制需要自带 cache,否则在回测 table 时,info 里没有的代码将很灾难。。。 # only works for HKD JPY USD GBP CNY EUR, not very general when data source gets diverse more try: if code in currency_info: return currency_info[code] elif (code.startswith("F") or code.startswith("M")) and code[1:].isdigit(): return "CNY" elif code.startswith("FT-") and len(code.split(":")) > 2: # be careful! FT-ABC:IOM has no currency information! return code.split(":")[-1] elif code.startswith("HK") and code[2:].isdigit(): return "HKD" currency = get_rt(code)["currency"] if currency is None: currency = "CNY" elif currency == "JPY": currency = "100JPY" except (TypeError, AttributeError, ValueError): logger.warning("set currency of %s as default CNY" % code) currency = "CNY" return currency
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
def get_market(code): """ 非常粗糙的通过代码获取交易市场的函数 :param code: :return: """ trans = { "USD": "US", "GBP": "UK", "HKD": "HK", "CNY": "CN", "CHF": "CH", "JPY": "JP", "EUR": "DE", "AUD": "AU", "INR": "IN", "SGD": "SG", } try: if code in market_info: return market_info[code] market = get_rt(code)["market"] if market is None: market = get_currency(code) market = trans.get(market, market) except (TypeError, AttributeError, ValueError, IndexError): market = "CN" return market
def get_stock_holdings( self, year=None, season=None, date=yesterdayobj(), threhold=100 ): """ 获取整个基金组合的底层股票持仓总和和细节,组合穿透 :param year: 基于的基金季报年份 :param season: 基于的基金季报季度 :param date: 默认昨天 :param threhold: 默认100。小于100元的底层股票将不在最后的结果中展示 :return: pd.DataFrame column: name, code, value, ratio """ d = {} if year is None or season is None: rd = convert_date(date) - pd.Timedelta(days=120) if not year: year = rd.year if not season: season = int((rd.month - 0.1) / 3) + 1 logger.debug("use %s, %s for fund report" % (year, season)) for f in self.fundtradeobj: if isinstance(f, itrade): if f.get_type() == "股票": code = f.code elif f.get_type() == "场内基金": code = f.code[2:] else: continue else: code = f.code value = f.briefdailyreport(date).get("currentvalue", 0) if value > 0: if code.startswith("SH") or code.startswith("SZ"): stock = code d[stock] = d.get(stock, 0) + value elif code == "mf": continue else: df = get_fund_holdings(code, year, season) if df is None: continue for _, row in df.iterrows(): stock = row["code"] stock = ttjjcode(stock) d[stock] = d.get(stock, 0) + row["ratio"] / 100 * value # print("%s has %s contribution from %s" %(stock, row["ratio"] / 100 * value, f.name)) l = [] for code, value in sorted(d.items(), key=lambda item: -item[1]): if value >= threhold: try: name = get_rt(code)["name"] except: name = code l.append([name, code, value]) fdf = pd.DataFrame(l, columns=["name", "code", "value"]) fdf["ratio"] = fdf["value"] / fdf["value"].sum() return fdf
def get_t1_rate(self, date=None, return_date=True): t1v, d = self.get_t1(date=date, return_date=True) cp = get_rt(self.code)["current"] r = (cp / t1v - 1) * 100 if return_date: return r, d else: return r
def get_t0_rate(self, percent=False, return_date=True): t0v, d = self.get_t0(percent=percent, return_date=True) cp = get_rt(self.code)["current"] r = (cp / t0v - 1) * 100 if return_date: return r, d else: return r
def get_t0_rate(self, percent=False, return_date=True): iopv = self.get_t0(percent=False, return_date=False) rtv = get_rt(self.code)["current"] r = (rtv / iopv - 1) * 100 if return_date: return r, self.today.strftime("%Y-%m-%d") else: return r
def get_t0(self, percent=False, return_date=True): """ 获取当日实时净值估计, 该接口每日凌晨到美股收盘(早晨),不保证自洽和可用 :param percent: bool, default False。现在有两种实时的预测处理逻辑。若 percent 是 True,则将 t0dict 的 每个持仓标的的今日涨跌幅进行估算,若为 False,则将标的现价和标的对应指数昨日收盘价的比例作为涨跌幅估算。不推荐使用 percent=True. :param return_date: bool, default True. return tuple, the second one is date in the format %Y%m%d :return: float """ if not self.t0dict: raise ValueError("Please provide t0dict for prediction") t1value = self.get_t1(date=None, return_date=False) t = 0 n = 0 today_str = self.today.strftime("%Y%m%d") for k, v in self.t0dict.items(): if not isinstance(v, dict): v = {"weight": v} if len(k.split("~")) > 1 and k.split("~")[-1].isdigit(): # 为了持仓中可以同标的多次出现的 workaround k = k.split("~")[0] w = v["weight"] shift = v.get("time", None) base = v.get("base", None) 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: if k in futures_info and not base: kf = futures_info[k] elif not base: kf = k[:-8] # k + "-futures" else: kf = base try: basev = self._base_value(kf, shift) except Exception as e: kf = get_alt(kf) if not kf: raise e else: basev = self._base_value(kf, shift) c = w / 100 * r["current"] / basev currency_code = get_currency_code(k) if currency_code: c = c * daily_increment(currency_code, today_str) # TODO: 中间价未更新,但实时数据不检查问题也不大 n += c n += (100 - t) / 100 t0value = n * t1value self.t0_delta = n if not return_date: return t0value else: return t0value, self.today.strftime("%Y-%m-%d")
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
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()
def get_t1(self, return_date=True): """ 获取昨日基金净值 :return: """ if not self.t1value_cache: last_r = get_rt(self.fcode) last_value, last_date = last_r["current"], last_r["time"] self.t1value_cache = (last_value, last_date) if return_date: return self.t1value_cache else: return self.t1value_cache[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")
def get_t2(self, return_date=True): """ 返回最新的已公布基金净值,注意这里严格按照最新公布,不一定是前两个交易日,可以更新,但更老会报错 DateMismatch :param return_date: :return: if return_date is True, tuple (value, %Y-%m-%d) """ if not self.t2value_cache: last_r = get_rt(self.fcode) last_value, last_date = last_r["current"], last_r["time"] self.t2value_cache = (last_value, last_date) if return_date: return self.t2value_cache else: return self.t2value_cache[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"]) self._arrange() if not name: try: self.name = get_rt(code)["name"] except: self.name = code
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("peb-" + code, start=start, end=end) self.name = get_rt(code)["name"] self.ratio = 1 self.title = "个股" self._gen_percentile()
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)