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 k_ar_diff (1). Based on the trace statistics I select stock pairs if the r0_trace > r0_crit (5% quantile) and r1_trace <= 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 statsmodels.tsa.stattools.adfuller/kpss function.

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
        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])[:, 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)

    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),
        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[0]
        hedge_ratio = row[1].loc['co_vec2']
        df_pair = training_data.loc[:, pair.split('-')]
        df_pair.loc[:, 'spread'] = df_pair.iloc[:, 0] - hedge_ratio * df_pair.iloc[:, 1]


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy