|
|
|
|
|
|
|
|
|
У нас уже есть почти все необходимые знания для создания советника. Из торговых функций мы не затрагивали только OrderModify() и OrderCloseBy(). Но прежде чем приступать к написанию сложных советников, необходимо четко представлять себе как работает советник в режиме реального времени, будучи прикреплен к графику инструмента, и как моделируется его работа в тестере при бектесте на исторических данных.
Многие трейдеры пишут индикаторы и советники, не представляя разницы между теорией и практикой. Прогнав советник в тестере и получив заманчивые результаты, они вешают эти советники на онлайновый график, часто позже удивляются несовпадению результатов (иногда разительному) и значениям индикаторов, снятых в онлайне и при бектесте. Поэтому, мы для начала создадим советник, за который нам не придется краснеть, и который одинаково хорошо работает на любом инструменте и на любом тайм-фрейме. Этот советник не сливает, потому что он не будет торговать. Единственное его назначение — вести запись о состоянии счета, поэтому я его назвал BlackBox («Черный ящик»), по аналогии с аппаратурой для записи полета авиалайнера.
Трейдеры придумывают сложные алгоритмы для открытия позиций, еще более сложные алгоритмы для трейлинга(подтягивания) стоплоссов (Stop Loss), проверяют значения индикаторов на «родном» и «чужом» тайм-фрейме, считают количество прибыльных и убыточных сделок в последней серии, в общем, отслеживают самые разнообразные события, которые могут произойти с ценой и с торговым счетом. Но при этом иногда забывают про два самых важных события.
Первое событие — это приход тика, именно по приходу нового тика выполняется каждый раз функция start() советника и индикатора. Это событие очевидно. Второе событие — это закрытие старого бара и зарождение нового. Эти два события между собой не равнозначны с точки зрения написания советника. Очевидно, что значение индикатора меняется в незавершенном баре с каждым изменением цены, поэтому большинство стратегий используют значения с последнего завершенного бара, то есть с первого бара.
Необходимо определить момент, когда старый бар закрылся, и пришел первый тик нового (нулевого) бара. В этот момент мы производим оценку текущей ситуации согласно алгоритму советника и принимаем решение о проведении или непроведении торговых операций. Это событие (рождение нового бара) мы будем проверять пользовательской функцией isNewBar() булевого типа, она будет возвращать true, если зародился новый бар, и false в противном случае. Если новый бар не появился — проводятся рядовые действия, которые можно делать на каждом новом тике.
Создаем новый советник с помощью «Мастера создания Советника». Никаких параметров для своей работы он не требует, поэтому заполняем только имя советника.
|
|
|
|
В блоке start() пишем несколько строчек простого алгоритма:
|
|
|
|
Видно, что на каждом тике проверяется факт появления нового бара и в случае положительного значения вызывается функция EveryBar(). Эта функция вызывается только один раз в начале каждого бара (если правильно написана isNewBar()) и в течение всего развития бара больше не вызывается. Кроме того, на каждом тике вызывается функция EveryTick() — в ней могут выполняться любые действия.
Как нам определить появление нового бара? Существуют два стандартных способа.
- Каждый бар имеет такую характеристику как Time[] — время отсчета(открытия) нового бара , и если мы будем каждый раз спрашивать значение Time[0] и сравнивать его с тем, что было на предыдущем тике, то в момент появления нового бара эти значения не совпадут.
- Второй вариант — запоминать каждый раз значение предопределенной переменной Bars. При появлении нового бара это значение также изменится.
Оба этих варианта равноправны, но я рассмотрю второй. Пишем функцию:
|
|
|
|
Обратите внимание, что переменная expertBars (в которой мы запоминаем значение Bars) является глобальной. Только переменные на глобальном уровне помнят свои значения между приходами каждого тика и запуска блока start() (из которого функция isNewBar() вызывается). Если выражение expertBars!=Bars истинно, значит родился новый бар, установим значение res равным true и не забудем запомнить значение Bars на данный момент — в следующий раз в будущем мы будем сравнивать Bars с текущим значением.
Таким образом, возвращаемое функцией значение res будет либо true либо false.
Идем далее, сочиняем функцию EveryBar(), чтобы долго не думать, я решил каждый новый бар распечатывать в логе список ордеров, как в скрипте OrderList. mq4 (статья «Ордера в MetaTrader4» . ).
|
|
|
|
Для этого из функции EveryBar() вызывается новая функция — PrintAllOrders() (распечатать все ордера).
|
|
|
|
Осталось написать только функцию EveryTick(). Я решил в этой функции вычислять средний профит за все тики, пришедшие в течение бара. Для этого на каждом тике суммируется текущая прибыль по счету (AccountProfit()), подсчитывается количество тиков, и при появлении нового бара сумма профитов делится на количество поступивших тиков, получаем в результате усредненную прибыль за бар.
|
|
|
|
Немного изменим функцию EveryBar() для вычисления средней прибыли.
|
|
|
|
Теперь мы с появлением нового бара вычисляем среднюю прибыль и обнуляем переменные SumProfit и ticksBars для дальнейшего из использования с чистого листа на новом баре. Запускаем на графике EURUSD M15 и видим вывод в лог:
|
|
|
|
Теперь наш советник будет каждые 15 минут выводить данные об ордерах и среднюю прибыль за этот период.
|
|
|
|
Думаю, логика работы этого советника не вызывает вопросов. Взять код советника можно здесь .
Перейти к статье «Тиковый индикатор в отдельном окне».
|
|
|
|
|
|