Before I start, I asked this question on Quant Finance before. But other Questions going into the same direction have been redirected to this stack exchange. Therefore I post this here as well.
I'm working with Python and use the
statsmodels.tsa.vector_ar.vecm.coint_johansen function to analyze if several stocks have a stationary error term with respect to pairs trading. I pass the log of the close prices for the stocks into the function and chose
constant term for
det_order (0) and number of lagged differences as 1 for
Based on the trace statistics I select stock pairs if the
r0_crit (5% quantile) and
r1_crit (5% quantile). Which is in line with the literature that I have read into that topic. Based on the weights for the pairs the resulting error should then be stationary.
Out of curiosity I then used the ADF and KPSS test on the resulting errors for the training periods, which then resulted in only 30% of the errors being stationary. Now I'm wondering where this issue has its roots, is it rather on the application of the Johansen-Test or based on the ADF/ KPSS-test.
The dataset contains daily close prices and the training period is either 1 year, 6 months, 3 months, or 1 month. The results for the ADF and KPSS tests do not really differ based on the training period duration.
For KPSS and ADF I use the default setting of the
Below is the code that determines if a pair has a stationary error (spread) for the training period.
# Here I define the Johansen summary def coint_johansen_summary(data,Quantil=95): if Quantil == 90: a = 0 elif Quantil == 99: a = 2 else: a = 1 cj_test = coint_johansen(data, 0, 1) r0_trace, r1_trace = cj_test.lr1 r0_crit, r1_crit = cj_test.cvt[:, a] co_int_vector = (cj_test.evec / cj_test.evec)[:, 0] return r0_trace, r0_crit, r1_trace, r1_crit, co_int_vector # Here I calculate the Johansen summary for each possible pair in my data set for pair in combinations(training_data.columns, 2): p1, p2 = pair pair_names.append(p1 + '-' + p2) r0_t, r0_c, r1_t, r1_c, co_vec = coint_johansen_summary(training_data.loc[:, [p1, p2]].values, Trace_Quantil) r0_trace.append(r0_t) r0_crit.append(r0_c) r1_trace.append(r1_t) r1_crit.append(r1_c) coint_vectors.append(co_vec) results = pd.DataFrame( data = np.concatenate( (np.array(r0_trace).reshape(-1, 1), np.array(r0_crit).reshape(-1, 1), np.array(r1_trace).reshape(-1, 1), np.array(r1_crit).reshape(-1, 1), np.array(coint_vectors)), axis = 1), columns = ['r0_trace', 'r0_crit', 'r1_trace', 'r1_crit', 'co_vec1', 'co_vec2'], index = pair_names ) # Here I chose which pairs fit the trace statistics co_int_pairs = results[(results.r0_trace > r0_crit) & (results.r1_trace <= r1_crit)] # Here I then calculate the spread for the training data for each pair. Afterwards I would calculate the ADF and KPSS Test. for row in co_int_pairs.iterrows(): pair = row hedge_ratio = row.loc['co_vec2'] df_pair = training_data.loc[:, pair.split('-')] df_pair.loc[:, 'spread'] = df_pair.iloc[:, 0] - hedge_ratio * df_pair.iloc[:, 1]