Repository: statsmodels/statsmodels

Issue:
coint on two identical time series return a coint pvalue not equal to 0
`from statsmodels.tsa.stattools import coint`
`coint(ANALYTICS_DATA['mid_vwap_TP'], ANALYTICS_DATA['mid_vwap_TP'])`

return:
`(-4.0238632646196653,
 0.0066382019114883646,
 array([-3.43036157, -2.86154511, -2.56677272]))`

I would imagine it would return 0...



Discussion:
Note: the function has changed in master and 0.8. The old version was never public and is buggy for several options.

but there is the same problem in the corner case, but not with a small noise added

```
>>> from statsmodels.tsa.stattools import coint
>>> zz = np.random.randn(500).cumsum()
>>> coint(zz, zz, maxlag=3, autolag=None)
(0.14404335520602779, 0.98874068144442639, array([-3.91852234, -3.34840209, -3.05296032]))
>>> help(coint)
Help on function coint in module statsmodels.tsa.stattools:

coint(y0, y1, trend='c', method='aeg', maxlag=None, autolag='aic', return_results=None)
    Test for no-cointegration of a univariate equation
... 

>>> coint(zz, zz + 1e-4 *np.random.randn(len(zz)), maxlag=3, autolag=None)
(-10.935213140495513, 1.1950011740648767e-18, array([-3.91852234, -3.34840209, -3.05296032]))
```

AFAIK: The error correction term, i.e. residual of cointegrating relationship is identically zero, so the slope parameter for the ec term in the difference regression will not be identified.
most likely we get "some" numbers because OLS is using pinv.

I think we can check r_squared or something similar of the cointegrating regression to find this case. But that also needs a threshold. 
Should we raise or return `(nan, 0, array(...))` ?

OLS question: why is the t-value not `nan`? I think it should be 0/0. too much floating point noise?
**correction** the ADF-diff regression is all zeros (zero endog and several zero exog), so nothing really makes sense.

@NoahLiot Do you have a preference between raising an exception or issuing a warning and returning (nan, 0) in this case?
nan still breaks code that compares the test statistic with the critical values. (for that -np.inf would be better)

Hello,

I would imagine the result on such could be issuing a warning with `(-np.inf, 0, array(...))` as this would make sense, and not break the test. 
Raising exception for something that should be mathematically possible to do seems a bit drastic since, for example, if the user loop over different data series, he would end up having to handle duplicate data. It also obfuscate further weird results investigation. This is how I was able to pinpoint this bug. Just my two cents.

Cheers!


Resolving patch:
diff --git a/statsmodels/tools/sm_exceptions.py b/statsmodels/tools/sm_exceptions.py
--- a/statsmodels/tools/sm_exceptions.py
+++ b/statsmodels/tools/sm_exceptions.py
@@ -114,4 +114,8 @@ class SpecificationWarning(UserWarning):
 
 
 class HessianInversionWarning(UserWarning):
-    pass
\ No newline at end of file
+    pass
+
+
+class ColinearityWarning(UserWarning):
+    pass
diff --git a/statsmodels/tsa/stattools.py b/statsmodels/tsa/stattools.py
--- a/statsmodels/tsa/stattools.py
+++ b/statsmodels/tsa/stattools.py
@@ -1010,8 +1010,16 @@ def coint(y0, y1, trend='c', method='aeg', maxlag=None, autolag='aic',
 
     res_co = OLS(y0, xx).fit()
 
-    res_adf = adfuller(res_co.resid, maxlag=maxlag, autolag=None,
-                             regression='nc')
+    if res_co.rsquared < 1 - np.sqrt(np.finfo(np.double).eps):
+        res_adf = adfuller(res_co.resid, maxlag=maxlag, autolag=None,
+                           regression='nc')
+    else:
+        import warnings
+        warnings.warn("y0 and y1 are perfectly colinear.  Cointegration test "
+                      "is not reliable in this case.")
+        # Edge case where series are too similar
+        res_adf = (0,)
+
     # no constant or trend, see egranger in Stata and MacKinnon
     if trend == 'nc':
         crit = [np.nan] * 3  # 2010 critical values not available


Test patch:
diff --git a/statsmodels/tsa/tests/test_stattools.py b/statsmodels/tsa/tests/test_stattools.py
--- a/statsmodels/tsa/tests/test_stattools.py
+++ b/statsmodels/tsa/tests/test_stattools.py
@@ -1,6 +1,7 @@
 from statsmodels.compat.numpy import recarray_select
 
 from statsmodels.compat.python import lrange
+from statsmodels.tools.sm_exceptions import ColinearityWarning
 from statsmodels.tsa.stattools import (adfuller, acf, pacf_ols, pacf_yw,
                                                pacf, grangercausalitytests,
                                                coint, acovf, kpss, ResultsStore,
@@ -339,6 +340,34 @@ def test_coint():
             assert_allclose(r1, r2, rtol=0, atol=6e-7)
 
 
+def test_coint_identical_series():
+    nobs = 200
+    scale_e = 1
+    np.random.seed(123)
+    y = scale_e * np.random.randn(nobs)
+    warnings.simplefilter('always', ColinearityWarning)
+    with warnings.catch_warnings(record=True) as w:
+        c = coint(y, y, trend="c", maxlag=0, autolag=None)
+    assert_equal(len(w), 1)
+    assert_equal(c[0], 0.0)
+    # Limit of table
+    assert_(c[1] > .98)
+
+
+def test_coint_perfect_collinearity():
+    nobs = 200
+    scale_e = 1
+    np.random.seed(123)
+    x = scale_e * np.random.randn(nobs, 2)
+    y = 1 + x.sum(axis=1)
+    warnings.simplefilter('always', ColinearityWarning)
+    with warnings.catch_warnings(record=True) as w:
+        c = coint(y, x, trend="c", maxlag=0, autolag=None)
+    assert_equal(c[0], 0.0)
+    # Limit of table
+    assert_(c[1] > .98)
+
+
 class TestGrangerCausality(object):
 
     def test_grangercausality(self):