def get_ret(self, period): if period in self.signal_ret.keys(): return self.signal_ret[period] else: # prepare data price = self.dv.get_ts("close_adj") high = self.dv.get_ts("high_adj") low = self.dv.get_ts("low_adj") can_exit = self.limit_rules["can_exit"] # 持有期收益 price = jutil.fillinf(price) df_ret = pfm.price2ret(price, period=period, axis=0, compound=True) price_can_exit = price.copy() price_can_exit[~can_exit] = np.NaN price_can_exit = price_can_exit.fillna(method="bfill") ret_can_exit = pfm.price2ret(price_can_exit, period=period, axis=0, compound=True) df_ret[~can_exit] = ret_can_exit[~can_exit] if self.benchmark is not None: benchmark_price = self.benchmark.loc[price.index] bench_ret = pfm.price2ret(benchmark_price, period, axis=0, compound=True) residual_ret = df_ret.sub(bench_ret.values.flatten(), axis=0) else: residual_ret = df_ret residual_ret = jutil.fillinf(residual_ret) residual_ret -= self.commission # 计算潜在上涨空间和潜在下跌空间 high = jutil.fillinf(high) upside_ret = compute_upside_returns(price, high, can_exit, period, compound=True) upside_ret = jutil.fillinf(upside_ret) upside_ret -= self.commission low = jutil.fillinf(low) downside_ret = compute_downside_returns(price, low, can_exit, period, compound=True) downside_ret = jutil.fillinf(downside_ret) downside_ret -= self.commission self.signal_ret[period] = { "return": residual_ret.shift(-period), "upside_ret": upside_ret.shift(-period), "downside_ret": downside_ret.shift(-period) } return self.signal_ret[period]
def _generate_data(self, signal, period, mask): ''' :param signal: :param period: int :param mask: :return: signal_data ''' signal = jutil.fillinf(signal).shift(1) rets = self.get_ret(period) mask = mask.reindex_like(signal) mask_signal = signal.isnull() mask = np.logical_or(mask.fillna(False), mask_signal) def stack_td_symbol(df): df = pd.DataFrame(df.stack(dropna=False)) # do not dropna df.index.names = ['trade_date', 'symbol'] df.sort_index(axis=0, level=['trade_date', 'symbol'], inplace=True) return df # ---------------------------------------------------------------------- # concat signal value res = stack_td_symbol(signal) res.columns = ['signal'] for ret_type in rets.keys(): res[ret_type] = stack_td_symbol( rets[ret_type].reindex_like(signal)).fillna(0) # 收益 mask = stack_td_symbol(mask) res = res.loc[~(mask.iloc[:, 0]), :] if len(res) > 0: print("Nan Data Count (should be zero) : {:d}; " \ "Percentage of effective data: {:.0f}%".format(res.isnull().sum(axis=0).sum(), len(res) * 100. / signal.size)) else: print("No signal available.") return res
def __init__(self, dv, limit_rules="A-share default", benchmark=None, commission=0.0008): """ :param dv: jaqs.dataview :param signal: pd.DataFrame :param limit_rules: 限制条件 dict {mask,can_enter,can_exit} """ self.dv = dv self.benchmark = benchmark self.commission = commission self.methods = { "mad": self._mad, "winsorize": self._winsorize, "barra": self._barra, "standard_scale": lambda x: pd.Series(scale(x), index=x.index, name="signal"), "standardize": self._standardize, } self._cap = None self._industry_standard = None self._style = None self.data = None self._signal = None self.residuals = None self.signal_ret = dict() # ========================================================================================= for field in ["trade_status", "close_adj", "high_adj", "low_adj"]: if field not in self.dv.fields: raise ValueError("请确保dv中必须提供的字段-%s存在!" % (field, )) if isinstance(limit_rules, str): if limit_rules == "A-share default": self.limit_rules = self._a_share_default_rule() else: raise ValueError( "limit_rules only support 'A-share default' now") elif isinstance(limit_rules, dict): for rule in limit_rules.keys(): if not (rule in ["mask", "can_enter", "can_exit"]): raise ValueError( "limit_rule的keys只能为'mask','can_enter','can_exit'") self.limit_rules = limit_rules else: raise ValueError("Type of limit_rules can only be dict or str.") if "can_exit" in self.limit_rules.keys(): if self.limit_rules["can_exit"] is not None: self.limit_rules["can_exit"] = jutil.fillinf( self.limit_rules["can_exit"]).astype(int).fillna(0).astype( bool) else: self.limit_rules["can_exit"] = pd.DataFrame( index=self.dv.get_ts("close_adj").index, columns=self.dv.get_ts("close_adj").columns, data=True) else: self.limit_rules["can_exit"] = pd.DataFrame( index=self.dv.get_ts("close_adj").index, columns=self.dv.get_ts("close_adj").columns, data=True) if "mask" in self.limit_rules.keys(): if self.limit_rules["mask"] is not None: self.limit_rules["mask"] = jutil.fillinf( self.limit_rules["mask"]).astype(int).fillna(0).astype( bool) else: self.limit_rules["mask"] = pd.DataFrame( index=self.dv.get_ts("close_adj").index, columns=self.dv.get_ts("close_adj").columns, data=False) else: self.limit_rules["mask"] = pd.DataFrame( index=self.dv.get_ts("close_adj").index, columns=self.dv.get_ts("close_adj").columns, data=False) if "can_enter" in self.limit_rules.keys(): if self.limit_rules["can_enter"] is not None: self.limit_rules["can_enter"] = jutil.fillinf( self.limit_rules["can_enter"]).astype(int).fillna( 0).astype(bool) self.limit_rules["mask"] = np.logical_or( self.limit_rules["mask"], ~(self.limit_rules["can_enter"])) del self.limit_rules["can_enter"]
def _fillinf(df): try: df = fillinf(df) except: pass return df