def _kernel_cov(self, z: Float64Array) -> Float64Array: nobs = z.shape[0] bw = self.bandwidth kernel = self._kernel assert kernel is not None kernel_estimator = KERNEL_LOOKUP[kernel] weights = kernel_estimator(bw, nobs - 1) out = cov_kernel(z, weights) return (out + out.T) / 2
def cov(self) -> NDArray: """Estimated covariance""" e = np.asarray(self._all_params) - self._params.T e = e[np.all(np.isfinite(e), 1)] nobs = e.shape[0] bw = self.bandwidth assert self._kernel is not None w = KERNEL_LOOKUP[self._kernel](bw, nobs - 1) cov = cov_kernel(e, w) return cov / (nobs - int(bool(self._debiased)))
def cov(self) -> NDArray: """Estimated covariance""" x = self._x nobs = x.shape[0] xpxi = inv(x.T @ x / nobs) eps = self.eps xe = x * eps assert self._time_ids is not None xe = DataFrame(xe, index=self._time_ids.squeeze()) xe = xe.groupby(level=0).sum() xe.sort_index(inplace=True) xe_nobs = xe.shape[0] bw = self._bandwidth if self._bandwidth is None: bw = float(np.floor(4 * (xe_nobs / 100)**(2 / 9))) assert bw is not None w = KERNEL_LOOKUP[self._kernel](bw, xe_nobs - 1) xeex = cov_kernel(xe.values, w) * (xe_nobs / nobs) xeex *= self._scale out = (xpxi @ xeex @ xpxi) / nobs return (out + out.T) / 2
def _single_cov(self, xe: NDArray, bw: float) -> NDArray: nobs = xe.shape[0] w = KERNEL_LOOKUP[self._kernel](bw, nobs - 1) return cov_kernel(xe, w)
def test_linear_model_parameters(data): mod = LinearFactorModel(data.portfolios, data.factors) res = mod.fit() f = mod.factors.ndarray p = mod.portfolios.ndarray n = f.shape[0] moments = np.zeros( (n, p.shape[1] * (f.shape[1] + 1) + f.shape[1] + p.shape[1])) fc = np.c_[np.ones((n, 1)), f] betas = lstsq(fc, p, rcond=None)[0] eps = p - fc @ betas loc = 0 for i in range(eps.shape[1]): for j in range(fc.shape[1]): moments[:, loc] = eps[:, i] * fc[:, j] loc += 1 b = betas[1:, :].T lam = lstsq(b, p.mean(0)[:, None], rcond=None)[0] pricing_errors = p - (b @ lam).T for i in range(lam.shape[0]): lam_error = (p - (b @ lam).T) @ b[:, [i]] moments[:, loc] = lam_error.squeeze() loc += 1 alphas = pricing_errors.mean(0)[:, None] moments[:, loc:] = pricing_errors - alphas.T mod_moments = mod._moments(eps, b, alphas, pricing_errors) assert_allclose(res.betas, b) assert_allclose(res.risk_premia, lam.squeeze()) assert_allclose(res.alphas, alphas.squeeze()) assert_allclose(moments, mod_moments) m = moments.shape[1] jac = np.eye(m) block1 = p.shape[1] * (f.shape[1] + 1) # 1,1 jac[:block1, :block1] = np.kron(np.eye(p.shape[1]), fc.T @ fc / n) # 2, 1 loc = 0 nport, nf = p.shape[1], f.shape[1] block2 = block1 + nf for i in range(nport): block = np.zeros((nf, nf + 1)) for j in range(nf): # rows for k in range(1, nf + 1): # cols block[j, k] = b[i][j] * lam[k - 1] if j + 1 == k: block[j, k] -= alphas[i] jac[block1:block2, loc:loc + nf + 1] = block loc += nf + 1 # 2, 2 jac[block1:block2, block1:block2] = b.T @ b # 3,1 block = np.zeros((nport, nport * (nf + 1))) row = col = 0 for _ in range(nport): for j in range(nf + 1): if j != 0: block[row, col] = lam[j - 1] col += 1 row += 1 jac[-nport:, :(nport * (nf + 1))] = block # 3, 2 jac[-nport:, (nport * (nf + 1)):(nport * (nf + 1)) + nf] = b # 3, 3: already done since eye mod_jac = mod._jacobian(b, lam, alphas) assert_allclose(mod_jac[:block1], jac[:block1]) assert_allclose(mod_jac[block1:block2, :block1], jac[block1:block2, :block1]) assert_allclose(mod_jac[block1:block2, block1:block2], jac[block1:block2, block1:block2]) assert_allclose(mod_jac[block1:block2, block2:], jac[block1:block2, block2:]) assert_allclose(mod_jac[block2:], jac[block2:]) s = moments.T @ moments / (n - (nf + 1)) ginv = np.linalg.inv(jac) cov = ginv @ s @ ginv.T / n order = np.zeros((nport, nf + 1), dtype=np.int64) order[:, 0] = np.arange(block2, block2 + nport) for i in range(nf): order[:, i + 1] = (nf + 1) * np.arange(nport) + (i + 1) order = np.r_[order.ravel(), block1:block2] cov = cov[order][:, order] cov = (cov + cov.T) / 2 assert_allclose(cov, res.cov) acov = cov[:block1:(nf + 1), :block1:(nf + 1)] jstat = float(alphas.T @ np.linalg.pinv(acov) @ alphas) assert_allclose(res.j_statistic.stat, jstat) assert_allclose(res.j_statistic.pval, 1 - stats.chi2(nport - nf).cdf(jstat)) get_all(res) res = LinearFactorModel(data.portfolios, data.factors).fit(cov_type="kernel", debiased=False) std_mom = moments / moments.std(0)[None, :] mom = std_mom.sum(1) bw = kernel_optimal_bandwidth(mom) w = kernel_weight_bartlett(bw, n - 1) s = cov_kernel(moments, w) cov = ginv @ s @ ginv.T / n cov = cov[order][:, order] cov = (cov + cov.T) / 2 assert_allclose(cov, res.cov)