Skip to content

Commit 321e241

Browse files
committed
TST: Test GMM
Complete initial testing of linear factor GMM
1 parent 8f6f908 commit 321e241

File tree

4 files changed

+42
-13
lines changed

4 files changed

+42
-13
lines changed

linearmodels/asset_pricing/covariance.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def __init__(self, xe, *, jacobian=None, inv_jacobian=None,
3131
self._jac = jacobian
3232
self._inv_jac = inv_jacobian
3333
self._center = center
34-
if (jacobian is None and inv_jacobian is None)\
34+
if (jacobian is None and inv_jacobian is None) \
3535
or (jacobian is not None and inv_jacobian is not None):
3636
raise ValueError('One and only one of jacobian or inv_jacobian must be provided.')
3737
self._debiased = debiased
@@ -41,6 +41,16 @@ def __init__(self, xe, *, jacobian=None, inv_jacobian=None,
4141
else:
4242
self._square = inv_jacobian.shape[0] == inv_jacobian.shape[1]
4343

44+
def __str__(self):
45+
return self.__class__.__name__
46+
47+
def __repr__(self):
48+
return self.__str__() + ', id: {0}'.format(hex(id(self)))
49+
50+
@property
51+
def config(self):
52+
return {'type': self.__class__.__name__}
53+
4454
@property
4555
def s(self):
4656
"""
@@ -142,6 +152,18 @@ def __init__(self, xe, *, jacobian=None, inv_jacobian=None,
142152
if bandwidth < 0:
143153
raise ValueError('bandwidth must be non-negative.')
144154

155+
def __str__(self):
156+
descr = ', Kernel: {0}, Bandwidth: {1}'.format(self._kernel,
157+
self.bandwidth)
158+
return self.__class__.__name__ + descr
159+
160+
@property
161+
def config(self):
162+
out = super(KernelCovariance, self).config
163+
out['kernel'] = self._kernel
164+
out['bandwidth'] = self.bandwidth
165+
return out
166+
145167
@property
146168
def kernel(self):
147169
"""Kernel used in estimation"""

linearmodels/asset_pricing/model.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,8 @@ def fit(self, cov_type='robust', debiased=True, **cov_config):
252252
rsquared=r2, total_ss=total_ss, residual_ss=residual_ss,
253253
param_names=param_names, portfolio_names=self.portfolios.cols,
254254
factor_names=self.factors.cols, name=self._name,
255-
cov_type=cov_type, model=self, nobs=nobs, rp_names=self.factors.cols)
255+
cov_type=cov_type, model=self, nobs=nobs, rp_names=self.factors.cols,
256+
cov_est=cov_est)
256257

257258
return LinearFactorModelResults(res)
258259

@@ -418,7 +419,6 @@ def fit(self, cov_type='robust', debiased=True, **cov_config):
418419
and ``bandwidth`` (a positive integer).
419420
"""
420421
# TODO: Refactor commonalities in estimation
421-
# TODO: Verify GLS implementation
422422
nobs, nf, nport, nrf, s1, s2, s3 = self._boundaries()
423423
excess_returns = not self._risk_free
424424
f = self.factors.ndarray
@@ -493,7 +493,8 @@ def fit(self, cov_type='robust', debiased=True, **cov_config):
493493
rsquared=r2, total_ss=total_ss, residual_ss=residual_ss,
494494
param_names=param_names, portfolio_names=self.portfolios.cols,
495495
factor_names=factor_names, name=self._name,
496-
cov_type=cov_type, model=self, nobs=nobs, rp_names=rp_names)
496+
cov_type=cov_type, model=self, nobs=nobs, rp_names=rp_names,
497+
cov_est=cov_est)
497498

498499
return LinearFactorModelResults(res)
499500

@@ -719,9 +720,11 @@ def fit(self, center=True, use_cue=False, steps=2, disp=10, max_iter=1000,
719720
options={'disp': bool(disp), 'maxiter': max_iter})
720721
params = res.x
721722
last_obj = res.fun
723+
iter = 1
722724
# 3. Step 2 using step 1 estimates
723725
if not use_cue:
724726
for i in range(steps - 1):
727+
iter += 1
725728
g = self._moments(params, excess_returns)
726729
w = weight_est.w(g)
727730
args = (excess_returns, w)
@@ -735,6 +738,7 @@ def fit(self, center=True, use_cue=False, steps=2, disp=10, max_iter=1000,
735738
if np.abs(obj - last_obj) < 1e-6:
736739
break
737740
last_obj = obj
741+
738742
else:
739743
args = (excess_returns, weight_est)
740744
obj = self._j_cue
@@ -752,7 +756,6 @@ def fit(self, center=True, use_cue=False, steps=2, disp=10, max_iter=1000,
752756
df=self.factors.shape[1], **cov_config)
753757

754758
full_vcv = cov_est.cov
755-
self._testing = {'s': cov_est.s}
756759
sel = slice((n * k), (n * k + k + nrf))
757760
rp = params[sel]
758761
rp_cov = full_vcv[sel, sel]
@@ -789,7 +792,8 @@ def fit(self, center=True, use_cue=False, steps=2, disp=10, max_iter=1000,
789792
rsquared=r2, total_ss=total_ss, residual_ss=residual_ss,
790793
param_names=param_names, portfolio_names=self.portfolios.cols,
791794
factor_names=self.factors.cols, name=self._name,
792-
cov_type=cov_type, model=self, nobs=nobs, rp_names=rp_names)
795+
cov_type=cov_type, model=self, nobs=nobs, rp_names=rp_names,
796+
iter=iter, cov_est=cov_est)
793797

794798
return GMMFactorModelResults(res)
795799

linearmodels/asset_pricing/results.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ def __init__(self, res):
3030
self._cols = ['alpha'] + ['{0}'.format(f) for f in self._factor_names]
3131
self._rp_names = res.rp_names
3232
self._alpha_vcv = res.alpha_vcv
33+
self._cov_est = res.cov_est
3334

3435
@property
3536
def summary(self):
@@ -110,7 +111,9 @@ def summary(self):
110111
headers=header,
111112
title=title)
112113
smry.tables.append(table)
113-
smry.add_extra_txt(['See full_summary for complete results'])
114+
smry.add_extra_txt(['Covariance estimator:',
115+
str(self._cov_est),
116+
'See full_summary for complete results'])
114117

115118
return smry
116119

@@ -201,7 +204,7 @@ def tstats(self):
201204
@property
202205
def cov_estimator(self):
203206
"""Type of covariance estimator used to compute covariance"""
204-
return self._cov_type
207+
return str(self._cov_est)
205208

206209
@property
207210
def cov(self):
@@ -262,6 +265,7 @@ def residual_ss(self):
262265
class GMMFactorModelResults(LinearFactorModelResults):
263266
def __init__(self, res):
264267
super(GMMFactorModelResults, self).__init__(res)
268+
self._iter = res.iter
265269

266270
@property
267271
def std_errors(self):
@@ -273,3 +277,7 @@ def std_errors(self):
273277
se = np.r_[ase, se[:nloadings]]
274278
se = se.reshape((nportfolio, nfactor))
275279
return pd.DataFrame(se, columns=self._cols, index=self._portfolio_names)
280+
281+
def iterations(self):
282+
"""Number of steps in GMM estimation"""
283+
return self._iter

linearmodels/tests/asset_pricing/test_linear_factor_gmm.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,35 +67,30 @@ def test_linear_model_gmm_moments_jacobian(data):
6767
get_all(res)
6868

6969

70-
@pytest.mark.skip
7170
def test_linear_model_gmm_smoke_iterate(data):
7271
mod = LinearFactorModelGMM(data.portfolios, data.factors)
7372
res = mod.fit(cov_type='robust', disp=5, steps=20)
7473
get_all(res)
7574

7675

77-
@pytest.mark.skip
7876
def test_linear_model_gmm_smoke_risk_free(data):
7977
mod = LinearFactorModelGMM(data.portfolios, data.factors, risk_free=True)
8078
res = mod.fit(cov_type='robust', disp=10)
8179
get_all(res)
8280

8381

84-
@pytest.mark.skip
8582
def test_linear_model_gmm_kernel_smoke(data):
8683
mod = LinearFactorModelGMM(data.portfolios, data.factors)
8784
res = mod.fit(cov_type='kernel', disp=10)
8885
get_all(res)
8986

9087

91-
@pytest.mark.skip
9288
def test_linear_model_gmm_kernel_bandwidth_smoke(data):
9389
mod = LinearFactorModelGMM(data.portfolios, data.factors)
9490
res = mod.fit(cov_type='kernel', bandwidth=10, disp=10)
9591
get_all(res)
9692

9793

98-
@pytest.mark.skip
9994
def test_linear_model_gmm_cue_smoke(data):
10095
mod = LinearFactorModelGMM(data.portfolios, data.factors, risk_free=True)
10196
res = mod.fit(cov_type='robust', disp=10, use_cue=True)

0 commit comments

Comments
 (0)