Навигация
Главная
Как стать клиентом
Скачать книги
Статьи
Контакты
Поиск
Ссылки
Статистика
Рейтинг Сайтов YandeG
 
Главная arrow Статьи arrow Статьи по MQL4 (продолжение3)
Статьи по MQL4 (продолжение3)

Первое правило бизнеса: защищайте свои инвестиции
Этикет банкира. 1775 год


До сих пор в советнике мы использовали только ордера с жестко установленным уровнем StopLoss и TakeProfit. Таким образом, открыв покупку, мы ждем срабатывания одного из двух уровней – либо мы получим прибыль, либо получим убыток. Третьего не дано. Но многие трейдеры считают более важным в торговой системе не вход в сделку, а систему выхода. Для более оптимального управления открытой позицией трейдеры используют скользящий стоп (Trailing Stop). Это позволяет передвигать уровень защитного стопа вслед за ценой, и в случае внезапного разворота забрать часть бумажной ( незафиксированной ) прибыли, вместо того, чтобы получить убыток по первоначальному стопу.

Прежде чем заняться написанием функции скользящего стопа, мы исправим все недостатки нашего советника торгующего по случайным сигналам. Для этого нам придется написать его фактически заново (Назовем его RTS.mq4 – Random Trade + Statistika).

 
 


Я использовал при создании функции-пустышки, то есть функции объявлены, но в них ничего не вычисляется, они возвращают пустые значения. Это бывает удобно при написании сложных систем, если у вас уже есть готовый алгоритм. Советник в таком виде компилируется без ошибок (функция isNewBar() просто не видна). Теперь мы можем каждую пустую функцию наполнять кодом и компилировать наш советник для проверки сделанных изменений на отсутствие грубых ошибок.

Схематически я изобразил это так:

 
 


Блок №1 реализован через проверку нового бара, если isNewBar() возвращает true (появился новый бар) , то сначала проверяется наличие сигнала на открытие позиции в покупку (SignalExist(OP_BUY)), и если такой сигнал есть, то делается попытка открыть ордер в покупку (GetOrder(OP_BUY)) . Аналогично делается проверка на открытие позиции в продажу. Теперь сигналы в покупку и продажу у нас разделены и не влияют друг на друга, мы избавились от недостатка предыдущей версии.

Обратите внимание ,что в качестве параметров я использовал зарезервированные слова из MQL-4 (OP_BUY и OP_SELL). Это облегчает понимание кода и вам самим, и тем, кто будет читать ваш код. Если бы я использовал вместо этого конкретные значения, то вам пришлось бы открывать справку, чтобы вспомнить, что означает параметр 0 при вызове функции GetOrder(0) .

 
 


Кроме того, в дальнейшем учет тонкостей открытия позиций OP_BUY и OP_SELL позволит нам отдельно оптимизировать советник в режимах LongOnly и ShortOnly.

Блок №2 состоит из простого вызова функции EveryTick(), которая также пока является пустой. В предыдущем советнике в функции deinit() мы использовали вызов функции HistoryCalculate(), я немного изменил вызов, чтобы расчет статистики производился только в тестере.

 
 


При работе в онлайне эта функция теперь не вызывается, но есть еще и оптимизация. При оптимизации она нам также не требуется. Добавим проверку IsOptimization().

 
 


Первой напишем функцию получения сигнала, для вычисления времени закрытия последнего времени добавили новую функцию – GetLastCloseTime().

 
 


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

Теперь пишем функцию GetOrder() на основе предыдущей версии. Я только изменил заполнение комментария, чтобы теперь мы могли анализировать движение цены за последние 3 бара. То есть, открываем позиции мы по-прежнему против тренда, но хотим найти связь между результатом сделки и направлением и размахом движения прямо перед открытием сделки. В дальнейшем вы можете в комментарий записывать любую интересующую вас информацию для последующего анализа.

 
 


Советник вроде полностью готов, но не хватает еще чуть-чуть. В предыдущей версии мы проверяли наличие открытых ордеров

 
 


и признали эту практику порочной. Но если не ставить проверку наличия открытых позиций, то теряем возможность контроля количества этих ордеров. Поэтому добавим новую внешнюю переменную MaxOpenOrder и функцию AllowedOpenOrder() (допустимость открытия новых ордеров определенного типа).

 
 


Теперь мы можем снова проверить нашу гипотезу о том, что при параметрах StopLoss=65 пунктов и TakeProfit=130 пунктов нужно совершать сделки против показаний индикатора MACD. Сохраним эту версию под именем RTS2.mq4. Запустим вновь тестирование на EURUSD H1 и получим один из результатов.

 
 


Прибыли нет, соотношение между прибыльными и убыточными сделками отражает соотношение между SL и TP. Но количество сделок в покупку и продажу теперь не зависит от модели LongOnly и ShortOnly. Теперь мы можем заняться написанием функции TrailingStop().

Основная функция скользящего стопа – защита бумажной прибыли с помощью передвижения уровня StopLoss в направлении открытой позиции. Если мы передвигаем стоп слишком быстро, то случайное движение против нашей позиции может выбить нас из сделки, после чего цена может опять пойти в нашем направлении. А если стоп передвигать слишком медленно – то неблагоприятный разворот может съесть большую часть нашей прибыли. Как видите, от алгоритма зависит все. Поэтому, сначала выдвигаем некую идею, а потом пытаемся ее запрограммировать.

Существует множество алгоритмов трейлинга, я рассмотрю только два. Я их для себя назвал динамический и статический. Динамический трейлинг – это перемещение защитного стопа при каждом изменении цены, если цена прыгнула в нашем направлении на 100 пунктов (допустим вверх для покупки) , то и защитный стоп тут же будет подтянут (вверх) на те же 100 пунктов. Статический трейлинг – это перемещение стопа по некоторым условиям, независящим от текущей цены. Например, перемещение стопов на каждом новом баре по какому-то алгоритму. Это позволяет игнорировать дерганье цены внутри бара.

Сначала реализуем трейлинг стоп динамический.

 
 


В этой функции все просто и дополнительного описания не требуется. Если TS=50, то текущий стоп будет на уровне 1.2643.

 
 


Добавим вызов трейлинга в вызов для каждого нового бара. В этом случае модификация ордеров будет происходить всегда одинаково (при появлении нового бара), независимо от модели тестирования.

 
 


Эту версию советника я сохранил под именем RTS+TS.mq4 (TS – Trailing Stop). Скомпилируем и прогоним на EURUSD H1.

 
 


Теперь мы можем рассмотреть вариант статического трейлинга.Введем понятие риска. Риск – это потенциальный убыток, который равен ходу цены от уровня открытия к уровню стопа. Это конечно некорректно, но не в этом суть. Идея будет такая: уровень риска должен уменьшаться пропорционально квадрату от времени. Если с момента открытия позиции прошло X времени и риск равен Y, то через X*2 времени риск должен уменьшиться в 4 раза. Грубо говоря, мы попытаемся реализовать скользящий стоп по аналогии с индикатором Parabolic.

 
 


Многие пытаются реализовать в советниках трейлинг на основе показаний индикатора Parabolic SAR. Но у этого способа есть один недостаток – индикатору все равно где вы вошли в позицию, фактически мы подстраиваемся под индикатор. Поэтому мы напишем алгоритм, который будет действительно менять уровень защитного стопа по принципу параболы и будет привязан к нашей точке входа.

Пусть в момент открытия ордера мы выставляем StopLoss на расстоянии S пунктов. При этом мы считаем, что если мы открыли сделку в правильном направлении, то через N баров мы уже будем в безубытке (уровень StopLoss можно будет передвинуть на уровень открытия ордера). Из школьной физики мы помним, что при движении по параболе расстояние S=(a*t^2)/2+V*t+C. Параметр а – ускорение, параметр V- начальная скорость, она у нас равна нулю, и параметр С – начальное смещение, оно равно значению стопа со знаком минус. Тогда S=(a*t^2)/2 или a=2*S/t^2 .

 
 


Если у нас первоначальный стоп равен 50 пунктов, а позиция должна выходить в безубыток через 5 баров, то a=(2*50)/5^2=100/25=4пункта/бар^2. Через 1 бар у нас стоп будет 4*1/2-50=-48, то есть на расстоянии 48 пунктов от цены открытия. Через 2 бара 4*2^2/2-50=-42 . Через 3 бара стоп 32 пункта, через 4 бара – 18 пунктов, через 5 баров: 4*5^2/2-50=0 – можем поставить ордер в безубыток , если к этому времени нас не закроет по стопу. Алгоритм есть, осталось написать код.

 
 


Функцию start() слегка меняем:

 
 


Компилируем советник RTS+TS2.mq4 и тестируем при тех же условиях и параметрах. Увы, этот вид трейлинг стопа не принес нам улучшения результатов. Возможно, это не та стратегия, где подходит параболический трейлинг. Мы можем еще немного изменить функцию EveryTick().

 
 


Тогда тестирование на других моделях («Все тики», например) изменит поведение нашего советника кардинально. Вы можете сами поэкспериментировать.Все варианты экспертов находятся здесь .
Готовая МТС, в виде программы на MQL-4 - это результат кропотливой работы. Сначала из наблюдений появляется некая идея, затем эта идея проверяется с помощью ручного тестирования на истории. Записываются дата и цена входа, причина выхода, подсчитываются прибыли и убытки. Если ручное тестирование подтвердило потенциальную прибыльность торгового подхода, наступает момент формализации алгоритма торговой системы.

Но не всегда получается формализовать сигналы входа, часто это очень сложная задача. Человек визуально может оценить текущую ситуацию в комплексе, а вот переложить правила на язык алгоритма затрудняется. Получается, что МТ4 тут бессилен? Частичный выход из этой ситуации есть. Если бы могли вручную подготовить сигналы для советника (вход по рынку, выставление отложенных ордеров и т.п.) , а с помощью тестера нашли бы оптимальные значения для защитного стопа, уровня Take Profit, перебрали разные варианты трейлинга…

Это неплохой вариант, но перспектива ручного набивания в файл даты, типа и направления сигнала может напрочь отбить охоту. Мы попробуем решить первую часть задачи (создание файла сигналов) с помощью нанесения на график стрелок разного вида и цвета. Потом все эти стрелки мы обработаем и запишем в файл их координаты. Открываем график, выбираем тип стрелки.

 
 


Наносим стрелку в нужное место и задаем ее цвет и записываем описание сигнала (sell limit).

 
 


Следующая стрелка должна иметь сигнал «buy limit» и быть синего цвета. А всего таких стрелок должно быть как минимум 50-60… Оптимизм немного угас. На каждую стрелку требуется 8-10 кликов мышки (посчитайте сами). Но у нас есть способ немного облегчить этот процесс, для этого мы используем скрипт с бесконечным циклом. Назовем его SmartArrow.mq4 (идея MetaQuotes). Создаем с помощью мастера:

 
 


Пока мы не дадим приказ о завершении работы скрипта, он так и будет в бесконечном цикле что-то делать (смотрите справку по IsStopped()). Этот скрипт ни на долю секунды не оставляет процессор в покое. Поэтому первым делом вставим в цикл функцию Sleep(1000), которая освобождает процессор хотя бы на 1 секунду.

 
 


Следующий наш шаг – научиться обрабатывать какое-то событие. Мы не можем скриптом выбрать в меню тип стрелки и «прилепить» ее к курсору, но мы можем отслеживать перемещение графических объектов на графике. Вставляем функцию ArrowPosChanged().

 
 


Проверить изменение положения любого графического объекта можно с помощью функции ObjectGet(ИмяОбъекта, ТипСвойства). Объект стрелка имеет только две координаты – первые координаты времени и цены. Кроме этого мы будем менять цвет и тип стрелки.

 
 


Факт изменения координаты можно выяснить сравнением значения координаты в текущий момент времени и в какой-то предыдущий. Поэтому введем глобальные переменные curPrice, prevPrice, сurrTime и prevTime. Получился такой код:

 
 


Мы добавили проверку, если стрелки с именем «SmartArrow» нет на графике – то скрипт завершает свою работу. Проверка первого вызова функции не влияет на работу скрипта, делает алгоритм более правильным – нет ложного возврата значения true при первом вызове функции ArrowPosChanged(). Запускаем для проверки скрипт:

 
 


Теперь дадим скрипту для работы стрелку с именем «SmartArrow», для этого просто переименуем любую стрелку на графике.

 
 


Запускаем скрипт и получаем сообщение (я добавил вывод Comment()).

 
 


Сохраняем это вариант скрипта и продолжаем дорабатывать. Первым делом при запуске скрипта создадим нашу стрелку «SmartArrow» автоматически и зададим для нее все атрибуты. Для этого добавим функцию init().

 
 


Мы пишем сигналы для советника, тестирование советника начинается не ранее сотового бара с конца, поэтому мы и создали нашу стрелку почти в самом конце графика. Удаляем наш скрипт (вы не забыли, он все еще крутится на графике?), компилируем SmartArrow2.mq4 и запускаем на графике

 
 


Запуск проходит удачно, закладка «Эксперты» подтверждает, срабатывает блок проверки первого вызова функции.

 
 


Проматываем график в самый конец , ищем зеленый кружок и не находим… Что не так, почему не работает? С помощью горячих клавиш Ctrl+B просматриваем список объектов, находим нашу стрелку – она там же, где и была!

 
 


Тут мы вспоминаем, что почти любая функция в МТ4 возвращает код ошибки, если мы добавим обработку этой ошибки, то возможно, поймем в чем дело.

 
 


Компилируем, запускаем и смотрим закладку «Эксперты»: ошибки нет. Ошибка в нашем алгоритме, ситуацию, когда стрелки нет на графике при запуске скрипта мы обрабатываем, а наличие этой стрелки дает сбой. Изменим еще раз код init():

 
 


Теперь код работает и мы находим зеленую стрелку там, где ей и положено быть. Осталось только написать обработку события «Перемещение стрелки». Условимся так: если стрелка оказывается ниже Low бара , то превращаем ее в стрелку «Вверх»(код 241) синего цвета, если выше High бара, то будет стрелка «Вниз» (код 242) красного цвета (мы отмечаем поступление сигналов limit). Вы можете выбрать любые другие коды для стрелок, в справке МЕ они показаны:

 
 


Если стрелка находится между High и Low – просто ругаемся на неумелое манипулирование стрелкой. При этом не забываем выводить в комментарии координаты стрелки в терминах время/цена.

 
 


Скрипт работает, только иногда не успевает изменить свойства перемещенной стрелки. Это происходит в том случае, если стрелку в момент изменения свойств держит пользователь. Добавим обработку этой ошибки (то есть ситуации, когда должно происходить изменение свойств графического объекта, но этого не происходит). Код обработки ошибки можно посмотреть в SmartArrow2.mq4. Компилируем и запускаем скрипт, перемещаем но не отпускаем стрелку и наблюдаем вывод в закладку «Эксперты».

 
 


Смотрим в справке «Коды ошибок» и узнаем, что «Объект не существует». То есть, для программы объект недоступен, так как он занят пользователем, и поэтому возвращается такая ошибка. Причина понятна, теперь нужно придумать как разрешить такую исключительную ситуацию. Можно было бы в случае возврата ошибки 4202 запускать цикл (бесконечный или с конечным числом попыток) установки нужного свойства, но всегда можно найти более простое решение. Посмотреть его можно в SmartArrow3.mq4.

Наконец мы победили визуальные ошибки, теперь нам получить файл сигналов. Наиболее напрашивающимся решением будет использование массивов. Каждый раз, когда определяется изменение координат стрелки, меняются свойства стрелки. В этот момент мы могли бы записывать в массив эти характеристики: время, цена и тип/код стрелки (вверх вниз). Объявим массив Arrows[1000][3]. Тысячи стрелок нам должно хватить. Какого типа должен быть массив? Время и код стрелки имеют тип int, а цена имеет тип double. Рассмотрим оба варианта.

 
 


Вариант с типом double прост:

 
 


Вариант с целочисленным массивом лишь немного сложнее. Нам нужно цену с Digits знаками после запятой привести к целому числу. Тут возможны два варианта:

 
 


Наконец, мы прошли по всем точкам/сигналам. Теперь нам нужно закончить работу скрипта и расставить все запомненные стрелки. Это наиболее удобно сделать в deinit().

 
 


После удаления скрипта на графике будут расставлены все наши стрелки. Первую часть – расстановка стрелок на графике – мы выполнили. Окончательный вариант сохранен как SmartArrow5.mq4. Все варианты скриптов находятся здесь

Ограничивайте убытки и дайте прибыли расти


Почему мы сразу в нашем скрипте не создали файл сигналов (при его завершении в deinit())? На это есть две причины:
  1. наши сигналы обнаруживаются только по факту перемещения стрелки SmartArrow на графике, а так как сигналы могут быть достаточно редкие, то при прокрутке графика мы не всегда сможем «перепрыгнуть» с одного сигнала на другой. Поэтому нам приходится применять технику тройного прыжка в легкой атлетике – делать промежуточную остановку (установку лишнего сигнала). Если бы такая функция была бы встроена в терминал МТ4, то значки мы могли бы ставить одним кликом на графике.
  2. если сигналов для тестирования необходимо нанести слишком много, то, вероятно, будут паузы между сеансами работы со скриптом. Мы можем делать несколько файлов в формате *.csv и сшивать их с помощью Excel.


Создадим отдельный скрипт для записи сигналов в файл. Поправляем положение стрелок на графике, удаляем ненужные (вынужденные лишние сигналы) и запускаем скрипт ArrowsToFile.mq4. Для его написания я скопировал готовые куски кода из WriteFile.mq4 , изменил только немного init().

 
 


Код для записи функции в файл не сложен, обратите внимание, что перед попыткой записи делается две заградительные проверки на тип объекта и имя объекта.

 
 


Для проверки скрипта добавим на график трендовую линию с именем SmartArrow555.

 
 


Запускаем скрипт на графике, мои 12 стрелок были обработаны.

 
 


Видим, что на графике оказалось три объекта, которые не являлись стрелками, также видим, что наши стрелки попали в обработку (отсортированы) не по времени, а по имени. В будущем нам может понадобиться операция сортировки. Открываем файл в Excel:

 
 


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

 
 


Сохраненный шаблон найдем в одной из папок МТ4:

 
 


Передаем этот файл другому человеку, он помещает в такую же папку на своем компьютере, открывает шаблон (signals.tpl) и видит в своем терминале полную копию вашего графика. На этом мы закончили с записью сигналов в файл, и нам предстоит научиться считывать эти данные из нашего файла.

Если есть операция записи в файл, то должна быть и обратная – чтение из файла. С массивами все понятно, мы можем всегда получить размер массива и организовать цикл for() для получения значений элементов массива по индексу элемента. Когда же мы открываем файл, мы обычно не знаем заранее сколько строк или значений в нем записано. Поэтому необходим какой-то стоп-сигнал, чтобы прервать цикл операций чтения из файла. Смотрим справочную систему и видим:

 
 


Функция FileIsEnding() является наиболее подходящей для завершения чтения из файла. Пишем простой скрипт для получения навыков, имя файла для чтения сигналов я заложил жестко.

 
 


Если бы это был индикатор или советник, то этот код привел бы к зависанию терминала! Важно: бесконечный цикл разрешается только в скриптах. Мы получили бесконечный цикл, так как не можем достигнуть конца файла. Необходима операция «прокрутки» файла, функция чтения из текстового файла FileReadString() как раз должна это обеспечить. Я не знал, как она работает, и решил проверить методом проб и ошибок.

 
 


Файл сигналов у нас маленький, поэтому через закладку «Эксперт» мы все должны увидеть. В Excel я насчитал 13 строк и 52 значения (13*4). Запускаю скрипт на любом графике и вижу лог:

 
 


Видим, что ожидаемые значения отличаются от напечатанных на единицу. Значит, скрипт делает один холостой ход, прежде чем определяет конец строки и конец файла. Это знание поможет нам для заполнения массива сигналов в советнике. Сохраним скрипт под именем ReadFile.mq4.

Советник для тестирования ручных сигналов мы писать не будем, возьмем RTS+TS3.mg4, назовем его CutTheLosses.mq4 и переопределим функцию init(). Функцию SignalExist() пока сделаем пустой.

 
 


Если мы знаем структуру текстового файла, то правильно прочитать его не сложно. Теперь мы имеем массив сигналов и можем приступить к тестированию советника. Для этого нам нужно научить функцию SignalExist() извлекать из Signals[] нужные сигналы. Используем простой алгоритм – в каждый момент времени пробегаем по всему массиву и сравниваем текущее время Time[0] со временем в Signals[i][0], если значения совпадают – проверяем тип сигнала и по нему выдаем ответ.

 
 


Функцию GetOrder() переделаем незначительно: удалим случайную составляющую, запись в комментарий ордера оставим прежней – вдруг пригодится в будущем.

 
 


Обратите внимание, в конструкцию else мы внесли дополнительную проверку на тип ордера. Если когда-нибудь мы передадим в эту функцию в качестве параметра OP_BUYLIMIT или OP_SELL_STOP, то это предотвратит нас в будущем от ошибочного открытия ордера в продажу (OP_SELL).

Советник готов, но если его запустить в тестере на EURUSD H1 (откуда мы брали сигналы), то ничего не выйдет. Смотрим закладку «Журнал» в тестере и видим:

 
 


Код ошибки (4103) подтверждает невозможность открытия файла. Дело в том, что все файловые операции в тестере производятся в своей собственной папке, поэтому скопируем наш файл в папку /tester/files .

 
 


Теперь тестирование советника по сигналам из файла проходит без ошибок. Более того, тестирование даже показывает прибыль! Оказывается, экзотический параболический трейлинг стоп имеет право на жизнь при определенных сигналах.

 
 


Теперь даже человек, который не написал и строчки кода (но прочитал данную статью), может создать свой файл сигналов, взять советник подобного класса, и, перебрав различные системы выхода (или трейлинга), проверить свою новую систему входов. А если система входов с трудом поддается формализации, то, наверно, это будет единственный путь (и на мой взгляд, не самый худший).
Все использованные файлы доступны здесь
Наш последний советник обладает только одним недостатком. В блоке init() имеется процедура открытия файла и чтения данных из него. При однократном прогоне в тестере это не страшно, но если мы захотим провести оптимизацию, то возникают две проблемы:
  1. падает скорость оптимизации, так как операции чтения из файлов на порядок (несколько порядков) медленнее, чем чтение из оперативной памяти;
  2. многократные операции открытия/закрытия файлов нежелательны для жесткого диска.
Программы в MQL-IV имеют глобальные переменные, объявленные в теле кода вне любой функции. Эти значения сохраняются в течение всей работы программы, и доступны из любой точки (функции) этой программы. Но для работы оптимизатора стратегий требуется некое иное решение, которое сохраняет значения переменных между запусками программ. В оптимизаторе мы многократно запускаем одну и ту же программу-советник, и иногда требуется помнить состояние некоторых переменных между отдельными запусками.

Для этих случаев существуют специальные переменные – Global Variables. В отличие от глобальных переменных на уровне одного кода/программы, эти переменные являются глобальными на уровне терминала. Таким образом, мы можем создать некую глобальную переменную в скрипте и задать ее значение (GlobalVariableSet()), а потом в индикаторе или советнике проверить это значение (GlobalVariableGet()).

В отличие от остальных видов переменных, эти глобальные переменные сохраняются и при закрытии терминала(в течение 4 недель). Предположим, наш советник должен открывать только одну позицию в день, и если факт открытия мы будем хранить в обычной переменной, то при закрытии терминала или внезапной пропаже питания значение этой переменной (назовем ее TradeOpen=true) не сохранится. И тогда при новом запуске терминала переменная TradeOpen примет значение по умолчанию (false). И советник вновь автоматически откроет новую позицию, что запрещено алгоритмом. Применение по настоящему глобальной переменной решает эту задачу.

Для освоения методики применения глобальных переменных сначала создадим скрипт. Этот скрипт будет создавать глобальный массив значение. Важно: глобальные переменные могут иметь только тип double. Не существует возможности создать массив глобальных переменных, но мы можем легко обойти это ограничение. Функции проверки и задания значений глобальных переменных оперируют именем этой переменной, заданной в текстовом формате. Поэтому, значение для массива по индексу мы можем задать как Имя_массива+индекс.

Назовем скрипт GlobalArray.mq4. Вместо глобального массива мы создадим массив глобальных переменных:

 
 


Код демонстрирует типичную работу с глобальными переменными – сначала проверяем наличие нужной переменной (GlobalVariableCheck(Имя_переменной)), а потом задаем ее значение. Запускаем скрипт на любом графике, жмем F3 и видим окно со списком глобальных переменных.

 
 


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

Все необходимые функции мы найдем в справочной системе MetaEditor. Функция GlobalVariableSetOnCondition() нам пока не нужна, остальные понятны.

 
 


Берем советник из предыдущей статьи, сохраняем его под именем CTL+GV.mq4(Global Variables). Делаем минимальные изменения в функции init(). Первым делом проверяем наличие глобальной переменной с именем FileOpened (файл уже открывали) , и если этой переменной еще нет – открываем файл, создаем множество глобальных переменных, содержащих сигналы на открытие позиций.

 
 


Также меняем немного функцию определения сигнала:

 
 


Как видите, пришлось внести минимальные изменения. Компилируем советника и запускаем тестер. После окончания тестирования жмем кнопку F3 и видим вновь созданные глобальные переменные.

 
 


Теперь мы можем проводить оптимизацию для наших входов – подбирать размер стопа и другие параметры трейлинга без замедления скорости оптимизации.

 
 


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

Вариантов использования глобальных переменных множество, вот некоторые из них:
      а) запомнить время последнего сигнала (на открытие позиции, значение индикатора);
      б) запомнить текущее число подряд идущих прибыльных сделок;
      в) передать из одного советника в другой какие-либо параметры (например, вероятность пробития важного уровня на другом инструменте);
      г) оптимальные параметры последней оптимизации в тестере и так далее.


Каждый сам может в какой-то момент понять необходимость их использования для повышения устойчивости работы МТС. Файлы доступны на форуме
 
         

Одни трейдеры в своей торговле опираются на показания индикаторов, другие отвергают индикаторы и говорят, что лучший индикатор – это цена. Но и те и другие считают важным использование в своей торговле разнообразных уровней. Одним из самых распространенных методов является выявление максимальной и минимальной цены на определенном временном интервале. В MQL-IV для этих целей есть функции Highest() и Lowest().

Предположим, что 20 октября 2006 года нам необходимо знать максимальную цену за 26 последних часов. Для этого я нанес на график фигуру Прямоугольник, чтобы визуально видеть нужный мне диапазон свечей.

 

 
Рис. 1
Рис. 1

 

Для того, чтобы нанести объект «Прямоугольник» на график, я сделал следующее: выбрал Меню «Вставка» - «Фигура» - «Прямоугольник».

 

 
Рис. 2
Рис. 2

 

Левой кнопкой мыши отметил верхний левый угол Прямоугольника, и не отпуская кнопки довел до правого нижнего угла желаемого Прямоугольника. Кроме того, я через свойства объекта задал (правой кнопкой мышки на выделенном объекте) установил цвет Прямоугольника.

 

 
Рис. 3
Рис. 3

 

Чтобы получить точную копию моего Прямоугольника, вы можете задать его координаты:

 

 
Рис. 4
Рис. 4

 

Много полезных сведений о возможностях терминала вы можете почерпнуть из статей на сайте разработчиков:
Секреты клиентского терминала MetaTrader 4
Секреты клиентского терминала MetaTrader 4: Система оповещений
Секреты клиентского терминала MetaTrader 4: Индикаторы
Секреты клиентского терминала MetaTrader 4: Библиотека файлов в MetaEditor'е

Из рисунка видно, что в Прямоугольник не попала часовая свеча со временем открытия 20-00 19 октября 2006 года. Это я сделал специально, чтобы разобраться с синтаксисом функции Highest(). Справочная система MetaEditor сообщает

 

 
Рис. 5
Рис. 5

 

Функция соответствует требованиям для практически всех функций в MQL-IV: сначала идет символ, для которого применяется функция, затем период (тайм-фрейм). То есть, эта функция позволяет вычислить значение на «неродном» символе и «неродном тайм-фрейме» - привычные уже для МТ4 универсальность и удобство. Дальше – указатель таймсерии, ведь мы можем искать не только самый высокий High[], но и самый высокий Close[], или даже самый высокий Low[]. Следующий параметр – count – указывает на какой выборке ищется значение. Последний параметр start – с какого бара мы смотрим искомую выборку.

Попробуем написать скрипт, который нарисует нам такой же объект «Прямоугольник» на часовом графике EURUSD. Прямоугольник охватывает 26 баров, его вверх будет проходить по максимальной цене за 26 баров, низ прямоугольника будет равен минимальной цене за 26 баров, правый край находится на нулевом баре(индекс бара равен нулю), левый край находится на 26 баре с индексом равным 25 (не забываем про это!). Через функцию Highest() мы найдем индекс того бара, который имеет максимальную цену High[], для этого мы напишем Highest(Symbol(),Period(),MODE_HIGH, 26,0). Мы указали на поиск бара с самым высоким High[] на протяжении 26 баров начиная с нулевого. Или надо было написать 25?

Сделаем скрипт TestHighestLowest.mq4 и посмотрим. Сначала мы вручную добавим нужные внешние параметры.

 

 
Рис. 6
Рис. 6

 

Вы видите, что я ввел новую переменную типа color, это позволит нам в будущем указывать цвет прямоугольника, а также переменную backGround булевого типа, ее мы будем использовать при задании желаемого типа отображения – будет прямоугольник иметь заливку или нет. Значение firstBar подставляется как параметр start для функции Highest() и Lowest().

Дописываем функцию start():

 

 
Рис. 7
Рис. 7

 

Необходимо разобраться, почему именно так вычисляется время первой координаты time1. А также почему время открытия firstBar оказалось временем второй координаты при создании объекта прямоугольник (OBJ_RECTANGLE).

Кроме того, мы не просто пытаемся создать объект прямоугольник, но и анализируем результат этой попытки. Часто пишут однозначный код, который не предусматривает непредвиденного поведения программы, если это станет привычкой, то в будущем гарантирует многие часы в поисках ошибок в своих программах. Обработка непредвиденной ситуации (в нашем случае неудачной попытки создания прямоугольника) называется обработкой исключения, сами ситуации – исключениями. В данном примере мы просто выводим в лог сообщение с кодом ошибки.

Код готов, запускаем его на нашем графике и немного меняем входной параметр:

 

 
Рис. 8
Рис. 8

 

Получаем следующий результат:

 

 
Рис. 9
Рис. 9

 

Видим, что наш скрипт действительно показывает максимальную и минимальную цену за 26 последних баров начиная от нулевого. Жмем Ctrl+B, выбираем наш прямоугольник и смотрим его свойства:

 

 
Рис. 10
Рис. 10

 

Я специально для примера выбирал пограничный случай, попробуем теперь исполнить скрипт со значением countBars=27. Запускаем, но ничего не происходит:

 

 
Рис. 11
Рис. 11

 

Вот и обработка ошибки пригодилась, значение кода ошибки можно посмотреть в справке MetaEditor’а. Удалим прямоугольник, созданный при первом запуске скрипта и заново запустим скрипт:

 

 
Рис. 12
Рис. 12

 

Видим, что максимум цены за 27 баров увеличился, значит мы правильно поняли назначение параметров. Необходимо только иметь ввиду, что если в искомой выборке окажутся два бара с одинаковыми экстремумами, функции Highest() и Lowest() вернут указание на тот бар, который имеет больший индекс, то есть укажут на более старый бар. В некоторых алгоритмах это может иметь большое значение.

У нашего скрипта есть одно неудобство – нам нужно указывать начальный бар (firstBar) числовым значением. Если нам нужно будет нарисовать такой прямоугольник где-то в глубине истории, то выяснение индекса нужного бара станет практически невозможным или придется придумывать средства для выяснения индекса этого бара. Существует целый класс задач, когда скрипту нужно передать индекс необходимого бара программным путем, для этого в MQL-IV есть специальные функции PriceOnDropped() и TimeOnDropped():

 

 
Рис. 13
Рис. 13

 

Введем функцию init() и добавим переменные прямо из примера, а также добавим вывод многострочного комментария:

 

 
Рис. 14
Рис. 14

 

Комбинация "косая черта и n" означает перевод строки, и ее наличие позволяет делать многострочные комментарии. Компилируем новый вариант скрипта и бросаем его на график (все остальные объекты я удалил):

 

 
Рис. 15
Рис. 15

 

Если мы просто выполним скрипт (не бросая на график, а двойным кликом мышки как обычно), то получим другую картинку:

 

 
Рис. 16
Рис. 16

 

Видно, что мы можем определить и способ запуска скрипта и координаты точки на графике, куда он был брошен. Используем новые знания и назовем новый вариант скрипта как TestHighestLowest2.mq4.

 

 
Рис. 17
Рис. 17

 

Запускаем новый вариант скрипта и получаем желаемый результат:

 

 
Рис. 18
Рис. 18

 

На этом знакомство с четырьмя новыми функциями можно считать законченным. Скрипты доступны на форуме


Многие трейдеры используют значение максимумов и минимумов за определенный период времени для создания торговой системы. Из известных - это торговая система Черепашек и каналы Дончиана. Мы рассмотрим алгоритм создания таких каналов с использованием функции iHighest() и iLowest() . Общепринятое название такого канала PriceChannel (ценовой канал), так и назовем индикатор. Наш индикатор будет показывать текущие уровни максимума и минимума за период N баров в виде линий. Вызываем «Мастер создания советников» и заполняем нужные поля.

 

 
Рис. 1
Рис. 1

 

В данном случае я выбрал значение 20 – именно период в двадцать дней использовался в системе Черепах для торговли на прорывах.

 

 
Рис. 2
Рис. 2

 

Так как мы должны видеть уровни канала, то индикатор будет рисоваться на графике цен. Одна линия будет показывать максимум, другая минимум.

 

 
Рис. 3
Рис. 3

 

Я немного дописал код для лучшего понимания и пользования. Осталось только написать код заполнения буферов значениями.

 

 
Рис. 4
Рис. 4

 

Функции iHighest() и iLowest() заменили устаревшие функции Highest() и Lowest(). Код получился очень простой, компилируем и прикрепляем к графику.

 

 
Рис. 5
Рис. 5

 

Через окно DataWindow мы можем посмотреть значения любого буфера индикатора на нужную дату. Единственный недостаток данного индикатора (на мой взгляд) – отсутствие визуального подтверждения прорыва 20-дневного диапазона. Чтобы исправить этот недостаток, мы немного изменим код индикатора и назовем его PriceChannel-2.mq4.

 

 
Рис. 6
Рис. 6

 

Новая версия индикатора выглядит более наглядно.

 

 
Рис. 7
Рис. 7

 

Разница между двумя версиями видна при их сравнении.

 

 
Рис. 8
Рис. 8

 

Есть множество стратегий, которые основываются на пробитии некоторого диапазона.

 

 
Рис. 9
Рис. 9

 

Причем, как трендовые, так и контртрендовые, основанные на ложных пробитиях. Недостатком данного индикатора является фиксированный период, за который ищутся экстремумы. Существуют разные способы приспособить индикаторы данного типа под текущую рыночную ситуацию. Было бы интересно, если бы в фазе флэта (консолидации) период нашего индикатора увеличивался, а при развитии тренда – уменьшался. Таким образом, мы были бы немного застрахованы от ложного прорыва флэта и преждевременного выхода из позиции по тренду.

Одним из таких способов является использование индикатора ADX(). Популярным значением периода для этого индикатора является значение в 14 дней. Если присмотреться к этому индикатору, то видно, что во флете его значения очень малы, а при тренде нередко превышают значения 40.

 

 
Рис. 10
Рис. 10

 

На одном из форумов я услышал про использование значения 150/ADX(14) в качестве переменного периода для индикатора PriceChannel. Изменим код нашего индикатора и назовем его ADXChannell.mq4.

 

 
Рис. 11
Рис. 11

 

Характер ценового канала изменился.

 

 
Рис. 12
Рис. 12

 

Можно придумать правила открытия позиций по тренду при касании ценой противоположной стороны канала.
Пока мы рассматривали только индикаторы, рисующие две линии – границы канала. Но иногда трейдеры добавляют третью линию, которая находится точно посередине между верхней и нижней границей канала. Назовем ее ватерлинией или нулевой линией (или линией баланса). Сделаем третью версию индикатора – PriceChannel3.mq4. Для этого достаточно добавить третий буфер и код для расчета его значений.

 

 
Рис. 13
Рис. 13

 

Набросим новый индикатор на график.

 

 
Рис. 14
Рис. 14

 

Тут можно спорить – получили ли мы дополнительную информацию от добавления средней линии или нет. Но я хочу обратить внимание на другое – в данном случае мы вычислили среднюю линию канала на основании его границ. Но есть индикаторы, которые строятся наоборот – берется некая линия (это может быть некая скользящая средняя) и от нее строятся границы канала по каким-то правилам.

Кроме обычных средних, в качестве средней линии могут быть использованы сложные алгоритмы, например адаптивная скользящая средняя Кауфмана - АМА ( Adaptive Moving Average by Perry Kaufman ). Этот индикатор достаточно хорошо знаком многим трейдерам, найти его несложно. Поэтому в качестве примера я решил взять другой индикатор – FRAMA. Прочитать о нем можно здесь - Fractal Adaptive Moving Average by John Ehlers . Сам код индикатора можно написать и по-другому, мой вариант не является классическим.

Можно сравнить среднюю линию PriceChannel и FRAMA.

 

 
Рис. 15
Рис. 15

 

Единственное достоинство, которое мы видим на глаз сразу – более плавный ход FRAMA по сравнению со средней линией PriceChannel. Попробуем построить канал от FRAMA. Для этого нам необходимо правило, по которому мы будем откладывать границы канала от FRAMA. Предлагаю для этого использовать отклонения цены закрытия Close от значения FRAMA. А точнее, стандартное отклонение этого распределения.

Итак, имеем: 1. значения индикатора FRAMA, которое мы будем показывать 2. отклонения цены Close от FRAMA, которые мы не будем показывать 3. верхняя граница канала, которую мы будем показывать 4. нижняя граница канала, которую мы будем показывать.
Значит, наш индикатор должен выводить (рисовать) три буфера и иметь один скрытый (вспомогательный) буфер для расчетов. МТ4 легко позволяет это, но нужно помнить, что неотображаемые буферы должны быть последними (иметь максимальный индекс).

 

 
Рис. 16
Рис. 16

 

Для того, чтобы построить границы канала, сначала мы должны в цикле посчитать значения FRAMA и отклонения, и только потом в отдельном цикле мы можем вычислять границы канала.

 

 
Рис. 17
Рис. 17

 

Для примера я использовал одну из функций, которая рассчитывается на массиве значений – iStdDevOnArray(). Полученный индикатор со значениями по умолчанию выглядит так.

 

 
Рис. 18
Рис. 18

Все индикаторы находятся здесь .
 
 
< Пред.   След. >
Торговые сигналы онлайн "DoxxoD" © 2010