def factors_stats(self, method_mu="hist", method_cov="hist", **kwargs): r""" Calculate the inputs that will be use by the optimization method when we select the input model='FM'. Parameters ---------- **kwargs : dict All aditional parameters of risk_factors function. See Also -------- riskfolio.ParamsEstimation.forward_regression riskfolio.ParamsEstimation.backward_regression riskfolio.ParamsEstimation.loadings_matrix riskfolio.ParamsEstimation.risk_factors """ X = self.factors Y = self.returns mu, cov, returns, nav = pe.risk_factors( X, Y, method_mu=method_mu, method_cov=method_cov, **kwargs ) self.mu_fm = mu self.cov_fm = cov self.returns_fm = returns self.nav_fm = nav value = af.is_pos_def(self.cov_fm, threshold=1e-8) if value == False: print("You must convert self.cov_fm to a positive definite matrix")
def assets_stats(self, method_mu="hist", method_cov="hist", **kwargs): r""" Calculate the inputs that will be use by the optimization method when we select the input model='Classic'. Parameters ---------- **kwargs : dict All aditional parameters of mean_vector and covar_matrix functions. See Also -------- riskfolio.ParamsEstimation.mean_vector riskfolio.ParamsEstimation.covar_matrix """ self.mu = pe.mean_vector(self.returns, method=method_mu, **kwargs) self.cov = pe.covar_matrix(self.returns, method=method_cov, **kwargs) value = af.is_pos_def(self.cov, threshold=1e-8) if value == False: print("You must convert self.cov to a positive definite matrix")
def blacklitterman_stats( self, P, Q, rf=0, w=None, delta=None, eq=True, method_mu="hist", method_cov="hist", **kwargs ): r""" Calculate the inputs that will be use by the optimization method when we select the input model='BL'. Parameters ---------- P : DataFrame of shape (n_views, n_assets) Analyst's views matrix, can be relative or absolute. Q: DataFrame of shape (n_views, 1) Expected returns of analyst's views. delta: float Risk aversion factor. The default value is 1. rf: scalar, optional Risk free rate. The default is 0. w : DataFrame of shape (n_assets, 1) Weights matrix, where n_assets is the number of assets. The default is None. eq: bool, optional Indicates if use equilibrum or historical excess returns. The default is True. **kwargs : dict Other variables related to the mean and covariance estimation. See Also -------- riskfolio.ParamsEstimation.black_litterman """ X = self.returns if w is None: w = self.benchweights if delta is None: a = np.matrix(self.mu) * np.matrix(w) delta = (a - rf) / (np.matrix(w).T * np.matrix(self.cov) * np.matrix(w)) delta = delta.item() mu, cov, w = pe.black_litterman( X=X, w=w, P=P, Q=Q, delta=delta, rf=rf, eq=eq, method_mu=method_mu, method_cov=method_cov, **kwargs ) self.mu_bl = mu self.cov_bl = cov value = af.is_pos_def(self.cov_bl, threshold=1e-8) if value == False: print("You must convert self.cov_bl to a positive definite matrix")
def bootstrapping(X, kind='stationary', q=0.05, n_sim=3000, window=3, seed=0): r""" Estimates the uncertainty sets of mean and covariance matrix through the selected bootstrapping method. Parameters ---------- X : DataFrame of shape (n_samples, n_features) Features matrix, where n_samples is the number of samples and n_features is the number of features. kind : str The bootstrapping method. The default value is 'stationary'. Posible values are: - 'stationary': stationary bootstrapping method, see `StationaryBootstrap <https://bashtage.github.io/arch/bootstrap/generated/arch.bootstrap.StationaryBootstrap.html#arch.bootstrap.StationaryBootstrap>`_ for more details. - 'circular': circular bootstrapping method, see `CircularBlockBootstrap <https://bashtage.github.io/arch/bootstrap/generated/arch.bootstrap.CircularBlockBootstrap.html#arch.bootstrap.CircularBlockBootstrap>`_ for more details. - 'moving': moving bootstrapping method, see `MovingBlockBootstrap <https://bashtage.github.io/arch/bootstrap/generated/arch.bootstrap.MovingBlockBootstrap.html#arch.bootstrap.MovingBlockBootstrap>`_ for more details. q : scalar Significance level of the selected bootstrapping method. The default is 0.05. n_sim : scalar Number of simulations of the bootstrapping method. The default is 3000. window: Block size of the bootstrapping method. Must be greather than 1 and lower than the n_samples - n_features + 1 The default is 3. seed: Seed used to generate random numbers for bootstrapping method. The default is 0. Returns ------- mu_l : DataFrame The q/2 percentile of mean vector obtained through the selected bootstrapping method. mu_u : DataFrame The 1-q/2 percentile of mean vector obtained through the selected bootstrapping method. cov_l : DataFrame The q/2 percentile of covariance matrix obtained through the selected bootstrapping method. cov_u : DataFrame The 1-q/2 percentile of covariance matrix obtained through the selected bootstrapping method. cov_mu : DataFrame The covariance matrix of estimation errors of mean vector obtained through the selected bootstrapping method. We take the diagonal of this matrix following :cite:`b-fabozzi2007robust`. Raises ------ ValueError When the value cannot be calculated. """ if not isinstance(X, pd.DataFrame): raise ValueError("X must be a DataFrame") if window >= X.shape[0] - window + 1: raise ValueError("block must be lower than n_samples - window + 1") elif window <= 1: raise ValueError("block must be greather than 1") rs = np.random.RandomState(seed) cols = X.columns.tolist() m = len(cols) mus = np.zeros((n_sim, 1, m)) covs = np.zeros((n_sim, m, m)) if kind == 'stationary': gen = bs.StationaryBootstrap(window, X, random_state=rs) elif kind == 'circular': gen = bs.CircularBlockBootstrap(window, X, random_state=rs) elif kind == 'moving': gen = bs.MovingBlockBootstrap(window, X, random_state=rs) else: raise ValueError( "kind only can be 'stationary', 'circular' or 'moving'") i = 0 for data in gen.bootstrap(n_sim): A = data[0][0] mus[i] = A.mean().to_numpy().reshape(1, m) covs[i] = A.cov().to_numpy() i += 1 mu_l = np.percentile(mus, q / 2 * 100, axis=0, keepdims=True).reshape(1, m) mu_u = np.percentile(mus, 100 - q / 2 * 100, axis=0, keepdims=True).reshape(1, m) cov_l = np.percentile(covs, q / 2 * 100, axis=0, keepdims=True).reshape(m, m) cov_u = np.percentile(covs, 100 - q / 2 * 100, axis=0, keepdims=True).reshape(m, m) cov_mu = mus.reshape(n_sim, m) - X.mean().to_numpy().reshape(1, m) cov_mu = np.cov(cov_mu.T) mu_l = pd.DataFrame(mu_l, index=[0], columns=cols) mu_u = pd.DataFrame(mu_u, index=[0], columns=cols) cov_l = pd.DataFrame(cov_l, index=cols, columns=cols) cov_u = pd.DataFrame(cov_u, index=cols, columns=cols) cov_mu = np.diag(np.diag(cov_mu)) cov_mu = pd.DataFrame(cov_mu, index=cols, columns=cols) if au.is_pos_def(cov_l) == False: cov_l = au.cov_fix(cov_l, method="clipped", threshold=1e-3) if au.is_pos_def(cov_u) == False: cov_u = au.cov_fix(cov_u, method="clipped", threshold=1e-3) return mu_l, mu_u, cov_l, cov_u, cov_mu