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 __init__(self, code, bondrate=None, riskfreerate=None, volatility=None, name=None): """ :param code: str. 转债代码,包含 SH 或 SZ 字头 :param bondrate: Optional[float]. 评估所用的债券折现率,默认使用中证企业债对应信用评级对应久期的利率 :param riskfreerate: Optioal[float]. 评估期权价值所用的无风险利率,默认使用国债对应久期的年利率。 :param volatility: Optional[float]. 正股波动性百分点,默认在一个范围浮动加上历史波动率的小幅修正。 :param name: str. 对于历史回测,可以直接提供 str,免得多次 get_rt 获取 name """ # 应该注意到该模型除了当天外,其他时间估计会利用现在的转股价,对于以前下修过转股价的转债历史价值估计有问题 self.code = code self.refbondrate = bondrate self.bondrate = self.refbondrate self.refriskfreerate = riskfreerate self.riskfreerate = self.refriskfreerate self.refvolatility = volatility self.volatility = self.refvolatility self.name = name r = rget("https://www.jisilu.cn/data/convert_bond_detail/" + code[2:]) r.encoding = "utf-8" b = BeautifulSoup(r.text, "lxml") self.rlist = [ float(re.search(r"[\D]*([\d]*.[\d]*)\%", s).group(1)) for s in b.select("td[id=cpn_desc]")[0].string.split("、") ] self.rlist.append(float(b.select("td[id=redeem_price]")[0].string)) self.rlist[-1] -= self.rlist[-2] # 最后一年不含息返多少 self.scode = (b.select("td[class=jisilu_nav]") [0].contents[1].string.split("-")[1].strip()) self.scode = ttjjcode(self.scode) # 标准化股票代码 self.zgj = float(b.select("td[id=convert_price]")[0].string) # 转股价 self.rating = b.select("td[id=rating_cd]")[0].string self.enddate = b.select("td[id=maturity_dt]")[0].string