Trading strategy for trading co-integrated stock pairs

    The purpose of this article is to share the simplest strategy of statistical arbitrage, based on the trading of co-integrated pairs of shares that were identified on the Moscow and New York exchanges.

    If we take a pair of co-integrated shares, then we have the opportunity to hedge and build a market-neutral strategy, when losses on one security will be offset by profits on another. What does it look like in practice?

    Trading strategy


    Let's say we have a co-integrated pair of stocks, X andY , as well as the prices of these shares for a certain period of time0 , . . . , T . For example, take a couple of stocks with tickers (VSYDP, NKHP) from my previousarticle onco-integration. For her, we have price data for 215 trading days.

    We will use the first half of the observations to determine the parameters of the trading strategy. Then, based on the parameters found, we will take the second half of the observations and conduct backtests, that is, test whether such a strategy will bring us money.

    For further discussion, I will need a spread of two stocks,s t = y t - β x t . In our example with a pair (VSYDP, NKHP), we already found the spread in the previous article about cointegration, so here I just duplicate the picture:

    So, we all want to buy cheap and sell expensive. If the spread goes below zero, the stockY (VSYDP) is cheaper than stockX (NKHP). Conversely, if the spread rises above zero, the stockY is more expensive than stockX (well, orX is cheaper compared toY ).

    Thus, in essence, a trading strategy is to buy a stockY and sell the stockX in the ratio1 : β if the spread is slightly below zero (below the lineG in the figure). When the spread returns back to zero, we need to close the position by sellingY and buyingX in the same ratio. In this case, we get a profit of sizeG :

    It is important to understand here that we should be able to sell stocks that we do not own - this is called short selling (short). It should also be noted that not all brokers include the possibility of a short sale in a standard package of services, so you may have to turn to it in order to expand your investment opportunities.

    In general, we compose a portfolio that contains one long position (long) and one short position (short) if the spread crosses a certain lineG or leaves behind it in the opposite direction from zero. And we close all positions when the spread returns back to zero.

    The next question that arises is “how to find the meaningG "?

    How to find the value G


    In addition to mathematics, I will immediately give here an implementation in matlab. The code below is a natural continuation of the code from the previous article on cointegration . We will need the first half of the observations,t = 0 , . . . , T / 2 , which we will consider as "history."

    T = length(testPrices);
    half = round(T/2); 
    

    First, find the average ratio Y andX for the first half of the observations.

    ˉ r =1T / 2 + 1 T / 2 t=0ytx t .


    sumRatio = 0;
    fori = 1 : half
        sumRatio = sumRatio + testPrices(i,1) / testPrices(i,2);
    end
    r = sumRatio / half;
    

    For a pair of stocks with tickers (VSYDP, NKHP), the calculated value ˉ r =34,3927. Then we calculate the maximum absolute value of the spread for the first half of the observations:

    m = max


    clear absspread
    fori = 1 : half
        absspread(i,1) = abs(testPrices(i,1) - r * testPrices(i,2));
    end
    m = max(absspread);
    

    For a pair of stocks with tickers (VSYDP, NKHP), the calculated value . Now we can determine the value by brute force: take a certain percentage of and try to trade on the “history” for various values ​​of this percentage, and then choose the value that will give the greatest profit. This will be the desired value for the line.

    Iterating over various values to find the best


    First we need to calculate the number of trades. The first trade, we denote it, we do when we first get into a position. In this case, we still do not get profit:


    Where - spread. Further successful trading moments will be:
    1. if a : ;
    2. if a : .

    Then the profit will be calculated as where - number of trades, - some percentage of . If there were no trades, then the profit is zero. Is the minimum profit, if you always trade in one ratio and .

    clear profit
    for h = 1:10
        g = 0.05 * h * m;
        profit(h,1) = 0.05 * h * 100;
        profit(h,2) = g;
        clear trade
        k = 1;
        fori = 1:half - 1ifabs(spread(i)) >= g
                trade(1,1) = i;
                trade(1,2) = spread(i);
                trade(1,3) = -1;
                trade(1,4) = beta;
                trade(1,5) = testPrices(i,1);
                trade(1,6) = testPrices(i,2);
                trade(1,7) = 0;
                startIndex = i;
                k = 2;
                breakendendif k == 1breakendfori = startIndex:half - 1if (trade(k-1,2) <= -g) && (spread(i) <= g) && (spread(i+1) >= g)
                trade(k,1) = i+1;
                trade(k,2) = spread(i+1);
                trade(k,3) = -1;
                trade(k,4) = beta;
                trade(k,5) = testPrices(i+1,1);
                trade(k,6) = testPrices(i+1,2);
                trade(k,7) = 0;
                k = k + 1;
            endif (trade(k-1,2) >= g) && (spread(i) > -g) && (spread(i+1) <= -g)
                trade(k,1) = i+1;
                trade(k,2) = spread(i+1);
                trade(k,3) = 1;
                trade(k,4) = -beta;
                trade(k,5) = testPrices(i+1,1);
                trade(k,6) = testPrices(i+1,2);
                trade(k,7) = 0;
                k = k + 1;
            endendif exist('trade', 'var')
            tradesNumber = size(trade,1);
            profit(h,3) = tradesNumber;
            profit(h,4) = (tradesNumber - 1) * 2 * g;
        else
            profit(h,3) = 0;
            profit(h,4) = 0;
        endend

    The table below shows the percentages and outcomes for a pair of stocks with tickers (VSYDP, NKHP).
    PercentTradesProfit
    five160.222471922.7
    ten320,4449five2563.6
    15480,6673five3845.3
    20640,8898five5127.1
    25801,1122five6408.9
    thirty961,3347five7690.7
    351121.6five8972.5
    401281.835127.1
    45144235768
    501602,236408.9
    551762.437049.8
    601922.737690.7
    652082.938331.6
    702243.138972.5
    752,403.339613.3
    802563.6310254
    852723.8one0
    90288400

    To determine the value , we just choose the value that gives the greatest profit based on the "history".

    [M, I] = max(profit(:,4));
    bestG = profit(I,2);
    

    However though (at 80% of ) gives the greatest profit, in practice we do not choose more than , due to the difficulties associated with further profit, therefore, take (at 35% of )

    Strategy Testing


    After determining trading strategy is applied to the second half of the observations.

    clear strategy
    fori = half + 1:T
        ifabs(spread(i)) >= bestG
            strategy(1,1) = i;
            strategy(1,2) = spread(i);
            if spread(i) > 0
                strategy(1,3) = -1;
                strategy(1,4) = beta;
            else
                strategy(1,3) = 1;
                strategy(1,4) = -beta;
            end
            strategy(1,5) = testPrices(i,1);
            strategy(1,6) = testPrices(i,2);
            strategy(1,7) = 0;
            startIndex = i;
            breakendendif exist('strategy', 'var')
        k = 2;
        fori = startIndex:T-1if (strategy(k-1,3) ~= 0)
                if (spread(i) >= 0) && (spread(i+1) <= 0)
                    strategy(k,1) = i+1;
                    strategy(k,2) = spread(i+1);
                    strategy(k,3) = 0;
                    strategy(k,4) = 0;
                    strategy(k,5) = testPrices(i+1,1);
                    strategy(k,6) = testPrices(i+1,2);
                    strategy(k,7) = strategy(k-1,2) - spread(i+1);
                    k = k + 1;
                endif (spread(i) <= 0) && (spread(i+1) >= 0)
                    strategy(k,1) = i+1;
                    strategy(k,2) = spread(i+1);
                    strategy(k,3) = 0;
                    strategy(k,4) = 0;
                    strategy(k,5) = testPrices(i+1,1);
                    strategy(k,6) = testPrices(i+1,2);
                    strategy(k,7) = spread(i+1) - strategy(k-1,2);
                    k = k + 1;
                endelseif (spread(i) <= bestG) && (spread(i+1) >= bestG)
                    strategy(k,1) = i+1;
                    strategy(k,2) = spread(i+1);
                    strategy(k,3) = -1;
                    strategy(k,4) = beta;
                    strategy(k,5) = testPrices(i+1,1);
                    strategy(k,6) = testPrices(i+1,2);
                    strategy(k,7) = 0;
                    k = k + 1;
                endif (spread(i) >= -bestG) && (spread(i+1) <= -bestG)
                    strategy(k,1) = i+1;
                    strategy(k,2) = spread(i+1);
                    strategy(k,3) = 1;
                    strategy(k,4) = -beta;
                    strategy(k,5) = testPrices(i+1,1);
                    strategy(k,6) = testPrices(i+1,2);
                    strategy(k,7) = 0;
                    k = k + 1;
                endendendendif exist('strategy', 'var')
        totalProfit = sum(strategy(:,7));
    else
        totalProfit = 0;
    end

    At profit is 3 times. In other words, the difference 3 times moves from 0 toand back. Please note that the implementation of such a strategy includes 6 trades, since in order to get into a position and exit from it, you need to make two trades.

    The figure and table below show all 6 points of trading.

    TradePosition Price Price Profit
    one1083145.9(-1; +35.6527)13200282-
    2128-211,447Liquidation97002783357.4
    3134-1161.9(+1; -35.6527)8500271-
    four17114,6605Liquidation85002381176.5
    five205-1184,5(+1; -35.6527)7800252-
    6212185,1035Liquidation81002221369.6
     Total5903.5

    The profit made here is not less than . It uses closing prices instead of intraday data, so we do not trade in points0 and . As can be seen from the table, the yield for 107 trading days amounted to 25.39% excluding commissions, volume, etc. That is, it is approximately 60.74% per annum for very rough estimates.

    Tests were also conducted for 58 couples on the NYSE. There, except for zero profit, the approximate annual yield for this strategy ranged from 23.34% to 208%, excluding commissions, volume, etc.

    Testing an Alternative Strategy


    Instead of closing a position when the spread approaches zero, you can flip the position when the spread reaches on the opposite side from zero. Suppose we sold and bought since the difference was greater .

    Now you can wait for the moment when the difference reachesbuy and sell . As a result, we will remain with the portfolio from a long position of size and short position size .

    clear strategyAlt
    fori = half + 1:T
        ifabs(spread(i)) >= bestG
            strategyAlt(1,1) = i;
            strategyAlt(1,2) = spread(i);
            if spread(i) > 0
                strategyAlt(1,3) = -1;
                strategyAlt(1,4) = beta;
            else
                strategyAlt(1,3) = 1;
                strategyAlt(1,4) = -beta;
            end
            strategyAlt(1,5) = testPrices(i,1);
            strategyAlt(1,6) = testPrices(i,2);
            strategyAlt(1,7) = 0;
            startIndex = i;
            breakendendif exist('strategyAlt', 'var')
        d = 2;
        fori = startIndex:T-1if (strategyAlt(d-1,2) >= bestG) && (spread(i) >= -bestG) && (spread(i+1) <= -bestG)
                strategyAlt(d,1) = i+1;
                strategyAlt(d,2) = spread(i+1);
                strategyAlt(d,3) = 1;
                strategyAlt(d,4) = -beta;
                strategyAlt(d,5) = testPrices(i+1,1);
                strategyAlt(d,6) = testPrices(i+1,2);
                strategyAlt(d,7) = strategyAlt(d-1,2) - spread(i+1);
                d = d + 1;
            endif (strategyAlt(d-1,2) <= -bestG) && (spread(i) <= bestG) && (spread(i+1) >= bestG)
                strategyAlt(d,1) = i+1;
                strategyAlt(d,2) = spread(i+1);
                strategyAlt(d,3) = -1;
                strategyAlt(d,4) = beta;
                strategyAlt(d,5) = testPrices(i+1,1);
                strategyAlt(d,6) = testPrices(i+1,2);
                strategyAlt(d,7) = spread(i+1) - strategyAlt(d-1,2);
                d = d + 1;
            endendendif exist('strategyAlt', 'var')
        totalAltProfit = sum(strategyAlt(:,7));
    else
        totalAltProfit = 0;
    end

    Such a strategy leads to one initial trade and one trade that reverses a position. These trades are presented in the figure and in the table below.

    TradePosition Price Price Profit
    one1083145.9(-1; +35.6527)13200282-
    2134-1161.9(+1; -35.6527)85002714307.8
     Total4307.8

    Please note that the profit from reversing a position is , therefore, the total profit in this case is at least . As can be seen from the table, the profitability for 107 trading days amounted to 18.53% excluding commissions, volume, etc. That is, it is approximately 44.32% per annum for very rough estimates.

    Tests were also conducted for 58 couples on the NYSE. There, except for zero profit, the approximate annual yield for this strategy ranged from 17.63% to 201.53%, excluding commissions, volume, etc.

    This change in the trading strategy reduces the number of trades on average by 2 times. At the same time, trading costs are reduced. If the difference moved up and down around 0, an alternative strategy would be more profitable. However, in the case considered, when the pair has a tendency to move between 0 and, the position is never turned over at all, while the main strategy creates and liquidates the position again and again ... and makes money.

    findings


    When trading in co-integrated pairs, theoretically, it is possible to extract stable profits, in particular, using the methods described above in the form of two trading strategies. In the case of the considered pair of shares (VSYDP, NKHP), the first method turned out to be more effective due to the tendency to fluctuate below zero in their difference.

    The possibility of generating stable profits when trading co-integrated pairs looks optimistic, but requires further analysis on demo accounts, which we will talk about next time.

    What to read on the topic


    Terry J. Watsham, Kate Parramow. Quantitative methods in finance / Per. from English under the editorship of M.R. Efimova. - M.: Finance, UNITI, 1999. - 527 p.

    According to this textbook, quantitative analysis at the HSE was taught for masters at the time. There is a section on cointegration.

    UPD. 2017 Backtest Results on the Moscow Exchange

    Also popular now: