Создание советника для открытия серии ордеров
После того, как я решил использовать валютное хеджирование в качестве основы торговой стратегии, 1-м делом я определил все необходимое для торговли. Итак, мне понадобится:
- индикатор прохода цены серии валютных пар от определенного дня и времени;
- советник для открытия серии валютных пар в одном направлении;
- советник для закрытия серии валютных пар.
В данном посте я напишу советник для открытия серии валютных пар в одном направлении.
Предварительно можно посмотреть тестирование созданного советника.
Сразу ответ на вопрос: почему советник, а не скрипт? Меня интересует открытие на новом баре (свече). Скрипт сразу после переноса на торговый график начнет свою работу. Советник же подождет до нового бара и только тогда откроет позиции.
Более того, советник, в случае сбоя, оставшись работать на графике будет проверять, все ли ордера по заданным валютным парам открыты. Если ордер по определенной валютной паре не найдется, советник его немедленно откроет.
Хочу отметить, что создание указанных советников и индикатора не обеспечит прибыльную и стабильную торговлю на Форекс. Это лишь помощники, которые облегчат процесс анализа торговой ситуации и операции с ордерами.
Чтобы избежать задание лишних внешних параметров и ускорить работу, будут 2 разных советника. 1-й на Buy, 2-й на Sell. Это снизит уровень возможных ошибок от невнимательности.
Пишем советник
Для тех, кто только начинает знакомиться с программированием на MQL 4, для лучшего понимания основ языка MQL, предварительно можно изучить запись в блоге о том, как написать индикатор, который выводит на экран спрэд по текущему торговому инструменту.
Для начинающих, многие вопросы программирования после прочтения указанной записи должны проясниться.
Определим необходимые внешние задаваемые переменные.
- количество лотов;
- отклонение от цены открытия;
- открываемая серия;
- проскальзывание.
Теперь открываем MetaEditor. Слева в Навигаторе выбираем вкладку «Файлы» и открываем советник Moving Averages. После открытия выделяем и удаляем все содержимое, затем жмем Файл – Сохранить как и сохраняем под именем «Buy_X».
Задаем внешние переменные
Первыми строчками задаем необходимые внешние переменные.
Для начинающих программистов на языке MQL4 – параметр extern указывает, что переменная может быть изменена внешне, т.е. в MetaTrader’е.
Переменная workLimit это ограничитель. Если цена ушла от цены открытия нового бара больше указанного уровне, ордер не будет открыт.
Переменная openSet в принципе небольшое дополнение. Хотя я никогда не использовал ранее доливки при торговле на Форексе, но на будущее решил предусмотреть этот параметр.
Значение переменной openSet будет указана в Комментарии в открываемом ордере. С помощью этого мы сможем определить с какой серией ордеров мы сейчас работаем.
Чтобы было понятно, объясню на примере: на новом баре мы открыли 5 ордеров 1-й серии. Через 10 минут мы решаем, что нужно сделать доливку. Мы вновь переносим советник на торговый график, в параметрах советника указываем номер серии 2 и на новом баре советник открывает на еще 5 ордеров.
Теперь объявляем глобальные переменные советника.
В массиве pairs[] мы задаем необходимые валютные пары. Размер массива в 14 элементов задан произвольно.
В массиве pairsOpen[] будет храниться количество открытых ордеров по каждой валютной паре. Т.е. после 1-го открытия у всех элементов в данном массиве должно быть значение 1. Значение 2 будет означать, что сделана доливка.
Массив openPrice[] содержит цены открытия текущего бара для каждой валютной пары.
Функция init()
Переходим к содержимому функции init() . Данная функция запускается при прикреплении советника к торговому графику, изменению настроек советника, а также смене символа или периода графика.
Сначала узнаем, в системе используется 5 и 3 знаков после запятой или 4 и 2.
Предопределенная переменная Digits хранит значение количества знаков после запятой текущего символа (валютной паре).
Главное помнить, что несмотря на то, что ордера мы собираемся открывать по нескольким валютным парам, советник присоединяется к графику одной валютной пары и именно с ней мы сейчас работаем.
Если 3 или 5 знаков после запятой, в переменную number заносим значение 10. Это множитель. К примеру, мы указали изначально, что проскальзывание равно 2 пункта. Но с 5-ю знаками после запятой это значение должно равняться 20. Как раз это мы и получим, помножив 2 на 10.
Теперь заполняем данными наши внешние переменные.
Переменной orderType мы присваиваем 0, т.к. пишем советник, который будет открывать ордера на Buy. Чтобы советник работал на Sell, переменной нужно присвоить значение 1 и перекомпилировать.
Важной переменной является magic , т.е. магическое число. Из всего потока ордеров наш советник будет искать среди открытых только ордера с данным магическим числом.
Хотя мы можем задать любой размер массива pairs[] , в переменную totalPairs необходимо поместить количество заданных элементов. Т.е. если 3 валютных пары, а размер массива 14, в переменной totalPairs должно быть значение 3.
Пройдемся в цикле по массиву pairs[] .
Изначально присваиваем переменной totalPairs значение 0. В переменную size помещаем ранг массива pairs[] .
В цикле проходимся по всем элементам массива pairs и если длина элемента больше 0, то увеличиваем количество валютных пар в переменной totalPairs .
Как только цикл доходит до пустого элемента, то с помощью оператора завершения break работа цикла завершается.
Советник должен знать реальное число валютных пар в массиве, т.к. это значение будет использоваться в дальнейшем в циклах и позволит избежать лишних проходов.
Теперь нам необходимо сохранить цены открытия по всем валютным парам. Нам это необходимо для отслеживания появления нового бара.
Т.к. проверка будет производиться также после открытия серии ордеров, то лучший вариант создать отдельную функцию.
Функция setOpenPrices() в цикле проходит по всем заданным валютным парам в массиве pairs и узнает с помощью функции iOpen(…) для каждой цену открытия, которая сохраняется в массиве openPrice .
Советник должен знать текущее положение на графике (текущий бар), иначе он не откроет ордера в нужное для нас время.
После того, как советник обновил время открытия для каждой валютной пары, необходимо проверить все открытые ордера.
Проверка ордеров также будет происходить при поступлении каждого нового тика. Вынесем проверку в отдельную функцию.
Обнуление массива pairsOpen необходимо, т.к. ордер, который советник считает за открытый мог уже быть закрыт трейдером, брокером или, к примеру, вылететь по Стоп-лоссу или Тейк-профиту.
Выборка ордеров происходит по типу ордера orderType , магическому числу magic и номеру сета openSet .
Если советник работает с доливкой, т.е. номер сета, к примеру 2, то его уже не интересуют ордера 1-го сета.
Мне кажется, что ограничение функциональности советника позволяет избежать ошибок в его дальнейшей работе.
Глобальная переменная количества ордеров count также обнуляется в начале функции и увеличивается с каждым новым выбранным ордеров.
Чтобы записать количество открытых ордеров в массив pairsOpen[] нужен номер валютной пары. Т.к. советник знает лишь наименование финансового инструмента, то приходиться дописать дополнительную функцию по получению номера getPairNumber(…) .
После того, как номер валютной пары в массиве pairs получен, соответствующий элемент массива pairsOpen[] становится равным номеру сета openSet .
После прохождения всех ордеров в цикле советник делает последнюю проверку перед выходом из функции.
Если подсчитанное в цикле количество ордеров count больше «0″, советник запишет во флаг работы work значение false , что поставит запрет на открытие новых ордеров.
Количество ордеров, подсчитанное и хранящееся в переменной count может быть меньше заданного, если, к примеру, при открытии ордеров произошла Критическая ошибка. Флаг work мы переставляем в положение false , чтобы на новом тике советник, проверив количество ордеров, открыл недостающие.
Если оставить флаг работы work в положении true , советник откроет недостающие ордера только на новом баре.
Теперь обратимся к функции getPairNumber(…) , которая находит номер элемента в массиве pairs[] .
Функция getPairNumber(string pair) получает строковое значение валютной пары, к примеру, «GBPUSD». В цикле функция проходится по всем элементам массива pairs[] .
Как только цикл доходит до искомой валютной пары if (pairs[i] == pair) , советник выходит из функции и возвращает номер валютной пары в массиве pairs[] .
За возврат целого числового значения отвечает параметр int , который стоит перед объявлением функции int getPairNumber(string pair) .
Функция start()
Функция start() совсем небольшая.
По сути советник лишь обновляет список ордеров и переходит к функции criteria() , которая отвечает за проверку условий для открытия ордеров.
Функция проверки условий criteria()
Советник на каждом новом тике должен проверить новый ли бар и принять решение об открытии ордеров.
В 1-м условии if (checkOpenPrices() && work) при поступлении нового бара и флаге work , разрешающим работу, со значение true советник запустит функцию на открытие ордеров.
В ином случае, если количество ордеров, найденных функцией loadOrders() , меньше количества валютных пар в массиве pairs[] и запрещенной работе ( work = false ) советник также запустит функцию на открытие ордеров openOrders() .
Функция checkOpenPrices() как и все содержимое функции criteria() запускается с приходом нового тика. Проверка всех валютных пар на образование новой свечи довольно натянута: в принципе можно было проверить лишь цену открытия текущего инструмента. Но я все-таки сделал по-другому:
Как только по 1-й валютной паре мы находим отличие в текущей цене открытия от той, что сохранена в массиве openPrice[] , функция checkOpenPrices() возвращает положительный ответ.
Функция checkOpenPrices() возвращает логический ответ return (true) , на что указывает префикс bool checkOpenPrices() перед названием функции при объявлении.
Если же количество открытых ордеров count меньше количества ордеров в нашем массиве pairs[] и флаг работы work установлен в false , это означает, что советник уже открыл ордера, но не полностью.
В таком случае советник также вызывает функцию openOrders() и она открывает недостающие ордера.
Напомню, что советник работает только с текущим сетом. Советник определит отсутствие ордера только по номеру сета в переменной openSet , несмотря на то, что могут быть открыты ордера по другому сету.
Функция открытия ордеров openOrders()
Условием для открытия ордера по определенной валютной паре является значение элемента массива pairsOpen[] . Если оно равно номеру сета openSet , то ордер не откроется.
Если значение pairsOpen[i] не равно сету, то ордер открывается с помощью функции openOrder(…) .
После открытия ордера элемент pairsOpen[i] получает значение номера сета openSet .
Переменная total, которая увеличивается с каждым новым открытым ордером, сообщит на экран количество открытых ордеров, после завершения работы цикла.
Как только ордера открылись, флаг работы work получает значение false , что позволит избежать ошибок.
Завершающий этап работы функции – обновить информацию советника о текущем баре. Т.е. ордера открылись на новом и, чтобы функция не вызывалась повторно, мы должны обносить цены открытия в массиве openPrice[] .
Таким образом, условие if (checkOpenPrices() && work) в функции criteria() более не позволит запустить функцию openOrders() .
Функция открытия ордера openOrder()
Функция bool openOrder(int type, int pairNumber) получает тип открываемого ордера int type и номер валютной пары int pairNumber в массиве pairs[] .
Функция openOrder() сама по себе элементарна. Переменная ticket получает значение -1 и используется в цикле while (ticket как условие.
Цикл while () работает до тех пор, пока ордер не будет открыт или не сработает оператор return(bool …) .
Главным условием для работы советника при открытии ордера является удаление цены от цены открытия бара. Уровень удаления задан в переменной workLimit . Проверка производится в следующем условии (для ордера Buy):
Если Текущая цена минус цена Открытия в пунктах больше лимита удаления, то мы выходим из функции openOrder() .
Важным моментом является перевод лимита к текущим условиям, а именно помножение на значение глобальной переменной number , которое было рассчитано в функции init() .
Если Дилинговый центр предоставляет котировки в MetaTrader 4 с 5-ю или 3-мя знаками после запятой, то значение переменной workLimit , которое задается в пунктах, помножается на 10 (значение глобальной переменной number ).
Так как работа ведется не с финансовым инструментом графика, к которому прикреплен советник, а с любой валютной парой, мы используем функцию MarketInfo(…) для того, чтобы узнать цену Bid или Ask или размер пункта.
Если бы работа велась по финансовому инструменту текущего графика, то мы бы использовали предопределенные переменные Bid , Ask и Point .
Как только подготовительные вычисления закончены, с помощью функции OrderSend(…) советник отправляет запрос на открытие ордера.
Переменная lot , т.е. количество лотов, у нас внешняя. Уровень проскальзывания, также заданный во внешней переменной slippage , помножается на переменную number , чтобы подстроиться под котировки с 5-ю или 3-мя знаками после запятой.
Уровень Стоп-лосса хоть и вынесен в отдельную переменную SL , но все равно равен 0 , так как меня он не интересует. При желании можно вывести его во внешнюю переменную и задавать значение из MetaTrader’а.
В комментарий к ордеру записывается номер сета, переведенный из числового в строковое значение DoubleToStr(openSet, 0) . Как раз по этому параметру и происходит отсев ордеров в функции loadOrders() .
Ордер открывается с магически числом, заданным в глобальной переменной magic . Это 2-й параметр, по которому происходит отсев ордеров в функции loadOrders() .
Если функция OrderSend() возвращает отрицательный номер тикета, то происходит проверка кода ошибки с помощью функции checkError() .
Код последней ошибки записывается в глобальную переменную lastError = GetLastError() .
В случае, если функция checkError() возвращает отрицательный результат прохождения проверки, функция openOrder() завершает работу.
В ином случае, если функция checkError() возвращает положительный ответ, цикл while (…) перезапускается.
Как только номер тикета, возвращаемый функцией OrderSend() станет положительным числом, функция openOrder() завершит работу с положительным ответом return (true) .
Функция обработки ошибок checkError()
Функция checkError() работает с глобальной переменной lastError , содержащей код последней ошибки. Код ошибки передается в оператор-переключатель switch(lastError) .
Ошибки разделены на Преодолимые и Непреодолимые. При преодолимых ошибках производится обновление данных с помощью функции RefreshRates() или обновление данных после небольшой задержки.
Функция Sleep(int milliseconds) помогает советнику осуществить задержку.
Как только предположительно удалось решить возникшую ошибку, функция checkError() возвращает результат: положительный или отрицательный.
Тестирование советника
Теперь, когда написание советника для валютного хеджирования закончено, проведем тестирование.
Как видно, советник успешно открыл 1-й сет, дополнил недостающую позицию и открыл 2-й сет после изменения входных параметров.
Советник Buy_X начал открывать ордера 1-го сета
Сигнал советника Buy_X об успешном открытии ордеров
Записи по работе советника Buy_X на вкладке Эксперт
Записи по работе советника Buy_X на вкладке Журнал
Терминал — Торговля — Закрываем один из ордеров
Советник Buy_X на новом тике открывает недостающий ордер
Меняем настройки советника Buy_X
Советник Buy_X делает доливку
Заключение
Далее в блоге я напишу, как добавить к советнику графическое сопровождение, чтобы на графике, где он работает было наглядно видно, собирается советник открывать ордера или нет.
Также я расскажу, как использовать тот же советник для закрытия перекрытых ордеров.
Полный код советника на языке MQL4:
Остались вопросы? Есть что сказать? Пишите на Форуме.
Советник открытия ордеров с автоматическим расчетом лота
Содержание:
- Брокер Alpari
- Брокер NPBFX
- Брокер InstaForex
В помощь трейдеру:
- Forex Tester 4 со скидкой
- Бездепозитные бонусы
- VPS для Форекс
Упростите и ускорьте свою торговлю с помощью советника для открытия ордеров со встроенными функциями мани менеджмента. Открывайте ордера в пару нажатий клавиш, без нудных расчетов размеров лота и ручного выставления стопов и тейков!
Что умеет советник Trade Predator
В первую очередь, советник Trade Predator полезен из-за ускорения открытия ордеров, плюс встроенных функций Мани Менеджмента. Больше не нужно рассчитывать размер лота вручную или с помощью дополнительного индикатора, все будет сделано автоматически и очень быстро.
- В пару нажатий клавиш открывать ордера с заданными Стоп лоссом и Тейк профитом;
- Включает в себя функции индикатора с автоматическим расчетом лота от депозита;
- Выдерживает заданный вами процент риска на сделку;
- Открытие сделки с помощью горячих клавиш, без применения мыши;
- Незаменим в первую очередь для пипсовщиков и скальперов, так как позволяет экономить драгоценные секунды и входить в рынок настолько быстро, насколько это возможно;
И на этом не все, подробности ниже, когда перейдем к разбору настроек, благо, их совсем не много.
Установка Trade Predator
- Для начала устанавливаем советник стандартным способом;
- После, перезапускаем торговый терминал и убеждаемся, что у нас включена Авто-торговля (горит зеленым);
- Дальше, выбрав в списке слева наш советник, перетаскиваем его на график и перед нами автоматически открывается окно настроек, которые мы сейчас и рассмотрим подробнее;
Настройка Trade Predator
- BUY_Order — отвечает за то, какой ордер будет выставлен по умолчанию, Buy — если ставим true, Sell — если false. Можно не менять, так как направление ордера можно в любой момент сменить прямо на графике, но об этом ниже;
- LotSize – размер лота, если вы используете фиксированную лотность для каждой позиции, если вы не планируете использовать фиксированный лот, оставьте в этом параметре нули. Ниже в настройках есть возможность выставить лотность в % от депозита и использовать вместо фиксированного лота;
- Slippage – допустимый размер проскальзывания. Если в момент открытия сделки произошло сильное ценовое движение и будет превышено значение допустимого проскальзывания, ордер не откроется;
- MagicNumber – идентификатор, который присваивается каждому ордеру, открытому с помощью Trade Predator. Этот параметр можно не менять. Позволяет советнику отличить ордера, открытые с его помощью, от других (открытых другими советниками или вручную);
- RewardFactor – множитель, определяющий соотношение размеров Take profit к Stop loss. К примеру, значение 3 будет означать, что Take profit будет в 3 раза больше, чем Stop loss. Или, другими словами, для Тейк профита будет использоваться размер Стоп лосса, умноженный на 3;
- RiskPercent – расчет лота от депозита, процент риска на сделку, в зависимости от размеров депозита. К примеру, если вы выставите 1%, то лотность ордера автоматически будет рассчитываться таким образом чтобы, в случае срабатывания Стопа, ваш убыток не превысил 1% от депозита;
- TakeProfit – размер Тейка по умолчанию;
- TextColor – цвет текста советника на графике;
- CustomStyles – включает/выключает 2 настройки ниже: SLStyle и TPStyle. Это чисто косметические настройки, которые отвечают за стиль отображения линий стопа и тейка (пунктир, сплошная и т.д);
После добавления советника на график в правом нижнем углу появятся следующие надписи
Здесь показаны направление ордера, который будет открыт, размеры тейка и стопа в пунктах и в денежном выражении, множитель соотношения SL к TP и размер будущей позиции в лотах. Если пока что-то кажется непонятным не расстраивайтесь, подробнее смотрите в видео ниже, все предельно просто и понятно на самом деле.
Горячие клавиши
Открытие и изменение параметров ордера непосредственно на графике осуществляется с помощью горячих клавиш W D X С
- W — изменить размер SL и TP
- D — изменить направление открываемой позиции (buy или sell)
- X — открыть позицию
- C — закрыть позицию
Нагляднее всего процесс настройки и работы с советником для открытия ордеров показан в видео
Скачать Trade Predator
Советник доступен для скачивания только зарегистрированным пользователям — Зарегистрируйтесь прямо сейчас, а после обновите эту страницу, чтобы появилась ссылка на скачивание.
https://cafeforex.ru/trading-blog/2011/06/20/%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5-%D1%81%D0%BE%D0%B2%D0%B5%D1%82%D0%BD%D0%B8%D0%BA%D0%B0-%D0%B4%D0%BB%D1%8F-%D0%BE%D1%82%D0%BA%D1%80%D1%8B%D1%82%D0%B8%D1%8F-%D1%81%D0%B5%D1%80%D0%B8%D0%B8-%D0%BE%D1%80%D0%B4%D0%B5%D1%80%D0%BE%D0%B2/