def peg(self): # PEG = PE / 过去12个月的EPS增长率 pe_daily = self.pe_daily basicepsyoy = self.basicepsyoy basicepsyoy = adjust_months(basicepsyoy) epsyoy = append_df(basicepsyoy, target_feq='D', fill_type='preceding') pe_daily = CALFUNC.del_dat_early_than(pe_daily, START_YEAR) epsyoy = CALFUNC.del_dat_early_than(epsyoy, START_YEAR) [pe_daily, epsyoy] = align(pe_daily, epsyoy) [h, l] = pe_daily.shape pe_ar = pe_daily.values eps_ar = epsyoy.values res = np.zeros([h, l]) for i in range(0, h): for j in range(0, l): if pd.isna(eps_ar[i, j]) or eps_ar[i, j] == 0: res[i, j] = np.nan else: res[i, j] = pe_ar[i, j] / eps_ar[i, j] res_df = pd.DataFrame(data=res, index=pe_daily.index, columns=pe_daily.columns) return res_df
def easy_bt(wei_stocks, basic_return_infor): data = Data() changepct_daily = data.CHANGEPECT_OPEN_DAILY changepct_daily = changepct_daily.shift(-1, axis=1) changepct_daily.dropna(how='all', axis=1, inplace=True) changepct_daily = changepct_daily / 100 wei_stocks, changepct_daily = align(wei_stocks, changepct_daily) # fee_type='No_fee' 不计算佣金和印花税, 'fee_1'计算佣金和印花税,不计算冲击成本 daily_return, net_value = back_test(changepct_daily, wei_stocks, fee_type='fee_1') # plt.plot(net_cpd) # 若有基准日度收益率,则计算累计超额收益率 if isinstance(basic_return_infor, str): # 有基准收益,算超额收益 basic_return = pd.read_csv(basic_return_infor, engine='python') basic_return = basic_return.set_index('date') if 'daily_return' in basic_return.columns: daily_excess_r = daily_return['daily_return'] - basic_return['daily_return'] # 若没有日度收益数据,则根据日度净值数据计算出日度收益收益数据 elif 'daily_return' not in basic_return.columns and 'net_value' in basic_return.columns: basic_return['daily_return'] = basic_return['net_value']/basic_return['net_value'].shift(1) - 1 daily_excess_r = daily_return['daily_return'] - basic_return['daily_return'] daily_excess_r.dropna(inplace=True) daily_excess_cum = (daily_excess_r + 1).cumprod() cum_excess_df = pd.DataFrame({'cum_excess_ret': daily_excess_cum}) elif isinstance(basic_return_infor, pd.DataFrame): if 'daily_return' not in basic_return_infor.columns and 'net_value' in basic_return_infor.columns: basic_return_infor['daily_return'] = basic_return_infor['net_value'] / \ basic_return_infor['net_value'].shift(1) - 1 daily_excess_r = daily_return['daily_return'] - basic_return_infor['daily_return'] daily_excess_r.dropna(inplace=True) daily_excess_cum = (daily_excess_r + 1).cumprod() cum_excess_df = pd.DataFrame({'cum_excess_ret': daily_excess_cum}) else: cum_excess_df = None return daily_return, net_value, cum_excess_df
def reverse_nm(self): ''' 1)在每个月底,对于股票s,回溯其过去N个交易日的数据(为方便处理, N取偶数); 2)对于股票s,逐日计算平均单笔成交金额D(D = 当日成交金额 / 当日成交笔数),将N个交易日按D值从大到小排序,前N/2 个交易日称为高D组,后N/2个交易日称为低D组; 3)对于股票s,将高D组交易日的涨跌幅加总[1],得到因子M_high;将低D组交易日的涨跌幅加总,得到因子M_low; 4) 对于所有股票,分别按照上述流程计算因子值。 ''' # n为20、60、180 deals = self.turnoverdeals turnovervalue = self.turnovervalue_daily # 成交额(万元) turnovervalue, deals = align(turnovervalue, deals) value_per_deal = turnovervalue / deals pct = self.changepct_daily / 100 value_per_deal, pct = align(value_per_deal, pct) def _cal_M_reverse(series, pct_chg=None): code = series.name series = series.dropna() if len(series) == 0: return None series = series.sort_values() if len(series) % 2 == 1: low_vals = series.iloc[:len(series) // 2 + 1] else: low_vals = series.iloc[:len(series) // 2] high_vals = series.iloc[len(series) // 2:] m_high = (pct_chg.loc[code, high_vals.index] + 1).cumprod().iloc[-1] - 1 m_low = (pct_chg.loc[code, low_vals.index] + 1).cumprod().iloc[-1] - 1 res = m_high - m_low return res if self._status == 'update': new_mes = self._get_update_month('REVERSE_20') # 若返回None,表示没有更新必要,因子计算函数同样返回None if not new_mes: return None reverse_20 = self.REVERSE_20 reverse_60 = self.REVERSE_60 reverse_180 = self.REVERSE_180 elif self._status == 'all': new_mes = [m for m in self._mes if m in value_per_deal.columns] reverse_20 = pd.DataFrame() reverse_60 = pd.DataFrame() reverse_180 = pd.DataFrame() for m in new_mes: print(m) loc = np.where(value_per_deal.columns == m)[0][0] if loc > 180: tmp_20 = value_per_deal.iloc[:, loc + 1 - 20:loc + 1].apply( _cal_M_reverse, axis=1, args=(pct, )) tmp_60 = value_per_deal.iloc[:, loc + 1 - 60:loc + 1].apply( _cal_M_reverse, axis=1, args=(pct, )) tmp_180 = value_per_deal.iloc[:, loc + 1 - 180:loc + 1].apply( _cal_M_reverse, axis=1, args=(pct, )) reverse_20 = pd.concat( [reverse_20, pd.DataFrame({m: tmp_20})], axis=1) reverse_60 = pd.concat( [reverse_60, pd.DataFrame({m: tmp_60})], axis=1) reverse_180 = pd.concat( [reverse_180, pd.DataFrame({m: tmp_180})], axis=1) reverse_20 = CALFUNC.del_dat_early_than(reverse_20, START_YEAR) reverse_60 = CALFUNC.del_dat_early_than(reverse_60, START_YEAR) reverse_180 = CALFUNC.del_dat_early_than(reverse_180, START_YEAR) res_dict = { "Reverse_20": reverse_20, "Reverse_60": reverse_60, "Reverse_180": reverse_180, } return res_dict
def month_bt(self): # 先对行和列取交集,然后再转换为array,再矩阵乘法得到每个月的收益,再判断是不是要去费, [self.weight, self.changePCT_np] = align(self.weight, self.changePCT_np) self.weight.fillna(0, inplace=True) self.changePCT_np.fillna(0, inplace=True) # 净值数据 if self.fee_type == 'No_fee': ret_df = self.weight * self.changePCT_np # 月度收益pct self.net_pct = ret_df.sum(axis=0) self.net_value = (self.net_pct + 1).cumprod() else: # 计算出,每个月仓位变动中,没有变动的比例和变动的比率是多少 unchanged_wei = pd.DataFrame(0, index=self.weight.index, columns=self.weight.columns) buy_wei = pd.DataFrame(0, index=self.weight.index, columns=self.weight.columns) sell_wei = pd.DataFrame(0, index=self.weight.index, columns=self.weight.columns) for col_i in range(0, len(self.weight.columns)): if col_i == 0: buy_wei[self.weight.columns[col_i]] = self.weight[self.weight.columns[col_i]] else: pre_i = col_i - 1 unchanged_wei[self.weight.columns[col_i]] = self.weight[self.weight.columns[pre_i:col_i + 1]].min( axis=1) cha = self.weight[self.weight.columns[col_i]] - self.weight[self.weight.columns[pre_i]] buy_wei[self.weight.columns[col_i]] = cha.where(cha > 0, 0) sell_wei[self.weight.columns[col_i]] = abs(cha.where(cha < 0, 0)) wei_ar = self.weight.values change_pct_ar = self.changePCT_np.values unchanged_wei_ar = unchanged_wei.values buy_wei_ar = buy_wei.values sell_wei_ar = sell_wei.values # 双边佣金率0.02%,建仓冲击成本0.5%,减仓冲击成本0.3%,卖出单边印花税0.1% tax = 0.0001 fee = 0.00002 buy_impact_cost = 0.001 sell_impact_cost = 0.001 net_value_ar = np.zeros(wei_ar.shape) h_n, l_n = net_value_ar.shape net_value_se = np.zeros(l_n) for col in range(0, l_n): wei_tmp = wei_ar[:, col] change_pct_tmp = change_pct_ar[:, col] unchanged_wei_tmp = unchanged_wei_ar[:, col] buy_wei_tmp = buy_wei_ar[:, col] sell_wei_tmp = sell_wei_ar[:, col] if col != 0: sell_wei_tmp.sum() net_value_ar[:, col - 1].sum() # 先减仓后才可加仓,建仓时产生的冲击成本对整体净值有影响 fee1 = (net_value_ar[:, col - 1] * sell_wei_tmp).sum() * (1 - sell_impact_cost) * tax # 交的印花税 fee2 = (net_value_ar[:, col - 1] * sell_wei_tmp * sell_impact_cost).sum() # 卖出冲击成本导致的减值 total_sell_impact_cost = fee1 + fee2 # 新建仓的股票的本期收益,新建仓时的冲击成本对本期收益产生影响 new_set = buy_wei_tmp * (1-fee) * (1 + change_pct_tmp/(1 + buy_impact_cost)) # 仓位不变的股票的本期收益,仓位不变,不收冲击成本影响 unc = unchanged_wei_tmp * (1 + change_pct_tmp) # 上期净值先减去卖出的成本得到本期基准净值,然后再得到本期的收益净值 if col != 0: net_value_ar[:, col] = net_value_se[col-1] * (1-total_sell_impact_cost) * (new_set + unc) net_value_se[col] = net_value_ar[col].sum() else: net_value_ar[:, col] = new_set net_value_se[col] = new_set.sum() net_value = pd.Series(net_value_se, index=self.weight.index) return net_value