Monday, March 21, 2011

[К черту мифы] Тестирование стратегии RSI на R.

В прошлый раз мы с вами установили программную среду R. Самое время теперь поразмяться на различных примерах и развенчать некоторые мифы. А может и наоборот, подтвердить. В любом случае, моя цель на ближайшее будущее, с помощью языка программирования R, проверить популярные стратегии торговли.

Одной из самых известных стратегий является стратегия торговли основанная на RSI (Relative Strength Index, индекс относительной силы). Она популярна, в первую очередь, в силу своей простоты. Хотя, существует множество вариантов этой стратегии, все они примерно сходятся в одном: покупаем, когда RSI ниже либо пробивает снизу определенное значение и продаем, когда RSI выше определенного значения либо пробивает его сверху. Кроме того, в этой стратегии учаcтвует такой параметр как период, это количество временных интервалов, на которых расчитывается RSI. Моей целью стала проверка прибыльности этой стратегии для различных значений параметров.

Внимание: при проверке стратегий не учитывалось проскальзывание и комиссии. Расчеты велись по дневным графикам а доходность считалась по цене закрытия. Позиция закрывалась на следующий день после открытия. На практике, благодаря стоплосам, проскальзыванию и комиссии доходность может отличаться. Проведенный ниже анализ и прилагаемый код является лишь прототипом и ни в коей мере не претендует на готовую модель для торговли.

Итак, для начала напишем функцию, которая позволит нам проводить тесты. Дабы не загромождать пост, полный код можно посмотреть по ссылке. Здесь же я приведу лишь часть, относящуюся к делу:

"testRSI" <- function(data, period = 7, high = 80, low = 20){

rsi =  RSI(Cl(data), n=period) #К счастью для нас индикатор RSI уже реализован в модуле TTR

sigup <-ifelse(rsi < low,1,0)
sigdn <-ifelse(rsi > high,-1,0)

sigup <- lag(sigup,1) 
sigdn <- lag(sigdn,1) 

sigup[is.na(sigup)] <- 0
sigdn[is.na(sigdn)] <- 0

sig <- sigup + sigdn

ret <- ROC(Cl(data)) #Изменение цены за один период времени.
ret[1] <- 0

eq_up <- cumprod(1+ret*sigup) #Прибыльность длинных позиций
eq_dn <- cumprod(1+ret*sigdn) #Прибыльность коротких позиций
eq_all <- cumprod(1+ret*sig) #Прибыльность стратегии

}

Теперь можно протестировать работу системы на индексе S&P500, данные возьмем с Yahoo.Finance:
  getSymbols("^GSPC", from="2005-03-17")
  testRSI(GSPC, period=7, high=70, low=30)
Результаты:
Покупаем если RSI(7) < 30,
продаем если RSI(7) > 70
Покупаем если RSI(2) < 10,
продаем если RSI(2) > 90




Средняя дневная прибыль %0.05460.04630.0083
Среднеквадратичное отклонение от прибыли %0.75740.70280.2836
Всего сигналов380129251
% удачных дней53.962.849.4
Максимальная просадка %15.36
Средняя дневная прибыль %0.0490.0339-0.0022
Среднеквадратичное отклонение от прибыли %0.7890.66240.4298
Всего сигналов402147255
% удачных дней53.260.549.0
Максимальная просадка %15.36
Повторим теперь все то же самое, но для нашего родного индекса ММВБ. Данные, на этот раз будем брать с финама. Воспользуемся так же скриптом, написанным в прошлый раз, для разбора этих самых данных.
  MICEX <- parseQuotes("C:\\data\\MICEX_050317_110317.txt")
  testRSI(MICEX , period=7, high=70, low=30)
Результаты:
Покупаем если RSI(7) < 30,
продаем если RSI(7) > 70
Покупаем если RSI(2) < 10,
продаем если RSI(2) > 90




Средняя дневная прибыль %0.01750.01570.0018
Среднеквадратичное отклонение от прибыли %1.7571.5300.864
Всего сигналов430109321
% удачных дней49.351.448.6
Максимальная просадка %54.9
Средняя дневная прибыль %0.0270.029-0.0022
Среднеквадратичное отклонение от прибыли %1.5771.37340.7759
Всего сигналов379140239
% удачных дней53.362.148.1
Максимальная просадка %37.2
К сожалению, на Российском рынке картинка получается хуже чем на буржуйском. Средняя доходность стратегии получается существенно меньше а максимальная просадка больше. Но это не беда, возьмем теперь да и попробуем поторговать отдельным эмитентом. Например, Газпром:
Покупаем если RSI(7) < 30,
продаем если RSI(7) > 70
Покупаем если RSI(2) < 10,
продаем если RSI(2) > 90




Средняя дневная прибыль %0.06390.0645-0.0005
Среднеквадратичное отклонение от прибыли %1.9171.7220.8434
Всего сигналов287116171
% удачных дней51.553.450.3
Максимальная просадка %51.3
Средняя дневная прибыль %-0.0040.007-0.011
Среднеквадратичное отклонение от прибыли %1.6961.4420.8931
Всего сигналов327149178
% удачных дней49.852.347.7
Максимальная просадка %57.9

Максимальная просадка счета превысила 50%. Увы, торговать по стратегии RSI в чистом виде с прибылью вряд ли получится. Но может быть, если добавить другие индикаторы ситуация изменится? Посмотрим...

А теперь, внимание, акция! Совершенно бесплатно :) только для тех, кто дочитал до этого места, проведу анализ ваших стратегий. Оставляйте в комментариях те стратегии, которые было бы интересно проанализировать лично вам, с внятным описанием, либо ссылкой на описание.
Для посетителей с комона, просьба комментарии писать в этом блоге,регистрация не требуется, на комоне бываю редко, могу пропустить.

11 comments:

  1. Сергей, спасибо за вашу серию постов про R. Похоже, это самое лучшее, что есть про R для квантов.

    Вопрос: автоматизируете торговлю вы тоже через R?

    ReplyDelete
  2. Матвей, спасибо за комментарий.

    Что касается автоматизации, ответ, и да и нет. С одной стороны я использую R для генерации сигналов, с другой стороны, до полностью автоматической торговли мне еще далеко. У меня есть маленькое приложение, которое висит в трее и периодически выполняет несколько скриптов на R. Если происходит что-то интересное - всплывает окно с сообщением и я уже решаю, что с этим делать.

    ReplyDelete
    Replies
    1. Сергей, у вас самописное приложение (которое висит в трее и выполняет скрипты) или вы используете какую-то универсальную связку (Scheduled tasks в Windows, например), которая выполняет скрипты по расписанию и показывает нотификации в трее?

      Delete
    2. Самописное приложение, полностью на Java (все-таки в глубине души я java-программист) и лишь математическая логика в нем на R.

      Delete
  3. Так RSI никто не торгует. У Вас получается контртренд, естественно, это путь в никуда. Мне кажется, R вообще неудобен для теста. Лучше генерировать идеи в R, а тестировать - в TSLab или WL.

    ReplyDelete
  4. Конечно лучше, но чтобы генерировать идеи необходимо хоть какое-то примитивное тестирование. Иначе как отличить интересную идею от мусора?

    ReplyDelete
  5. Есть интересная идея, на данный момент занимаюсь её изучением. Это не стратегия, это скорее технический подход.

    В RSI используется экспоненциальное сглаживание, а можно использовать wavelet анализ. Он помогает убрать "шум" и выявить тренд. Это конечно теоретически пока что, и вот как раз эффективность хочу выяснить.

    Что касается стратегии, то можно выделить различные зоны, где наблюдается "повторения" из периода в период. Соответственно помогает вырабатывать стратегию.

    ReplyDelete
  6. Позапускал ваш код. По-моему он делает не то, что нужно. Он в длинной позиции только тогда когда RSI находится выше high, а в короткой, когда RSI ниже low. Или так и было задумано?

    Как всё-таки написать классическую стратегию?

    ReplyDelete
    Replies
    1. Так и было задумано, вообще есть несколько стратегий торговли по RSI и это одна из них.
      Что Вы подразумеваете под классической стратегией? Вход в позицию при пересечении RSI порогового значения?
      Это можно реализоать модифицировав условия
      sigup <-ifelse(rsi < low,1,0)
      sigdn <-ifelse(rsi > high,-1,0)
      Нужно использовать помимо rsi еще и lag(rsi). Хорошо бы тогда еще и условиях выхода из позиции изменить потому как сейчас мы просто ждем один день.

      Delete
  7. Интересно, похвально, но только как абстрактное упражнение. Проблема в том, что вы рассматриваете рынок как сигнал. Но для того, чтобы успешно работать на современных рынках, нужно понимать, что рынок - это структура. Да, важным следствием является P(t), который вы анализируете, но анализ следствия многофакторных процессов структурных изменений на мой взгляд не является оптимальным подходом. Хотя для освоения R работа отличная!

    ReplyDelete
  8. после запуска testRSI выдает ошибку
    Ошибка в assign(name, value, envir = lenv) :
    попытка использовать имя переменной нулевой длины

    ReplyDelete