|
|
|
|
|
|
|
|
|
Функция OrderSend() в MQL-4 является , пожалуй, самая распространенная, так именно с ее помощью выставляются отложенные ордера и открываются ордера по рынку. Рассмотрим применение этой функции на примере скрипта, который будет разворачивать открытую позицию по конкретному инструменту. Предположим, у нас имеются одна или несколько открытых позиций по EURUSD в одну сторону, в покупку. Происходит резкое движение цены вверх, цена достигает целевого уровня (по нашим предположениям), на котором необходимо фиксировать прибыль (закрывать позиции), и еще желательно открыть позицию в том же объеме в обратном направлении, на продажу. В условиях быстрого рынка, когда цена меняется достаточно быстро, проделать такую операцию вручную быстро не получится. Нам требуется:
- Закрыть последовательно одну или несколько ордеров в покупку.
- Открыть один ордер в продажу с таким же суммарным объемом.
MQL-4 и MetaTrader4 позволяют все эти операции провести одним действием. Дело в том, что в терминале МТ4 разрешается закрывать одну открытую позицию противоположной открытой позицией, то есть ордера в покупку закрывать ордерами в продажу. При этом дополнительно получается экономия в размере спреда. Если у нас есть ордер EURUSD buy по цене 1.2000 объемом 0.1 лота и ордер EURUSD sell по цене 1.2500 объемом 0.1 лота( и больше открытых ордеров по EURUSD нет), то независимо от того, где находится в данный момент цена, общая текущая прибыль по этим двум ордерам будет составлять 500 пунктов ( Point=0.001). Свопы, начисляемые по этим двум ордерам в итоге будут отрицательными, но для нашего случая это не имеет значения. В МТ4 разрешено закрыть ордер в покупку, открытый по цене 1.2000, ордером в продажу по цене продажи (1.2500). Разумеется, объемы взаимозакрываемых ордеров должны быть равны.
Допустим, в данный момент Bid по EURUSD равен 1.2600. Если закроем каждый из двух ордеров независимо, то получим: buy 1.2000 закрываем по 1.2600 (+600 пунктов) и sell 1.2500 кроем по 1.2603 ( Bid + спред 3 пункта) – итого имеем минус 103 пункта. В сумме прибыль 600-103=497 пунктов. Предполагается, что цена между двумя закрытиями не менялась. Если бы мы закрывали один ордер другим, то получили бы 500 пунктов (1.2500-1.2000).
Если объемы встречных ордеров не совпадают, то полностью закрывается ордер с меньшим объемом и остается один ордер с объемом равным разнице между взаимозакрываемыми ордерами. То есть, в случае 0.2 buy и 0.1 sell , после частичного закрытия остался бы ордер 0.1 buy по цене покупки. Вот это свойство мы и используем в разворотном скрипте Revers.mq4. Мы проверим наличие ордеров, открытых только в одну сторону, подсчитаем суммарный объем открытой позиции по этому инструменту и откроем удвоенный объем в противоположную сторону. Таким образом, после работы скрипта у нас будут ордер(а) с объемом V1 и ордер в противоположную сторону с объемом V2=2*V1. Затем мы сможем вручную закрыть необходимые ордера и оставить только один ордер с объемом V2-V1=2*V1-V1=V1.
Построим скрипт по принципу преодоления препятствий. Если на каком-то этапе условия не складываются – скрипт досрочно прекращает работу. Первым делом(как обычно) встраиваем защиту от реала:
|
|
|
|
Если скрипт окажется запущенным на реальном счете, то появится предупреждение и скрипт закончит работу. Далее проверяем наличие ордеров:
|
|
|
|
Нет ордеров ( OrdersTotal() равно нулю) – нет смысла продолжать работу, выход.
Но даже, если ордера и есть, необходимо убедиться, что присутствуют ордера на нашем символе (график инструмента, на котором мы запускаем скрипт), и эти ордера являются открытыми, а не отложенными. Для этого пройдем в цикле по списку и посчитаем ордера по нашему инструменту. Заодно посчитаем и объемы ордеров в покупку и в продажу, по отдельности.
|
|
|
|
Видно, что если ордер открыт не по нашему символу (OrderSymbol()!=Symbol()), работа с ордером прекращается и происходит переход к следующему по списку ордеру (оператор continue;) . После прохода по списку ордеров, можно по очереди проверить отдельно наличие ордеров в покупку и ордеров в продажу, но можно проверить оба счетчика одной операцией – сложив счетчики. Если сумма равна нулю – значит открытых ордеров нет, есть только отложенные.
|
|
|
|
Такой вариант нам тоже не подходит, выходим. Следующая проверка – нам нужно, чтобы были только ордера в покупку, либо только в продажу. Мы хотим иметь однозначно определенную ситуацию. Я проверил это простым умножением счетчиков, если произведение равно нулю – значит имеем только ордера одного вида.
|
|
|
|
Если скрипт после всех этих проверок еще не завершил работу – значит наступило время открытия встречного ордера с удвоенным размером.
|
|
|
|
Получение удвоенного ордера сделано не напрямую, то есть, вместо того, чтобы написать
reversLots=2*sellLots (для ордеров, открытых в покупку) , была использована сложная конструкция. Для чего это сделано? Размер позиции имеет тип double, и не всегда в компьютерных расчетах 2.0 умножит на 2.0 равно 4.0. Это связано с ошибками округления не целочисленных переменных в двоичной системе счисления. Хотя 2 умножить на 2 всегда равно 4. Поэтому, сначала мы умножаем buyLots на 10 (без точки) и удваиваем полученный целочисленный результат. Например, 0.2(лот)*10*2=4. Получили целочисленный результат четыре. Теперь нам необходимо получить размер reversLots(0.4) в 10 раз меньший с точностью до одного знака до запятой. Для этого мы делим intLots (целые лоты) типа int на 10.0 типа double (обратите внимание на точку) и нормализуем (усекаем) результат до точности в один знак после запятой. В итоге получаем значение 0.4 с необходимой точностью.
Если бы мы разделили 4 на 10 (4/10) , то, так как оба числа являются целыми(int), результат деления вычислялся бы по правилам целочисленной арифметики. Так как 4(целое) на 10 (целое) не делится, то ответ был бы ноль. Это одна из самых распространенных ошибок, нельзя забывать разницу между целочисленным делением и делением вещественных чисел в компьютерных вычислениях. Если вспомнить случай с Буратино, то его не сильно обманывали, когда говорили, что «5 на 3 не делится, вот тебе одна монета».
Итак, размер разворотного ордера вычислен, осталось только отправить торговую команду на сервер. Иногда цены двигаются настолько быстро, что скрипт выполнял вычисления, текущие цены уже совсем не такие как на момент запуска скрипта. Необходимо всегда иметь самые свежие цены, чтобы обезопасить себя от отказа сервера по причине устаревания цены. Для этой цели служит команда RefreshRates(), рекомендую почитать справку по ней. При выполнении этой команды не происходит обращения к торговому серверу брокера, поэтому ее можно использовать так часто, как вам это необходимо, не боясь нарваться на санкции брокера. Наконец, можно отправить торговый приказ OrderSend(). Хотя справка по этой функции содержит всю необходимую информацию, должен выделить две основные ошибки при ее использовании:
- часто забывают указать символ, по которому открывается ордер. То есть, просто пропускают первый параметр этой функции (Symbol()), а ведь в MQL-4 можно открывать ордера по «чужому» инструменту (в тестере нельзя). Компилятор эту ошибку прощает и ничего не сообщает, потом пользователь удивляется тому, что ордера не открываются .
- при открытии ордера типа OP_BUY(покупка по рынку) подставляют в качестве цены открытия Bid(цена продажи), для продажи – наоборот. Естественно, ордер вряд ли будет открыт.
Кроме того, желательно параметр Slippage (проскальзывание) не ставить равным нулю, так как повышается вероятность отказа из-за изменения цены - пока ваш торговый приказ дойдет до обработки торговым сервером, цена может измениться, нулевой параметр проскальзывания будет воспринят сервером как отказ от совершения сделки по несколько иной цене и ордер не пройдет.
На самом деле количество ошибок, возвращаемых терминалом или торговым сервером весьма велико, эти ошибки помогают перенаправить алгоритм в нужное русло. Для того, чтобы узнать код возвращенной ошибки, существует функция GetLastError(). Необходимо опрашивать этой функцией код ошибки каждый раз, когда мы получили ошибочную ситуацию. Так как, в случае неудачи OrderSend() возвращает значение -1, то мы выводим эту ошибку в том случае, если ticket меньше нуля (можно было проверить и ticket==-1).
Если ордер открылся нормально, сообщение об ошибке мы не увидим. Скрипт готов, я запускаю его на графике USDCHF, по которому был открыт ордер в покупку в размере 0.1 лота.
|
|
|
|
Видно, что скрипт открыл противоположный ордер в двойном размере.
|
|
|
|
В комментарии ордера указано «revers order» - наша метка. Теперь мы можем в любой момент кликнуть на один из этих ордеров, выбрать тип «Закрыть перекрытые ордера»,осталось только нажать желтую кнопку.
|
|
|
|
На этом первое знакомство с функцией OrderSend() для ордеров, открываемых по рынку, можно считать законченным. Скрипт по статье можно скачать здесь .
Перейти к статье «Принцип работы советника».
|
|
|
|
|
|