Overview

The graph of the trading positions for cointegrated assets (LII, ZBRA) in the 4th time period (shift 1500) is shown below:
The results of (LII,ZBRA) across the first three time periods it passed are also provided.

1000 Sharpe Ratio  1000 Total Return [%]  1000 Alpha  500 Sharpe Ratio  500 Total Return [%]   500 Alpha  0 Sharpe Ratio  0 Total Return [%]    0 Alpha  1000 Beta  500 Beta    0 Beta
(LII, ZBRA)           1.611765               0.713498    0.122776          5.045619              2.339839    0.451484        6.113495            4.683680   1.352321   0.537455 -1.310287

Revised Approach

Implementation


def runner(stock_pair, shift_parameter:int, filter_func,inputs=None,outputs=None,*args):

    parameters = args

    if inputs:

        args = stock_pair[[x for x in stock_pair.columns if (str(shift_parameter) + inputs[0]) == x]]
        args = args.squeeze(1)
        args.index = [tuple(x) for x in args.index]

        p_list = [[x[0],x[1],shift_parameter,args[x]] + list(parameters) for x in stock_pair.index]

    else:
        p_list = [[x[0], x[1], shift_parameter] + list(parameters) for x in stock_pair.index]

    with Pool(processes=15) as pool:
        filter_results = pool.map(filter_func, p_list)
    filter_results = np.array(filter_results)
    col = outputs

    val = [str(shift_parameter) +  ' '  + col[x] for x in range(len(col))]

    series_coint = pd.DataFrame(index=stock_pair.index,data= filter_results,columns=val)
    series_coint = series_coint[series_coint != 0].dropna(axis=0)

    return pd.concat([series_coint,stock_pair],axis=1,join='outer').dropna()

def runner_multiple(stock_pair_list, shift_parameter_list, filter_func, *args):  
    inner = [' correlation']  
    outer = ['Sharpe Ratio','Total Return [%]','Alpha']  
  
    if len(shift_parameter_list) == 1:  
        return runner(stock_pair_list, shift_parameter_list[0], filter_func, inner, outer,*args)  
    return runner_multiple(runner(stock_pair_list, shift_parameter_list[0], filter_func,inner,outer,*args),shift_parameter_list[1:],  
                           filter_func,*args)
                           
def cointegration_filter(cur_stock):  

    cur_pair=get_time_period(list(cur_stock[0:2]), custom_data=True, num_data_points=500,shift=int(cur_stock[2])+1)  

    model = m.OLS(cur_pair[cur_stock[0]], m.add_constant(cur_pair[cur_stock[1]])).fit()  
    resudials = model.resid  
    adf_result = adfuller(resudials)  
    arr = np.zeros(2)  
    if adf_result[1] < .050000 + .0000000001:  
  
        arr = np.array([model.params.iloc[1],adf_result[1]])
    return arr

The code which executes the above is given
def run():
    full_data = pd.read_parquet('Close_h.parquet')
    shape =full_data().shape  
    full_stock_list = pd.read_parquet('Data/Prices/C_h_max.parquet').columns  
    b_test_size = 500  
    parameters = [x * b_test_size for x in list(range(int(shape[0]/b_test_size) - 1))]  

    outer = ['Beta','P'] 
    stock_pair = [(x,full_stock_list[y]) for x in full_stock_list for y in range(list(full_stock_list).index(x) + 1,len(full_stock_list) )]     
    inner = None  
  
    runner_multiple(pd.DataFrame(index=stock_pair),parameters,cointegration_filter,inner,outer,'h').to_parquet('Coint_h_.parquet')

For signal generation, the ADF test will be executed on the residuals of the pairs of the stocks, for each time period, meaning there will be a total of 4 p-values, each of which must be less than .05. The stocks which have this for all time periods will be returned, with p-values, and the beta.

Analysis of Results

The results for 3 time periods are provided
              1000 Beta    1000 P  500 Beta     500 P    0 Beta       0 P
[AIG, FTNT]   -0.083102  0.047680 -0.284665  0.017259  0.257739  0.039461
[AME, XYL]     0.726617  0.046429 -0.533354  0.034288  0.579746  0.006538
[APO, HWM]    -0.411172  0.015298  0.097011  0.006966  1.199704  0.000872
[BDX, CAH]     0.135149  0.046288  0.214275  0.048949 -0.384267  0.000344
[BDX, CBRE]    0.060702  0.049266  0.379405  0.015167  0.055965  0.000334
[BDX, CDNS]   -0.121985  0.015403  0.262596  0.024705 -0.044974  0.005623
[BDX, CPRT]   -0.154714  0.046403  0.181982  0.048639 -0.264519  0.006679
[BDX, DHI]     0.063386  0.047875  0.363210  0.040529  0.011405  0.000897
[BDX, EIX]     0.125288  0.042882 -0.195688  0.035605  0.039419  0.000868
[BDX, EW]     -0.058409  0.018648  0.309808  0.046106  0.047884  0.000379
[BDX, HLT]    -0.195437  0.041674  0.565638  0.005037 -0.008447  0.007146
[BDX, LIN]     0.294134  0.046631  0.331551  0.041965 -0.043177  0.004620
[BDX, MAR]    -0.158159  0.030317  0.475997  0.039473  0.000157  0.001231
[BDX, MGM]    -0.103435  0.011550  0.310071  0.025108  0.070420  0.000277
[BDX, ORLY]    0.125403  0.046683  0.251336  0.038608 -0.091899  0.001540
[BDX, PTC]    -0.264898  0.020838  0.455175  0.018323  0.002720  0.001169
[BDX, SNA]     0.379158  0.038147  0.342321  0.021637 -0.088651  0.001608
[BDX, SWK]     0.107498  0.041371  0.388762  0.012328 -0.007670  0.001304
[BDX, WY]      0.253453  0.037636  0.254308  0.031156  0.121255  0.000231
[BR, JNJ]      0.555380  0.032972  0.369620  0.016312  1.497904  0.038210
[BR, LH]       0.473417  0.034636  0.366693  0.005316  1.372902  0.007034
[CAH, KDP]     1.362023  0.049249 -0.883024  0.027570 -0.161966  0.018422
[CARR, ESS]    1.205232  0.011339  1.431153  0.018837  0.586101  0.004010
[CZR, IBKR]    0.537455  0.007325 -1.310287  0.046855 -0.222065  0.034051
[CZR, KDP]    -0.139386  0.030781 -1.180052  0.042852  1.208031  0.018901
[CZR, ORLY]   -0.092348  0.025337  1.434448  0.045476 -0.490495  0.034259
[CZR, PCG]    -0.139136  0.031334 -1.552383  0.047963  0.672767  0.035189
[CZR, ROK]     0.661486  0.046493  2.302104  0.033505  0.351829  0.039953
[D, DGX]       1.207393  0.025756  1.035669  0.000387  0.652909  0.008598
[D, PM]        0.663087  0.008645  1.025006  0.035694  1.053789  0.045147
[DLR, MPC]    -0.516878  0.001527 -0.099889  0.006386  0.502615  0.040538
[ELV, SYK]     0.354081  0.020848 -0.687293  0.025601  0.384177  0.028553
[HUBB, VLO]   -0.218121  0.002736  0.451207  0.026030  1.226155  0.038493
[ISRG, REGN]   0.892592  0.008572  0.553883  0.006981  1.363003  0.017960
[KMI, NI]      0.489898  0.017513  1.013257  0.023299  1.071053  0.008799
[LII, REGN]    0.661044  0.023640  0.426604  0.027499  0.447949  0.031363
[LII, ZBRA]    0.759033  0.029653  0.325289  0.040034  0.461861  0.005646
Optimization of parameters in the validation periods was implemented with the total return being maximized as the criteria.
def objective(trial:optuna.trial.Trial):  
  
    shape = pd.read_parquet('Data/Prices/C_h_max.parquet').shape  
    b_test_size = 500  
    parameters = [x * b_test_size for x in list(range(int(shape[0]/b_test_size) - 1))][0:5]  
  
    arr = pd.read_parquet('coint.parquet')  
  
    arr.index = [tuple(x) for x in arr.index]  
    arr = arr[[x for x in arr.columns if 'Beta' in x]]  
    inner = [' Beta']
    outer = ['Sharpe Ratio','Total Return [%]','Alpha']
  
    z_threshold = trial.suggest_float('z_threshold', 1.2, 1.9)  
    roll = trial.suggest_int('roll',20,50,step=5)  
    results = runner_multiple(arr, [0,500,1000], port_sim,inner,outer,'h',z_threshold, roll, )  
    results = results[[x for x in list(results.columns) if 'Total Return' in x]].mean(axis=1).sum()
  
    return results

The results (only a sample is shown)

Total Return  params_roll  params_z_threshold
3  58.262771           45            1.140969
0  56.390493           45            1.199639
4  54.469744           35            1.251505
5  54.280807           50            1.161072
1  53.661921           40            1.235526
7  53.661921           40            1.232752
8  53.094989           50            1.269552
9  52.923594           50            1.294377
6  52.495792           50            1.287840
2  45.287221           40            1.182545
The simulation of the portfolio on the testing period gives the results. The best stocks pair by total return

1000 Sharpe Ratio  1000 Total Return [%]  1000 Alpha  500 Sharpe Ratio  500 Total Return [%]   500 Alpha  0 Sharpe Ratio  0 Total Return [%]    0 Alpha  1000 Beta  500 Beta    0 Beta
(LII, ZBRA)           1.611765               0.713498    0.122776          5.045619              2.339839    0.451484        6.113495            4.683680   1.352321   0.537455 -1.310287 -0.222065
(CZR, ROK)            2.975080               3.481201    0.872476          9.472084              4.546082    1.236299        2.131693            0.856918   0.109566  -0.139386 -1.180052  1.208031
(ELV, SYK)            3.536863               3.126990    0.742342          9.563635              4.451920    1.221365        2.146127            0.856840   0.090308  -0.139136 -1.552383  0.672767
(CZR, KDP)            1.684640               2.946003    1.039572          6.384076             11.039806    4.882317        7.506912           24.010073  48.755809   0.661486  2.302104  0.351829
(CZR, PCG)            1.354914               4.814620    4.674047          3.403989             11.006988    8.085121        1.759185            5.525991   2.749191   0.354081 -0.687293  0.384177
(CZR, IBKR)           4.283332               1.136713    0.226230          2.326260              0.282280    0.051063        4.817141            0.475679   0.086839   0.489898  1.013257  1.071053
(KMI, NI)             5.627234              22.327100   41.515242          3.737291             23.853729  124.276119        1.258513            3.845521   1.555360   0.759033  0.325289  0.461861