Пересечение скользящих средних

Пересечение скользящих средних

За основу возьмём простую систему на двух экспоенциальных средних скользящих: быстрая пересекает медленную снизу вверх — закрываем шорт и открываем лонг, медленная пересекает быструю сверху вниз — закрываем лонг и открываем шорт... Начнём, как обычно с выбора инструмента. Один из моих любимых в последнее время — Роснефть. Не сильно волатильная бумага. Удобная для анализа и прогнозов.

Дневник разработки торгового робота

Автор: Дмитрий Титаренко

Сегодня 25 мая 2011 года. За окном солнечно и тепло, но не смотря на это настроение не совсем радостное. Рынок, начиная с 6-мая всё больше напоминает бойню или живодёрню. И, честно говоря, за два дня слить всю прибыль, которая была заработана тяжкими усилиями моих роботов, оказалось не комильфо. Восходящий тренд по индексу ММВБ от 20-го января 2009 года оказался сломлен. Все нервничают, ведь с 20-го января 2009 по 06 апреля 2011 индекс ММВБ вырос всего немногим более, чем на 236,36% и надо бы хотя бы немного скорректироваться. Хотя бы на половинку этого роста. В район 1000 пунктов. И не исключено, что «сволонтильность» будет только возрастать. Да и Европа, думаю, подольёт масла в огонь. Поэтому надо что-то менять в алгоритмах. Хотя бы то, что торговать в шорт было, мягко говоря, в течение всего этого повышательного тренда, не очень рациональным поступком. Но теперь, ситуация, очевидно изменится. И встаёт вопрос — торговать ли только в шорт, только в лонг или и в шорт и в лонг.

Работать я привык в Wealth-Lab. Точнее — разрабатывать роботов. Для готовых разработок уже давно и прочно написана своя программа, которая стоит в датацентре, где ей тепло, светло и интернетно. Поэтому, чтобы не утруждать Wealth-Lab такой штукой, как автотрейдинг, я просто переношу разработанные алгоритмы из Wealth-Lab на свою платформу, проверяю соответствие исторических сигналов и кривой доходности с Wealth-Lab и тогда запускаю на тестирование на реальных биржевых данных. И только после 2-х-3-х месяцев соответствия реальных данных модельным запускаю автотрейдинг. Ну, и небольшим бонусом конечно является более, чем 6 лет работы в Wealth-Lab, все глюки и подводные камни которого знаю как свои пять пальцев. Версия 3.0 (да-да :)).

Начнём, как обычно с выбора инструмента. Один из моих любимых в последнее время — Роснефть. Не сильно волатильная бумага. Удобная для анализа и прогнозов.

Подготовим источник данных. Для подготовки я использую обычную выгрузку из собственной БД, в которую постоянно качаются данные since 2007. Формат данных ASCII. Разделитель табуляция. Объём выгружать не буду. Считаю, что толку с него мало. Объём данных возьму такой, который постоянно беру — последние 12 месяцев. И рассчитывается быстро, и оптимизируется недолго.

Вот такой получился исходник данных:

20100525       103000       202.38000       202.75000       201.10000       201.50000
20100525       104500       201.48000       201.50000       199.35000       200.50000
20100525       110000       200.50000       200.90000       200.32000       200.88000
20100525       111500       200.62000       203.76000       200.60000       202.13000
20100525       113000       202.01000       202.80000       201.55000       202.63000
20100525       114500       202.63000       203.44000       202.15000       202.45000
<......>       <....>       <.......>       <........>      <.......>       <.......>

Загружаем в велс.
….мама-зузу-бабулие....
А уже 06 июня, да. День рождения Пушкина, между прочим.


Примерно так вот это выглядит после загрузки данных.
За основу возьмём простую систему на двух экспоенциальных средних скользящих: быстрая пересекает медленную снизу вверх — закрываем шорт и открываем лонг, медленная пересекает быструю сверху вниз — закрываем лонг и открываем шорт.

Вот исходный код данной системы в велс-лабе с комментариями.

//--------------------------------------------------
{#OptVar1 10;1;100;1}
{#OptVar2 20;1;100;1}
var Bar: integer;
var EMA1: integer;
var EMA2: integer;
var p: integer;
var bLongSAR: boolean;

EMA1:=EMASeries(#Close,#OptVar1); //определение первой  экспоненциальной средней скользящей
//EMA=Exponential Moving Average. Берётся от цены закрытия  #Close. С параметром #OptVar1=10
EMA2:=EMASeries(#Close,#OptVar2);//определение второй  экспоненциальной средней скользящей
//EMA=Exponential Moving Average. Берётся также от цены  закрытия #Close. С параметром #OptVar2=20

for Bar := 101 to BarCount - 1 do //для каждой свечки от 101  до последней повторяем блок
begin //отсюда
  if  not LastPositionActive then  //в том случае, если позиций у нас нет...
  begin
    if  @EMA1[Bar]>@EMA2[Bar] then //..., и есть пересечение вверх...
      BuyAtClose(Bar,  'Наш первый вход'); //...то покупаем по цене закрытия
    end
    else //иначе, если у  нас открыта позиция (а у нас она всегда будет только одна)
    //т. к. рассказ про  методики управления размерами позиции выходит за рамки
    //данного обсуждения
    begin
      p:=LastPosition;   //параметру p присваиваем идентификатор  последней позиции
      if  CrossOver(Bar,EMA1,EMA2) then   //если  происходит пересечение вверх...
      begin
        if  PositionLong(p) then //...и опрос позиции показывает, что она длинная...
        begin
          SellAtClose(Bar,#All,'Реверсная  продажа'); //...,то закрываем лонг, если таковой был...
          ShortAtClose(Bar,#All);   //...и открываем шорт
        end;
      end;
      if  CrossUnder(Bar,EMA1,EMA2) then //если происходит пересечение вниз...
      begin
        if  PositionShort(p) then //...и опрос позиции показывает, что она короткая...
        begin
          CoverAtClose(Bar,#All,'');  //..., то закрываем шорт, если таковой был...
          BuyAtClose(Bar,'');  //...и открываем лонг
        end;
      end;
    end;
  end;
end;  //и досюда
//вообще - язык велс лаба очень похож на паскаль.
//и гораздо круче всяких  "языков изи (Easy Language)"
//--------------------------------------------------

Попробуем нажать клавишу F5, и посмотреть, что получилось

Как кривая доходности, так и распределение прибыли представляют собой жалкое зрелище.
Кривая доходности ни разу не обгоняет рынок, а распределение прибыли подсказывает нам, что в такие игры играть не стоит. Попробуем провести оптимизацию «с наскока», то есть, быстрым методом Монте-Карло, чтобы упражняться в бесполезной в данном случае исключающей оптимизацией. Сохраняем наши труды, закрываем скрипт. Открываем раздел «Optimization», и начинаем оптимизировать. :)



С одной стороны ситуация улучшилась, с другой надо сделать поправку на то, что в реальности просадки будут больше, а доходность меньше — рынок постоянно меняется.

Рассмотрим следуюущий механизм улучшения работы торгового робота.

Писать долго, конечно, но мы никуда и не спешим.
Зелёный треугольник обозначает точку входа в длинную позицию (BuyAtClose).
Красный треугольник обозначает точку выхода из длинной позиции (SellAtClose).
Сиреневый треугольник обозначает точку входа в короткую позицию (ShortAtClose)
Синий треугольник обозначает точку выхода из которкой позиции (CoverAtClose).
Синяя линия обозначает восходящую тенденцию.
Красная линия обозначает нисходящую тенденцию.

В двух словах, суть изложенного: при повышательном тренде не открывать коротких позиций (потому что они заведомо убыточны), при понижательном тренде не открывать длинных позиций по той же причине.
Но, как говорится, знал бы прикуп, жил бы в Сочи. Как определить, какая тенденция господствует в данный момент на рынке? Повышательная или понижательная? (Про отсутствие тенденции в данном коротком опусе тоже рассказывать не будем, ибо долго). Обратимся к тем же экспоненциальным средним скользящим, только с очень большим периодом.
Итак — считаем, что повышательный тренд имеет место быть на рынке тогда, когда более быстрая экспоненциальная средняя скользящая находится выше, чем медленная. Понижательный тренд имеет место быть на рынке тогда, когда более быстрая экспоненциальная средняя скользящая находится ниже, чем медленная. Периоды возьмём примерно на глаз — для медленной скользящей 480, для быстрой 300.
В результате получаем вот такой исходный код:

{#OptVar1 10;1;100;1}        //39
{#OptVar2 20;1;100;1}        //64

var Bar: integer;
var EMA1: integer;
var EMA2: integer;
var TrendEMAFast: integer;
var TrendEMASlow: integer;
var p: integer;
var firstEntry: integer;
var bLongSAR: boolean;
var isUpTrend: boolean;

firstEntry:=0;
TrendEMAFast:=EMASeries(#Close, 300); //определение быстрой  EMA для тренда.
PlotSeries(TrendEMAFast,0,#Black,#Thin);
TrendEMASlow:=EMASeries(#Close, 480); //определение  медленной EMA для тренда.
PlotSeries(TrendEMASlow,0,#Black,#Thin);
EMA1:=EMASeries(#Close,#OptVar1); //определение первой  экспоненциальной средней скользящей
PlotSeries(EMA1,0,#Red,#Thin);
//EMA=Exponential Moving Average. Берётся от цены закрытия  #Close. С параметром #OptVar1=10
EMA2:=EMASeries(#Close,#OptVar2);//определение второй  экспоненциальной средней скользящей
PlotSeries(EMA2,0,#Blue,#Thin);
//EMA=Exponential Moving Average. Берётся также от цены  закрытия #Close. С параметром #OptVar2=20
for Bar := 481 to BarCount - 1 do //для каждой свечки от 101  до последней повторяем блок
begin //отсюда
  //определяем, каков глобальный тренд на рынке
  // if @TrendEMAFast[Bar]>=@TrendEMASlow[Bar] then  isUpTrend:=true
  // else isUpTrend:=false;
  //if  not   then  //в том случае, если позиций  у нас нет...
  if not LastPositionActive then
  begin
    if  CrossOver(Bar,EMA1,EMA2) then //..., и быстрая скользящая находится выше  медленной...
    begin
      if  @TrendEMAFast[Bar]>@TrendEMASlow[Bar] //...и тренд повышательный
      then  BuyAtMarket(Bar+1, 'Верх кода'); //...то  покупаем по рыночной цене
    end;
    if  CrossUnder(Bar,EMA1,EMA2) then //..., и быстрая скользящая находится выше  медленной...
    begin
      if @TrendEMAFast[Bar]<@TrendEMASlow[Bar]  //...и тренд понижательный
      then  ShortAtMarket(Bar+1, 'Верх кода'); //...то  открываем шорт по рыночной цене
    end;
  end;

  if LastPositionActive  then  //иначе, если у нас открыта позиция
  //(а у нас она всегда будет только одна)
  //т. к. рассказ про  методики управления размерами позиции выходит за рамки
  //данного обсуждения
  begin
    p:=LastPosition;   //параметру p присваиваем идентификатор  последней позиции
    if  CrossUnder(Bar,EMA1,EMA2) then   //если  происходит пересечение вверх...
    begin
      if  PositionLong(p) then //... и опрос позиции показывает, что она длинная...
      begin
        SellAtMarket(Bar+1,#All,'Реверсная  продажа'); //...,то закрываем лонг, если таковой был...
        if @TrendEMAFast[Bar]<@TrendEMASlow[Bar]  then //...и если тренд понижательный...
        ShortAtMarket(Bar+1,'Реверсная  продажа');   //... то открываем  шорт...
      end;
    end;
    if  CrossOver(Bar,EMA1,EMA2) then //если происходит пересечение вниз...
    begin
      if  PositionShort(p) then //...и опрос позиции показывает, что она короткая...
      begin
        CoverAtMarket(Bar+1,#All,'Реверсная  покупка'); //..., то закрываем шорт, если таковой был...
        if  @TrendEMAFast[Bar]>@TrendEMASlow[Bar] then //...и если тренд повышательный
        BuyAtMarket(Bar+1,  'Реверсная покупка'); //...то открываем лонг
      end;
    end;
  end;

end;  //и досюда.
//вообще - язык велс лаба очень похож на паскаль.
//и гораздо круче всякиз  "языков изи (Easy Language)"

Также проведём оптимизацию

Как говорится, результат налицо — сделок стало меньше, они стали более качественными. Фактор прибыли составляет аж 3.37. Просадка вообще смехотворная — всего 8.87%. А отношение прибыльных сделок к убыточным возросло практически до 80%.

Написать администратору