PDA

View Full Version : Отклонение от состояния покоя. Часть 1.



Scriptong
07-03-2011, 20:50
Своим существованием различного рода рынки напрямую обязаны наличию у человека необходимости в обмене товаров, которая на рынке приобретает статус "покупки" или "продажи". Акт купли-продажи становится возможным только в том случае, когда существует две стороны - покупатель и продавец. Торговый процесс будет протекать до тех пор, пока на одного предложение продажи существует одно предложение покупки. В момент отсутствия одной из сторон сделки торговый процесс утрачивает равновесие. Если не хватает покупателей, то возникает профицит (излишек товара), приводящий к падению цены, что далеко не всегда приводит к появлению покупателей. Если же не хватает продавцов, то ощущается дефицит (недостача товара). Выход из этой ситуации наиболее часто одинаков - повышение цены. Как и в первом случае, эта мера не является панацеей и вполне может привести к полной распродаже товара, даже по завышенной цене. Таков закон спроса и предложения, который не дает успокоиться рыночной цене.
В результате выходит, что состояние полного покоя - некая абстракция, т.к. рынку покой "может только сниться" (рынку Форекс, видимо, по субботам и воскресеньям). Тем не менее, даже в разгар торгов такая абстракция может существовать некоторое время. Посудите сами: у продавца есть достаточное количество товара, который ему обязательно нужно продать. В то же время, имеется достаточное количество покупателей товара. Какой смысл продавцу повышать цену? Торговый процесс будет протекать стабильно, без смены цены. До поры до времени, конечно. То есть приходим к выводу, что рынок всегда будет стремиться к равновесию, без достижения которого само существование рынка становится бессмысленным.
Предсказание точки равновесия представляется довольно сложным занятием, т.к. для этого нужно точно знать количество требуемого и предлагаемого в будущем товара. Имея дело с рынком Форекс, говорить о количестве вообще не приходится. В распоряжении трейдера есть лишь история изменения цены... Но позвольте, что значит "лишь"? История изменения цены это не так уж и мало. На подобных графиках без труда можно найти зоны покоя - это "области плотной штриховки" (термин А. Элдера). Трейдеры называют их флэтовыми зонами. Путь цены к флэту всегда проходит через ее изменение - рост или падение. По аналогии с законом спроса и предложения, предположим, что рост цены - это компенсация предыдущих падений цены, а падение, в свою очередь, является компенсацией предыдущих ростов. Если эта гипотеза верна, то для предсказания будущего направления движения цены необходимо располагать информацией об имеющемся дисбалансе в количественном росте и падении.
На чем может быть основан расчет дисбаланса? Здесь поможет любимый инструмент большинства трейдеров - средняя скользящая линия, точнее, две линии, пересечение которых образует замкнутые области (см. рис. 1).

http://www.forextrade.ru/media/Image/MQLabs/104_ag/figure1.png
Рис. 1. Замкнутые области между двумя средними скользящими линиями.

В итоге получаем два вида областей: область роста (красная гистограмма) и область падения (синяя гистограмма). Каждая область характеризуется занимаемой площадью, которая и будет нас интересовать. Если суммировать площади однотипных областей за некоторый период, то получим два значения, по соотношению которых можно судить о наличии баланса или дисбаланса в распределении сил, определяющих направление движения цены. Так, если суммарная площадь областей роста цены превышает суммарную площадь областей падения цены, то можно говорить о дисбалансе в пользу роста. Этот дисбаланс необходимо расценивать, как вероятность будущего падения цены, при котором сложившийся перевес роста будет компенсирован нисходящим движением.
Расчет площади каждой области не потребует знаний высшей математики, которая предлагает находить площадь фигуры, образованной двумя кривыми, путем интегрирования функций, соответствующих кривым, на определенном отрезке. В терминале Meta Trader 4 ось абсцисс - это время, исчисляемое в барах (свечах), а бары не имеют какой-либо ширины. Видимое пространство между ними необходимо лишь для обеспечения удобства пользователю и может быть увеличено или уменьшено, что не должно влиять на рассчитываемый размер площади. Поэтому для расчета площади рассматриваемых областей достаточно сложить ценовые разности между скользящими средними линями.
Следующим этапом расчета будет сложение площадей, соответствующих однотипным областям. Для получения правильных значений необходимо обеспечить равные условия для каждого типа области, т.е. взять одинаковое количество соответствующих типов областей (например, 10 одних и 10 других). Нет смысла производить расчеты на основании всей имеющейся истории котировок, т.к., во-первых, глубина истории от одного пользователя к другому может существенно меняться, что не применит сказаться на результатах. Во-вторых, при увеличении глубины выборки соотношение сумм площадей будет стремиться к равновесию (при увеличении количества подбрасываний монетки количество выпадений орла и количество выпадений решки стремится к равным значениям), из которого нельзя получить сколько-нибудь полезную информацию. По завершении расчета остается лишь сопоставить полученные значения. Наиболее удобный способ сопоставления - указание соотношения величин в процентах.
Задачи расчета, сопоставления и отображения вычисленных значений будет решать индикатор Square. Для получения решения задачи требуется указание исходных данных, каковые в данном случае представлены следующим набором:

// == Настроечные параметры индикатора ==================================================
extern int MACDFast = 24; // Период быстрой средней MACD
extern int MACDSlow = 120; // Период медленной средней MACD
extern int MACDPrice = 5; // Цена расчета средней:
// 0 - Close
// 1 - Open
// 2 - High
// 3 - Low
// 4 - Median
// 5 - Typical
// 6 - Weighted Close
extern int MACDMethod = 3; // Метод расчета средней
// 0 - Simple
// 1 - Exponential
// 2 - Smoothed
// 3 - Linear Weighted
extern int SamplingDepth = 10; // Глубина выборки. Должна быть четным
// ..числом более 1
extern int MaxLevel = 80; // Значение соотношения площади роста
// ..и площади падания, при котором..
// ..не рекомендуется открывать..
// ..сделки против соотношения
// == Окончание блока ==================================================================

Первые четыре настроечных параметра имеют префикс MACD, хотя на самом деле все они относятся к обычным средним линиям. Появление префикса MACD в названиях входных параметров объясняется тем фактом, что в процессе расчета разности двух средних линий получается значение другого, не менее известного, индикатора - MACD. В таком представлении параметров явно чувствуется отсутствие еще одного наименования - MACDSignal (период сигнальной линии индикатора MACD). В данном случае указание периода сигнальной линии не требуется, т.к. ее значение в программе не используется.
Значение глубины выборки указывается при помощи параметра SamplingDepth. Значение 10 означает, что для расчета будет взято 5 последних областей каждого типа: 5 положительных и 5 отрицательных. Отсюда следует ограничение, накладываемое на величину выборки: это должно быть четное число (иначе не получится взять равное количество областей каждого типа) не меньшее, чем 2 (1 - нечетное число, а 0, хоть и четное, не дает возможности получить хоть какую-нибудь выборку).
Последний параметр MaxLevel указывает предельно рассматриваемое соотношение площадей роста и падения. Например, при соотношении 60% (площадь роста) и 40% (площадь падения) имеем дисбаланс в сторону роста. В итоге можно говорить о том, что в ближайшее время перевес в пользу роста будет компенсирован, т.е. разовьется нисходящее движение. Если рассматриваемое соотношение было получено, когда быстрая средняя пересекла медленную сверху вниз (классический сигнал открытия короткой сделки), то факт наличия дисбаланса является подтверждением такого сигнала. С другой стороны, во время устойчивых трендов дисбаланс может достигать некоторого критического соотношения. Например, 80% против 20%, как указано в приведенном коде. То есть сумма площадей одной из областей не просто превышает сумму площадей другой области, а доминирует над оппонентом. В таких ситуациях сигналы, противоположные тренду, чаще говорят о начале коррекции, чем о затухании тренда. Поэтому при доминировании одного из типов областей над другим лучше воздержаться от заключения сделок как в одну, так и в другую сторону.
Еще одним назначением параметра MaxLevel является указание момента закрытия текущей сделки. Например, сделка была открыта при соотношении 75% к 25% в направлении преобладающего типа областей. За время существования сделки тренд в сторону преобладания продолжился, и соотношение сумм площадей выросло до критического значения - 80% к 20%. Логичным действием в этом случае является закрытие сделки, т.к. рынок вошел в фазу высокой неопределенности: тренд может продолжиться, а может резко развернуться.
Рассмотрим первую необходимую часть индикатора - функцию расчета сумм площадей роста и падения:

//+-------------------------------------------------------------------------------------+
//| Вычисление площади положительной и отрицательной областей |
//+-------------------------------------------------------------------------------------+
void Square(int k)
{
int cnt = 0; // Счетчик областей роста и падения
Above = 0; // Сумма площадей роста
Below = 0; // Сумма площадей падения
while (cnt < SamplingDepth && k < Bars) // Рассчитываем, пока не достигнем..
{ // ..нужной глубины выборки или..
// ..достигнем конца истории
if (MACD[k] > 0) // Положительные считаем отдельно
Above += MACD[k];
else // Отрицательные отдельно
Below += MACD[k];
if ((MACD[k] > 0 && MACD[k+1] <= 0) || // Если произошел переход от одной..
(MACD[k] < 0 && MACD[k+1] >= 0)) // ..области к другой, то увеличиваем
cnt++; // ..счетчик областей
k++; // Переходим к следующему бару
}
}
Функция Square обладает единственным аргументом - k. Через него передается номер бара, начиная с которого необходимо производить расчет площадей. Сумма площадей роста сохраняется в переменной Above, а сумма площадей падения цены - в переменной Below. Обе переменные являются глобальными, поэтому объявления в пределах функции не требуют. Все, что требуется в данном случае - это обнуление значения переменных, чтобы предыдущие результаты не были учтены в текущем расчете.
Далее следует цикл "пока", счетчиком которого является переменная cnt, хранящая количество учтенных областей роста или падения цены. Цикл может быть прерван по двум причинам: достижение необходимого количества областей роста или падения цены (SamplingDepth) или достижение конца имеющейся истории котировок. Переменная Above увеличивает свое значение, когда быстрая средняя скользящая линия оказывается выше, чем медленная средняя линия. Признаком этого является значение индикаторного буфера MACD, который рассчитывается в функции start, но не отображается на графике. Аналогично переменной Above, при нахождении быстрой средней линии ниже медленной, происходит изменение значения переменной Below, которое уменьшается, т.к. значение буфера MACD при этом является отрицательным.
Счетчик cnt увеличивает свое значение в тех случаях, когда значения MACD на текущем и предыдущем барах имеют разный знак, что говорит о моменте пересечения средних линий, а, следовательно, о смене типа области.
Полученные значения Above и Below нуждаются в отображении на графике, чтобы пользователь смог увидеть их и проанализировать. Эту задачу решает функция ShowInfo:

//+-------------------------------------------------------------------------------------+
//| Отображение объектов-надписей |
//+-------------------------------------------------------------------------------------+
void ShowInfo(datetime time, double price, double koef)
{
// - 1 - == Инициализация текстового значения надписей ==================================
if (Above-Below != 0) // Если соотношение площадей не равно,
{ // ..то выведем полученные соотношения
string UText = DoubleToStr(Above/(Above-Below)*100, 1) + "%";
string DText = DoubleToStr(-Below/(Above-Below)*100, 1) + "%";
}
else // Если соотношение площадей равно, то
{ // ..выведем значения 50% и 50%
UText = "50%";
DText = "50%";
}
// - 1 - == Окончание блока =============================================================

// - 2 - == Отображение верхнего значения ===============================================
string name = prefix + time + "U"; // Формирование уникального имени..
// ..объекта
if (ObjectFind(name) < 0) // Если объект не найден, то..
{
ObjectCreate(name, OBJ_TEXT, 0, time, price+koef/5);// ..создаем его
ObjectSet(name, OBJPROP_COLOR, indicator_color1);// Устанавливаем цвет надписи
ObjectSetText(name, UText, 8); // Заполняем надпись содержимым
}
// - 2 - == Окончание блока =============================================================

// - 3 - == Отображение нижнего значения ================================================
name = prefix + time + "D"; // Формирование уникального имени..
// ..объекта
if (ObjectFind(name) < 0) // Если объект не найден, то..
{
ObjectCreate(name, OBJ_TEXT, 0, time, price-koef/5);// ..создаем его
ObjectSet(name, OBJPROP_COLOR, indicator_color2);// Устанавливаем цвет надписи
ObjectSetText(name, DText, 8); // Заполняем надпись содержимым
}
// - 3 - == Окончание блока =============================================================
}
Аргументов у функции три: time и price - время и цена на графике, где необходимо отобразить информацию, koef - отступ в пунктах от указанной цены, который нужно учесть при отображении каждого из двух значений. Так как значения два, то отображение их с одинаковыми координатами приведет к "каше" на экране. Поэтому для вычисления координаты по вертикали одного значения к заданной цене price добавляется значение koef, а для вычисления координаты другого значения из price вычитается koef.
В первом блоке функции производится подсчет значений соотношений Above и Below, а также преобразование чисел в строку. В строковой переменной UText сохраняется текстовое представление числа, соответствующее части суммы площадей роста от общего числа площадей роста и падения, а в переменной DText - части площадей падения от общего числа площадей. Так как для расчета процентного соотношения требуется выполнить операцию деления, то перед выполнением деления производится проверка равенства знаменателя нулю. Знаменателем в данном случае выступает выражение Above - Below (знак "минус" используется для получения суммы, т.к. переменная Below содержит отрицательное значение). Ноль может быть получен лишь в одном случае, когда значения переменных равны. Равенство переменных означает, что соотношение сумм площадей роста и падения цены равно, т.е. "50% на 50%", как и указывается в конечной части блока 1.
Второй и третий блоки алгоритмически подобны. Их задачей является создание графического объекта - текстовой надписи. Название графического объекта должно быть уникальным и, в то же время, должно легко идентифицироваться индикатором как собственное. Поэтому имя объекта состоит из трех частей. Первая часть одинакова для всех объектов графика. Это значение переменной prefix, которое можно изменять, редактируя файл исходного кода индикатора. В данном случае это строка "iSquare_". Вторая часть имени - время (X-координата) на графике, соответствующее объекту. Время является уникальной характеристикой объекта, кроме одного случая - появление второй надписи на этом же баре, т.к. необходимо отобразить два значения. Для различия между двумя объектами, соответствующими одному и тому же времени, необходима третья часть имени - литера "U" (для верхней надписи) или "D" (для нижней надписи). После определения имени производится проверка существования объекта и, если объект с таким именем не найден, создание объекта с установкой нужного цвета и текста.
Использование рассмотренных функций осуществляется в главной функции индикатора start:

//+-------------------------------------------------------------------------------------+
//| Custom indicator iteration function |
//+-------------------------------------------------------------------------------------+
int start()
{
if (!Activate) return(0);
// - 1 - == Вычисление необходимости обновления данных ==================================
int limit, i; // Переменные цикла
if (LastBar == Time[0]) // Если текущий бар уже был рассчитан, то
limit = 0; // ..пересчитываем только его
else // ..если не был рассчитан, то..
if (LastBar == 0) // ..при первом входе рассчитаем значения
limit = Bars-3; // ..для всех имеющихся баров
else // Если расчет производился на каком-то..
{ // ..другом баре, то..
limit = iBarShift(NULL, 0, LastBar + Period()*60);// ..начнем расчет с первого..
// ..бара после него
if (MACD[limit+1] > 0) // Также не забудем обработать..
Above += MACD[limit+1]; // ..окончательное значение для..
else // ..сформированного бара limit+1,..
Below += MACD[limit+1]; // .. прибавив значение площади роста..
// ..или площади падения
}
LastBar = Time[0]; // Отметим новый бар
// - 1 - == Окончание блока =============================================================

// - 2 - == Отображение индикатора на истории ===========================================
for(i = limit; i >= 0; i--) // По всем непосчитанным барам
{
MACD[i] = iMA(NULL, 0, MACDFast, 0, MACDMethod, MACDPrice, i) - // Вычисление..
iMA(NULL, 0, MACDSlow, 0, MACDMethod, MACDPrice, i);// ..разности..
// ..значений средних скользящих (MACD)
// - 2.1 - == Пересечение снизу вверх (сигнал покупки) ============================
if (MACD[i+1] > 0 && MACD[i+2] <= 0) // Если произошло пересечение снизу вверх
{
double k = iATR(NULL, 0, 14, i); // Подсчитаем среднюю волатильность
Square(i+2); // Рассчитаем площади
if (Above - Below == 0) // При равенстве площадей выведем значок..
GreateUp[i] = Low[i] - k; // .."большой палец вверх". Можно..
else // ..открываться
if (MathAbs(Below/(Above - Below))*100 >= MaxLevel ||// При большом..
Above/(Above - Below)*100 >= MaxLevel)// ..превосходстве одной площади..
Up[i] = Low[i] - k; // ..падения над другой выведем "стрелку..
// ..вверх", заходить нежелательно
else // Если уровень MaxLevel не преодолен,то..
GreateUp[i] = Low[i] - k; // ..выведем значок "большой палец вверх"
ShowInfo(Time[i] + 180*Period(), // Покажем результат соотношения
MathMax(Up[i], GreateUp[i]),
k);
FollowBuy = true; // Необходимо отслеживать площадь роста
FollowSell = false; // Площадь падения отслеживать не нужно
}
// - 2.1 - == Окончание блока =====================================================

// - 2.2 - == Пересечение сверху вниз (сигнал продажи) ============================
if (MACD[i+1] < 0 && MACD[i+2] >= 0) // Если произошло пересечение сверху вниз
{
k = iATR(NULL, 0, 14, i); // Подсчитаем среднюю волатильность
Square(i+2); // Рассчитаем площади
if (Above - Below == 0) // При равенстве площадей выведем значок..
Down[i] = High[i] + k; // .."большой палец вниз". Можно..
else // ..открываться
if (Above/(Above - Below)*100 >= MaxLevel ||// При большом превосходстве..
MathAbs(Below/(Above - Below))*100 >= MaxLevel)// ..одной площади..
Down[i] = High[i] + k; // ..над другой выведем "стрелку вниз",..
// ..заходить нежелательно
else // Если уровень MaxLevel не преодолен,то..
GreateDown[i] = High[i] + k; // ..выведем значок "большой палец вниз"
ShowInfo(Time[i] + 180*Period(), // Покажем результат соотношения
MathMax(Down[i], GreateDown[i]),
k);
FollowSell = true; // Необходимо отслеживать площадь падения
FollowBuy = false; // Площадь роста отслеживать не нужно
}
// - 2.2 - == Окончание блока =====================================================

// - 2.3 - == Слежение за длинной сделкой =========================================
if (FollowBuy) // Если нужно продолжать подсчет сумм..
{ // ..после получения сигнала
k = 0; // Значение MACD текущего бара
if (i == 0) // Если текущий бар нулевой, то занесем..
k = MACD[i]; // ..значение MACD в переменную k
else // Иначе можно увеличивать общую сумму
Above += MACD[i]; // Увеличим площадь роста
if (Above + k - Below == 0) continue; // При делении на ноль выходим
if ((Above+k)/(Above+k-Below)*100 >= MaxLevel)// Если ссоотношение площади роста
{ // .. и площади падения превысило MaxLevel
CloseTrade[i] = High[i] + iATR(NULL, 0, 14, i);// ..отметим на графике этот..
FollowBuy = false; // ..факт и прекратим дальнейшее..
} // ..отслеживание событий
else // Иначе не отмечаем
CloseTrade[i] = 0;
}
// - 2.3 - == Окончание блока =====================================================

// - 2.4 - == Слежение за короткой сделкой ========================================
if (FollowSell) // Если нужно продолжать подсчет сумм..
{ // ..после получения сигнала
k = 0; // Значение MACD текущего бара
if (i == 0) // Если текущий бар нулевой, то занесем..
k = MACD[i]; // ..значение MACD в переменную k
else // Иначе можно увеличивать общую сумму
Below += MACD[i]; // Увеличим площадь падения
if (Above-Below-k == 0) continue; // При делении на ноль выходим
if (MathAbs((Below+k)/(Above-Below-k))*100 >= MaxLevel)// Если ссоотношение..
{ // ..площади падения и площади роста..
// ..превысило MaxLevel
CloseTrade[i] = Low[i] - iATR(NULL, 0, 14, i);// ..отметим на графике этот..
FollowSell = false; // ..факт и прекратим дальнейшее..
} // ..отслеживание событий
else // Иначе не отмечаем
CloseTrade[i] = 0;
}
// - 2.4 - == Окончание блока =====================================================
}
// - 2 - == Окончание блока =============================================================

return(0);
}
Специфика индикатора такова, что оптимизация его вычислений при помощи стандартной функции IndicatorCounted затруднительна. Трудность заключается в том, что значения Above и Below, как видно из кода функции start, формируются накопительным итогом от момента пересечения средних скользящих. При использовании IndicatorCounted потребовалось бы повторное нахождение последнего пересечения средних линий и пересчет значений сумм площадей роста и падения на каждом тике. Реализованный метод расчета (без IndicatorCounted) этого недостатка лишен. Вместо IndicatorCounted используется принцип, хорошо зарекомендовавший себя при использовании в экспертах - определение времени открытия последнего бара, на котором расчет уже производился. Время сохраняется в переменной LastBar. Определение номера бара, с которого нужно производить расчет, занимает код всего первого блока. Если в LastBar сохранено значение, соответствующее текущему бару (бару с индексом 0), то становится понятно, что расчет на текущем баре уже производился и необходимо лишь произвести незначительные изменения для бара с индексом 0. Если значение LastBar не соответствует текущему бару, то в этом случае возможно два варианта. Первый вариант - индикатор только что загружен (значение LastBar равно нулю). Значит, следует произвести расчет значений индикатора по всей доступной истории, т.е., начиная с бара № Bars-3 (для расчета нужно хотя бы два бара слева от расчетного бара). Второй вариант - произошла смена бара. Смена бара может происходить в штатном порядке - бар, индекс которого был 0, приобрел индекс 1, а новый бар появился. А может случиться и так, что на некоторое время пропала связь с сервером брокера, и последний рассматриваемый бар получил индекс более чем 1. Тогда необходимо пересчитать всю историю от последнего посчитанного бара до текущего. Получаем, что неважно, какой номер бара имеется в данном случае - 1 или 10. Главное, чтобы было учтено значение на прошлом баре, а цикл пересчета запущен с первого после обрыва связи бара. Номер бара рассчитывается при помощи функции iBarShift, а учет значения на последнем баре проводится простым прибавлением к значениям переменных Above и Below.
Второй блок функции start - это пересчет истории от бара с индексом limit, рассчитанного в блоке 1, до бара с индексом 0. Индикатор содержит шесть индикаторных буферов, значения четырех из которых видны пользователю, а одного, MACD, не видны. Видимые буфера: Up - "стрелка вверх", GreateUp - "большой палец вверх", Down - "стрелка вниз", GreateDown - "большой палец вниз", CloseTrade - "галочка". Up и Down отображаются на каждом пересечении средних, если не произошло отображения буферов GreateUp и GreateDown. GreateUp и GreateDown отображаются, если соотношение сумм площадей роста и падения не превысило значение MaxLevel, то есть рекомендуется открывать сделку. Если сделку открывать уже поздно (доминирует одна из областей), то отображается обычная "стрелка вверх" или "стрелка вниз". Если после открытия сделки достигается уровень соотношения MaxLevel, то отображается "галочка". Алгоритмически подобными блоками являются пары 2.1 и 2.2, 2.3 и 2.4.
Блоки 2.1 и 2.2 исполняются при обнаружении пересечения средних скользящих линий (2.1 - быстрая пересекла медленную снизу вверх, 2.2 - сверху вниз). В этот момент вызывается функция Square, рассчитывающая значения Above и Below. В зависимости от их соотношения отображается один из буферов, отвечающих за движение вверх (в случае 2.2 - вниз). После этого вызывается функция ShowInfo, выводящая соотношение Above и Below на график, а затем активизируется один из флагов FollowBuy или FollowSell, означающих, что необходимо отслеживать дальнейший рост площади соответствующей области.
Блоки 2.3 и 2.4 отслеживают рост площади соответствующей области с тем, чтобы вывести значок "галочки", означающий прекращение торговли в данном направлении, т.е. достижение критического соотношения, которое больше или равно, чем значение MaxLevel. Присутствие переменной k в каждом блоке нужно для того, чтобы в переменных Above и Below находились значения, соответствующие сформированным барам. Для текущего, нулевого, бара рассчитывается значение переменной k, учитываемое при подсчете текущего соотношения. При достижении соотношения уровня MaxLevel слежение за ростом площади области прекращается.
Результат работы разработанного индикатора показан на рис. 2.

http://www.forextrade.ru/media/Image/MQLabs/104_ag/figure2.png
Рис. 2. Результат работы индикатора Square.

Критический уровень соотношения сумм площадей областей роста и падения цены здесь установлен 70%. Первая сделка на рис. 2 - короткая. Соотношение в пользу падения (67.6% против 32.4%) и критический уровень не достигнут, поэтому сделку можно открывать. Как видно, решение оказалось правильным. Индикатор также подсказал точку выхода, которая оказалась на 120 пунктов ниже точки входа. Затем последовал неправильный совет - открытие длинной сделки (соотношение 64.5% против 35.5% в пользу падения). Возможно, в случаях, когда соотношение не в пользу направления сделки, не стоит открывать сделки. Через три часа после открытия длинной следует сигнал открытия короткой, но он не подтверждается соотношением сумм площадей роста и падения - падение достигло критического уровня. Короткая сделка не открывается. А вот последующая длинная открывается и успешно отрабатывает, если, конечно, к тому моменту была закрыта предыдущая длинная сделка.
О том, как наилучшим образом интерпретировать показания индикатора Square, поговорим во второй части материала. Рассуждения будут основаны на результатах тестирования эксперта, работающего по сходному с индикатором Square принципу.

Индикатор Square (http://www.forextrade.ru/media/Image/MQLabs/104_ag/Square.mq4)

Ivanov
09-03-2011, 18:31
Здравствуйте Scriptong . Для корректного расположения объектов (разрешение монитора у каждого своё) попробовал изменить значение koef , чтобы избежать "каши" , но результата добился лишь после изменения в коде с
ObjectCreate(name, OBJ_TEXT, 0, time, price+koef/5)
на
ObjectCreate(name, OBJ_TEXT, 0, time, price+Х*Point)
Х - число пунктов
С минусом по аналогии , если это не только у меня , может сразу подкорректировать немного . С уважением .

simplesimple
09-03-2011, 22:29
очень интересный подход..
хотя я предпочитаю разницу между 1 SMA и вершинами (минимум для медвежьей свечи, максимум для бычьей свечи).. то есть соединяем вершины и получаем 2 линии (сма и вершины).. вот между ними и стоит замерять площадь... на дневном графике...

Scriptong
10-03-2011, 15:31
Здравствуйте Scriptong . Для корректного расположения объектов (разрешение монитора у каждого своё) попробовал изменить значение koef , чтобы избежать "каши" , но результата добился лишь после изменения в коде с
ObjectCreate(name, OBJ_TEXT, 0, time, price+koef/5)
на
ObjectCreate(name, OBJ_TEXT, 0, time, price+Х*Point)
Х - число пунктов
С минусом по аналогии , если это не только у меня , может сразу подкорректировать немного . С уважением .

Данная проблема, к сожалению, не решается простым подбором коэффициентов или отступом в пунктах. Отступ в пунктах годиться только для небольшого участка истории на конкретном таймфрейме. Для правильного подхода необходимо отслеживать текущий масштаб графика (увеличение, уменьшение) и период. Но и это еще не все, т.к., например, при максимальном увеличении (нажать "+" до максимума) начинает действовать другое ограничение - нет возможности расположить объект между свечами. Единственный объект в MT4, который лишен этого недостатка, это текстовая метка, но ее нельзя расположить на истории (за пределами экрана).
Поэтому в текущей реализации индикатора "красиво" сделать не удалось. Коэффициент подобран для "среднего" масштаба, который приведен на рис. 2

Scriptong
12-03-2011, 14:48
Задачу поиска наиболее оптимального варианта интерпретации показаний индикатора Square, рассмотренного в предыдущей статье (http://www.forextrade.ru/mqlabs/07.03.2011-mqlabs-otklonenie-ot-sostoyaniya-pokoya-chast-1), можно решить только одним способом - составить несколько вариантов торговых правил и сравнить их между собой. Такой путь и был пройден перед написанием второй части материала. Результатом пройденного пути стала стратегия, базирующаяся на показаниях индикатора Square. Основные правила стратегии совпадают с правилами интерпретации показаний индикатора, описанными в первой части материала. Совпадение также касается значения одного из количественных параметров - MaxLevel. Принципы функционирования стратегии рассмотрим более подробно.
Как и подавляющее большинство других стратегий, рассматриваемая тактика в качестве фундамента использует две средние скользящие линии. Два соседних пересечения линий образуют замкнутую область (см. рис. 1 в статье Отклонение от состояния покоя. Часть 1 (http://www.forextrade.ru/mqlabs/07.03.2011-mqlabs-otklonenie-ot-sostoyaniya-pokoya-chast-1)). Если средняя скользящая линия с меньшим периодом (быстрая средняя линия) находится выше средней скользящей линии с большим периодом (медленная средняя линия), то область идентифицируется как область роста цены. При нахождении быстрой линии ниже медленной линии замкнутая область характеризуется падением цены. Используя расстояние между средними линиями по оси ординат (шкала цен), можно вычислить площадь каждой полученной области. Суммируя площадь одинакового типа областей, взятых в равном количестве, получаем два значения. Одно значение характеризует сумму площадей роста цены, а другое - сумму площадей падения цены. Имея численные значения, легко соотнести их между собой, выразив отношение в процентах.
Основополагающей гипотезой стратегии является следующее предположение: при наличии превосходства сумм площадей одного типа над другим типом рынок будет стремиться компенсировать дисбаланс в соотношении сил. Например, если сумма площадей роста превышает сумму площадей падения, то цена будет стремиться увеличить площадь области падения, и наоборот, если сумма площадей падения превышает сумму площадей роста, то цена будет стремиться увеличить площадь области роста. Момент пересечения средних линий, если следовать классическому способу использования индикатора Moving Average, соответствует сигналу открытия длинной или короткой сделки. Если этот сигнал подтверждается имеющимся дисбалансом в соотношении площадей роста и падения цены, то сделку необходимо открыть. Сигнал, полученный от пересечения средних линий, считается подтвержденным только до тех пор, пока соотношение площадей роста и падения цены не достигнет некоторого критического уровня. В этом случае открывать сделку в какую-либо сторону не рекомендуется.
Индикатор Square при помощи значков "большой палец вверх", "большой палец вниз", "стрелка вверх" и "стрелка вниз" показывает границы сформированных областей, характеризующиеся пересечением средних скользящих линий. Справа от каждого значка указывается соотношение сумм площадей роста и падения цены в процентах. Верхнее значение соответствует доле областей роста цены, а нижнее - доле областей падения цены. Если соотношение сил не достигло критического уровня, то отображается значок "большой палец вверх" или "большой палец вниз", направление которого зависит от направления пересечения средних линий. При достижении критического уровня в соотношении сумм площадей роста и падения цены на границах областей отображается значок "стрелка вниз" или "стрелка вверх", означающих, что не рекомендуется открывать какую-либо сделку, а имеющуюся позицию лучше закрыть. Значение критического уровня соотношения указывается при помощи настроечного параметра MaxLevel индикатора Square.
Еще одним информационным значком индикатора является "галочка", появление которой не привязано к границе области. Отображение "галочки" сигнализирует о достижении критического уровня соотношения сумм площадей роста и падения цены, произошедшее внутри какой-либо области. В этот момент рекомендуется закрыть текущую сделку, не дожидаясь образования границы области.
По сигналам индикатора Square легко совершать открытие и закрытие сделок, но его показания ничего не говорят о расположении уровней стоп-приказа и профита сделки. Правила расположения "пожарных" уровней необходимо разработать отдельно (см. рис. 1).

http://www.forextrade.ru/media/Image/MQLabs/105_ag/figure1.png
Рис. 1. Установка уровня стоп-приказа.

Способ размещения уровня стоп-приказа можно выбрать, на основании границ областей, показанных индикатором Square. Достаточно использовать максимальную цену области роста и минимальную цену области падения. В момент открытия короткой сделки уровнем стоп-приказа будет максимальная цена предыдущей области. Это всегда область роста. В случае открытия длинной сделки уровень стоп-приказа необходимо разместить по цене минимума предыдущей области - области падения. На рис. 1 предлагаемые уровни обозначены горизонтальными линиями. Границы областей сдвинуты относительно сигналов на один бар влево. Это не ошибка. Сигнальные значки индикатора отображаются во время открытия свечи по факту формирования предыдущего бара. То есть о начале очередной области мы всегда будем говорить с опозданием на один бар, т.к. в момент открытия бара нельзя быть уверенным в том, что ко времени окончания его формирования средние линии создадут пересечение.
Последним штрихом в разработке стратегии является принятие правил установки уровня профита. Здесь напрашивается два варианта: расчет профита, исходя из величины стоп-приказа, и отсутствие профита вообще, что позволит закрывать сделку по обратному сигналу. Недостаток первого варианта заключается в неполучении сверхприбылей на затяжных трендах, начавшихся после жесткого флэта. Пример такой ситуации показан на рис. 1 - вторая длинная сделка, у которой размер стоп-приказа достаточно небольшой. В случае привязки профита к размеру стоп-приказа прибыль будет составлять лишь небольшую часть от тренда. Недостатком второго варианта (отсутствие профита) является позднее закрытие сделки во многих случаях, т.к. сигналы любого индикатора следуют с некоторым запаздыванием, что съедает львиную долю прибыли. То есть оба варианта приводят к значительным потерям прибыли в случае получения правильного сигнала.
Наиболее подходящим решением проблемы профита в данном случае видится некоторый компромисс: не ставить профит, но сделать подвижным уровень стоп-приказа. Речь идет об одном из вариантов трейлинг-стопа. Классический вариант трейлинг-стопа заключается в установлении фиксированной величины отступа уровня стоп-приказа от текущей цены. Так делать не рекомендуется, т.к. волатильность рынка изменчива, что приводит к срабатыванию стоп-приказа слишком рано или слишком поздно. Предлагаемый вариант трейлинг-стопа заключается в отслеживании рыночной волатильности и установке стоп-приказа на расстоянии в несколько средних волатильностей от текущей цены. Таким образом, срабатывание уровня стоп-приказа при правильном подборе коэффициента волатильности будет, чаще всего, означать окончание текущего движения, и позволит сохранить значительную часть накопленной прибыли (см. рис. 2).

http://www.forextrade.ru/media/Image/MQLabs/105_ag/figure2.png
Рис. 2. Подтяжка уровня стоп-приказа.

Среднюю волатильность за выбранный период показывает стандартный индикатор ATR. На каждой новой свече значение индикатора считывается и умножается на эмпирически подобранный для торгового инструмента коэффициент. Например, для валютной пары EURUSD этот коэффициент равен 5. Как показано на рис. 2, в момент открытия сделки размер трейлинг-стопа составлял 145 пунктов (29 х 5 = 145). С каждым последующим баром размер трейлинг-стопа изменялся (сначала падал, затем рос), но расстояния между рыночной ценой и ценой открытия сделки было недостаточно для переноса уровня стоп-приказа. Первое перемещение уровня стоп-приказа состоялось лишь спустя двое суток после открытия сделки. Средняя волатильность вернулась к уровню 30 пунктов, а текущая цена возросла до значения 1.2152, что позволило переместить уровень стоп-приказа на уровень 1.2002. Аналогичным образом производились перемещения стоп-приказа на уровни 1.2052 и 1.2143.
Имея полное описание торговых правил стратегии, можно переходить к написанию эксперта, призванного помочь в тестировании стратегии и ее исполнении. Сигнальная часть эксперта реализована в теле функции GetSignal:

//+-------------------------------------------------------------------------------------+
//| Генерация сигналов открытия сделки |
//+-------------------------------------------------------------------------------------+
void GetSignal(int i)
{
Signal = 0;
// - 1 - == Расчет значений средней скользящей линии и сигнальной линии MACD ============
double MACD = iMA(NULL, 0, MACDFast, 0, MACDMethod, MACDPrice, i) - // Вычисление..
iMA(NULL, 0, MACDSlow, 0, MACDMethod, MACDPrice, i);// ..разности..
//..значений средних скользящих (MACD)
double MACD2 = iMA(NULL, 0, MACDFast, 0, MACDMethod, MACDPrice, i+1) - // Вычисление..
iMA(NULL, 0, MACDSlow, 0, MACDMethod, MACDPrice, i+1);// ..разности..
//..значений средних скользящих (MACD)
// - 1 - == Окончание блока =============================================================

// - 2 - == Генерация сигнала покупки ===================================================
if (MACD > 0) // Положительная зона MACD
{
SellSL = MathMax(SellSL, High[i]); // Отслеживаем стоп коротких
Above += MACD; // Продолжаем рассчитывать сумму..
// ..площадей роста
if (MACD2 < 0) // Произошло пересечение средних линий
{
SellSL = High[i]; // "Обнуляем" значение стопа коротких
Square(i+1); // Рассчитаем сумму площадей роста и..
// ..падения
Above += MACD; // Производим небольшую коррекцию в..
// ..пользу роста
if (Above - Below == 0) return; // При равенстве знаменателя 0 уходим
Signal = 2; // Текущий сигнал, как минимум, должен
// ..привести к закрытию короткой
if (100*Above/(Above - Below) < MaxLevel &&// Если соотношение..
-100*Below/(Above - Below) < MaxLevel)// ..меньше критического уровня,
Signal = 1; // ..то даем сигнал покупки..
return; // ..и завершаем функцию
}
if (Above - Below != 0) // Знаменатель не равен нулю
if (100*Above/(Above - Below) >= MaxLevel)// Соотношение площадей превысило..
Signal = -2; // ..критический уровень. Сигналим о..
// ..закрытии длинной
}
// - 2 - == Окончание блока =============================================================

// - 3 - == Генерация сигнала продажи ===================================================
if (MACD < 0) // Отрицательная зона MACD
{
BuySL = MathMin(BuySL, Low[i]); // Отслеживаем стоп длинных
Below += MACD; // Продолжаем рассчитывать сумму..
// ..площадей падения
if (MACD2 > 0) // Произошло пересечение средних линий
{
BuySL = Low[i]; // "Обнуляем" значение стопа длинных
Square(i+1); // Рассчитаем сумму площадей роста и..
// ..падения
Below += MACD; // Производим небольшую коррекцию в..
// ..пользу падения
if (Above - Below == 0) return; // При равенстве знаменателя 0 уходим
Signal = -2; // Текущий сигнал, как минимум, должен
// ..привести к закрытию длинной
if (-100*Below/(Above - Below) < MaxLevel &&// Если соотношение..
100*Above/(Above - Below) < MaxLevel) // ..меньше критического уровня,
Signal = -1; // ..то даем сигнал продажи..
return; // ..и завершаем функцию
}
if (Above - Below != 0) // Знаменатель не равен нулю
if (-100*Below/(Above - Below) >= MaxLevel)// Соотношение площадей превысило..
Signal = 2; // ..критический уровень. Сигналим о..
// ..закрытии короткой
}
// - 3 - == Окончание блока =============================================================
}
Аргумент функции i указывает номер бара, для которого необходимо произвести расчет сигнала. В первом блоке вычисляются значения разности средних скользящих линий для указанного бара и бара, находящегося слева от него. В зависимости от полученного значения переменной MACD выполняется второй или третий блок функции.
При положительном значении переменной MACD (быстрая средняя линия выше медленной линии) исполняется второй блок. В нем обновляются значения двух глобальных переменных: SellSL и Above. Переменная SellSL содержит значение максимальной цены, достигнутой рынком за время существования области роста. На основании значения SellSL в дальнейшем производится установка стоп-приказа короткой сделки. Переменная Above, по аналогии с кодом индикатора Square, несет в себе значение суммы площадей роста цены. В случае фиксации пересечения средних линий (значение MACD2 < 0) переменные SellSL и Above не обновляются, а рассчитываются заново. Значение SellSL становится равным максимуму свечи с индексом i, а значение Above рассчитывается путем вызова функции Square, код которой полностью повторяет код функции Square из одноименного индикатора. Одновременно с Above, в функции рассчитывается значение переменной Below, отвечающей за сумму площадей падения цены. Увеличение значения Above после вызова функции Square объясняется необходимостью учета площади формирующейся области, которая не учитывается в функции Square.
При пересечении средних линий по умолчанию генерируется сигнал закрытия короткой сделки (Signal = 2). Для генерации сигнала открытия длинной сделки, включающей сигнал закрытия короткой, необходимо, чтобы соотношение площадей роста и падения цены не превысило критический уровень MaxLevel. В этом случае переменная Signal принимает значение 1.
Последняя алгоритмическая конструкция блока 2 отслеживает достижение критического уровня соотношения площадей роста и падения во время существования длинной сделки (аналог участка кода индикатора Square, отслеживающего необходимость отображения "галочки"). Если достигнут критический уровень соотношения, то переменная Signal принимает значение -2, что соответствует сигналу закрытия длинной сделки.
Третий блок подобен блоку 2. Разница заключается в обновлении значения переменной Below (сумма площадей падения) и переменной BuySL, указывающей минимальное значение цены, достигнутое за время формирования области падения. В блоке 3 происходит генерация следующих сигналов: закрытие длинной сделки (Signal = -2, если обнаружено пересечение средних линий сверху вниз), открытие короткой сделки (Signal = -1, если при пересечении средних соотношение Above и Below не достигло критического уровня) и закрытие короткой сделки (Signal = 2, если соотношение Below и Above достигло критического уровня за время существования короткой сделки).
Использование значения переменной Signal происходит в функции Trade, отвечающей за открытие и закрытие сделок:

//+-------------------------------------------------------------------------------------+
//| Открытие позиций |
//+-------------------------------------------------------------------------------------+
bool Trade()
{
// - 1 - == Сигнал открытия длинной сделки ==============================================
if (Signal > 0 && Type != OP_BUY) // Необходимо открыть длинную
{
if (Type == OP_SELL) // Если имеется короткая сделка, то..
if (!CloseDeal(Ticket)) // ..закроем ее. При неудаче вернем..
return(false); // ..ошибку
if (Signal == 1) //Если необходимо открыть новую сделку
{
RefreshRates(); // Обновление значений Bid и Ask
if (OpenOrderCorrect(OP_BUY, Lots, NP(Ask),// Открытие позиции Buy
NP(BuySL - Tick), 0) != 0)
return(false);
}
}
// - 1 - == Окончание блока =============================================================

// - 2 - == Сигнал открытия короткой сделки =============================================
if (Signal < 0 && Type != OP_SELL) // Необходимо открыть короткую
{
if (Type == OP_BUY) // Если имеется длинная сделка, то..
if (!CloseDeal(Ticket)) // ..закроем ее. При неудаче вернем..
return(false); // ..ошибку
if (Signal == -1) //Если необходимо открыть новую сделку
{
RefreshRates(); // Обновление значений Bid и Ask
if (OpenOrderCorrect(OP_SELL, Lots, NP(Bid),// Открытие позиции Sell
NP(SellSL + Spread + Tick), 0) != 0)
return(false);
}
}
// - 2 - == Окончание блока =============================================================

return(True); // Все операции завершены успешно
}
Код функции стандартен. Изменения, коснувшиеся ее по сравнению с кодом, использовавшемся в предыдущем эксперте (см. статью Три белых солдата и три черных вороны (http://www.forextrade.ru/mqlabs/27.02.2011-mqlabs-tri-belyh-soldata-i-tri-chernyh-vorony)), заключаются в отсутствии расчета уровня профита. Второе отличие - это разделение значения сигнала не только на положительные и отрицательные значения, но и по типам: открытие и закрытие. Например, в блоке 1 при значении Signal = 2 выполняется только закрытие короткой сделки, а открытие длинной пропускается. Для выполнения обоих действий необходимо, чтобы значение переменной Signal было равно 1.
Кроме открытия и закрытия сделок в торговых правилах стратегии мы указали возможность перемещения уровня стоп-приказа вслед за ценой на расстоянии, кратном средней текущей волатильности. Для этого потребуется разработка новой функции, которую назовем CheckStop:

//+-------------------------------------------------------------------------------------+
//| Проверка правильности расположения уровня стоп-приказа |
//+-------------------------------------------------------------------------------------+
bool CheckStop()
{
// - 1 - == Определение ордера, с которым необходимо работать ===========================
if (Ticket > 0) // Существует ли сделка?
if (OrderSelect(Ticket, SELECT_BY_TICKET)) // Если существует, выберем ее из..
{ // .. списка ордеров
RefreshRates(); // Обновим значения Bid и Ask
double price = 0; // Нулевое значение цены говорит об..
// ..отсутствии необходимости..
// ..модификации
// - 1 - == Окончание блока =============================================================

// - 2 - == Вычисление цены нового стопа длинной сделки =================================
if (Type == OP_BUY) // Если имеем дело с длинной сделкой
{
price = Bid - VolKoef*iATR(NULL, 0, VolPeriod, 1);// Рассчитаем цену для..
// ..уровня стоп-приказа
if (price < OrderOpenPrice() || // Если расчетная цена ниже цены..
price < OrderStopLoss() + Tick || // ..открытия или ниже текущего стопа,
Bid - price <= StopLevel) // ..или цена слишком близка, ..
price = 0; // ..то стоп изменению не подлежит
}
// - 2 - == Окончание блока =============================================================

// - 3 - == Вычисление цены нового стопа короткой сделки ================================
if (Type == OP_SELL) // Если имеем дело с короткой сделкой
{
price = Ask + VolKoef*iATR(NULL, 0, VolPeriod, 1);// Рассчитаем цену для..
// ..уровня стоп-приказа
if (price > OrderOpenPrice() || // Если расчетная цена выше цены..
price > OrderStopLoss() - Tick || // ..открытия или выше текущего стопа
price - Ask <= StopLevel) // ..или цена слишком близка, ..
price = 0; // ..то стоп изменению не подлежит
}
// - 3 - == Окончание блока =============================================================

// - 4 - == Модификация уровня стоп-приказа сделки ======================================
if (price > 0) // Если цена нового стоп-приказа..
// ..определена, то модифицируем
if (WaitForTradeContext()) // Если свободен торговый поток, то..
{ // ..производим модификацию
if (!OrderModify(Ticket, 0, NP(price), OrderTakeProfit(), 0))// При..
return(false); // ..неудачной модификации вернем..
} // ..ошибку
else // Если не дождались освобождения..
return(false); // ..торгового потока, вернем ошибку
// - 4 - == Окончание блока =============================================================
}
return(true);
}
Предполагается, что до вызова этой функции следует вызов другой функции, FindOrders, в которой формируются значения переменных Type и Ticket. Переменная Ticket должна содержать уникальный номер сделки, открытой экспертом, или -1, если сделка не найдена. Переменная Type, в случае нахождения сделки, указывает ее тип (0 - Buy, 1 - Sell).
В первом блоке определяется существование сделки и выбор ордера из списка ордеров. Переменная price содержит 0, если уровень стоп-приказа в изменении не нуждается, и значение нового уровня, если стоп-приказ нужно передвинуть.
Второй и третий блоки вычисляют новый уровень стоп-приказа для длинной и короткой сделок соответственно. Новая цена должна находиться в положительной зоне сделки и быть лучше текущего уровня стоп-приказа. Также не забываем о том, что нельзя установить стоп-приказ, если текущая цена находится ближе, чем минимальный уровень стопов, установленный брокером. Переменная VolKoef - это настроечный параметр эксперта, указывающий коэффициент волатильности, подбираемый для каждого торгового инструмента индивидуально. VolPeriod - еще один настроечный параметр эксперта, при помощи которого пользователь может изменить период расчета средней волатильности.
В четвертом блоке выполняется изменение уровня стоп-приказа текущей сделки при условии, что значение price не равно нулю и свободен торговый поток. В случае возникновения ошибок при выполнении торговых операций, функция вернет значение false. При успешном завершении возвращаемое значение равно true.
Выполнение проверки уровня стоп-приказа необходимо выполнять на каждом новом тике. Поэтому стандартный код функции start, используемый в большинстве экспертов проекта MQLabs, нуждается в изменении:

//+-------------------------------------------------------------------------------------+
//| Функция start эксперта |
//+-------------------------------------------------------------------------------------+
int start()
{
// - 1 - == Можно ли работать эксперту? =================================================
if (!Activate || FatalError) return(0);
// - 1 - == Окончание блока =============================================================

// - 2 - == Обновление информации о торговых условиях ===================================
if (!IsTesting())
{
Tick = MarketInfo(Symbol(), MODE_TICKSIZE); // минимальный тик
Spread = ND(MarketInfo(Symbol(), MODE_SPREAD)*Point);// текущий спрэд
StopLevel = ND(MarketInfo(Symbol(), MODE_STOPLEVEL)*Point);//текущий уровень стопов
FreezeLevel = ND(MarketInfo(Symbol(), MODE_FREEZELEVEL)*Point);// уровень заморозки
}
// - 2 - == Окончание блока =============================================================

// - 3 - == Проверка необходимости подтяжки стопа =======================================
FindOrders(); // Поиск своего ордера
if (!CheckStop()) // Подтяжка стопа
return(0); // Неудачная подтяжка, выходим
// - 3 - == Окончание блока =============================================================

// - 4 - == Контроль открытия нового бара ===============================================
if (LastBar == Time[0]) // Если на текущем баре уже были..
return(0); // ..произведены необходимые действия,
// ..то прерываем работу до..
// ..следующего тика
// - 4 - == Окончание блока =============================================================

// - 5 - == Генерация сигнала открытия сделки ===========================================
if (LastSignal != Time[0]) // На текущем баре еще не был..
{ // ..произведен расчет индикатора
GetSignal(1); // Генерация сигнала открытия
LastSignal = Time[0]; // Расчет значений индикатора на..
} // ..текущем баре был произведен
// - 5 - == Окончание блока =============================================================

// - 6 - == Выполнение торговых операций ================================================
if (Signal != 0) // Имеется сигнал открытия сделки
if (!Trade()) // Открытие и закрытие сделок
return(0);
// - 6 - == Окончание блока =============================================================

LastBar = Time[0];

return(0);
}
Код, исполняющийся только при формировании нового бара, находится теперь только в двух последних блоках - 5 и 6. Код всех остальных блоков исполняется на каждом тике.
Потиковое исполнение затронуло блоки 1 и 2, собирающие информацию о возможности работы эксперта и об имеющемся рыночном окружении.
В блоке 3 перед проверкой уровня стоп-приказа вызывается функция FindOrders, при помощи которой определяется наличие сделки, открытой экспертом. Вслед за ней вызывается функция CheckStop, которая не даст выполниться коду, расположенного после нее, до тех пор, пока не будет успешно выполнена подтяжка стоп-приказа текущей сделки в случае ее наличия.
Блоки 4, 5 и 6 можно назвать стандартными, т.к. они присутствуют в большинстве экспертов MQLabs. Функция четвертого блока - не дать выполниться блокам 5 и 6 на текущем баре повторно. Блок 5 отвечает за расчет сигнала путем вызова функции GetSignal, а блок 6 - за открытие и закрытие сделок при наличии любого сигнала (Signal != 0).
Полный исходный код эксперта Square можно получить по ссылке, размещенной в конце статьи. Там же расположена ссылка на обновленный код индикатора Square (вторая версия), в котором исправлены некоторые мелкие ошибки, приводившие к несоответствию показаний индикатора и действий эксперта.


Тестирование советника
Тестирование стратегии проводилось на историческом периоде 08.01.2009 - 05.03.2011 и таймфрейме Н1. Для каждой валютной пары был индивидуально подобран коэффициент волатильности VolKoef, а также периоды расчета средних скользящих линий MACDFast и MACDSlow. Остальные параметры принимались со значениями, используемыми по умолчанию. Результаты тестирования приведены на рис. 3-6.
EURUSD. Значения изменяемых параметров: MACDFast = 6, MACDSlow = 99, VolKoef = 5. Кривая баланса только во второй части тестирования показывает стабильный рост. Первая половина тестирования прошла под аккомпанемент стабильного болтания в районе нуля. Чистая прибыль достигла значения 3 822 доллара при максимальной просадке 1 599 долларов. Фактор восстановления 2.39. Для евро это низкий показатель.

http://www.forextrade.ru/media/Image/MQLabs/105_ag/EURUSD.gif
Рис. 3. Результаты тестирования эксперта Square на валютной паре EURUSD.

USDCHF. Значения изменяемых параметров: MACDFast = 4, MACDSlow = 31, VolKoef = 2. Вид кривой баланса, даже без рассмотрения статистических показателей, свидетельствует о полном отсутствии перспектив стратегии применительно к USDCHF.

http://www.forextrade.ru/media/Image/MQLabs/105_ag/USDCHF.gif
Рис. 4. Результаты тестирования эксперта Square на валютной паре USDCHF.

GBPUSD. Значения изменяемых параметров: MACDFast = 3, MACDSlow = 88, VolKoef = 1. Наблюдаемый вид кривой баланса можно назвать самым стабильным из первых трех рассмотренных результатов. Смазанным вышло окончание тестирования - вновь приходится констатировать, что последние 70 сделок в совокупности привели к убыткам. Общая чистая прибыль 1 381 доллар, максимальная просадка 520 долларов. Фактор восстановления 2.66.

http://www.forextrade.ru/media/Image/MQLabs/105_ag/GBPUSD.gif
Рис. 5. Результаты тестирования эксперта Square на валютной паре GBPUSD.

USDJPY. Значения изменяемых параметров: MACDFast = 20, MACDSlow = 31, VolKoef = 3. На всем тестируемом участке кривая баланса выглядит стабильно, причем эта стабильность достойна самых лестных отзывов. Статистика тоже смотрится солидно: чистая прибыль 4 655 долларов, максимальная просадка 565 долларов. Фактор восстановления 8.24. Отличный, уверенный результат!

http://www.forextrade.ru/media/Image/MQLabs/105_ag/USDJPY.gif
Рис. 6. Результаты тестирования эксперта Square на валютной паре USDJPY.

Доработка стратегии для использования в AutoGraf 4.0
Управление стратегией Square в среде AutoGraf 4 требует использования восьми настроечных параметров. Соответствие параметров эксперта и параметров стратегии следующее: MACDFast - AT_1, MACDSlow - AT_2, MACDPrice - AT_3, MACDMethod - AT_4, SamplingDepth - AT_5, MaxLevel - AT_6, VolKoef - AT_7, VolPeriod - AT_8.
Параметр Lots, как обычно, можно настраивать во время исполнения стратегии при помощи панели настроек (нижняя часть графика, параметр Lot). Параметры OpenOrderSound и MagicNumber в стратегии, работающей в среде AutoGraf, недоступны пользователю для изменения. Их можно менять только путем редактирования исходного кода программы.
Запуск стратегии "Отклонение от состояния покоя" в среде AutoGraf 4.0 состоит из следующих шагов:

Получить файл по ссылке Файлы стратегий для AutoGraf 4.0 (http://www.forextrade.ru/media/Image/MQLabs/105_ag/AG_Square.zip) и распаковать полученный архив в папку MT4\experts\libraries (с перезаписью файлов AG_AT.ex4 и AG_AT.mq4).
Запустить AutoGraf (прикрепить индикатор AG_ind, а затем эксперт AG_exp).
Для работы стратегии в ключе приведенных результатов в окне настроек AutoGraf (закладка "Входные параметры") установить нужные значения параметров AT_1 - AT_8. Полное повторение результатов при этом не гарантируется.
Выбрать стратегию №5. Для этого необходимо передвинуть вверх значок So и среди названий стратегий найти значок S5, который также потянуть вверх.
Запустить функцию автоматической торговли, передвинув значок AT в верхнее положение.


Советник Square (http://www.forextrade.ru/media/Image/MQLabs/105_ag/Square_Expert.mq4)
Индикатор Square_V2 (http://www.forextrade.ru/media/Image/MQLabs/105_ag/Square_V2.mq4)
Файлы стратегий для AutoGraf 4.0 (http://www.forextrade.ru/media/Image/MQLabs/105_ag/AG_Square.zip)
Развернутые результаты тестирования эксперта (http://www.forextrade.ru/media/Image/MQLabs/105_ag/test.zip)


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