def get_gz(self): gz = [0, 0] for f in self.funds: east = EastFund(f['fid']) fprice = east.get_gz() if fprice[0] == 0: continue gz[0] = gz[0] + fprice[0] * f['p'] / 100 gz[1] = gz[1] + fprice[1] * f['p'] / 100 for i in range(1, 30): d = datetime.datetime.now() d = datetime.datetime(d.year, d.month, d.day, 0, 0, 0) - datetime.timedelta(days=i) if d in self.xnjz and d in self.price_list: ratio0 = self.xnjz[d][0] / self.price_list[d][0] ratio1 = self.xnjz[d][1] / self.price_list[d][1] break gz[0] = round(gz[0] / ratio0, 4) gz[1] = round(gz[1] / ratio1, 4) return gz
def load_fundprice(self): fr = open(self.record_path, 'r') for line in fr.readlines(): arr = line.strip().split(',') d = datetime.datetime.strptime(arr[1], '%Y-%m-%d') self.price_list[d] = (float(arr[2]), float(arr[2])) fr.close() jz = {} prices = [] for f in self.funds: east = EastFund(f['fid']) fprice = east.load_fundprice() for k in fprice.keys(): if k not in jz: jz[k] = [0, 0] jz[k][0] = jz[k][0] + fprice[k][0] * f['p'] / 100 jz[k][1] = jz[k][1] + fprice[k][1] * f['p'] / 100 for k in jz: jz[k][0] = round(jz[k][0], 4) jz[k][1] = round(jz[k][1], 4) self.xnjz = jz return jz
def __init__(self, index_key): """ 初始化数据结构 """ index_list = { 'hs300': { 'index': {'code': 'SH000300', 'name': u'沪深300', 'vq': 'pe'}, 'fund': {'fid': '100038', 'byear': 2011 }}, 'sh50': { 'index': {'code': 'SH000016', 'name': u'上证50', 'vq': 'pe'}, 'fund': {'fid': '001548', 'byear': 2016 }}, 'zzbonus': { 'index': {'code': 'SH000922', 'name': u'中证红利', 'vq': 'pe'}, 'fund': {'fid': '090010', 'byear': 2011 }}, 'gem': { 'index': {'code': 'SZ399006', 'name': u'创业板', 'vq': 'pe'}, 'fund': {'fid': '003765', 'byear': 2018 }}, 'zz500': { 'index': {'code': 'SH000905', 'name': u'中证500', 'vq': 'pe'}, 'fund': {'fid': '003986', 'byear': 2018 }}, 'zzbank': { 'index': {'code': 'SZ399986', 'name': u'中证银行', 'vq': 'pb'}, 'fund': {'fid': '001594', 'byear': 2016 }}, 'zzhouse': { 'index': {'code': 'SZ399393', 'name': u'国证地产', 'vq': 'pb'}, 'fund': {'fid': '160218', 'byear': 2014 }}, 'zzzq': { 'index': {'code': 'SZ399975', 'name': u'全指证券', 'vq': 'pb'}, 'fund': {'fid': '004069', 'byear': 2018 }}, 'zzxf': { 'index': {'code': 'SH000932', 'name': u'主要消费', 'vq': 'pe'}, 'fund': {'fid': '000248', 'byear': 2016 }}, 'food': { 'index': {'code': 'SZ399396', 'name': u'食品饮料', 'vq': 'pe'}, 'fund': {'fid': '001631', 'byear': 2016 }}, 'zzwine': { 'index': {'code': 'SZ399997', 'name': u'中证白酒', 'vq': 'pe'}, 'fund': {'fid': '161725', 'byear': 2016 }}, 'hkhs': { 'index': {'code': 'HKHSI', 'name': u'香港恒生', 'vq': 'pe'}, 'fund': {'fid': '000948', 'byear': 2016 }}, 'sz60': { 'index': {'code': 'SZ399701', 'name': u'深证基本面60', 'vq': 'pe'}, 'fund': {'fid': '530015', 'byear': 2012 }}, 'yy100': { 'index': {'code': 'SH000978', 'name': u'医药100', 'vq': 'pe'}, 'fund': {'fid': '001550', 'byear': 2016 }}, 'zzyl': { 'index': {'code': 'SZ399989', 'name': u'中证医疗', 'vq': 'pe'}, 'fund': {'fid': '162412', 'byear': 2016 }}, '300value': { 'index': {'code': 'SH000919', 'name': u'300价值', 'vq': 'pe'}, 'fund': {'fid': '310398', 'byear': 2011 }}, 'zz100': { 'index': {'code': 'SH000903', 'name': u'中证100', 'vq': 'pe'}, 'fund': {'fid': '213010', 'byear': 2011 }}, 'base50': { 'index': {'code': 'SH000925', 'name': u'基本面50', 'vq': 'pe'}, 'fund': {'fid': '160716', 'byear': 2011 }}, 'car': { 'index': {'code': 'SZ399417', 'name': u'新能源车', 'vq': 'pe'}, 'fund': {'fid': '501057', 'byear': 2019 }}, } self.index_pbe = {} self.fund_jz = {} self.trade_days = {} self.index = index_list[index_key]['index'] self.fund = index_list[index_key]['fund'] self.fid = self.fund['fid'] self.buylog_path = './buylog.' + self.fid self.dj = Danjuan(self.index['code'], self.index['vq']) self.east = EastFund(self.fid)
class FundValue(): """ 从蛋卷基金获取指数信息。 Attributes: index_list: 字典,保存所有指数和对应基金的信息,key为助记符 index_pbe: 字典,保存指数的历史pe/pb,{datetime: pe} fund_jz: 字典, 保存基金的历史价格,{datetime: (nav, nav2)} fid 为基金编码,nav为基金净值,nav2为累计净值。 trade_days: 所有交易日的集合,各个基金可能不同,每计算一个基金则需要取一次交集。 east: 对象,根据 fid 和东方财富 api 获取基金净值信息。 dj: 对象,根据 index_code 和蛋卷 api 获取指数估值信息。 """ def __init__(self, index_key): """ 初始化数据结构 """ index_list = { 'hs300': { 'index': {'code': 'SH000300', 'name': u'沪深300', 'vq': 'pe'}, 'fund': {'fid': '100038', 'byear': 2011 }}, 'sh50': { 'index': {'code': 'SH000016', 'name': u'上证50', 'vq': 'pe'}, 'fund': {'fid': '001548', 'byear': 2016 }}, 'zzbonus': { 'index': {'code': 'SH000922', 'name': u'中证红利', 'vq': 'pe'}, 'fund': {'fid': '090010', 'byear': 2011 }}, 'gem': { 'index': {'code': 'SZ399006', 'name': u'创业板', 'vq': 'pe'}, 'fund': {'fid': '003765', 'byear': 2018 }}, 'zz500': { 'index': {'code': 'SH000905', 'name': u'中证500', 'vq': 'pe'}, 'fund': {'fid': '003986', 'byear': 2018 }}, 'zzbank': { 'index': {'code': 'SZ399986', 'name': u'中证银行', 'vq': 'pb'}, 'fund': {'fid': '001594', 'byear': 2016 }}, 'zzhouse': { 'index': {'code': 'SZ399393', 'name': u'国证地产', 'vq': 'pb'}, 'fund': {'fid': '160218', 'byear': 2014 }}, 'zzzq': { 'index': {'code': 'SZ399975', 'name': u'全指证券', 'vq': 'pb'}, 'fund': {'fid': '004069', 'byear': 2018 }}, 'zzxf': { 'index': {'code': 'SH000932', 'name': u'主要消费', 'vq': 'pe'}, 'fund': {'fid': '000248', 'byear': 2016 }}, 'food': { 'index': {'code': 'SZ399396', 'name': u'食品饮料', 'vq': 'pe'}, 'fund': {'fid': '001631', 'byear': 2016 }}, 'zzwine': { 'index': {'code': 'SZ399997', 'name': u'中证白酒', 'vq': 'pe'}, 'fund': {'fid': '161725', 'byear': 2016 }}, 'hkhs': { 'index': {'code': 'HKHSI', 'name': u'香港恒生', 'vq': 'pe'}, 'fund': {'fid': '000948', 'byear': 2016 }}, 'sz60': { 'index': {'code': 'SZ399701', 'name': u'深证基本面60', 'vq': 'pe'}, 'fund': {'fid': '530015', 'byear': 2012 }}, 'yy100': { 'index': {'code': 'SH000978', 'name': u'医药100', 'vq': 'pe'}, 'fund': {'fid': '001550', 'byear': 2016 }}, 'zzyl': { 'index': {'code': 'SZ399989', 'name': u'中证医疗', 'vq': 'pe'}, 'fund': {'fid': '162412', 'byear': 2016 }}, '300value': { 'index': {'code': 'SH000919', 'name': u'300价值', 'vq': 'pe'}, 'fund': {'fid': '310398', 'byear': 2011 }}, 'zz100': { 'index': {'code': 'SH000903', 'name': u'中证100', 'vq': 'pe'}, 'fund': {'fid': '213010', 'byear': 2011 }}, 'base50': { 'index': {'code': 'SH000925', 'name': u'基本面50', 'vq': 'pe'}, 'fund': {'fid': '160716', 'byear': 2011 }}, 'car': { 'index': {'code': 'SZ399417', 'name': u'新能源车', 'vq': 'pe'}, 'fund': {'fid': '501057', 'byear': 2019 }}, } self.index_pbe = {} self.fund_jz = {} self.trade_days = {} self.index = index_list[index_key]['index'] self.fund = index_list[index_key]['fund'] self.fid = self.fund['fid'] self.buylog_path = './buylog.' + self.fid self.dj = Danjuan(self.index['code'], self.index['vq']) self.east = EastFund(self.fid) def init_index_pbe(self, time='all'): """ 获取pe/pb的通用接口,time可以为1y, 3y """ self.index_pbe = self.dj.init_pbe(time) if self.trade_days == {}: self.trade_days = set(self.index_pbe.keys()) else: self.trade_days = self.trade_days & set(self.index_pbe.keys()) return self.index_pbe def init_fund_jz(self): """ 获取指定基金的价格,可以获取当前净值和累计净值 """ self.fund_jz = self.east.load_fundprice() if self.trade_days == {}: self.trade_days = set(self.fund_jz.keys()) else: self.trade_days = self.trade_days & set(self.fund_jz.keys()) return self.fund_jz def get_weight_pe(self, cur_pe, w30, n=2): """ 获取 pe 权重,以30水位线做基准,超过30水位线则不买。否则越低越买。 经回测上证50,此参数对购买影响不大。 """ if n < 0: return 1 if cur_pe > w30 or cur_pe <= 0: return 0 # 加强 pe 的权重,越低越买 return (w30/cur_pe) ** n def get_weight_price(self, cur_price, wprice, n=4): """ 获取 price 权重,实际采用前365天的平均价格。 超过该价格,则不买,否则多买。 """ if cur_price > wprice or cur_price <= 0: return 0 # 加强 price 的权重,越低越买 return (wprice/cur_price) ** n def get_buylog_water(self, buy_log): """ 长期购买一段时间,计算当前购买的水位线。利用水位线进一步提高购买比例,事实证明没用。 """ if len(buy_log) <= 50: return (0, len(buy_log)) else: fprice = buy_log[-1] if fprice == 0: return (0, len(buy_log)) sorted_log = sorted(buy_log) weight = 1.0 * sorted_log.index(fprice) / len(sorted_log) return (round(weight, 4), len(buy_log)) def get_buylog(self, end_date=None, days=365*5, n_pe=2, n_price=4, base=100): buylog = self.load_buylog(end_date, n_pe, n_price, base) buylist = [] if end_date is None: end_date = datetime.datetime.combine(datetime.date.today(), datetime.datetime.min.time()) - datetime.timedelta(days=1) begin_date = end_date - datetime.timedelta(days=days) dt = begin_date for i in range(days + 1): dt = begin_date + datetime.timedelta(days=i) if dt not in self.trade_days or dt not in buylog: continue if buylog[dt]['capital'] > 0: buylist.append(buylog[dt]['capital']) return buylist def load_buylog(self, end_date=None, n_pe=2, n_price=4, base=100): buylog = {} if end_date is None: n = datetime.datetime.now() - datetime.timedelta(days=1) end_date = datetime.datetime(n.year, n.month, n.day, 0, 0, 0) max_dt = datetime.datetime(1970, 1, 1) try: fr = open(self.buylog_path, 'r') for line in fr.readlines(): arr = line.strip().split(',') d = datetime.datetime.strptime(arr[1], '%Y-%m-%d') max_dt = d if d > max_dt else max_dt buylog[d] = {} buylog[d]['capital'] = int(arr[2]) buylog[d]['amount'] = float(arr[3]) fr.close() if end_date <= max_dt: return buylog else: print('Need fetch new buylog') newlog = {} begin_date = max_dt for i in range((end_date - begin_date).days + 1): dt = begin_date + datetime.timedelta(days=i) res = self.buy_1day(dt, n_pe, n_price, base) newlog[dt] = { 'capital': res['capital'], 'amount': res['amount'] } buylog = self.save_buylog(newlog) print(newlog) return buylog except Exception as e: print('First fetch buylog') newlog = {} begin_date = datetime.datetime(self.fund['byear'], 1, 1, 0, 0, 0) for i in range((end_date - begin_date).days + 1): dt = begin_date + datetime.timedelta(days=i) res = self.buy_1day(dt, n_pe, n_price, base) newlog[dt] = { 'capital': res['capital'], 'amount': res['amount'] } buylog = self.save_buylog(newlog) return buylog def save_buylog(self, newlog): buylog = {} try: fr = open(self.buylog_path, 'r') for line in fr.readlines(): arr = line.strip().split(',') d = datetime.datetime.strptime(arr[1], '%Y-%m-%d') buylog[d] = {} buylog[d]['capital'] = int(arr[2]) buylog[d]['amount'] = float(arr[3]) fr.close() buylog.update(newlog) with open(self.buylog_path, 'w') as fw: for d in sorted(buylog.keys()): result = [] result.append(self.fid) result.append(d.strftime('%Y-%m-%d')) result.append(str(buylog[d]['capital'])) result.append(str(buylog[d]['amount'])) line = ','.join(result) fw.write(line) fw.write('\n') return buylog except Exception as e: print('First create buylog') with open(self.buylog_path, 'w') as fw: for d in sorted(newlog.keys()): result = [] result.append(self.fid) result.append(d.strftime('%Y-%m-%d')) result.append(str(newlog[d]['capital'])) result.append(str(newlog[d]['amount'])) line = ','.join(result) fw.write(line) fw.write('\n') return newlog def buy_1day(self, dt=None, n_pe=2, n_price=4, base=100): """ 对指定的某一天进行购买,用于测试,默认买100块钱。 dt is None,表示今天购买,否则校验是否为交易日。 """ res = { 'is_today': False, 'fid': self.fid, 'index_name': self.index['name'] } if len(self.trade_days) < 600: res['capital'] = 0 res['amount'] = 0 return res # 非今天申购,且非交易日,则不予购买。 if dt is not None and dt not in self.trade_days: res['capital'] = 0 res['amount'] = 0 return res # 如果当天购买,则采用实时最新估值。 if dt is None: res['is_today'] = True dt = datetime.datetime.combine(datetime.date.today(), datetime.datetime.min.time()) res['price'] = self.east.get_gz() (real_price, cur_price) = (res['price'][0], res['price'][1]) # 如果取估值有问题,可能是假日,不申购。 if real_price < 0: res['capital'] = 0 res['amount'] = 0 # 否则采用当天的净值来计算 else: res['price'] = self.fund_jz.get(dt) (real_price, cur_price) = (res['price'][0], res['price'][1]) # 计算 pe 权重,由于 pe 无法预估,因此采用前一天的 pe 计算 # 考虑到波动周期性,pe 标准改用最近5年的30水位线(好买评估依据),也可以采用10年,保证指数经历一轮牛熊(蛋卷评估依据) # 不宜采用1-2年方案,受行业周期影响较大。 res['pe30'] = self.dj.get_pbe_nwater(dt, 30, 365*5) res['pe50'] = self.dj.get_pbe_nwater(dt, 50, 365*5) res['pe70'] = self.dj.get_pbe_nwater(dt, 70, 365*5) res['pe90'] = self.dj.get_pbe_nwater(dt, 90, 365*5) for i in range(30): res['pe'] = self.index_pbe.get(dt - datetime.timedelta(days=i), -1) if res['pe'] > 0: break weight_pe = self.get_weight_pe(res['pe'], res['pe30'], n_pe) # 为更安全,price 标准采用最近1年的均值,时间过长,可能无法申购。也可以考虑采用最近1年,2年均值的最小值。 # res['avg_price'] = min(self.get_avg_price(dt)[1], self.get_avg_price(dt, 50, 365*2)[1]) res['avg_price'] = self.east.get_avg_price(dt) weight_price = self.get_weight_price(cur_price, res['avg_price'][1], n_price) weight = weight_pe * weight_price res['capital'] = int(math.ceil(base*weight)) # 以累计净值计算购买数量,不准确。 res['amount'] = 0 if cur_price > 0: res['amount'] = round(res['capital'] / cur_price, 2) del(res['is_today']) res['price'] = list(res['price']) res['avg_price'] = list(res['avg_price']) res['price'][0] = round(res['price'][0], 4) res['price'][1] = round(res['price'][1], 4) res['avg_price'][0] = round(res['avg_price'][0], 4) res['avg_price'][1] = round(res['avg_price'][1], 4) return res def buy_longtime(self, begin_date, end_date, n_pe=2, n_price=4, base=100): """ 长期购买一段时间,用于测试。默认买100块钱。以最后一天累计净值为基准计算盈利。 """ fid = self.fid buylog = self.load_buylog(end_date) b_capital = 0 b_amount = 0 # 获取最高盈利点 maxg = [0, begin_date] for i in range((end_date - begin_date).days + 1): dt = begin_date + datetime.timedelta(days=i) if dt not in self.trade_days: continue # 按购买金额的水位线加权购买,效果略有提升。 # 可参考水位线进行一次性投入,不宜作为长期购买指标。 # buylist = self.get_buylog(dt) # buylist.append(buylog[dt]['capital']) # buyw = int(self.get_buylog_water(buylist)[0] * 100) # if buyw <= 40: # weight = 0 # elif 40 < buyw and buyw < 70: # weight = 2 # elif buyw >= 70: # weight = 4 weight = 1 b_capital = b_capital + buylog[dt]['capital'] * weight b_amount = b_amount + buylog[dt]['amount'] * weight fprice = float(self.fund_jz[dt][1]) if b_capital > 0: g = round((fprice * b_amount - b_capital) / b_capital * 100, 2) if g > maxg[0]: maxg[0] = g maxg[1] = dt.strftime('%Y-%m-%d') win = 0 if b_capital == 0 else ( b_amount * fprice - b_capital) * 100 / b_capital win = str(round(win, 2)) + '%' avg_price = 0 if b_amount == 0 else (b_capital / b_amount) return (round(b_capital, 2), round(b_amount, 2), maxg, win, round(avg_price, 4), fprice)
def __init__(self, fid=''): EastFund.__init__(self, fid) self.xnjz = {} if fid == 'njbqg': self.funds = [ { 'fid': '001975', 'p': 12 }, { 'fid': '001714', 'p': 11 }, { 'fid': '001764', 'p': 11 }, { 'fid': '001856', 'p': 11 }, { 'fid': '005354', 'p': 11 }, { 'fid': '009277', 'p': 11 }, { 'fid': '160133', 'p': 11 }, { 'fid': '163415', 'p': 11 }, { 'fid': '450009', 'p': 11 }, ] elif fid == 'njbcz': self.funds = [ { 'fid': '003328', 'p': 15 }, { 'fid': '003847', 'p': 15 }, { 'fid': '001764', 'p': 10 }, { 'fid': '001975', 'p': 10 }, { 'fid': '004241', 'p': 10 }, { 'fid': '005354', 'p': 10 }, { 'fid': '009277', 'p': 10 }, { 'fid': '163415', 'p': 10 }, { 'fid': '450009', 'p': 10 }, ] elif fid == 'wwxf': self.funds = [ { 'fid': '519718', 'p': 12.9 }, { 'fid': '164902', 'p': 11.92 }, { 'fid': '519723', 'p': 11.92 }, { 'fid': '519782', 'p': 9.91 }, { 'fid': '006793', 'p': 6.4 }, { 'fid': '008204', 'p': 5.96 }, { 'fid': '519755', 'p': 12.78 }, { 'fid': '519752', 'p': 10.41 }, { 'fid': '519738', 'p': 9.7 }, { 'fid': '519772', 'p': 3.62 }, { 'fid': '519704', 'p': 2.68 }, { 'fid': '005001', 'p': 1.8 }, ] else: self.funds = []
fv.init_index_pbe() fv.init_fund_jz() today = fv.buy_1day(base=base) buylog = fv.get_buylog() buylog.append(today['capital']) today['buy_water'] = fv.get_buylog_water(buylog) if today['capital'] > 0: cmd = 'echo {},{},{} >>~/buy_fund_log.csv'.format( datetime.datetime.now().strftime('%Y-%m-%d'), fv.fid, today['capital']) os.system(cmd) (sub, con) = create_email(today) subject += sub content += con for i in ('000215',): ef = EastFund(i) ef.load_fundprice() today = ef.buy_1day2(base=base) buylog = ef.get_buylog() buylog.append(today['capital']) today['buy_water'] = ef.get_buylog_water(buylog) today['fid'] = i (sub, con) = create_1fund_email(today) subject += sub content += con for i in ('qg',): n = Njb('qg') n.init_xnjz() today = n.buy_1day(n=10, base=base) buylog = n.get_buylog()