factor_list = ['Factor1', 'Factor7', 'Factor9'] period = 20 from jaqs_fxdayu.research.signaldigger import process factor_dict = dict() index_member = id_member for name in factor_list: signal = dv.get_ts(name) # 调整符号 print(name) signal = process.winsorize(factor_df=signal, alpha=0.05, index_member=index_member) # 去极值 # print(signal.shape) signal = process.standardize(signal, index_member) # z-score标准化 保留排序信息和分布信息 # print(signal.shape) # signal = process.neutralize(signal,group=dv.get_ts("sw1"),float_mv = dv.get_ts("float_mv")) #行业中性化 print(signal.shape) factor_dict[name] = signal for name in factor_list: dv.append_df(factor_dict[name], name + '_J') # rollback_period代表滚动窗口所用到的天数,即用前多少期的数据来计算现阶段的因子权重。 通常建议设置时间在半年以上,可以获得相对稳定的预期结果 period = 20 # 20天的持有期 # 多因子组合-动态加权参数配置 props = {
#根据之前的分析,可知momentum、ps、pe、pb这4个因子在几个持有期下与股票收益的关系(ic)都是负的,故需进一步分析这4个人因子 #先统一调整成正相关关系,并且需要完成以下几个步骤 #去极值 #标准化 -- z-score/rank; z-score标准化,也称为标准化分数,这种方法根据原始数据的均值和标准差进行标准化(会保留因子原有的基本分布信息), #经过处理后的数据符合标准正态分布,即均值为0,标准差为1(根据下面的转化函数很容易证明),转化函数为:(X-均值)/标准差.而rank标准化会保留因子的排序信息,但会删除因子的分布信息. #行业市值中性化(这一步是为了防止所选股票的因子在行业或市值上有过多的暴露,(例如:所选股票全是某一行业或全是小市值的股票)) from jaqs_fxdayu.research.signaldigger import process factor_dict = dict() #处理后的因子factor_dict index_member = dv.get_ts("index_member") for name in ["pb", "pe", "ps", "momentum"]: signal = -1 * dv.get_ts(name) # 调整符号 这里表示统一调整成正相关关系 process.winsorize(factor_df=signal, alpha=0.05, index_member=index_member ) #factor_df=signal是输入的因子值,alpha=0.05表示去掉因子值左右两端5%的数值, #index_member=index_member表示需要传入的是否是指数成分的一个dataframe,传入后仅会处理指数成分股,对非指数成分股则不会进行处理 signal = process.standardize(signal, index_member) #z-score标准化 保留排序信息和分布信息 # signal = process.rank_standardize(signal,index_member) #rank标准化 因子在截面排序并归一化到0-1(只保留排序信息) # # 行业市值中性化(这步可以做也可以不做,其作用是在横截面上把行业风格与市值风格给剔除掉) # signal = process.neutralize(signal, # group=dv.get_ts("sw1"),# 行业分类标准 # float_mv = dv.get_ts("float_mv"), #流通市值 可为None 则不进行市值中性化 # index_member=index_member,# 是否只处理时只考虑指数成份股 # ) factor_dict[name] = signal #输出结果,结果为字典 #4_施密特正交化 ''' 对筛选后的因子进行组合,一般有以下常规处理: 1:因子间存在较强同质性时,可以使用施密特正交化方法对因子做正交化处理,用得到的正交化残差作为因子,这样可以剔除同质化部分. 但是这也有一个弊端:由于正交化会破坏因子的经济学逻辑,并剔除一些原有的信息(包括原因子之间的联系,这可能导致不好的影响),造成与原因子差别太大,固也可以不使用或谨慎使用. 2:因子组合加权,常规的方法有:等权重、以某个时间窗口的滚动平均ic为权重(理由是ic越大因子效果越好,因而分配更多的权重)、 以某个时间窗口的滚动ic_ir为权重(与前面同理,同时考虑了预测效益即稳定性)、最大化(使上一期因子效果最大化)上个持有期的ic_ir为目标处理权重、最大化上个持有期的ic为目标处理权重
def generate_residuals(self, signal, style="float_mv", industry_standard="sw1", cap="float_mv", preprocessing=("mad", "neutralization", "standardize")): ''' :param signal: :param style: :param industry_standard: :param cap: :param preprocessing: :return: ''' self._signal = signal if isinstance(signal, pd.Series) else signal.stack() self._signal = self._signal.rename("signal") # self.style_factor = None # 还没想好方案, 市值 if isinstance(style, str): if not (style in self.dv.fields): raise ValueError("请确保dv中提供了风格因子(style)字段-%s存在!" % (style, )) self._style = np.log(self.dv.get_ts(style)) # Attention! elif style is None: raise ValueError("not yet ready for complete Barra style factors") elif isinstance(style, pd.DataFrame): # 为什么需要DataFrame我给忘了 self._style = style elif isinstance(style, pd.Series): self._style = style.unstack() else: raise ValueError( "style should be one of str, DataFrame and Series") # 风格因子去极值标准化 self._style = process.standardize(process.mad( self._style)).stack().rename("style") # 用于barra标准化 if isinstance(cap, str): if not (cap in self.dv.fields): raise ValueError("请确保dv中提供了市值因子(cap)字段-%s存在!" % (cap, )) self._cap = self.dv.get_ts(cap).stack() elif isinstance(cap, pd.DataFrame): self._cap = self._cap.stack() elif isinstance(cap, pd.Series): pass else: raise ValueError("cap should be one of str, DataFrame and Series") self._cap = self._cap.reindex_like(self._signal).apply(np.log) if isinstance(industry_standard, str): # str的话只支持一个指标 if not (industry_standard in self.dv.fields): raise ValueError("请确保dv中提供了行业分类标准(industry_standard)字段-%s存在!" % (industry_standard, )) self._industry_standard = self.dv.get_ts(industry_standard).stack() elif isinstance(industry_standard, pd.DataFrame): self._industry_standard = industry_standard.stack() elif isinstance(industry_standard, pd.Series): self._industry_standard = industry_standard else: raise ValueError( "industry_standard should be one of str, DataFrame and Series") def drop_nan(s): """ 主要是有一支'000748.SZ'的股票 """ return s[s != "nan"] self._industry_standard = drop_nan( self._industry_standard).rename("industry") self.data = pd.concat( [self._signal, self._style, self._industry_standard], axis=1).dropna() # ===========================================上面预处理====================================================== self.residuals = pd.concat( self._generate_cross_sectional_residual(preprocessing))
from jaqs_fxdayu.research.signaldigger import process negative = ['alpha206','alpha197','alpha195','alpha211','alpha194','alpha64_','alpha56_','alpha42_','pb','pe'] factor_dict = dict() for name in factor_lis: print(name) if name in negative: signal = -1*dv.get_ts(name) # 调整符号 else: signal = dv.get_ts(name) signal = process.winsorize(factor_df=signal,alpha=0.05)#去极值 # 行业市值中性化 signal = process.neutralize(signal, group=dv.get_ts("sw1"),# 行业分类标准 ) signal = process.standardize(signal) #z-score标准化 保留排序信息和分布信息 factor_dict[name] = signal import pickle with open('Neutralized_Postive_Data.pkl','wb') as f: pickle.dump(factor_dict,f) import pickle #导入已经经过预处理的因子数据 with open("Neutralized_Postive_Data.pkl",'rb') as f2: factor_dict = pickle.load(f2)
def test_multi_factor(): from jaqs_fxdayu.research.signaldigger import multi_factor, process dv = DataView() dv.load_dataview(dataview_folder) dv.add_formula("momentum", "Return(close_adj, 20)", is_quarterly=False, add_data=True) mask = mask_index_member(dv) can_enter, can_exit = limit_up_down(dv) ic = dict() factors_dict = { signal: dv.get_ts(signal) for signal in ["pb", "pe", "ps", "momentum"] } for period in [5, 15]: ic[period] = multi_factor.get_factors_ic_df( factors_dict, price=dv.get_ts("close_adj"), high=dv.get_ts("high_adj"), # 可为空 low=dv.get_ts("low_adj"), # 可为空 n_quantiles=5, # quantile分类数 mask=mask, # 过滤条件 can_enter=can_enter, # 是否能进场 can_exit=can_exit, # 是否能出场 period=period, # 持有期 benchmark_price=dv. data_benchmark, # 基准价格 可不传入,持有期收益(return)计算为绝对收益 commission=0.0008, ) factor_dict = dict() index_member = dv.get_ts("index_member") for name in ["pb", "pe", "ps", "momentum"]: signal = -1 * dv.get_ts(name) # 调整符号 process.winsorize(factor_df=signal, alpha=0.05, index_member=index_member) # 去极值 signal = process.rank_standardize( signal, index_member) # 因子在截面排序并归一化到0-1(只保留排序信息) signal = process.standardize(signal, index_member) # z-score标准化 保留排序信息和分布信息 # 行业市值中性化 signal = process.neutralize( signal, group=dv.get_ts("sw1"), float_mv=dv.get_ts("float_mv"), index_member=index_member, # 是否只处理时只考虑指数成份股 ) factor_dict[name] = signal # 因子间存在较强同质性时,使用施密特正交化方法对因子做正交化处理,用得到的正交化残差作为因子 new_factors = multi_factor.orthogonalize( factors_dict=factor_dict, standardize_type="rank", # 输入因子标准化方法,有"rank"(排序标准化),"z_score"(z-score标准化)两种("rank"/"z_score") winsorization=False, # 是否对输入因子去极值 index_member=index_member) # 是否只处理指数成分股 # 多因子组合-动态加权参数配置 props = { 'price': dv.get_ts("close_adj"), 'high': dv.get_ts("high_adj"), # 可为空 'low': dv.get_ts("low_adj"), # 可为空 'ret_type': 'return', # 可选参数还有upside_ret/downside_ret 则组合因子将以优化潜在上行、下行空间为目标 'benchmark_price': dv.data_benchmark, # 为空计算的是绝对收益 不为空计算相对收益 'period': 30, # 30天的持有期 'mask': mask, 'can_enter': can_enter, 'can_exit': can_exit, 'forward': True, 'commission': 0.0008, "covariance_type": "shrink", # 协方差矩阵估算方法 还可以为"simple" "rollback_period": 120 } # 滚动窗口天数 comb_factors = dict() for method in [ "equal_weight", "ic_weight", "ir_weight", "max_IR", "max_IC", "factors_ret_weight" ]: comb_factors[method] = multi_factor.combine_factors( factor_dict, standardize_type="rank", winsorization=False, weighted_method=method, props=props)