def test_linear_model_gmm_kernel_smoke(data): mod = LinearFactorModelGMM(data.portfolios, data.factors) res = mod.fit(cov_type="kernel", disp=10) get_all(res) str(res._cov_est) res._cov_est.__repr__() str(res._cov_est.config)
def test_linear_model_gmm_smoke_risk_free(data): mod = LinearFactorModelGMM(data.portfolios, data.factors, risk_free=True) res = mod.fit(cov_type="robust", disp=10) get_all(res) str(res._cov_est) res._cov_est.__repr__() str(res._cov_est.config)
def test_linear_model_gmm_moments_jacobian(data): mod = LinearFactorModelGMM(data.portfolios, data.factors) res = mod.fit(cov_type="robust", disp=0, debiased=False) params = np.r_[res.betas.values.ravel(), res.risk_premia.values.ravel(), mod.factors.ndarray.mean(0), ] mod_mom = mod._moments(params[:, None], True) mom = [] p = mod.portfolios.ndarray f = mod.factors.ndarray n = f.shape[0] fc = np.c_[np.ones((n, 1)), f] mu = f.mean(0)[None, :] lam = res.risk_premia.values[None, :] x = f - mu + lam b = res.betas.values for i in range(p.shape[1]): eps = p[:, i:(i + 1)] - x @ b[[i]].T for j in range(fc.shape[1]): mom.append(eps * fc[:, [j]]) mom.append(f - mu) mom_arr = np.hstack(tuple(mom)) mod_jac = mod._jacobian(params, True) jac = np.zeros((mom_arr.shape[1], params.shape[0])) nport, nf = p.shape[1], f.shape[1] # 1,1 jac[:(nport * (nf + 1)), :nport * nf] = np.kron(np.eye(nport), fc.T @ x / n) # 1, 2 col = [] for i in range(nport): col.append(fc.T @ np.ones((n, 1)) @ b[[i]] / n) col = np.vstack(tuple(col)) jac[:(nport * (nf + 1)), nport * nf:nport * nf + nf] = col # 1, 3 col = [] for i in range(nport): col.append(-fc.T @ np.ones((n, 1)) @ b[[i]] / n) col = np.vstack(tuple(col)) jac[:(nport * (nf + 1)), -nf:] = col # 2,2 jac[-nf:, -nf:] = np.eye(nf) assert_allclose(mom_arr, mod_mom) assert_allclose(jac, mod_jac) me = mom_arr - mom_arr.mean(0)[None, :] s = me.T @ me / n s = (s + s.T) / 2 cov = np.linalg.inv(jac.T @ np.linalg.inv(s) @ jac) / n cov = (cov + cov.T) / 2 assert_allclose(np.diag(cov), np.diag(res.cov), rtol=5e-3) get_all(res)
def test_linear_model_time_series(data): mod = TradedFactorModel(data.portfolios, data.factors) mod.fit() res = mod.fit() get_all(res) all_params = [] all_tstats = [] nobs, nport = data.portfolios.shape nf = data.factors.shape[1] e = np.zeros((nobs, (nport * (nf + 1)))) x = np.zeros((nobs, (nport * (nf + 1)))) factors = sm.add_constant(data.factors) loc = 0 for i in range(data.portfolios.shape[1]): if isinstance(data.portfolios, pd.DataFrame): p = data.portfolios.iloc[:, i:(i + 1)] else: p = data.portfolios[:, i:(i + 1)] ols_res = _OLS(p, factors).fit(cov_type='robust', debiased=True) all_params.extend(list(ols_res.params)) all_tstats.extend(list(ols_res.tstats)) x[:, loc:(loc + nf + 1)] = factors e[:, loc:(loc + nf + 1)] = ols_res.resids.values[:, None] loc += nf + 1 cov = res.cov.values[(nf + 1) * i:(nf + 1) * (i + 1), (nf + 1) * i:(nf + 1) * (i + 1)] ols_cov = ols_res.cov.values assert_allclose(cov, ols_cov) assert_allclose(res.params.values.ravel(), np.array(all_params)) assert_allclose(res.tstats.values.ravel(), np.array(all_tstats)) assert_allclose(res.risk_premia, np.asarray(factors).mean(0)[1:]) xpxi_direct = np.eye((nf + 1) * nport + nf) f = np.asarray(factors) fpfi = np.linalg.inv(f.T @ f / nobs) nfp1 = nf + 1 for i in range(nport): st, en = i * nfp1, (i + 1) * nfp1 xpxi_direct[st:en, st:en] = fpfi f = np.asarray(factors)[:, 1:] xe = np.c_[x * e, f - f.mean(0)[None, :]] xeex_direct = xe.T @ xe / nobs cov = xpxi_direct @ xeex_direct @ xpxi_direct / (nobs - nfp1) assert_allclose(cov, res.cov.values) alphas = np.array(all_params)[0::nfp1][:, None] alpha_cov = cov[0:(nfp1 * nport):nfp1, 0:(nfp1 * nport):nfp1] stat_direct = float(alphas.T @ np.linalg.inv(alpha_cov) @ alphas) assert_allclose(res.j_statistic.stat, stat_direct) assert_allclose(1.0 - stats.chi2.cdf(stat_direct, nport), res.j_statistic.pval)
def test_linear_model_gmm_kernel_bandwidth_smoke(data): mod = LinearFactorModelGMM(data.portfolios, data.factors) res = mod.fit(cov_type="kernel", bandwidth=10, disp=10) get_all(res)
def test_linear_model_gmm_smoke_iterate(data): mod = LinearFactorModelGMM(data.portfolios, data.factors) res = mod.fit(cov_type="robust", disp=5, steps=20) get_all(res)
def test_linear_model_gmm_cue_smoke(data): mod = LinearFactorModelGMM(data.portfolios, data.factors, risk_free=True) res = mod.fit(cov_type="robust", disp=10, use_cue=True) get_all(res)
def test_linear_model_parameters_risk_free_gls(data): mod = LinearFactorModel(data.portfolios, data.factors, risk_free=True) p = mod.portfolios.ndarray sigma = np.cov(p.T) val, vec = np.linalg.eigh(sigma) sigma_m12 = vec @ np.diag(1.0 / np.sqrt(val)) @ vec.T sigma_inv = np.linalg.inv(sigma) mod = LinearFactorModel(data.portfolios, data.factors, risk_free=True, sigma=sigma) assert 'using GLS' in str(mod) 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] + 1 + p.shape[1])) fc = np.c_[np.ones((n, 1)), f] betas = lstsq(fc, p)[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 bc = np.c_[np.ones((p.shape[1], 1)), betas[1:, :].T] lam = lstsq(sigma_m12 @ bc, sigma_m12 @ p.mean(0)[:, None])[0] pricing_errors = p - (bc @ lam).T for i in range(lam.shape[0]): lam_error = pricing_errors @ sigma_inv @ bc[:, [i]] moments[:, loc] = lam_error.squeeze() loc += 1 alphas = p.mean(0)[:, None] - bc @ lam moments[:, loc:] = pricing_errors - alphas.T mod_moments = mod._moments(eps, bc, lam, alphas, pricing_errors) assert_allclose(res.betas, bc[:, 1:]) 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 + 1 bct = sigma_inv @ bc at = sigma_inv @ alphas for i in range(nport): block = np.zeros((nf + 1, nf + 1)) for j in range(nf + 1): # rows for k in range(1, nf + 1): # cols block[j, k] = bct[i][j] * lam[k] if j == k: block[j, k] -= at[i] jac[block1:block2, loc:loc + nf + 1] = block loc += nf + 1 # 2, 2 jac[block1:block2, block1:block2] = bc.T @ sigma_inv @ bc # 3,1 block = np.zeros((nport, nport * (nf + 1))) row = col = 0 for i in range(nport): for j in range(nf + 1): if j != 0: block[row, col] = lam[j] col += 1 row += 1 jac[-nport:, :(nport * (nf + 1))] = block # 3, 2 jac[-nport:, (nport * (nf + 1)):(nport * (nf + 1)) + nf + 1] = bc # 3, 3: already done since eye mod_jac = mod._jacobian(bc, 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.cov.values[:block1:(nf + 1), :block1:(nf + 1)], acov) assert_allclose(res.j_statistic.stat, jstat, rtol=1e-1) assert_allclose(res.j_statistic.pval, 1 - stats.chi2(nport - nf - 1).cdf(jstat), rtol=1e-2) get_all(res)
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)[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])[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, lam, 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 i 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)
def test_linear_model_gmm_smoke_risk_free(data): mod = LinearFactorModelGMM(data.portfolios, data.factors, risk_free=True) res = mod.fit(cov_type='robust', disp=10) get_all(res)
def test_linear_model_gmm_smoke(data): mod = LinearFactorModelGMM(data.portfolios, data.factors) res = mod.fit(cov_type='robust', disp=5) get_all(res)