目录

1. 指标介绍

1.1 定义

1.2 使用方法

2. 案例演示

2.1 获取数据

2.2 数据转换

2.3 计算CPR值

2.4 完整代码

3. 指标评价

3.1 指标优点

3.2 指标缺点

免责声明:本文由作者参考相关资料,并结合自身实践和思考独立完成,对全文内容的准确性、完整性或可靠性不作任何保证。同时,文中提及的基金仅作为举例使用,不构成推荐;文中所有观点均不构成任何投资建议。请读者仔细阅读本声明,若读者阅读此文章,默认知晓此声明。

1. 指标介绍

1.1 定义

交叉积比率主要用来评价基金自身的业绩持续性,其主要思路如下:

将评价期分为等长区间,以区间收益率作为评价指标进行排序,将每一区间收益率位于同类基金中位数以上的基金定义为赢家“W”,将每一区间收益率位于同类基金中位数以下的基金定义为输家“L”,统计连续两期对应的组合编码评级,然后依次滚动(例如基金在连续6个周期长度里的评级为“WLLWWL”,对应的组合编码评级为“WL,LL,LW,WW,WL”)统计各组合评级的出现次数,计算出交叉积比率(后续简称为CPR)。

交叉积比率公式为: CPR = (WW*LL)/(WL*LW)

    WW表示的对应组合评级出现的次数(WL,LW,LL依次类推)因为可能存在WL或者LW为0的情况,因此当二者出现为0的情况时,将其取值为1。

1.2 使用方法

关于CPR的数值解释如下:

CPR值所处区间 情形 结论
[0,1) WW和LL的数量占绝对劣势 不存在业绩持续性
[1,2) WW和LL的数量相对占优 业绩持续性不显著
[2,3) WW和LL的数量占优 具备一定的业绩持续性
>3 WW和LL的数量占绝对优势 业绩持续性显著

在计算CPR值时,所选样本的同类基金数量越多,其结果越可靠(基金分类,举例如偏股型,混合型,灵活配置型等);所统计的周期不能低于6个(例如,使用的日度收益率数据不得低于6个连续交易日)。另外,CPR值越高仅能说明过去业绩相对持续性更强(可理解强的持续强,弱的持续弱),并不能代表过去基金业绩更好。

2. 案例演示

2.1 获取数据

本文使用akshare开源api,选择6只指数型ETF为例,时间区间为20221201--20221230,进行后续的计算演示。

首先,使用一个自定义函数获取单只ETF对应的涨跌幅数据,代码如下:

import pandas as pd
import akshare as akdef get_one_etf_sum_nav_return(fund_code, start_day, end_day):'''获取单只ETF场内基金累计净值的涨跌幅----------fund_code:str,基金代码,格式如'510500'start_day:str,开始日期,格式如'2022-12-01'end_day:str,结束日期,格式如'2022-12-01'Returns:dataFream,第一列为日期,第二列为涨跌幅-------'''data = ak.fund_etf_fund_info_em(fund_code)data['date'] = data['净值日期'].apply(lambda x: str(x))new_data = data.loc[(data['date'] >= start_day) & (data['date'] <= end_day)]out_data = pd.DataFrame({'date': new_data['date'], fund_code: new_data['日增长率'] / 100})return out_dataif __name__ == '__main__':start_day, end_day = '2022-12-01', '2022-12-30'data = get_one_etf_sum_nav_return('510500', start_day, end_day)

对应得到的结果如下:

接下来,根据对基金代码对应的列表进行循环,合并每只ETF的数据,代码如下:

def get_all_data(fund_code_list, start_day, end_day):'''获取合并后的基金数据----------fund_code_list:list,基金代码列表start_day:str,开始日期,格式如'2022-12-01'end_day:str,结束日期,格式如'2022-12-01'Returns:DataFrame-------'''all_data = pd.DataFrame()for fund_code in fund_code_list:one_data = get_one_etf_sum_nav_return(fund_code, start_day, end_day)if all_data.empty:all_data = all_data.append(one_data)else:all_data = pd.merge(all_data, one_data, how='outer', on='date')return all_dataif __name__ == '__main__':start_day, end_day = '2022-12-01', '2022-12-30'code_list = ['513100', '513330', '510500', '515390', '513500','510050']all_data = get_all_data(code_list, start_day, end_day)

对应得到的结果如下:

此处得到的all_data本身没有数据缺失,若存在数据缺失的情况,我们应当采取相应的行均值填充或者其他具有逻辑性的填充;对于数据缺失较为严重的情况,应该考虑更换数据对应的频率,例如将日度切换为周度或者月度,以此来保证底层数据的有效性。

2.2 数据转换

在得到了基础数据all_data的基础上,需要先将对应基金的涨跌幅数转换成w或者l。举例来说,20221201的涨跌幅数据中,515390,513330,510500三只基金的涨跌幅大于中位数(当日6只基金的涨跌幅对应的中位数),因此其对应都是w,其余三只基金对应l。以此类推,将每个周期下的涨跌幅进行转换,代码如下:

def get_w_or_l(in_data):'''将基金的涨跌幅数据对应转换成w/l----------all_data:DataFrame,所有基金的涨跌幅数据Returns:DataFrame,转换后的基金w/l数据-------'''copy_data = in_data.copy()col_list = copy_data.columns[1::]copy_data['mid'] = copy_data.median(axis=1)for col_name in col_list:copy_data['test'] = copy_data[col_name] - copy_data['mid']in_data[col_name] = copy_data['test'].apply(lambda x: 'w' if x >= 0 else 'l')return in_dataif __name__ == '__main__':start_day, end_day = '2022-12-01', '2022-12-30'code_list = ['513100', '513330', '510500', '515390', '513500', '510050']all_data = get_all_data(code_list, start_day, end_day)new_data = all_data.copy()w_l_data = get_w_or_l(new_data)

得到的数据如下:

接下来,将w_l_data中每只基金相邻2单元格的w,l进行拼接,得到组合。举例来看,513100在20221201和20221202对应的分别是l,l,因此其在20221202对应的组合编码为ll,在20221201日无组合编码(因为今日设定为起始日)。以此类推,统计每交易日对应的组合编码,代码如下:

def get_com_df(new_w_l):# 对相邻两个编码进行组合col_list = new_w_l.columns[1::]last_df = pd.DataFrame()for col_name in col_list:# 循环所有列one_fund_list = []for num in range(1, len(new_w_l)):# 对单个基金的编码进行组合com_code = new_w_l[col_name].values[num] + new_w_l[col_name].values[num - 1]one_fund_list.append(com_code)one_fund_df = pd.DataFrame({'date':new_w_l['date'][1::],col_name:one_fund_list})if last_df.empty:last_df = last_df.append(one_fund_df)else:last_df = pd.merge(last_df, one_fund_df, how='outer', on='date')return last_dfif __name__ == '__main__':start_day, end_day = '2022-12-01', '2022-12-30'code_list = ['513100', '513330', '510500', '515390', '513500', '510050']all_data = get_all_data(code_list, start_day, end_day)new_data = all_data.copy()w_l_data = get_w_or_l(new_data)new_w_l = w_l_datacom_data = get_com_df(new_w_l)

得到的结果如下:

2.3 计算CPR值

根据每只基金的组合编码,计算其对应的CPR值,并划分出CPR值所处的区间,代码如下:

def CPR(data_list):# 计算CPR的值# data_list:组合编码对应的列表if len(data_list) < 5:cpr = 'null'else:wl = max(data_list.count('wl'), 1)lw = max(data_list.count('lw'), 1)ww = data_list.count('ww')ll = data_list.count('ll')cpr = (ww * ll) / (lw * wl)return cprdef get_CPR_mean(value):# 获取CPR值对应的含义# value:对应CPR的值if 0 < value < 1:d_type = '不存在业绩持续性'elif 1 <= value < 2:d_type = '业绩持续性不显著'elif 2 <= value < 3:d_type = '具备一定的业绩持续性'elif value > 3:d_type = '业绩持续性显著'return d_typedef main(com_data):col_list = com_data.columns[1::]main_df = pd.DataFrame()for col_name in col_list:one_fund_cpr = CPR(com_data[col_name].tolist())one_fund_cpr_mean = get_CPR_mean(one_fund_cpr)one_df = pd.DataFrame({'基金代码': [col_name],'CPR值': [one_fund_cpr],'CPR含义': [one_fund_cpr_mean]})main_df = main_df.append(one_df)return main_dfif __name__ == '__main__':start_day, end_day = '2022-12-01', '2022-12-30'code_list = ['513100', '513330', '510500', '515390', '513500', '510050']all_data = get_all_data(code_list, start_day, end_day)new_data = all_data.copy()w_l_data = get_w_or_l(new_data)new_w_l = w_l_data.copy()com_data = get_com_df(new_w_l)main_df = main(com_data)

对应的结果为:

从结果看,所举例基金在对应时间区间内均不具备业绩持续性。当然这里仅仅作为举例,所使用的的基金样本数量太少,周期太短,所得出的结果也是不可靠的。

2.4 完整代码


import pandas as pd
import akshare as akdef get_one_etf_sum_nav_return(fund_code, start_day, end_day):'''获取单只ETF场内基金累计净值的涨跌幅----------fund_code:str,基金代码,格式如'510500'start_day:str,开始日期,格式如'2022-12-01'end_day:str,结束日期,格式如'2022-12-01'Returns:DataFrame,第一列为日期,第二列为涨跌幅-------'''data = ak.fund_etf_fund_info_em(fund_code)data['date'] = data['净值日期'].apply(lambda x: str(x))new_data = data.loc[(data['date'] >= start_day) & (data['date'] <= end_day)]out_data = pd.DataFrame({'date': new_data['date'], fund_code: new_data['日增长率'] / 100})return out_datadef get_all_data(fund_code_list, start_day, end_day):'''获取合并后的基金数据----------fund_code_list:list,基金代码列表start_day:str,开始日期,格式如'2022-12-01'end_day:str,结束日期,格式如'2022-12-01'Returns:DataFrame-------'''all_data = pd.DataFrame()for fund_code in fund_code_list:one_data = get_one_etf_sum_nav_return(fund_code, start_day, end_day)if all_data.empty:all_data = all_data.append(one_data)else:all_data = pd.merge(all_data, one_data, how='outer', on='date')return all_datadef get_w_or_l(in_data):'''将基金的涨跌幅数据对应转换成w/l----------all_data:DataFrame,所有基金的涨跌幅数据Returns:DataFrame,转换后的基金w/l数据-------'''copy_data = in_data.copy()col_list = copy_data.columns[1::]copy_data['mid'] = copy_data.median(axis=1)for col_name in col_list:copy_data['test'] = copy_data[col_name] - copy_data['mid']in_data[col_name] = copy_data['test'].apply(lambda x: 'w' if x >= 0 else 'l')return in_datadef get_com_df(new_w_l):# 对相邻两个编码进行组合col_list = new_w_l.columns[1::]last_df = pd.DataFrame()for col_name in col_list:# 循环所有列one_fund_list = []for num in range(1, len(new_w_l)):# 对单个基金的编码进行组合com_code = new_w_l[col_name].values[num] + new_w_l[col_name].values[num - 1]one_fund_list.append(com_code)one_fund_df = pd.DataFrame({'date': new_w_l['date'][1::],col_name: one_fund_list})if last_df.empty:last_df = last_df.append(one_fund_df)else:last_df = pd.merge(last_df, one_fund_df, how='outer', on='date')return last_dfdef CPR(data_list):# 计算CPR的值# data_list:组合编码对应的列表if len(data_list) < 5:cpr = 'null'else:wl = max(data_list.count('wl'), 1)lw = max(data_list.count('lw'), 1)ww = data_list.count('ww')ll = data_list.count('ll')cpr = (ww * ll) / (lw * wl)return cprdef get_CPR_mean(value):# 获取CPR值对应的含义# value:对应CPR的值if 0 < value < 1:d_type = '不存在业绩持续性'elif 1 <= value < 2:d_type = '业绩持续性不显著'elif 2 <= value < 3:d_type = '具备一定的业绩持续性'elif value > 3:d_type = '业绩持续性显著'return d_typedef main(com_data):col_list = com_data.columns[1::]main_df = pd.DataFrame()for col_name in col_list:one_fund_cpr = CPR(com_data[col_name].tolist())one_fund_cpr_mean = get_CPR_mean(one_fund_cpr)one_df = pd.DataFrame({'基金代码': [col_name],'CPR值': [one_fund_cpr],'CPR含义': [one_fund_cpr_mean]})main_df = main_df.append(one_df)return main_dfif __name__ == '__main__':start_day, end_day = '2022-12-01', '2022-12-30'code_list = ['513100', '513330', '510500', '515390', '513500', '510050']all_data = get_all_data(code_list, start_day, end_day)new_data = all_data.copy()w_l_data = get_w_or_l(new_data)new_w_l = w_l_data.copy()com_data = get_com_df(new_w_l)main_df = main(com_data)print(main_df)

上述代码完整阐述了交叉积比率的计算过程,有许多地方为了可视化的举例,代码存在重复或者复杂的情况,读者阅读时请根据所需忽略部分问题。

3. 指标评价

3.1 指标优点

1. 思路新颖,将复杂问题抽象化,便于理解。

2. 计算得到的结果具有较为清晰的评判标准。

3. 在对基金评价的同时,也可以衍生为基金筛选,行情分析的工具。

3.2 指标缺点

1. 数据基于历史,选择不同的数据频率或周期长度得到的结论可能不同。

2. 所需的基金底层数据量较大,数据处理过程相对繁琐。

3. 所选择的基金样本不同,得到的结论可能不同。

本期分享到此结束,有何问题欢迎交流。

免责声明:本文由作者参考相关资料,并结合自身实践和思考独立完成,对全文内容的准确性、完整性或可靠性不作任何保证。同时,文中提及的基金仅作为举例使用,不构成推荐;文中所有观点均不构成任何投资建议。请读者仔细阅读本声明,若读者阅读此文章,默认知晓此声明。

基金评价专题3:交叉积比率相关推荐

  1. 基金评价专题1:绩效指标

    免责声明:本文由作者参考相关资料,并结合自身实践和思考独立完成,对全文内容的准确性.完整性或可靠性不作任何保证.同时,文中提及的基金仅作为举例使用,不构成推荐:文中所有观点均不构成任何投资建议.请读者 ...

  2. 基金评价专题4:H_M(双贝塔)模型

    目录 1. 模型介绍 1.1 模型公式 1.2 模型使用 2. 实际案例 2.1 获取数据 2.2 线性回归 2.3 获取时序结果 2.4 完整代码 3. 模型评价 3.1 模型优点 3.2 模型缺点 ...

  3. 3.《解读基金:我的投资观与实践》读书笔记

    作者 封面 季凯帆 康峰 文章目录 第一章 投资的准备 1. 咱们该投资吗? 2. 什么是开放式基金? 3. 态度决定一切 第二章 三步曲之一:投资目标的确定 1. 基金的风险是什么? 2. 拿多少钱 ...

  4. 基民读基金评级常犯四个错误

    (转)基民读基金评级常犯四个错误     2007-09-05 00:41:27 大 中 小 标签: 证券/理财 基金 纠错一:从前冠军不等于现在牛基 ·景顺内需从第1名跌到第106名 ·一二季度前3 ...

  5. 解读基金—读书框架笔记①

    解读基金-读书框架笔记① 基础概念 投资目标的确定 基金风险 投资金额比例 & 投资时间 复利计算"72"法则 & 基金选择的关键点 基金基础知识 & 品种 ...

  6. python基金比较上机题_手把手教你用python选基金

    买基金是上班族用零钱进行投资的正确姿势.而自己用数据来选基金比听别人推荐买什么基金要好上一百倍. 步骤如下: 1.获取网上的基金的排名信息,使用四四三三法则筛选出排名靠前的基金.2.获取网上的基金的基 ...

  7. 投资者选择量化基金的四个指标

    第一步,看量化基金的风格 "好的量化基金"的标准是不一样的,比较基金一定要对同一风格的基金进行对比.例如业绩比较基准是沪深300和中证500的量化基金风格是完全不一样的.在对比量化 ...

  8. 基金投资入门2:基金的风险等级

    基金产品的风险等级 由于不同类型的公募基金产品在预期收益和风险特征方面的差异是很大的,在基金的销售过程中要根据基金投资人的风险承受能力销售不同风险等级的产品,即把合适的产品卖给合适的基金投资人.那么如 ...

  9. python基金筛选_手把手教你用python选基金

    买基金是上班族用零钱进行投资的正确姿势.而自己用数据来选基金比听别人推荐买什么基金要好上一百倍. 步骤如下: 1.获取网上的基金的排名信息,使用四四三三法则筛选出排名靠前的基金.2.获取网上的基金的基 ...

最新文章

  1. AI在这张“问卷”上首次超越人类,SuperGLUE被微软谷歌两家“攻破”
  2. log4j.properties中的这句话“log4j.logger.org.hibernate.SQL=DEBUG ”该怎么写在log4j.xml里面呢?...
  3. wireshark提取流量包中的文件_[技术]Wireshark抓取的数据包提取文件
  4. OpenPCDet:点云3D目标检测开源库
  5. 规范 : 日期在不同国家与项目之间的关系
  6. nit计算机应用基础考试系统,NIT考试计算机应用基础试题
  7. 三菱FX3U 485ADP与东元TECO变频器N310通讯实战程序
  8. PDF删除页面免费的方法有什么?PDF怎么删除页面的技巧你不能错过
  9. SDUT 2021 Spring Individual Contest(for 20) - 1
  10. 《浮生六记》年表整理
  11. 任意模数ntt_MTT:任意模数NTT
  12. 【开学季】30款高质量的自学网站,总有一款适合你
  13. The Rust Programming Language - 第11章 测试 - 11.1 编写测试
  14. excel拆分数据快捷操作
  15. 基于simulink的超级电容,电池及DC motor充放电系统仿真
  16. 【ES6】阮一峰ES6学习之Generator 函数(一)
  17. 认识3dmax 轴心
  18. 9-2 什么是零点漂移?产生零点漂移的主要原因是什么?差动放大电路为什么能抑制零点漂移?
  19. C++:斐波那契数列(迭代和递归)
  20. tomcat启动项目

热门文章

  1. express框架在nodejs中的使用+解决跨域问题(cors三方库)
  2. 王者荣耀出装与铭文推荐助手微信小程序源码支持多种流量主
  3. 一天掌握C51单片机基础1-计算机数值与MCS51单片机
  4. 数据分析实战项目-蛋壳公寓投诉分析
  5. 反向传播算法(BP算法)
  6. linux动态CPU监控图
  7. 木偶然-2016.9.10
  8. bu薪水 华为cloud_华为推进ICT组织变革:Cloud amp; AI成第四大BG
  9. 计算机毕业设计Java个人事务管理系统(源码+系统+mysql数据库+Lw文档)
  10. unity2D-笔记 玩家跳跃时,游戏场景消失