Торговая стратегия для торговли коинтегрированными парами акций
Цель данной статьи — поделиться простейшей стратегией статистического арбитража, основанной на торговле коинтегрированными парами акций, которые были выявлены на Московской и Нью-Йоркской биржах.
Если мы возьмём пару коинтегрированных акций, то у нас есть возможность захеджироваться и построить рыночно-нейтральную стратегию, когда убытки по одной бумаге будут компенсироваться прибылями по другой. Как это выглядит на практике?
Торговая стратегия
Допустим, у нас есть коинтегрированная пара акций, и , а также цены этих акций за некий период времени . Для примера возьмём пару акций с тикерами (VSYDP, NKHP) из моей предыдущей статьи про коинтеграцию. Для неё у нас есть данные о ценах за 215 торговых дней.
Первую половину наблюдений мы будем использовать, чтобы определить параметры торговой стратегии. Затем, основываясь на найденных параметрах, мы возьмём вторую половину наблюдений и проведём бэктесты, то есть протестируем, принесёт ли нам такая стратегия деньги.
Для дальнейших рассуждений мне понадобится спред двух акций, . В нашем примере с парой (VSYDP, NKHP) мы уже находили спред в предыдущей статье про коинтеграцию, так что здесь я просто продублирую картинку:
Итак, все мы хотим покупать дёшево и продавать дорого. Если спред уходит ниже нуля, акция (VSYDP) дешевле, чем акция (NKHP). И, наоборот, если спред поднимается выше нуля, акция дороже, чем акция (ну или дешевле по сравнению с ).
Таким образом, по сути, торговая стратегия состоит в том, чтобы купить акцию и продать акцию в соотношении , если спред находится несколько ниже нуля (ниже линии на рисунке). Когда же спред возвращается обратно к нулю, нам нужно закрыть позицию, продав и купив в том же соотношении. В этом случае мы получаем прибыль размера :
Здесь важно понимать, что у нас должна быть возможность продавать акции, которыми мы не владеем, — это называется короткой продажей (шорт). Также необходимо отметить, что не все брокеры включают возможность короткой продажи в стандартный пакет услуг, так что, возможно, вам придётся дополнительно обратиться к нему для расширения своих инвестиционных возможностей.
В общем, мы составляем портфель, который содержит одну длинную позицию (лонг) и одну короткую позицию (шорт), если спред пересекает некоторую линию или уходит за неё в противоположную от нуля сторону. И мы закрываем все позиции, когда спред возвращается обратно к нулю.
Следующий вопрос, который возникает, — это «как найти значение »?
Как найти значение
Помимо математики, я сразу буду приводить здесь реализацию в матлабе. Приведённый ниже код является естественным продолжением кода из предыдущей статьи про коинтеграцию. Нам понадобится первая половина наблюдений, , которую мы будем рассматривать как «историю».
T = length(testPrices);
half = round(T/2);
Для начала найдём среднее отношение и для первой половины наблюдений.
sumRatio = 0;
for i = 1 : half
sumRatio = sumRatio + testPrices(i,1) / testPrices(i,2);
end
r = sumRatio / half;
Для пары акций с тикерами (VSYDP, NKHP) расчётное значение . Затем вычислим максимум абсолютного значения спреда для первой половины наблюдений:
clear absspread
for i = 1 : half
absspread(i,1) = abs(testPrices(i,1) - r * testPrices(i,2));
end
m = max(absspread);
Для пары акций с тикерами (VSYDP, NKHP) расчётное значение . Теперь мы можем определить значение путём перебора: возьмём некоторый процент от и попробуем поторговать на «истории» при различных значениях этого процента, а затем выберем то значение, которое даст наибольшую прибыль. Это и будет искомое значение для линии .
Перебор различных значений для поиска наилучшего
Для начала нам нужно посчитать количество трейдов. Первый трейд, обозначим его , мы делаем, когда впервые встаём в позицию. В этом случае мы ещё не получаем прибыль:
где — спред. Далее успешными моментами трейдов будут:
- если : : , где — количество трейдов, — некоторый процент от . Если трейдов не было, тогда прибыль равна нулю. — это минимальная прибыль, если всегда торговать одним соотношением и .
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; for i = 1:half - 1 if abs(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; break end end if k == 1 break end for i = startIndex:half - 1 if (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; end if (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; end end if 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; end end
В таблице ниже показаны проценты и исходы для пары акций с тикерами (VSYDP, NKHP).Процент Трейды Прибыль 5 160,2224 7 1922,7 10 320,4449 5 2563,6 15 480,6673 5 3845,3 20 640,8898 5 5127,1 25 801,1122 5 6408,9 30 961,3347 5 7690,7 35 1121,6 5 8972,5 40 1281,8 3 5127,1 45 1442 3 5768 50 1602,2 3 6408,9 55 1762,4 3 7049,8 60 1922,7 3 7690,7 65 2082,9 3 8331,6 70 2243,1 3 8972,5 75 2403,3 3 9613,3 80 2563,6 3 10254 85 2723,8 1 0 90 2884 0 0
Чтобы определить значение , мы просто выбираем то значение, которое даёт наибольшую прибыль, основанную на «истории».[M, I] = max(profit(:,4)); bestG = profit(I,2);
Однако, хотя (при 80% от ) даёт наибольшую прибыль, на практике мы не выбираем больше, чем , из-за трудностей, связанных с дальнейшим извлечением прибыли, поэтому возьмём (при 35% от ).Тестирование стратегии
После определения торговая стратегия применяется ко второй половине наблюдений.clear strategy for i = half + 1:T if abs(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; break end end if exist('strategy', 'var') k = 2; for i = startIndex:T-1 if (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; end 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) = spread(i+1) - strategy(k-1,2); k = k + 1; end else if (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; end if (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; end end end end if exist('strategy', 'var') totalProfit = sum(strategy(:,7)); else totalProfit = 0; end
При прибыль извлекается 3 раза. Другими словами, разность 3 раза перемещается от 0 до и обратно. Обратите внимание, что реализация такой стратегии включает 6 трейдов, так как для того, чтобы встать в позицию и выйти из неё, требуется совершить два трейда.На рисунке и в таблице ниже показаны все 6 моментов торговли.
Трейд Позиция Цена Цена Прибыль 1 108 3145,9 (-1; +35,6527) 13200 282 - 2 128 -211,447 Ликвидация 9700 278 3357,4 3 134 -1161,9 (+1; -35,6527) 8500 271 - 4 171 14,6605 Ликвидация 8500 238 1176,5 5 205 -1184,5 (+1; -35,6527) 7800 252 - 6 212 185,1035 Ликвидация 8100 222 1369,6 Итого 5903,5
Прибыль, полученная здесь, составляет не менее . Здесь используются цены закрытия вместо данных внутри дня, поэтому мы не торгуем в точках , 0 и . Как видно из таблицы, доходность за 107 торговых дней составила 25,39% без учёта комиссий, объёма и пр. То есть это примерно 60,74% годовых по очень грубым прикидкам.Также были проведены тесты для 58 пар на NYSE. Там, если не считать нулевой прибыли, приблизительная годовая доходность при данной стратегии колебалась от 23,34% до 208% без учёта комиссий, объёма и пр.
Тестирование альтернативной стратегии
Вместо того, чтобы закрывать позицию, когда спред приближается к нулю, можно перевернуть позицию, когда спред достигает на противоположной стороне от нуля. Предположим, что мы продали и купили , так как разность была больше .Теперь можно дождаться того момента, когда разность достигнет , купить и продать . В результате мы останемся с портфелем из длинной позиции размера и короткий позиции размера .
clear strategyAlt for i = half + 1:T if abs(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; break end end if exist('strategyAlt', 'var') d = 2; for i = startIndex:T-1 if (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; end if (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; end end end if exist('strategyAlt', 'var') totalAltProfit = sum(strategyAlt(:,7)); else totalAltProfit = 0; end
Такая стратегия приводит к одному начальному трейду и одному трейду, который переворачивает позицию. Эти трейды представлены на рисунке и в таблице ниже.Трейд Позиция Цена Цена Прибыль 1 108 3145,9 (-1; +35,6527) 13200 282 - 2 134 -1161,9 (+1; -35,6527) 8500 271 4307,8 Итого 4307,8
Обратите внимание, что прибыль от перевертывания позиции равна , поэтому общая прибыль в данном случае составляет не менее . Как видно из таблицы, доходность за 107 торговых дней составила 18,53% без учёта комиссий, объёма и пр. То есть это примерно 44,32% годовых по очень грубым прикидкам.Также были проведены тесты для 58 пар на NYSE. Там, если не считать нулевой прибыли, приблизительная годовая доходность при данной стратегии колебалась от 17,63% до 201,53% без учёта комиссий, объёма и пр.
Данное изменение в торговой стратегии уменьшает количество трейдов в среднем в 2 раза. При этом сокращаются торговые издержки. Если бы разность двигалась вверх и вниз около 0, альтернативная стратегия была бы более прибыльной. Однако в рассмотренном случае, когда у пары есть тенденция двигаться между 0 и , позиция вообще ни разу не переворачивается, тогда как основная стратегия создаёт и ликвидирует позицию снова и снова… и делает деньги.
Выводы
При торговле коинтегрированными парами, теоретически, можно извлекать стабильную прибыль, в частности, с помощью способов, описанных выше в виде двух торговых стратегий. В случае с рассмотренной парой акций (VSYDP, NKHP) первый способ оказался более эффективным из-за наличия в их разности тенденции колебаться ниже нуля.Возможность извлечения стабильной прибыли при торговле коинтегрированными парами выглядит оптимистично, однако требует дальнейшего анализа на демо-счетах, о чём мы и поговорим в следующий раз.
Что почитать по теме
Терри Дж. Уотшем, Кейт Паррамоу. Количественные методы в финансах / Пер. с англ. под ред. М.Р. Ефимовой. — М.: Финансы, ЮНИТИ, 1999. — 527 с.По этому учебнику в своё время преподавали для магистров количественный анализ в Вышке. Там есть раздел, посвящённый коинтеграции.