Цель данной статьи — поделиться простейшей стратегией статистического арбитража, основанной на торговле коинтегрированными парами акций, которые были выявлены на Московской и Нью-Йоркской биржах.
Если мы возьмём пару коинтегрированных акций, то у нас есть возможность захеджироваться и построить рыночно-нейтральную стратегию, когда убытки по одной бумаге будут компенсироваться прибылями по другой. Как это выглядит на практике?
Допустим, у нас есть коинтегрированная пара акций, и , а также цены этих акций за некий период времени . Для примера возьмём пару акций с тикерами (VSYDP,NKHP) из моей предыдущей статьи про коинтеграцию. Для неё у нас есть данные о ценах за 215 торговых дней.
Первую половину наблюдений мы будем использовать, чтобы определить параметры торговой стратегии. Затем, основываясь на найденных параметрах, мы возьмём вторую половину наблюдений и проведём бэктесты, то есть протестируем, принесёт ли нам такая стратегия деньги.
Для дальнейших рассуждений мне понадобится спред двух акций, . В нашем примере с парой (VSYDP,NKHP) мы уже находили спред в предыдущей статье про коинтеграцию, так что здесь я просто продублирую картинку:
Итак, все мы хотим покупать дёшево и продавать дорого. Если спред уходит ниже нуля, акция (VSYDP) дешевле, чем акция (NKHP). И, наоборот, если спред поднимается выше нуля, акция дороже, чем акция (ну или дешевле по сравнению с ).
Таким образом, по сути, торговая стратегия состоит в том, чтобы купить акцию и продать акцию в соотношении , если спред находится несколько ниже нуля (ниже линии на рисунке). Когда же спред возвращается обратно к нулю, нам нужно закрыть позицию, продав и купив в том же соотношении. В этом случае мы получаем прибыль размера :
Здесь важно понимать, что у нас должна быть возможность продавать акции, которыми мы не владеем, — это называется короткой продажей (шорт). Также необходимо отметить, что не все брокеры включают возможность короткой продажи в стандартный пакет услуг, так что, возможно, вам придётся дополнительно обратиться к нему для расширения своих инвестиционных возможностей.
В общем, мы составляем портфель, который содержит одну длинную позицию (лонг) и одну короткую позицию (шорт), если спред пересекает некоторую линию или уходит за неё в противоположную от нуля сторону. И мы закрываем все позиции, когда спред возвращается обратно к нулю.
Следующий вопрос, который возникает, — это «как найти значение »?
Помимо математики, я сразу буду приводить здесь реализацию в матлабе. Приведённый ниже код является естественным продолжением кода из предыдущей статьи про коинтеграцию. Нам понадобится первая половина наблюдений, , которую мы будем рассматривать как «историю».
Для начала найдём среднее отношение и для первой половины наблюдений.
Для пары акций с тикерами (VSYDP,NKHP) расчётное значение . Затем вычислим максимум абсолютного значения спреда для первой половины наблюдений:
Для пары акций с тикерами (VSYDP,NKHP) расчётное значение . Теперь мы можем определить значение путём перебора: возьмём некоторый процент от и попробуем поторговать на «истории» при различных значениях этого процента, а затем выберем то значение, которое даст наибольшую прибыль. Это и будет искомое значение для линии .
Для начала нам нужно посчитать количество трейдов. Первый трейд, обозначим его , мы делаем, когда впервые встаём в позицию. В этом случае мы ещё не получаем прибыль:
где — спред. Далее успешными моментами трейдов будут:
Затем прибыль будет рассчитываться как , где — количество трейдов, — некоторый процент от . Если трейдов не было, тогда прибыль равна нулю. — это минимальная прибыль, если всегда торговать одним соотношением и .
В таблице ниже показаны проценты и исходы для пары акций с тикерами (VSYDP,NKHP).
Чтобы определить значение , мы просто выбираем то значение, которое даёт наибольшую прибыль, основанную на «истории».
Однако, хотя (при 80% от ) даёт наибольшую прибыль, на практике мы не выбираем больше, чем , из-за трудностей, связанных с дальнейшим извлечением прибыли, поэтому возьмём (при 35% от ).
После определения торговая стратегия применяется ко второй половине наблюдений.
При прибыль извлекается 3 раза. Другими словами, разность 3 раза перемещается от 0 до и обратно. Обратите внимание, что реализация такой стратегии включает 6 трейдов, так как для того, чтобы встать в позицию и выйти из неё, требуется совершить два трейда.
На рисунке и в таблице ниже показаны все 6 моментов торговли.
Прибыль, полученная здесь, составляет не менее . Здесь используются цены закрытия вместо данных внутри дня, поэтому мы не торгуем в точках , 0 и . Как видно из таблицы, доходность за 107 торговых дней составила 25,39% без учёта комиссий, объёма и пр. То есть это примерно 60,74% годовых по очень грубым прикидкам.
Также были проведены тесты для 58 пар на NYSE. Там, если не считать нулевой прибыли, приблизительная годовая доходность при данной стратегии колебалась от 23,34% до 208% без учёта комиссий, объёма и пр.
Вместо того, чтобы закрывать позицию, когда спред приближается к нулю, можно перевернуть позицию, когда спред достигает на противоположной стороне от нуля. Предположим, что мы продали и купили , так как разность была больше .
Теперь можно дождаться того момента, когда разность достигнет , купить и продать . В результате мы останемся с портфелем из длинной позиции размера и короткий позиции размера .
Такая стратегия приводит к одному начальному трейду и одному трейду, который переворачивает позицию. Эти трейды представлены на рисунке и в таблице ниже.
Обратите внимание, что прибыль от перевертывания позиции равна , поэтому общая прибыль в данном случае составляет не менее . Как видно из таблицы, доходность за 107 торговых дней составила 18,53% без учёта комиссий, объёма и пр. То есть это примерно 44,32% годовых по очень грубым прикидкам.
Также были проведены тесты для 58 пар на NYSE. Там, если не считать нулевой прибыли, приблизительная годовая доходность при данной стратегии колебалась от 17,63% до 201,53% без учёта комиссий, объёма и пр.
Данное изменение в торговой стратегии уменьшает количество трейдов в среднем в 2 раза. При этом сокращаются торговые издержки. Если бы разность двигалась вверх и вниз около 0, альтернативная стратегия была бы более прибыльной. Однако в рассмотренном случае, когда у пары есть тенденция двигаться между 0 и , позиция вообще ни разу не переворачивается, тогда как основная стратегия создаёт и ликвидирует позицию снова и снова… и делает деньги.
При торговле коинтегрированными парами, теоретически, можно извлекать стабильную прибыль, в частности, с помощью способов, описанных выше в виде двух торговых стратегий. В случае с рассмотренной парой акций (VSYDP, NKHP) первый способ оказался более эффективным из-за наличия в их разности тенденции колебаться ниже нуля.
Возможность извлечения стабильной прибыли при торговле коинтегрированными парами выглядит оптимистично, однако требует дальнейшего анализа на демо-счетах, о чём мы и поговорим в следующий раз.
Терри Дж. Уотшем, Кейт Паррамоу. Количественные методы в финансах / Пер. с англ. под ред. М.Р. Ефимовой. — М.: Финансы, ЮНИТИ, 1999. — 527 с.
По этому учебнику в своё время преподавали для магистров количественный анализ в Вышке. Там есть раздел, посвящённый коинтеграции.
UPD. Результаты бэктестов за 2017 год на Московской бирже
Если мы возьмём пару коинтегрированных акций, то у нас есть возможность захеджироваться и построить рыночно-нейтральную стратегию, когда убытки по одной бумаге будут компенсироваться прибылями по другой. Как это выглядит на практике?
Торговая стратегия
Допустим, у нас есть коинтегрированная пара акций, и , а также цены этих акций за некий период времени . Для примера возьмём пару акций с тикерами (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 с.
По этому учебнику в своё время преподавали для магистров количественный анализ в Вышке. Там есть раздел, посвящённый коинтеграции.
UPD. Результаты бэктестов за 2017 год на Московской бирже