def check_nv(self): # check whether first/last period is missing ps = self.fund.price_series if (~ps[ps.index[ps.index <= self._first_month_end]].notnull()).all() \ or (~ps[ps.index[ps.index >= self._last_month_start]].notnull()).all(): raise DataError("First/Last-period NV Missing") # check whether all return equals 0 if (self.fund.return_series.dropna() == 0).all(): raise DataError("All return equal to Zero") min_lengths = { "w": { "style": 12, "industry": 24 }, "d": { "style": 12, "industry": 24 } } n = len(self.fund.price_series.dropna()) if n < min_lengths[self.freq][self.factor_type]: raise DataError("Data Missing Too much, only got {n}".format(n=n)) return True
def check_before_pre(self): # Check Foundation Date(or First Nv Date) if self.portfolio.foundation_date is not None: if self.date - relativedelta(months=6) < self.portfolio.foundation_date: raise DataError( "Founded less than 6 months: foundation_date: {fd}, statistic_date: {sd}".format( fd=self.portfolio.foundation_date, sd=self.date)) else: raise DataError("Missing Foundation Date / First Nv Date") if len(self.portfolio.return_series.dropna()) < 12: raise DataError("Valid Sample less than 12")
def check_totalnv_len(self): min_lengths = {"w": {"style": 24, "industry": 52}, "d": {"style": 24, "industry": 52}} n = len(self.fund.price_series_whole.dropna()) N = min_lengths[self.freq][self.factor_type] if n < N: raise DataError("Total NV length doesn't satisfy, {n}/{N}".format(n=n, N=N)) return True
def load_type(self): sql = "SELECT stype_code FROM base.fund_type_mapping " \ "WHERE fund_id = '{fid}' AND typestandard_code = '601' AND type_code <> '60109' " \ "AND flag = 1 AND stype_code IS NOT NULL".format(fid=self.fund_id) tp = self.engine.execute(sql).fetchone() if tp is None: raise DataError("Fund {fid} does not have a classification".format(fid=self.fund_id)) return tp[0]
def data(self): self.check_before_pre() x_data = self.factors.data.rename(columns=self.factor_names).fillna(method="ffill").dropna() y_data = (self.portfolio.return_series.fillna(method="ffill") - self.benchmark_rf.return_series.fillna(method="ffill")).dropna() x_data, y_data = [_ for _ in x_data.align(y_data, axis=0, join="inner")] if len(x_data) < 12: raise DataError("Valid Sample less than 12 after align ") return x_data, y_data
def __init__(self, fund_id: str, start: dt.date, end: dt.date, freq: str, factor_type: str, ftypes=None, **kwargs): """ Args: fund_id: str start: datetime.date, or None end: datetime.date freq: str, optional {"w", "m"} factor_type: str, optional {"style", "industry"} ftypes: dict, or None **kwargs: arguments for `scipy.optimize.minize` function, including tol: float, default 1e-18 options: dict, default None """ self.start, self.end = start, end self.factor_type = factor_type self.freq = freq if self.start is None: if self.factor_type == "style": self.start = self.end - relativedelta(months=6) elif self.factor_type == "industry": self.start = self.end - relativedelta(years=1) self.start = dt.date( self.start.year, self.start.month, cld.monthrange(self.start.year, self.start.month)[1]) + dt.timedelta(1) self.fund = Portfolio(fund_id, self.start, self.end, self.freq) self.sampler = Sampler(self.fund, self.freq, factor_type) if not self.sampler.pass_check(): raise DataError("do not pass check") if ftypes is None: print("ftypes not set, automatically selecting...") # 股票型 if self.fund.stype in {"020101"}: if self.factor_type == "style": ftypes = FactorUsed.STOCK elif self.factor_type == "industry": ftypes = FactorUsed.SWS_28 # 纯债型, 混合债券型 elif self.fund.stype in {"020201", "020202"}: if self.factor_type == "style": ftypes = FactorUsed.BOND # 混合策略 elif self.fund.stype in { "020301", "020302", "020303", "020304", "020305" }: if self.factor_type == "style": ftypes = FactorUsed.BLEND if ftypes is None: raise DataError("Unsupported fund type for {} sharpe model".format( self.factor_type)) self.factors = Factors(ftypes, self.start, self.end, freq) SharpeFactor.__init__(self, self.fund, self.factors, **kwargs) self.factor_type = factor_type
def _init_cons_by_stype(self): # 股票型 if self.stype in {"020101"}: self._cons.extend([ { "type": "ineq", "fun": lambda x: sum(x[self.tf[FactorClassification.STOCK_CODE]]) - 0.8 }, { "type": "ineq", "fun": lambda x: sum(x[self.tf[FactorClassification.CASH_CODE]]) - 0 }, ]) # 债券策略 elif self.stype in {"020201", "020202"}: self._cons.extend([ { "type": "ineq", "fun": lambda x: sum(x[self.tf[FactorClassification.BOND_CODE]]) - 0.8 }, { "type": "ineq", "fun": lambda x: sum(x[self.tf[FactorClassification.CASH_CODE]]) - 0 }, ]) # 混合策略 elif self.stype in {"020301", "020302", "020303", "020304", "020305"}: self._cons.extend([ { "type": "ineq", "fun": lambda x: 0.8 - sum(x[self.tf[FactorClassification. STOCK_CODE]]) }, { "type": "ineq", "fun": lambda x: 0.8 - sum(x[self.tf[FactorClassification. BOND_CODE]]) }, { "type": "ineq", "fun": lambda x: sum(x[self.tf[FactorClassification.CASH_CODE]]) - 0 }, ]) else: raise DataError("No supported fund type")