请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  数理科学 帖子:3353613 新帖:3

ETF量化投资指数估值计算源代码

螺罗丝发表于:5 月 10 日 07:44回复(1)

20180620 更新:
ETF量化投资v2.0

源代码公布于知识星球

新版代码计算速度提升两倍以上,并同时计算了指数的加权、等权、中位数、算数平均四种PE和PB指标,还添加了综合估值计算方法。

========================================


将“ETF量化投资”指数估值计算的源代码公开,代码本来是自己用的,功能基本满足自己的需求,但代码没有规范和详细注释,望各位见谅。

每天的指数PE、PB的计算需要全部成分股,非常耗时,因而使用了增量更新。
首次运行还是非常耗时的,因而分享的代码结果中只计算了'000990.XSHG','000991.XSHG', 其他指数,去掉注释即可。
对于指数的波动率,用了 Benchmark Volatility(基准波动率)#风险指标 ,各位自己补充。

import numpy as npimport pandas as pdimport matplotlib.pyplot as pltimport time, requests, os, xlrd, sysfrom datetime import timedelta,datepd.set_option('precision',3)def calc_state(data):if data < 10.0:return u'极度低估'elif 10 <= data  and data < 20:return u'低估'elif 20 <= data  and data < 40:return u'正常偏低'elif 40 <= data  and data < 60:return u'正常'elif 60 <= data  and data < 80:return u'正常偏高'elif 80 <= data  and data < 90:return u'高估'elif 90 <= data:return u'极度高估'def convert_code(code):if code.endswith('XSHG'):return 'sh' + code[0:6]elif code.endswith('XSHE'):return 'sz' + code[0:6]    def get_index_pe_pb_date(code,date):'''指定日期的指数PE_PB(等权重)'''stocks = get_index_stocks(code, date)q = query(valuation).filter(valuation.code.in_(stocks))df = get_fundamentals(q, date)if len(df)>0:pe = len(df)/sum([1/p if p>0 else 0 for p in df.pe_ratio])pb = len(df)/sum([1/p if p>0 else 0 for p in df.pb_ratio])return (round(pe,2), round(pb,2))else:return float('NaN')def get_index_pe_pb(code, start_date=None, end_date=None):'''指数历史PE_PB'''if start_date is None:start_date = get_security_info(code).start_dateif start_date < date(2005,01,04): #只计算2005年以来的数据start_date = date(2005,01,04)if end_date is None:end_date = pd.datetime.today() - timedelta(1)x = get_price(code, start_date=start_date, end_date=end_date, frequency='daily', fields='close')date_list = x.index.tolist()#     print date_listpe_list = []pb_list = []for d in date_list: #交易日pe_pb = get_index_pe_pb_date(code,d)pe_list.append(pe_pb[0])pb_list.append(pe_pb[1])df = pd.DataFrame({'PE': pd.Series(pe_list, index=date_list),'PB': pd.Series(pb_list, index=date_list)})return dfdef get_hk_data(data_root='./'):'''获取港股数据'''for idx in ['hsi', 'hscei']:    url = '''http://sc.hangseng.com/gb/www.hsi.com.hk/HSI-Net/static/revamp/contents/\en/dl_centre/reports_stat/monthly/pe/%s.xls''' % (idx)data_path = '%s%s.xls' % (data_root,idx)ret = requests.get(url)if ret.ok is True:with open(data_path, "wb") as f:f.write(ret.content)print 'write data: %s' % (data_path)for i in range(10):day_str = (date.today() - timedelta(1) - timedelta(i)).strftime('%d%b%y')url = 'http://sc.hangseng.com/gb/www.hsi.com.hk/HSI-Net/static/revamp/contents/en/indexes\/report/%s/idx_%s.csv' % (idx, day_str[1:] if day_str.startswith('0') else day_str)print urlret = requests.get(url)if ret.ok is True:data_path = '%s_daily.csv' % (idx)with open(data_path, "wb") as f:                    f.write(ret.content[2:].decode("utf16").encode('utf8'))print 'write data: %s' % (data_path)breakdef read_hk_data(idx, daily=True,data_root='./'):'''读取港股数据'''data_path = '%s%s.xls' % (data_root,idx)xlr_f = xlrd.open_workbook(data_path)table = xlr_f.sheet_by_index(0)day_idx = table.col_values(0)[13:]pe = table.col_values(1)[13:]pe = [item for item in pe if item is not u'']day_idx = [xlrd.xldate.xldate_as_datetime(item, datemode=0) for item in day_idx if item is not u'']df = pd.DataFrame(pe, index=day_idx, columns=['PE'])if daily is True:if idx == 'hscei':hscei_data = file('hscei_daily.csv', 'r').read().splitlines()[-1].replace('\"','').split('\t') #PE:[-1]df = df.append(pd.DataFrame(float(hscei_data[-1]),index=[pd.Timestamp(hscei_data[0])],columns=['PE']))elif idx == 'hsi':hsi_data = file('hsi_daily.csv', 'r').read().splitlines()[2].replace('\"','').split('\t') #PE:[9]df = df.append(pd.DataFrame(float(hsi_data[9]),index=[pd.Timestamp(hsi_data[0])],columns=['PE']))return df[df.iloc[-1].name.date()-timedelta(365 * 15) : ] # 只用最近十五年数据def pe_pb_analysis(index_list=['000300.XSHG','000905.XSHG'],data_root='./'):'''PE_PB分析'''all_index = get_all_securities(['index'])hk_idx_name = {'hscei':u'国企指数','hsi':u'恒生指数'}pe_results = []pe_code_list = []pb_results = []pb_code_list = []#沪深for code in index_list:data_path = '%s%s_pe_pb.csv'%(data_root,convert_code(code))index_name = all_index.ix[code].display_namedf_pe_pb = pd.DataFrame.from_csv(data_path)df_pe_pb = df_pe_pb[df_pe_pb.iloc[-1].name.date() - timedelta(365*10):] #最长十年的数据if len(df_pe_pb)<250*3: #每年250个交易日,小于3年不具有参考价值#                 print code, 'samples:', len(df_pe_pb), index_namecontinuepe_ratio = len(df_pe_pb.PE[df_pe_pb.PE<df_pe_pb.iloc[-1].PE])/float(len(df_pe_pb.PE))*100pb_ratio = len(df_pe_pb.PB[df_pe_pb.PB<df_pe_pb.iloc[-1].PB])/float(len(df_pe_pb.PB))*100        pe_results.append([index_name, df_pe_pb.iloc[-1].PE, '%.2f'%pe_ratio, calc_state(pe_ratio),                           min(df_pe_pb.PE), max(df_pe_pb.PE), '%.2f'%median(df_pe_pb.PE), '%.2f'%std(df_pe_pb.PE),                           df_pe_pb.iloc[0].name.date()])pb_results.append([index_name, df_pe_pb.iloc[-1].PB, '%.2f'%pb_ratio, calc_state(pb_ratio),                           min(df_pe_pb.PB), max(df_pe_pb.PB), '%.2f'%median(df_pe_pb.PB),'%.2f'%std(df_pe_pb.PB),                           df_pe_pb.iloc[0].name.date()])pe_code_list.append(code)pb_code_list.append(code)#港股for code in ['hsi', 'hscei']:df_pe = read_hk_data(code)pe_ratio = len(df_pe.PE[df_pe.PE<df_pe.iloc[-1].PE])/float(len(df_pe.PE))*100pe_results.append([hk_idx_name[code], df_pe.iloc[-1].PE, '%.2f'%pe_ratio, calc_state(pe_ratio), 
                           min(df_pe.PE), max(df_pe.PE),'%.2f'%median(df_pe.PE),'%.2f'%std(df_pe.PE), 
                           df_pe.iloc[0].name.date()])pe_code_list.append(code.upper())#     print '估值日期: ', df_pe_pb.iloc[-1].name.date()date_str = df_pe_pb.iloc[-1].name.date().strftime('%Y%m%d')pe_columns=[u'名称', u'当前PE', u'百分位(%)', u'估值状态', u'最小', u'最大', u'中位数', u'标准差', u'起始日期']pe_df = pd.DataFrame(data=pe_results,index=pe_code_list,columns=pe_columns)pe_df.index = pe_df[u'名称']del pe_df[u'名称']pe_df.index.name = date_strpb_columns=[u'名称', u'当前PB', u'百分位(%)', u'估值状态', u'最小', u'最大', u'中位数', u'标准差', u'起始日期']pb_df = pd.DataFrame(data=pb_results,index=pb_code_list,columns=pb_columns)pb_df.index = pb_df[u'名称']del pb_df[u'名称']pb_df.index.name = date_strreturn (pe_df.sort([u'百分位(%)'],ascending=True), pb_df.sort([u'百分位(%)'],ascending=True))def get_hs_data(index_list,data_root='./'):'''增量更新沪深指数估值数据'''for code in index_list:print u'正在计算:', codedata_path = '%s%s_pe_pb.csv'%(data_root,convert_code(code))if os.path.exists(data_path):#增量更新df_pe_pb = pd.DataFrame.from_csv(data_path)start_date = df_pe_pb.iloc[-1].name + timedelta(1)df_pe_pb = pd.concat([df_pe_pb, get_index_pe_pb(code, start_date)]) else:#初次计算print 'init'df_pe_pb = get_index_pe_pb(code)df_pe_pb.to_csv(data_path)index_list =[    #     '000016.XSHG','000300.XSHG','000905.XSHG','000852.XSHG','399006.XSHE','399005.XSHE',#宽指数#     '000001.XSHG','399001.XSHE','000902.XSHG','000985.XSHG',#大盘指数#     '000015.XSHG','000922.XSHG','000827.XSHG','000978.XSHG',#策略指数'000990.XSHG','000991.XSHG',#全指医药消费#     '399967.XSHE','399975.XSHE',#中证军工、证券]get_hk_data() #港股数据get_hs_data(index_list) #沪深数据(pe_df, pb_df) = pe_pb_analysis(index_list)print pe_df.index.namepe_df.index.name = Nonepe_df
write data: ./hsi.xls
http://sc.hangseng.com/gb/www.hsi.com.hk/HSI-Net/static/revamp/contents/en/indexes/report/hsi/idx_25Jan17.csv
write data: hsi_daily.csv
write data: ./hscei.xls
http://sc.hangseng.com/gb/www.hsi.com.hk/HSI-Net/static/revamp/contents/en/indexes/report/hscei/idx_25Jan17.csv
write data: hscei_daily.csv
正在计算: 000990.XSHG
正在计算: 000991.XSHG
20170125

当前PE百分位(%)估值状态最小最大中位数标准差起始日期
国企指数7.6712.71低估5.8036.1710.885.352002-01-31
恒生指数11.3732.60正常偏低7.6124.2613.913.552002-01-31
全指消费44.6352.47正常28.7785.6643.559.892011-08-02
全指医药44.5453.82正常28.1388.2443.509.662011-08-02
pb_df.index.name = Nonepb_df

当前PB百分位(%)估值状态最小最大中位数标准差起始日期
全指医药4.2958.17正常2.758.324.030.872011-08-02
全指消费3.7368.97正常偏高2.406.463.370.642011-08-02

全部回复

0/140

量化课程

    移动端课程