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"]
Beispiel #4
0
 def _fillinf(df):
     try:
         df = fillinf(df)
     except:
         pass
     return df