Блог им. orekton

Стохастик с фильтром на Qpile (часть 2)

    • 06 февраля 2012, 19:21
    • |
    • orekton
  • Еще
Первая часть статьи http://smart-lab.ru/blog/36779.php. С кодом для метатрейдера http://robostroy.ru/community/article.aspx?id=235. Вторая часть посвящена реализации стратегии на Qpile. Привожу сокращенный вариант, оригинал http://robostroy.ru/community/Article.aspx?id=246

В первой части статьи мы разработали и оптимизировали торговую стратегию «Стохастический индикатор + трендовый фильтр на скользящих средних» при помощи Метатрейдера. Теперь пришло время перевести ее на Quik. Полный текст робота на Qpile состоит из двух частей. Первая часть – библиотека функций (см. Приложение 1), вторая часть – тело робота (см. Приложение 2).
Настройка робота
Перед тем как использовать данного робота, необходимо настроить его параметры. Они находятся в самом начале тела робота в виде переменных, начинающихся с префикса “p”.


 
'параметры
pFirmid=«MC0000200000»
pAccount=«L01-00000F00»             'клиентский счет
pClienCode=«1337»                   'код клиента
pLots=10                            'количество лотов акций
 





 
Обратите внимание на параметр pLots. Он показывает, каким количеством лотов акций мы торгуем. Не путать с количеством акций! Количество акций мы задавали в Метатрейдере,  здесь же мы имеет дело именно с лотами. Для акций «Газпрома», например, в одном лоте 10 акций.
Для корректного считывания данных об индикаторах необходимо задать в параметрах pGraphikNameShort, pGraphikNameLong и pGraphikNameStoch те имена графиков, которые необходимо установить ранее рис 1.  В эти переменные мы задаем то, что пишем в поле «идентификатор» соответствующего графика. 
  • pGraphikNameShort  — идентификатор графика быстрого MA
  • pGraphikNameLong – идентификатор графика медленного MA.
  • pGraphikNameStoch – идентификатор графика стохастика.
 
Рис 1. Задаем идентификатор графиков
 
Графики, разумеется, должны быть открыты, когда вы будете запускать робота. То есть мы должны открыть график «Газпрома» со стохастиком и скользящими средними и сослаться на все это дело в параметрах робота. Согласен, это ужасно неудобно. Но так работает Quik и мы не можем на это повлиять.
Тикер акции мы задаем в параметре pInstrument, код класса в pClass, эти данные можно взять из текущей таблицы параметров Quik (см. рис. 2)
 
Рис 2. Текущая таблица параметров
 
Поскольку робот выставляет заявки с тэйк-профитом, необходимо задать параметры защитного спреда и смещения (переменные pSafeSpread, pOFFSET, pOFFSET_UNITS, pSPREAD_UNITS – см. комментарии в тексте программы), см рис 3.
 
Рис. 3. Параметры тэйк-профита
 
В прошлом примере у нас были оптимизационные параметры для Метатрейдера, в таблице ниже я привожу соответствия этих параметров и переменных для Qpile.



Временной интервал нужно задать на графике и в параметрах робота (параметр pIntervalTime). В Qpile нет средств для определения интервала графика, поэтому параметры графика приходиться дублировать в коде. Если этого не сделать, то программа  не сможет рассчитать время сигнальной и предыдущей свечи.  Допустимые значения интервала 1, 5, 15 минут. Работу с часовыми и большими таймфреймами данный алгоритм не предусматривает.  Я не стал его реализовывать, чтобы не усложнять программу. Думаю, для дейтрейдинга актуальные таймфреймы не более 15 минут. Вообще в Qpile много нет из того, что мне понадобилось в процессе реализации робота, ниже я расскажу, как обходился без всего этого.
Также нужно задать размер пункта в переменной pPoint, для «Газпрома» это 1 копейка.
В переменную pDivision мы ставим знак «/» или «//» в зависимости от брокера. Он нужен для того, чтобы программа могла писать комментарий в выставленные заявки – тоже особенность Qpile. Комментарий нужен, чтобы программа могла находить отложенные заявки при проверке уже выставленной заявки и  избежать многократного дублирования заявки. При поиске программа смотри комментарий. Может возникнуть вопрос «почему / или // зависит от брокера?». Дело в том, что для выставления комментария используется параметра транзакции CLIENT_CODE и текст через разделитель «/». Некоторые брокеры обрабатывают транзакции немножко по-другому, и ставить приходиться «//». Вообще, по этому поводу я задавал вопрос на форуме quik.ru, вот здесь www.quik.ru/forum/qpile/84591/84591.
Для выставления стоп-лосса на ММВБ нужно обязательно оставлять резерв для проскальзывания, выставляя цену заявки немножко хуже цены срабатывания. Насколько хуже – задаем в параметре pSpread, в пунктах.
У нас остались pBegStock, pEndStock, pEndStock, pEndHour – эти переменные характеризуют время работы биржи (торговой сессии ММВБ).  Дело в том, что в Quik очень туго с обработкой данных типа «дата и время» и для расчетов, завязанных на времени, приходиться писать свой алгоритм. В нем-то я и использовал pBegStock, pEndStock, pEndStock, pEndHour. Но об этом алгоритме ниже, когда начну разбирать процесс создания робота по шагам.
Итак, если робот настроен правильно и вы запустили его, то на экране у вас должен появиться протокол работы робота (рис. 4), в котором выводятся различные промежуточные переменные, по которым можно судить, правильно ли работает робот. Этот протокол позволяет мониторить работу робота во время отладки и тестирования.
 
Рис. 4. Протокол работы робота
 
Перевод стратегии на Qpile. Шаг 1. Перепишем основные функции стратегии
Для того чтобы было понятно, как работает этот робот и как самому писать таких роботов на Qpile,  я расскажу о процессе перевода стратегии с Метатрейдера. Первым делом я переписал основные функции, которые реализуют стратегию, в частности, Strategy, Signal и filter.  Тут ничего сложного, просто другой язык. Cм., например, функцию Strategy на Метатрейдере
void Strategy()
{
   int Dir=Signal();
   if (Dir!=0) {
      if(filter(Dir)==0) return;
      int PosSize=GetPosSize();
      if(PosSize!=0) {
         if(Dir==1) Trade(Dir,PosSize); else Trade(Dir,PosSize);
      }
   }
}




 
и на Qpile:
func Strategy()
  Dir=Signal()
   if (Dir<>0)
      if filter(Dir)==0
            return
        end if   
      PosSize=GetPosSize()
      if PosSize<>0
            Trade(Dir,PosSize)
      end if     
   end if
end func




 
Как видите, тут ничего перепрограммировать не нужно – меняем синтаксис и все. А вот когда дело дошло до деталей, пришлось многое переписывать.
Перевод стратегии на Qpile. Шаг 2. Перепишем торговые функции
C реализацией функции Trade на Qpile не все так просто. Во-первых, на ММВБ и на Forex отличаются типы заявок. В частности, на ММВБ нет такого понятия, как отложенный ордер. Есть только обычная и стоп-заявка. К счастью, Quik позволяет вводить зависимые заявки. Поэтому мы можем эмулировать отложенный ордер, выставляя обычную заявку (эмулирует отложенный ордер на отскок). Связанная заявка Take profit + Stop Loss «По исполнению» (эмулирует стоп-лосс и тэйк-профит отложенного ордера). Возникает сигнал на покупку, но нам нужно войти не сразу, а на откате. Величину отката мы оптимизировали в Метатрейдере. Мы выставляем обычную заявку по сигнальной цене минус размер отката (для лонгов) или плюс размер отката (для шортов). Затем устанавливается связанная заявка тейк-профита и стоп-лосса, которая зависима от нашей обычной заявки эмулирующей отложенный ордер.
Выставление этих двух заявок робот производит отдельно, хотя они и связанные. Дело в том, что Quik – позволит ввести (хоть программно, хоть «ручками») связанную заявку тогда и только тогда, когда в системе уже есть базовая заявка (та, с которой надо «связаться»). При возникновении сигнала робот выставляет обычную заявку. Затем с помощью другой функции ищет эту отложенную заявку и, если находит ее, выставляет Take profit + Stop Loss «По исполнению». Такая схема позволяет восстановить Take profit + Stop Loss на следующей торговой сессии, так как эту заявку в отличие от обычной возможно выставить только со сроком действия «в течении торговой сессии». Иными словами, если торговая сессия кончилась, то при следующем запуске робота он у нас восстановит Take profit + Stop Loss, так как обычная заявка у нас выставляется со сроком действия «до отмены» и не должна пропадать после окончания торговой сессии. Получается, что робот проверяет заявку на исполнение и после исполнения выставляет связанные заявки тейк-профит и стоп-лосс, которые действительны в ходе текущей торговой сессии.
К сожалению, эта схема имеет ряд недостатков:
  • Если обычная заявка исполниться, то на следующую торговую сессию робот не сможет автоматически выставить Take profit + Stop Loss, такую заявку нам придется восстанавливать вручную.
  • Если у нас очень маленькая разница между ценой исполнения заявки и текущей котировкой (маленький параметр pPending), то заявка может исполниться сразу. Тогда  Take profit + Stop Loss автоматически не выставятся. Как я уже писал выше, для  «Take profit + Stop Loss» и нужна активная заявка, на основании которой он может быть выставлен. Тут такой заявки не будет.
Поскольку цель настоящей статьи показать, как автоматизировать на Qpile торговую стратегию, а не попытаться написать идеального робота, то решение этих проблем оставляю для будущих статей.
Так же пришлось коренным образом переписать функцию проверки открытых позиций IsOrder. Эту функция выполняет проверку в два этапа. Сначала ищет выставленную заявку (новая функция is_any_order). Поиск происходит путем перебора всех заявок и сравнения их инструмента с заданным, а так же статуса с «Активным». Заявка найдена – значит, функция IsOrder вернет «единичну» — что означает «есть заявка или открыта позиция», новой заявки нам выставлять не нужно. Если такую проверку не сделать, робот будет выставлять заявку при каждом своем запуске (по умолчанию это 10 секунд, но этот интервал можно менять), пока есть сигнал.  Если такой заявки не обнаружено, то проверяется остаток. Если он отличен от нуля, то  тоже считаем, что есть позиция и заявку не выставляем.  Остаток запрашивает ранее функция get_position, которая вызывает стандартную функцию Qpile: DEPO_CURRENT_BALANCE. Стоит заметить, что эта функция работает только для ММВБ. Для ФОРТС существует другая функция запроса позиций по бумагам, поэтому, если вы хотите, чтобы робот торговал, допустим, фьючерсами, то придется переписать get_position, но вопросы ФОРТС выходят за рамки этой статьи. 
Перевод стратегии на qpile. Шаг 3. Реализуем тело робота
Вот добрались и до самого робота. И первая проблема, с которой пришлось столкнуться, это то, что в Qpile нет средств, что бы взять данные свеч, с заданным номером от текущей. Посмотрите, например, как это сделано в программе на Метатрейдере в советнике из первой части статьи:
int start()
  {
   MALong=iMA(NULL,0,MALongPer,0,MODE_SMA,PRICE_CLOSE,1);
   MAShort=iMA(NULL,0,MAShortPer,0,MODE_SMA,PRICE_CLOSE,1);
   StohPrev=iStochastic(NULL,0,5,3,3,MODE_SMA,0,MODE_MAIN,2);
   StohPrevSig=iStochastic(NULL,0,5,3,3,MODE_SMA,0,MODE_SIGNAL,2);
   StohCurr=iStochastic(NULL,0,5,3,3,MODE_SMA,0,MODE_MAIN,1);
   StohCurrSig=iStochastic(NULL,0,5,3,3,MODE_SMA,0,MODE_SIGNAL,1);
   Strategy();
   return(0);
  }




 
В этой части программы мы получаем значения индикаторов по номеру свечу (последний параметр в функции iMA – получить скользящую среднюю или iStochastic – получить значение стохастика). Например, если нам надо взять  значение текущей еще не сформировавшейся свечи, то мы используем аргумент 0, если той, что сзади нее (более ранняя свеча), то 1, еще более ранняя 2 и так далее. В Quik этого сделать не получится. Значение индикатора можно получить исключительно по дате и времени.  Вот пример аналогичного кода на Qpile из нашего робота:

'Вычисляем индикаторы
slice=GET_CANDLE_EX(pGraphikNameShort, vLastDate, vLastTime)
lines = Get_Value (slice,«LINES»)
line = Get_Collection_Item (lines, 0)
MAShort = Get_Value (line,«Close»)+0
 
slice=GET_CANDLE_EX(pGraphikNameLong, vLastDate, vLastTime)
lines = Get_Value (slice,«LINES»)
line = Get_Collection_Item (lines, 0)
MALong = Get_Value (line,«Close»)+0
….




 
Иными словами, сначала нам нужно вычислить время открытия текущей свечи, затем время предыдущей. Причем в Qpile отсутствуют какие-либо сервисные средства для работы с   датой и временем. Поясню этот момент подробнее. Мы знаем, что в минуте 60 секунд, а в часе 60 минут. Как нам к заданному времени прибавить, скажем, 2 часа, 1 минуту и 23 секунды?  Сначала нам надо сложить секунды. Если получилось больше 60, то вычитаем из количества секунд 60 и к минутам добавляем 1. Затем так же складываем минуты, потом часы, при необходимости прибавляем сутки. Таким образом, чтобы получить время, большее или меньшее заданного на заданный интервал, нужно написать довольно-таки сложный и объемный алгоритм. Во многих языках, таких как C++, C#, Delphi, встроенный язык 1С, есть библиотеки, которые избавляют программистов от необходимости изобретать велосипед – бери да пользуйся. В Qpile таких алгоритмов нет.
Ну что ж придется писать свои алгоритмы.
Итак, вот что делает робот в самом начале своей работы:
  1. Запрашивает дату и время с торгового сервера.
  2. Разбивает дату и время на отдельные переменные: год, месяц, день, часы, минуты, секунды.
Вычитаем из времени интервал и округляем время. Если время окажется меньше времени начала торговой сессии, то переходим к предыдущей дате. Для вычисления времени предыдущей свечи аналогичным образом вычитаем интервал из даты и времени текущей свечи. Вот для чего нам нужны параметры, характеризующие время начала и окончания торговой сессии.
 Для упрощения алгоритма мы не учитываем переход с первого числа месяца на последний предыдущего. В первый тайм-фрейм перового дня месяца мы просто не торгуем, считая, что такое допущение погоды не сделает.
После вычисления даты и времени нужных нам свечей мы вызываем функции Qpile, которые берет с графиков значения индикаторов. Затем вызываем торговые функции, после чего выводим значения промежуточных переменных в протокол, который позволяет нам мониторить работу робота.
★17
4 комментария
А где вы берутся котировки Российского рынка для Метатрейдера?
avatar
Arman, брал отсюда www.finam.ru/analysis/export/default.asp
avatar
Я не все прочитал, вопрос: можно скачать робота и пристроить к своему квику?
avatar
maximus79, в посте есть сылка на код. как настроить робота описано
avatar

теги блога orekton

....все тэги



UPDONW
Новый дизайн