Jump to content

Recommended Posts

Programmer

Урок 85 - Ожидание нового бара и простой пример (часть 2)

 

Всех приветствую!

Сегодня мы разберем код из предыдущего урока, а главное - поймем, как работает функция IsNewBar()

 

//+------------------------------------------------------------------+
//|                                                     Lesson83.mq4 |
//|                                Copyright © 2012, StockProgrammer |
//|                                          [email protected] |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2012, StockProgrammer"
#property link      "[email protected]"

 

Этот код - всего лишь шапка. Это просто описание программы.

 

int start()
{
  ...
  return(0);
}

 

Основная функция "советника". Я пишу "советник" в кавычках, потому что, как Вы помните, эта программа не торгует, а просто информирует на о состоянии индикатора MACD. По сути, это нечто подобное индикатору, но мы организовали его как советник, чтобы показать, что и такое возможно в мире программистов :)

 

   if(IsNewBar())
  {
     ...
  }

 

Проверяем условие - вызываем ф-ю IsNewBar(), и если она вернула значение TRUE, то выполняем код в фигурных скобках.

 

     double Main = iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,1);
     double Signal = iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,1);

 

Код в фигурных скобках - считываем значение линий индикатора MACD за предыдущий бар (текущий бар еще не закрыт - поэтому значение индикатора не установлено).

 

     Alert("Main = ", Main, " Signal = ", Signal);

 

Выводим значения Alert'ом на экран трейдера.

Обратим внимание на устройство ф-ии IsNewBar():

 

bool IsNewBar()

 

Объявление функции. После этой строчки программа знает, что есть такая функция, что она не требует входных параметров, и что она возвращает значение типа BOOL.

 

{
    ...
}

 

Тело функции.

 

   static datetime BARflag = 0;

 

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

 

   datetime now = Time[0];

 

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

 

   if(BARflag < now)
  {
     BARflag = now;         
     return(1);
  }

  else
  {
     return(0);
  }

 

Самый главный алгоритм - проверяем, если флаг указывает на время открытия текущего бара. Если нет (флаг указывает либо на НУЛЬ, либо на время открытия более раннего бара) - тогда перезаписываем в флаг время открытия текущего бара и возвращаем 1 (TRUE) из функции IsNewBar(). Если да - возвращаем 0 (FALSE) из функции IsNewBar().

 

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

 

Нам удалось так легко запрограммировать этот алгоритм благодаря статическим переменным. Думаю, их стоит обсудить на следующем уроке более детально.

 

Надеюсь, данный урок показался Вам полезным.

До встречи на следующем уроке!

 

© Kirill. [email protected]

Link to post
Share on other sites
  • 3 weeks later...
  • Replies 131
  • Created
  • Last Reply

Top Posters In This Topic

  • Programmer

    132

Top Posters In This Topic

Popular Posts

Урок 17 - Ваш первый советник (часть 4)   Мы дошли до края света в поисках истины! Нет, сегодня я не пил. Я просто счастлив, что наконец-то мы дошли до последней части серии уроков, посвящённых наше

Урок 7 - Функции   Добро пожаловать в мир функций языка MQL4. Работа с функциями в любом языке состоит из двух этапов: - изучение функций, что, порой, очень скучное занятие - использование функци

Урок 19 - Работа с шаблонами   Мы уже создали наш первый индикатор, советник и скрипт в предыдущих уроках. Каждый раз, когда мы писали программу, нам приходилось начинать с нуля. Это полезно для но

Posted Images

Programmer

Урок 86 - Иерархии переменных (Часть 1)

 

Привествую читателей курса!

 

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

 

Переменные могут принадлежать к одной из трех основных иерархий. И, как ни странно, это не те три названия, которые я только что перечислил. Три основные иерархии переменных в MQL4 - это локальные переменные, глобальные (на уровне программы) переменные и глобальные (на уровне терминала) переменные.

 

Назовем глобальные (на уровне программы) переменные "Глобальные1", а глобальные (на уровне терминала) переменные "Глобальные2" для простоты. Заметьте, что в указанных иерархиях есть две вариации "Глобальных" переменных, а слово "Статические" не встречается ни разу.

 

Статичность переменной - это нечто совсем иное. Если хотите, это характеристика, которую можно приписать переменной. Со статичностью мы разберемся немного позже, а в ближайщих уроках мы разберем указанные три иерархии переменных. Мы начнем с самых простых - "Локальных" переменных, постепенно переходя к более "высоким" переменным. Также я планирую разобрать несколько примеров для наглядности. Так что пристегните ремни.

 

До встречи на следующем уроке!

 

© Kirill. [email protected]

Link to post
Share on other sites
Programmer

Урок 87 - Иерархии переменных (Часть 2)

 

Всем привет!

 

Сегодня мы рассмотрим первую иерархию переменных:

 

Локальные Переменные

 

Что такое локальные переменные? Локальные переменные - это те переменные, которые мы используем повсюду в наших программах каждый день. Эти переменные имеют ограниченную область жизни, и именно поэтому они очень просты в обращении. Пример:

 

int start()
{
  double Var1;
  ...
}

 

Объявленная подобным образом переменная будет локальной для функции start(). Многие языки программирования устроены так, что локальные переменные живут только внутри ближайших фигурных сокобок {}. При выходе за фигурные скобки переменная умирает. Однако, разработчики MQL4 решили не идти по этому пути - переменная может жить вне своих фигурных скобок. В MQL4 ограничение на жизнь переменной более слабое - локальная переменная будет жить только внутри фигурных скобок своей функции. Пример:

 

int start()
{
  double Var1; 
  if(1==1)
  {
     double B = 0.95;
  }
  Var1 = B;

}

 

Данный код откомпилируется успешно. А вот код:

 

int start()
{
  double Var1; 
  ...
}

int hi()
{
  double B; 
  B = Var1;
}

 

Выдаст ошибку при компиляции. Это происходит, потому что переменная Var1 локальна для функции start(), а значит, в функции hi() компилятор про нее ничего не знает. И даже если вызвать ф-ию hi() из ф-ии start(), компилятор все равно выдаст ошибку:

 

int start()
{
  double Var1; 
  hi();
  ...
}

int hi()
{
  double B; 
  B = Var1;
}

 

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

 

int start()
{
  int A = 1; 
  Print(A);
  ...
  A=15;

  return(0);
}

 

При первом входе в функцию start() функция Print() распечатает значение "1". При втором входе в функцию start() функция Print() все равно распечатает значение "1", поскольку значение "15" было потеряно при выходе из start(), а при повторном входе переменная A родилась заново. Так будет каждый раз.

 

Опережая ваши вопросы, скажу, что не надо путать жизнь внутри функции с передачей значения в функцию. К примеру, вы спросите: "А как же переменная 'A' живет внутри функции Print(), если у нас не получилось реализовать подобное с функцией hi()??"

 

Дело в том, что в примере с функцией hi() мы пытались реализовать нечто совершенно другое. Мы хотели, чтобы функция hi() обращалась с переменной A, как со своей. А функция Print() этого не делает - она лишь берет последнее известное значение переменной и работает с ним. Если быть конкретней, это значение записывается в некоторую локальныю переменную в функции Print(), с которым эта функция уже спокойно работает.

 

Надеюсь, все теперь понятно с локальными переменными. Если остались еще вопросы - пишите.

До встречи на следующем уроке!

 

© Kirill. [email protected]

Link to post
Share on other sites
Programmer

Урок 88 - Иерархии переменных (Часть 3)

 

Всем привет!

 

Сегодня мы рассмотрим вторую иерархию переменных:

 

Глобальные1 (на уровне программы)

 

Что такое глобальные переменные? На самом деле мы уже вводили данное понятие, как и понятие локальных переменных, - в Уроке 8 - Переменные в MQL4. Т.к. это было ровно 80 уроков назад, я освежу Вашу память :)

 

В восьмом уроке курса мы говорили о зонах видимости переменных. Вводились два понятия - локальная зона видимости и глобальная зона видимости. Глобальные1 - это переменные с глобальной зоной видимости. Внурти рассматриваемой программы они объявлены на уровне объявления функций, и потому, как и функции, видны в любой части программы. Рассмотрим пример:

 

double ABC_X = 15.6;
bool flag = true;

int start()
{
  double Var1;
  Var1 = Open[0];
  ...
  ABC_X = Var1;
  ...
  my_custom_function();
  ...
}

void my_custom_function()
{
  if(flag == true)
  {
     Print("ABC_X = ", ABC_X);
     ...
  }
  ...
}

 

В данном примере переменные ABC_X и flag являются глобальными и видны как внутри функции start(), так и внутри функции my_custom_function(). Как видите, глобальные переменные объявлены на том же уровне, что и обе функции. Кстати, поскольку глобальные переменные видны внутри всех функций в программе, это делает их очень привлекатыльным решением при поиске посредника для обмена данными между двумя функциями. Это не самый лучший выбор с точки зрения эстетики, стиля и вредных привычек программирования - но об этом мы поговорим в одном из будущих уроков, когда будем говорить об эстетике, стиле и вредных привычках программирования.

 

Надеюсь, данный урок показался Вам полезным. В следующий раз мы подробно обратимся к достаточно важному частному случаю Глобальных1 переменных - мы рассмотрим внешние переменные.

До встречи на следующем уроке!

 

© Kirill. [email protected]

Link to post
Share on other sites
Programmer

Урок 89 - Иерархии переменных (Часть 4)

 

Всем привет!

 

Сегодня мы рассмотрим подкласс второй иерархии переменных:

 

Глобальные1 -> Внешние переменные

 

Отдельным подклассом Глобальных1 переменных являются Внешние переменные. Внешение - это Глобальные1 переменные, наделенные особым свойством - они отображаются в окне настроек программы (советника/индикатора/скрипта). Ключевое слово "extern" используется для объявления переменных данного вида. Внешние переменные используются для хранения входных данных программы.

 

post-50854-1404218438,9213_thumb.jpg

Рис. 1 - Окно настроек индикатора

 

Выше Вы видите пример окошка настроек индикатора MyIndicator2.mq4, который мы написали в Уроке 68 - Функция iCustom() (Часть 3) - это было целых двадцать уроков назад. Напомню, что этот индикатор мы придумали сами, и он устроен таким образом, что он принимает три входных значния, которые Вы видите на рисунке:

 

int period
int adjustment_factor
double adjustment_percent

Полный код индикатора Вы можете найти в 68-м уроке, а я скопирую лишь те его части, где учавствуют указанные три переменные:

 

//+------------------------------------------------------------------+
//|                                                 MyIndicator2.mq4 |
//|                                Copyright © 2011, StockProgrammer |
//|                                          [email protected] |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2011, StockProgrammer"
#property link      "[email protected]"

...

extern int period =  14;
extern int    adjustment_factor  = 50;
extern double adjustment_percent = 0.1;

...

int init()
{
  ...

  return(0);
}

int start()
 {
  ...

  for(int i=Bars-counted_bars; i>=0; i--)
  {
     ...

     double adjustment = 0;
     for(int j=i; j<i+adjustment_factor; j++)
        adjustment += iCustom(Symbol(), Period(), "MyIndicator1", 0, j) * (High[j]+Low[j])/2 * adjustment_percent/100;
     Buffer1[i] = Buffer0[i] + adjustment;
  }
  return(0);
 }
//+------------------------------------------------------------------+

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

 

Надеюсь, данный урок показался Вам полезным.

До встречи на следующем уроке!

 

© Kirill. [email protected]

Link to post
Share on other sites
  • 4 weeks later...
Programmer

Урок 90 - Специальные параметры

 

Приветсвтую, читатели Курса MQL4!

 

Сегодня мы немного отвлечемся от иерархий переменных и рассмотрим тему, которую еще не затрагивали. Раз уж мы в прошлом уроке обратились к написанному нами ранее индикатору, давайте обсудим функции, которые уникальны для пользовательских индикаторов. Это семейство функций так и называется "Функции пользовательских индикаторов".

 

 

1 - IndicatorBuffers()

 

Синтаксис:

void IndicatorBuffers( int count)

 

Описание:

Распределяет память для буферов, используемых для вычислений пользовательского индикатора. Количество буферов не может превышать 8 и быть менее значения, указанного в свойстве indicator_buffers. Если пользовательский индикатор требует дополнительных буферов для счета, следует использовать эту функцию для указания общего числа буферов

 

Параметры:

count - Количество расчетных буферов. от indicator_buffers до 8 буферов.

 

 

2 - IndicatorCounted()

 

Синтаксис:

int IndicatorCounted( )

 

Описание:

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

 

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

 

Параметры:

- отсутствуют -

 

 

3 - IndicatorDigits()

 

Синтаксис:

void IndicatorDigits( int digits)

 

Описание:

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

 

Параметры:

digits - Формат точности, число цифр после десятичной точки.

 

 

4 - IndicatorShortName()

 

Синтаксис:

void IndicatorShortName( string name)

 

Описание:

Установка "короткого" имени пользовательского индикатора для отображения в подокне индикатора и в окне DataWindow.

 

Параметры:

name - Новое короткое имя.

 

 

5 - SetIndexArrow()

 

Синтаксис:

void SetIndexArrow( int index, int code)

 

Описание:

Назначение значка для линии индикаторов, имеющей стиль DRAW_ARROW.

Нельзя использовать коды стрелок вне диапазона 33-255.

 

Параметры:

index - Порядковый номер линии. Должен быть от 0 до 7.

code - Код символа из шрифта Wingdings или одним из предопределенных стрелок.

 

 

6 - SetIndexBuffer()

 

Синтаксис:

bool SetIndexBuffer( int index, double array[])

 

Описание:

Связывает переменную-массив, объявленный на глобальном уровне, с предопределенным буфером пользовательского индикатора. Количество буферов, необходимых для расчета индикатора, задается с помощью функции IndicatorBuffers() и не может быть больше 8. В случае успешного связывания возвращается TRUE, иначе FALSE. Чтобы получить расширенные сведения об ошибке, следует вызвать функцию GetLastError().

 

Параметры:

index - Порядковый номер линии. Должен быть от 0 до 7.

array[] - Ссылка на массив, который будет связан с расчетным буфером.

 

 

7 - SetIndexDrawBegin()

 

Синтаксис:

void SetIndexDrawBegin( int index, int begin)

 

Описание:

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

 

Параметры:

index - Порядковый номер линии. Должен быть от 0 до 7.

begin - Номер позиции начала отрисовки линии индикатора.

 

 

8 - SetIndexEmptyValue()

 

Синтаксис:

void SetIndexEmptyValue( int index, double value)

 

Описание:

Устанавливает значение пустой величины для линии индикатора. Пустые значения не рисуются и не показываются в DataWindow. По умолчанию значение пустой величины - EMPTY_VALUE

 

Параметры:

index - Порядковый номер линии. Должен быть от 0 до 7.

value - Новое "пустое" значение.

 

 

9 - SetIndexLabel()

 

Синтаксис:

void SetIndexLabel( int index, string text)

 

Описание:

Установка имени линии индикатора для отображения информации в окне DataWindow и всплывающей подсказке.

 

Параметры:

index - Порядковый номер линии индикатора. Должен быть от 0 до 7.

text - Текст описания линии индикатора. NULL означает, что значение этой линии не показывается в DataWindow.

 

 

10 - SetIndexShift()

 

Синтаксис:

void SetIndexShift( int index, int shift)

 

Описание:

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

 

Параметры:

index - Порядковый номер линии. Должно быть от 0 до 7.

shift - Величина смещения в барах.

 

 

11 - SetIndexStyle()

 

Синтаксис:

void SetIndexStyle( int index, int type, int style=EMPTY, int width=EMPTY, color clr=CLR_NONE)

 

Описание:

Устанавливает новый тип, стиль, ширину и цвет для указанной линии индикатора.

 

Параметры:

index - Порядковый номер линии. Должен быть от 0 до 7.

type - Стиль отрисовки линии индикатора. Может быть одним из перечисленных стилей отрисовки линии.

style - Стиль линии. Используется для линий толщиной в 1 пиксель. Может быть одним из перечисленных стилей линии. Пустое значение (EMPTY) указывает, что стиль не будет изменен.

width - Ширина линии. Допустимые значения - 1,2,3,4,5. Пустое значение (EMPTY) указывает, что ширина не будет изменена.

clr - Цвет линии. Отсутствие параметра означает, что цвет не будет изменен.

 

 

12 - SetLevelStyle()

 

Синтаксис:

void SetLevelStyle( int draw_style, int line_width, color clr=CLR_NONE)

 

Описание:

Устанавливает новый стиль, ширину и цвет для горизонтальных уровней индикатора, выводимого в отдельное окно.

 

Параметры:

draw_style - Стиль линии. Может быть одним из перечисленных стилей линии. Пустое значение (EMPTY) указывает, что стиль не будет изменен.

line_width - Ширина линии. Допустимые значения - 1,2,3,4,5. Пустое значение (EMPTY) указывает, что ширина не будет изменена.

clr - Цвет линии. Пустое значение CLR_NONE указывает, что цвет не будет изменен.

 

 

13 - SetLevelValue()

 

Синтаксис:

void SetLevelValue( int level, double value)

 

Описание:

Устанавливает значение для указанного горизонтального уровня индикатора, выводимого в отдельное окно.

 

Параметры:

level - Номер уровня (0-31).

value - Значение для указанного уровня.

 

 

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

До встречи на следующем уроке!

 

© Kirill. [email protected]

Link to post
Share on other sites
Programmer

Урок 91 - Иерархии переменных (Часть 5)

 

Всех приветствую!

 

В сегодняшнем уроке мы вернемся к иерархии переменных. Мы уже рассмотрели Локальные Переменные и Глобальные1 Переменные, включая отдельный подкласс последних, называемый "Внешние Переменные". Настало время обратиться к Глобальным2 Переменным.

 

Что же представляют из себя глобальные (на уровне терминала) переменные?

Это класс переменных, обладаюших сособым свойством - они видны из любой программы, работающей внурти данного теримнала. Будь то скрипт, индикатор или советник - если в терминале в данный момент времени есть глобальная переменная, к ней можно обратиться изнутри этой программы. Глобальные переменные объявляются и модифицируются посредством специальных функций, которые мы рассморели в Уроке 22 - Глобальные переменные.

 

Основные две функции при работе с глобальными2 переменными - это: GlobalVariableSet() и GlobalVariableGet(). Первая из этих функций позволяет создать глобальную переменную или задать значение существующей глобальной переменной. Вторая - считывает значение глобальной переменной. Рассмотрим пример:

 

GlobalVariableSet("MyGlobVar",15.5);
double A = GlobalVariableGet("MyGlobVar"); 

Данный код задает глобальной переменной MyGlobVar значение 15.5 (создавая указанную переменную, если она еще не существует), а затем програма присваивает локальной переменной A значение, считанное из этой же глобальной переменной MyGlobVar. Вот так достаточно просто обращаться с глобальными переменными. Есть также и другие полезные функции для работы с данным классом переменных, например GlobalVariableDel(), GlobalVariableSetOnCondition() и пр. - мы уже рассматривали эти функции и они интуитивно понятны. Про глобальные переменные важно знать, что каждая из них хранится в терминале ровно 4 недели после последнего обращения, затем переменные удаляются автоматически.

 

Надеюсь, данный урок показался Вам полезным. В следующий раз мы изучим статические переменные.

До встречи на следующем уроке!

 

© Kirill. [email protected]

Edited by Programmer
Link to post
Share on other sites
Programmer

Урок 92 - Статические переменные

 

Всех приветствую!

 

Сегодня мы обратимся к статическим переменным.

Что же означает статичность переменной? Как было сказано в Уроке 86, статичность переменной - это характеристика, которой может обладать переменная. Статичность означает, что когда при исполнении кода, программа выходит за пределы границы видимости данной переменной (не завершая своего исполнения, естественно), а затем возвращается обратно, статическая переменная будет сохранять свое значение.

 

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

 

На самом деле, вопрос с подвохом :) Дело в том, что глобальные переменные (как на уровне программы - глобальные1, так и на уровне терминала - глобальные 2) уже статичны поумолчанию! Действительно, глобальные1 имеют зону видимости "вся программа", а глобальные2 - "весь терминал", поэтому первые всегда будут сохранять свое значение пока программа исполняется, а вторые - даже если терминал будет перезагружен. Таким образом, выборочную статичность можно присваивать только локальными переменным. Рассмотрим этот процесс подробней.

 

int start()
{
  static int progress = 1;
  ...(1)
  progress = 2;
  ...(2)
  return(0);
}

 

В данном примере, при первом вызове ф-ии start() будет создана переменная progress и она будет проинициализирована значением '1'. Первая часть кода будет отработана с переменной progress, у которой значение '1'. Затем в коде переменная меняет свое значение на '2', и остаток кода будет выполнен с этим значением переменной. Когда же в терминал прийдет новый тик, и функция start() будет вызвана вновь, то значение переменной progress будет не '1' как на первым тике, а будет '2' - потому что значение переменной было сохранено с последнего раза. Вот именно так устроены статические переменные. Точно такой же принцип работает в случае, когда статическая переменная объявлена в некой пользовательской функции, которая, в свою очередь, уже вызывается из функции start().

 

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

До встречи на следующем уроке!

 

© Kirill. [email protected]

Link to post
Share on other sites
  • 3 weeks later...
Programmer

Урок 93 - Классы переменных в действии

 

Всем привет!

Сегодня мы рассмотрим большой пример реального советника. А затем будет небольшое задание... (см. ниже)

 

Пример

 

//+------------------------------------------------------------------+
//|                                                         SSRC.mq4 |
//|                                                           Kirill |
//|                                          [email protected] |
//+------------------------------------------------------------------+
#property copyright "Kirill"
#property link      "[email protected]"


extern string     s0                = "Setup: Main";
extern int        Magic             = 1121;
extern double     lots              = 0.1;
extern int        StopLoss          = 700;
extern int        TakeProfit        = 1400;
extern bool       UseTrail          = true;
extern bool       TrailWhileMinus   = false;
extern int        Trail             = 700;

extern string     s1                = "Setup: SSRC";
extern int        SnakeRange        = 5; 
extern int        FilterPeriod      = 21; 
extern double     MartFiltr         = 2;
extern int        PriceConst        = 6;


int               slip              = 3;

int Ticket[1000];

void deinit() 
{
  SemaphoreDeinit("TRADECONTEXT");

  return(0);
}


int start()
{
  static datetime TimeFlag = 0;
  datetime        TimeLast = Time[0];
  if(TimeFlag < TimeLast)
  {
     TimeFlag = TimeLast;

     int SSRC = GetSignal_SSRC();

     AnalyzeSignals(SSRC);

     if(UseTrail == true) TrailAllOrders();
  }
  return(0);
}



void AnalyzeSignals(int SSRC)
{
  static int ticket = 0;
  bool res;

  ticket = RefreshTicket(ticket);


  if(ticket != 0)
  {
     OrderSelect(ticket, SELECT_BY_TICKET);      
     int type = OrderType();

     if(SSRC == OP_BUY && type == OP_SELL)
     {
        SemaphoreTake("TRADECONTEXT"); 
        res = OrderClose(ticket, OrderLots(), Ask, slip);
        SemaphoreReturn("TRADECONTEXT");
        if(!res) {Alert("OrderClose Error: ", GetLastError());}
        else ticket = 0;
     }

     else if(SSRC == OP_SELL && type == OP_BUY)
     {
        SemaphoreTake("TRADECONTEXT"); 
        res = OrderClose(ticket, OrderLots(), Bid, slip);
        SemaphoreReturn("TRADECONTEXT");
        if(!res) {Alert("OrderClose Error: ", GetLastError());}
        else ticket = 0;
     } 
  }   


  if(ticket == 0)
  {   
     if(SSRC == OP_BUY)
     {
        SemaphoreTake("TRADECONTEXT"); 
        ticket = OrderSend(Symbol(), OP_BUY, lots, Ask, slip, Bid - StopLoss*Point, Bid + TakeProfit*Point, NULL, Magic);
        SemaphoreReturn("TRADECONTEXT");
        if(ticket < 0) {Alert("OrderSend Error: ", GetLastError());}
     }

     else if(SSRC == OP_SELL)
     {
        SemaphoreTake("TRADECONTEXT");
        ticket = OrderSend(Symbol(), OP_SELL, lots, Bid, slip, Ask + StopLoss*Point, Ask - TakeProfit*Point, NULL, Magic);
        SemaphoreReturn("TRADECONTEXT");
        if(ticket < 0) {Alert("OrderSend Error: ", GetLastError());}
     }
  }      
}

int RefreshTicket(int ticket)
{
  bool res;

  if(ticket <= 0)
     return(0);
  else
  {
     res = OrderSelect(ticket, SELECT_BY_TICKET);
     if(!res)
        return(0);
     else if(OrderCloseTime() != 0)
        return(0);
  }

  return(ticket);      //all ok, ticket still valid
}

int GetSignal_SSRC()
{
  int ret;
  double SSRC_1 = iCustom(Symbol(), Period(), "SSRC_Alert", SnakeRange, FilterPeriod, MartFiltr, PriceConst, 0, 1);
  static double SSRC_2;      //because the indicator is wierd - it redraws 

  Alert("SSRC_2=",SSRC_2, " SSRC_1=",SSRC_1);

  if(SSRC_2<=-0.75 && SSRC_1>-0.75)
     ret = OP_BUY;
  else if(SSRC_2>=0.75 && SSRC_1<0.75)
     ret = OP_SELL;
  else
     ret = -1;

  SSRC_2 = SSRC_1;
  return(ret);
}

int TrailAllOrders()
{
  int i, total;

  total = CreateTicketArray(OP_BUY, Magic);
  for(i=0; i<total; i++)
     TrailingStop(Ticket[i]);

  total = CreateTicketArray(OP_SELL, Magic);
  for(i=0; i<total; i++)
     TrailingStop(Ticket[i]);
}

void TrailingStop(int ticket)
{
  int res;
  OrderSelect(ticket, SELECT_BY_TICKET);

  if(OrderType() == OP_BUY)
     if(TrailWhileMinus == true || Bid-OrderOpenPrice()>Point*Trail) 
        if(Bid - OrderStopLoss() > Trail*Point)
        {
           SemaphoreTake("TRADECONTEXT");
           res = OrderModify(OrderTicket(), 0, Bid - Trail*Point, OrderTakeProfit(), 0);
           SemaphoreReturn("TRADECONTEXT"); 
           if(res<0)
              Alert("TrailingStop OrderModify Error: ", GetLastError());
       }

  if(OrderType() == OP_SELL)
     if(TrailWhileMinus == true || OrderOpenPrice()-Ask>Point*Trail) 
        if(OrderStopLoss() - Ask > Trail*Point)
        {
           SemaphoreTake("TRADECONTEXT");
           res = OrderModify(OrderTicket(), 0, Ask + Trail*Point, OrderTakeProfit(), 0);
           SemaphoreReturn("TRADECONTEXT");        
           if(res<0)
              Alert("TrailingStop OrderModify Error: ", GetLastError());
        }
}

int CreateTicketArray(int dir, int SysID)
{
  int total=OrdersTotal(), i, c=0; if (total<=0) return (0);
   for(i=0;i<total;i++) { OrderSelect(i, SELECT_BY_POS); if ((OrderType()==dir) && (OrderMagicNumber()==SysID)) { Ticket[c] = OrderTicket(); c++; } }
   return (c);
}

//------------------------------------------SEMAPHORE------------------------------------------

int critical = 0; 

void SemaphoreTake(string SEM)
{ 
  if(GlobalVariableCheck(SEM) == false)
     GlobalVariableSet(SEM, 0);

  while(1==1)
  {
     if(GlobalVariableSetOnCondition(SEM, 1.0, 0.0))
     {
        critical = 1;
        Print("SEMAPHORE \"", SEM, "\" TAKEN. CURRENT VALUE: ", GlobalVariableGet(SEM));
        break;
     }
     else
     {
        Print("ATTEMPT TO CAPTURE SEMAPHORE \"", SEM, "\" FAILED. SEMAPHORE BUSY. WAITING 0.1 SEC. CURRENT VALUE: ", GlobalVariableGet(SEM));
        Sleep(100);
     }
  }
} 

void SemaphoreReturn(string SEM)
{
  GlobalVariableSet(SEM, 0.0);
  Print("SEMAPHORE \"", SEM, "\" RETURNED. CURRENT VALUE: ", GlobalVariableGet(SEM));
  critical = 0;
}

void SemaphoreDeinit(string SEM)
{
  if(critical == 1)
  {
     GlobalVariableSet(SEM,      0.0);
  }  
}

 

 

Задание

 

Ваше задание для самостоятельного изучения - найти в коде и назвать классы/иерархии/статичность следующих 15ти переменных:

Magic

StopLoss

TakeProfit

slip

TRADECONTEXT

TimeFlag

TimeLast

SSRC

ticket

res

SSRC_1

SSRC_2

total

critical

SEM

 

Например, переменная TakeProfit принадлежит классу Глобальные1, подклассу Внешние переменные.

Помните, что Вы знаете следующие иерархии: Локальные, Глобальные1 и Глобальные2. Также помните, что локальные переменные могут быть как статическими, так и нет.

 

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

Удачи!

 

© Kirill. [email protected]

Link to post
Share on other sites
  • 3 weeks later...
Programmer

Урок 94 - Классы переменных в действии (часть 2)

 

Приветствую читателей!

Сегодня мы рассмотрим решение задания из предыдущего урока. Для того, чтобы определить класс/иерархию/статичность переменной необходимо обратиться к строке, в которой переменная объявлена. Начнем по порядку:

 

Magic - глобальная1 (т.е. глобальная на уровне советника) внешняя переменная типа int

extern int Magic = 1121;

 

StopLoss - глобальная1 внешняя переменная типа int

extern int StopLoss = 700;

 

TakeProfit - глобальная1 внешняя переменная типа int

extern int TakeProfit = 1400;

 

slip - глобальная1 переменная типа int (заметьте - эта переменная уже не внешняя)

int slip = 3;

 

TRADECONTEXT - глобальная2 (т.е. глобальная на уровне терминала) переменная

if(GlobalVariableCheck(SEM) == false)
  GlobalVariableSet(SEM, 0);

 

TimeFlag - локальная статическая переменная типа datetime внутри функции start()

static datetime TimeFlag = 0;

 

TimeLast - локальная переменная типа datetime внутри функции start()

datetime TimeLast = Time[0];

 

SSRC - локальная переменная типа int внутри функции start()

int SSRC = GetSignal_SSRC();

 

ticket - локальная статическая переменная типа int внутри функции AnalyzeSignals()

static int ticket = 0;

PS: с этим именем есть еще одна переменная - см ниже

 

ticket - локальная переменная типа int внутри функции RefreshTicket()

int RefreshTicket(int ticket)

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

 

res - локальная переменная типа bool внутри функции AnalyzeSignals()

bool res;

 

res - локальная переменная типа bool внутри функции RefreshTicket()

bool res;

 

res - локальная переменная типа int внутри функции TrailingStop()

int res;

 

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

 

SSRC_1 - локальная переменная типа double внутри функции GetSignal_SSRC

double SSRC_1 = iCustom(Symbol(), Period(), "SSRC_Alert", SnakeRange, FilterPeriod, MartFiltr, PriceConst, 0, 1);

 

SSRC_2 - локальная статическая переменная типа double внутри функции GetSignal_SSRC

static double SSRC_2;

 

total - локальная переменная типа int внутри функции TrailAllOrders()

int i, total;

 

total - локальная переменная типа int внутри функции CreateTicketArray()

int total=OrdersTotal(), i, c=0; 

 

Кстати, вот хороший пример множественного объявления переменных. В обоих случаях в одной строчке с переменной total объявляются и другие переменные типа int.

 

critical - глобальная1 переменная типа int

int critical = 0; 

 

А вот это был вопрос на засыпку! Вы наверное, уже начали думать, что глобальные1 (на уровне советника) переменные должны быть объявлены в самом начале программы? А вот и нет. Они могут быть объявлены в любом месте. Более того, любая переменная, объявленная вне тел всех функций является глобальной1.

 

SEM - локальные переменные типа string внутри функций SemaphoreTake, SemaphoreReturn и SemaphoreDeinit

void SemaphoreTake(string SEM)
void SemaphoreReturn(string SEM)
void SemaphoreDeinit(string SEM)

 

Справились? Отлично!

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

 

© Kirill. [email protected]

Link to post
Share on other sites
  • 2 weeks later...
Programmer

Урок 95 - Ссылки

 

Всем привет!

 

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

 

post-50854-1404218866,4524_thumb.jpg

Рис. 1 - Ссылка в программировании

 

Что такое ссылки и зачем они нужны?

В одном из самых первых уроков (а именно - в 7м уроке) данного курса мы изучили функции. Как помните, я тогда привел аналогию мясорубки:

 

Функция очень похожа на мясорубку - вы кладёте в неё мясо, лук, специи и получаете фарш.

Мясо, лук и специи называются параметрами функции (входными параметрами функции), фарш - возвращаемое значение. А механизм мясорубки - тело функции.

При передаче параметров обыкновенным образом, в функцию передаются значения входных параметров, а не сами параметры. И поэтому, все, что происходит внутри функции никак не может повлиять на "исходные копии" самих параметров. Отсюда возникает закономерный вопрос - "Можно ли как-нибудь передать параметры в функцию так, чтобы функция могла работать не просто с их значениями, а с исходными копиями напрямую?"

 

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

 

Рассмотрим пример:

//+------------------------------------------------------------------+
//|                                                  TEST SCRIPT.mq4 |
//|                                                           Kirill |
//|                                          [email protected] |
//+------------------------------------------------------------------+
#property copyright "Kirill"
#property link      "[email protected]"

int init()
{
  int A = 5;

  NormalFunction(A);
  Alert("Check A = ",A);
  FunctionWithReference(A);
  Alert("Check A = ",A);
}
void NormalFunction(int X)
{
  X = X + 10;
  Alert("Inside NormalFunction X = ", X);
}
void FunctionWithReference(int& X)
{
  X = X + 10;
  Alert("Inside FunctionWithReference X = ", X);
}

Как видим, в функции start() объявляется переменная A типа int, и ей присваивается значение 5. Затем, эта переменная передается в две различные функции:

 

1) void NormalFunction(int X)

При вызове этой функции, в нее передается значение переменной A. В данном случае, переменная X - это совершенно новая переменная, внутренняя для функции NormalFunction(). Все, что происходит с переменной X никак не влияет на переменную A.

 

2) void FunctionWithReference(int X)

При вызове этой функции, в нее передается ссылка на переменную A. В данном случае, переменная X - это ссылка на переменную A. И поэтому любое изменение переменной X просто-напросто означает изменение переменной A.

 

Значения переменных A и X этого тестового скрипта фиксируются посредством четырех Alert. При запуске скрипт выдает следующие сообщения (читать снизу вверх), наглядно иллюстрирующие, как меняется / не меняется переменная A:

 

post-50854-1404218866,5111_thumb.jpg

Рис. 2 - Пример использования ссылок

 

Ограничения на применение ссылок

Существуют некоторые ограничения при работе с ссылками:

- Если в определении функции не указано, что в нее передаются ссылки, то передать ссылку не получится

- По ссылке нельзя передать элемент массива, но зато можно передать ссылку на сам массив

- Согласно справке MQL4, для библиотечных функций не предусмотрена передача параметров по ссылке. Это ограничение не касается массивов - массивы можно передавать по ссылке в библиотечные функции

 

Надеюсь, что данный урок показался Вам полезным.

До встречи на следующем уроке!

 

© Kirill. [email protected]

Link to post
Share on other sites
  • 3 weeks later...
Programmer

Урок 96 - Борьба с реквотами

 

Привествую' date=' дорогие друзья!

Недавно получил вот такое сообщение от одного из читателей курса:

 

Здравствуйте Кирилл! Вы можете показать как дописать в исходный код советника блок, который в случае реквот позволяет советнику успешно открывать позиции на реальном счету?

С уважением, Андрей

 

Спасибо за отличный вопрос.

Сегодня я расскажу как именно нужно бороться с реквотами.

 

Реквот - это ошибка 138 (ERR_REQUOTE), возвращаемая советнику терминалом в случае, когда не получилось открыть ордер по указанной цене. Наличие реквотов (в разумных количествах) при торговле с типом исполнения Instant Execution абсолютно нормально, т.к. порой цена может меняться очень быстро. Изменения могут происходит с интервалами много меньшими времени, которое требуется для обработки сигнала на исполнение ордера сервером.

 

Как бороться с реквотами?

Самый очевидный и простой способ борьбы с реквотами - это выжидание. Зачастую, попытка выставления ордера попадает на всплеск активности рынка, и потому реквотится. Достаточно подождать всего несколько секунд, рынок успокоиться, и выставить ордер удастся. Ниже приведен модуль, который я использую для борьбы с реквотами:

 

//+------------------------------------------------------------------+
//|                                                  AntiRequote.mq4 |
//|                                                           Kirill |
//|                                          [email protected] |
//+------------------------------------------------------------------+
#property copyright "Kirill"
#property link      "[email protected]"


extern string     sA = "AntiRequotes parameters";
extern int        MaxAttempts = 3;  //Limit of attempts
extern int        DelaySeconds = 3; //Delay in seconds


//------------------------------------- AntiRequoteOrderSend -------------------------------------

int AntiRequoteOrderSend(string symbol, int cmd, double volume, double price, int slippage, double stoploss, double takeprofit, string comment="", int magic=0, datetime expiration = 0, color arrow_color = CLR_NONE)
{
     int ticket = 0;
     int cnt = 0;
     while(true)
     {  
        if(cnt >= MaxAttempts) {Print("Order NOT opened after ", MaxAttempts, " attempts"); break;}
        cnt ++;
        ticket = OrderSend(symbol, cmd, volume, price, slippage, stoploss, takeprofit, NULL, magic, expiration, arrow_color);
        if(ticket > 0) break; //Order was successfully opened
        Sleep(DelaySeconds*1000);   
     } 
     return(ticket);
}

 

Как Вы понимаете, я использую функцию AntiRequoteOrderSend() для подмена функции OrderSend(). Это позволяет при любом выставлении ордера совершать MaxAttempts попыток с задержкой DelaySeconds секунд между двумя последовательными попытками.

 

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

 

Надеюсь, теперь понятно, как можно бороться с реквотами в MQL4.

До встречи на следующем уроке!

 

© Kirill. [email protected]

Link to post
Share on other sites
  • 2 weeks later...
Programmer

Урок 97 - Семафоры (Часть 1)

 

Приветствую друзья!

 

Мы приближаемся к юбилейному 100-му уроку MQL4, и прежде, чем мы перейдем этот воодущевляющий рубеж, я бы хотел затронуть тему, о которой я обещал рассказать Вам уже давно. В ближайщих уроках я бы хотел поговорить об атомарных операциях и семафорах.

 

Предпосылки

 

Тема обсуждения, "атомарные операции и семафоры", звучит непонятно и сложно. Предлагаю, для начала, обратиться к задаче, которую мы будем решать. Это поможет проиллюстрировать актуальность проблемы.

 

Какие основные отличия ручной торговли от автоматической? Скорость, точность, удобство - эти и другие преимущества торговых роботов неоспоримы. Однако, как ни странно, они приносят с собой некоторые недостатки. Один из них и является центральной проблемой сегодняшнего урока - обструкция.

 

Торгую руками, мы можем совершать только одну операцию в каждый момент времени. Компьютер же, настолько быстр, что он может делать десятки миллиардов операций в секунду (0.01 терафлопс*). И это может вызывать проблемы. Рассмотрим пример.

 

post-50854-1404219056,4353_thumb.gif

Рис. 1 - Пример обструкции советников

 

Допустим, у Вас в одном терминале работают два сосетника на графиках EURUSD и AUDUSD.

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

 

В предыдущем уроке, Урок 96 - Борьба с реквотами, мы рассмотрели способ обработки ошибки 138 (ERR_REQUOTE) при выставлении ордера. Подобный способ можно использовать и для обработки ошибки 146. Однако, это обший метод обработки ошибки; используя его, мы не предотвращаем ошибку, а лишь обрабатываем ее специальным образом. Т.е. мы лечим симптом, а не проблему. При правильном программировании надо применять грамотные методы, поэтому сегодня мы рассмотрим иной подход.

 

Концепция

 

Для решения проблемы торгового потока нам необходимо ввести некий флаг, который будет говорить о состоянии торгового потока (0 - свободен, 1 - занят). При желании совершить торговую операцию советник будет проверять сосотояние флага. Если флаг равен 1, то надо ждать. Если флаг равен 0, то торговый поток свободен - советник захватывает торговый поток, выставляет флаг в значение 1, совершает свою операцию, и по ее завершении выставляет значение флага обратно на 0, сообщая остальным советникам, что торговый поток свободен.

 

post-50854-1404219056,5229_thumb.gif

Рис. 2 - Флаг сигнализирует состояние торгового потока

 

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

 

По завршении работы с торговом потоком советник EA1 освобождает его и менят цвет флага на зеленый. При следующей проверке флага советник EA2 видит, что он свободен, и успешно захватывает флаг, изменяя цвет флага на красный. Таким образом, все торговые операции благополучно совершаются, а ошибки не возникают.

 

Вклинивание

 

Однако, не все так просто. Захват флага подразумевает две опреации: 1. Проверка флага, и 2. Изменение значение флага. Поскольку, как мы выяснили, компьютер может совершать триллионы операций в секунду, может случиться так, что между операциями 1 и 2 первого советника произойдет вклинивание другого советника. Что это значит? Это значит, что EA1 проверит цвеи флага, и у видит, что флаг зеленый. Но прежде, чем EA1 приступит к операции смены цвета флага на красный, советник EA2 тоже проверит значение флага. Поскольку флаг еще не захвачен советником EA1, EA2 тоже увидит, что флаг зеленый и приступит к операции смены цвета флага. После этого флаг дважды "поменяет" свой цвет: Зеленый -> Красный, и затем Красный -> Красный. Каждый из советников будет думать, что он захватил флаг, и они оба попытаются обратиться к торговому потоку. В результате, произойдет ошибка.

 

post-50854-1404219056,6048_thumb.gif

Рис. 3 - Пример вклинивания (обведено красным)

 

Атомарные операции

 

Для решения проблемы вклинивания, впрочем, как и в иных целях, применяются Атомарные Операции.

 

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

Wikipedia

 

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

 

post-50854-1404219056,6914_thumb.gif

Рис. 4 - Атомарная операция

 

Сегодня мы рассмотрели теоретические основы того, как, используя атомарные операции и захваты флагов, мы можем решить проблему обструкции торговых советников. Уверен, что сегодня Вы узнали много нового. Атомарные операции - это уже достаточно выскокий уровень программирования и они применяются во многих прикладных задачах, не только в MQL4. Поэтому знания, приобретенные сегодня, много где еще Вам пригодятся.

 

Надеюсь, Вам понравился сегодняшний урок и мои веселые картинки :)

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

 

* Подробней про производительность процессора читайте тут: http://ru.wikipedia.org/wiki/Терафлопс

 

До встречи на следующем уроке!

 

© Kirill. [email protected]

Link to post
Share on other sites
Programmer

Урок 98 - Семафоры (Часть 2)

 

Всем привет!

 

В прошлом уроке мы познакомились с проблемой обструкции и теоретическими основами атомарных операций, позволяющих решить избежать Ошибку 146 ("Торговый поток занят").

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

 

Семафор

 

В википедии данное понятие определяется следующим образом:

 

Семафор — объект, позволяющий войти в заданный участок кода не более чем n потокам.

Wikipedia

 

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

 

post-50854-1404219090,3792_thumb.gif

Рис. 1 - Семафор

 

Итак, теперь мы можем перейти к коду:

 

//+------------------------------------------------------------------+
//|                                                    SEMAPHORE.mq4 |
//|                                                           Kirill |
//|                                          [email protected] |
//+------------------------------------------------------------------+
#property copyright "Kirill"
#property link      "[email protected]"

//------------------------------------------SEMAPHORE------------------------------------------
//-------------------------------------External Functions--------------------------------------
//данную функцию необходимо обязательно вставить внутрь функции Deinit()
//на случай, если советник завершает работу, пока он в процессе посылки запроса на семафор
void SemaphoreDeinit(string SEM) 
{
  if(critical == 1)
  {
     GlobalVariableSet(SEM, 0.0); 
  }  
}

//вместо OrderSend()
int SemOrderSend(string sem_symbol, int sem_cmd, double sem_volume, double sem_price, int sem_slippage, double sem_stoploss, double sem_takeprofit, string sem_comment="", int sem_magic=0, datetime sem_expiration=0, color sem_arrow_color=CLR_NONE) 
{
  int ret;
  SemaphoreTake("TRADECONTEXT"); 
  ret=OrderSend(sem_symbol, sem_cmd, sem_volume, sem_price, sem_slippage, sem_stoploss, sem_takeprofit, sem_comment, sem_magic, sem_expiration, sem_arrow_color);
  SemaphoreReturn("TRADECONTEXT");
  return(ret);
}

//вместо OrderModify()
int SemOrderModify(int sem_ticket, double sem_price, double sem_stoploss, double sem_takeprofit, datetime sem_expiration, color sem_arrow_color=CLR_NONE)
{
  bool ret;
  SemaphoreTake("TRADECONTEXT"); 
  ret=OrderModify(sem_ticket, sem_price, sem_stoploss, sem_takeprofit, sem_expiration, sem_arrow_color);
  SemaphoreReturn("TRADECONTEXT");
  return(ret);
}

//вместо OrderClose()
int SemOrderClose(int sem_ticket, double sem_lots, double sem_price, int sem_slippage, color sem_Color=CLR_NONE)
{
  bool ret;
  SemaphoreTake("TRADECONTEXT"); 
  ret=OrderClose(sem_ticket, sem_lots, sem_price, sem_slippage, sem_Color);
  SemaphoreReturn("TRADECONTEXT");
  return(ret);
}

//вместо OrderDelete()
int SemOrderDelete(int sem_ticket, color sem_Color=CLR_NONE)
{
  bool ret;
  SemaphoreTake("TRADECONTEXT"); 
  ret=OrderDelete(sem_ticket, sem_Color);
  SemaphoreReturn("TRADECONTEXT");
  return(ret);
}

//-----------------------------Internal Functions and Variables--------------------------------
int critical = 0;

//захват семафора
void SemaphoreTake(string SEM)
{ 
  if(GlobalVariableCheck(SEM) == false)
     GlobalVariableSet(SEM, 0);

  while(1==1)
  {
     if(GlobalVariableSetOnCondition(SEM, 1.0, 0.0)) //получили доступ
     {
        critical = 1;
        break;   //выходим из цикла ожидания доступа
     }
     else
     {
        Print("ATTEMPT TO CAPTURE SEMAPHORE \"", SEM, "\" FAILED. SEMAPHORE BUSY. WAITING 0.1 SEC. CURRENT VALUE: ", GlobalVariableGet(SEM));
        Sleep(100);
     }
  }
} 

//возврат семафора
void SemaphoreReturn(string SEM)
{
  GlobalVariableSet(SEM, 0.0);
  Print("SEMAPHORE \"", SEM, "\" RETURNED. CURRENT VALUE: ", GlobalVariableGet(SEM));
  critical = 0;
}

 

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

OrderSend() -> SemOrderSend()

OrderModify() -> SemOrderModify()

OrderClose() -> SemOrderClose()

OrderDelete() -> SemOrderDelete()

И добавить вызов функции SemaphoreDeinit() в свою функцию Deinit()

 

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

До встречи на следующем уроке!

 

© Kirill. [email protected]

Link to post
Share on other sites
Programmer

Урок 99 - Семафоры (Часть 3)

 

Приветствую, друзья!

 

Сегодня мы разберем код модуля работы с семафором, приведенный в предыдущем уроке. Но для того, чтобы понять данный код, необходимо сначала познакомиться с функцией GlobalVariableSetOnCondition(), которую мы уже рассматривали в Уроке 22 - Глобальные переменные. Я напомню:

 

GlobalVariableSetOnCondition()

 

Синтаксис:

bool GlobalVariableSetOnCondition( string name, double value, double check_value)

 

Описание:

Устанавливает новое значение существующей глобальной переменной, если текущее значение переменной равно значению третьего параметра check_value. Если переменной не существует, функция сгенерирует ошибку ERR_GLOBAL_VARIABLE_NOT_FOUND (4058) и вернет FALSE. При успешном выполнении функция возвращает TRUE, иначе FALSE. Для того, чтобы получить информацию об ошибке, необходимо вызвать функцию GetLastError(). Если текущее значение глобальной переменной отличается от check_value, функция вернет FALSE. Функция обеспечивает атомарный доступ к глобальной переменной, поэтому она может быть использована для организации семафора при взаимодействии нескольких одновременно работающих экспертов в пределах одного клиентского терминала.

 

Параметры:

name - Имя глобальной переменной.

value - Новое значение.

check_value - Значение для проверки текущего значения глобальной переменной.

Как видно из описания, именно эта функция позволяет осщуствлять в MQL4 ту атомарную операцию, о которой мы говорили в предыдущем уроке: {прочитать значение флага, если флаг зеленый, сделать его красным}. Еще сразу становится ясно, что в качестве семафора всегда используется глобальная (на уровне терминала) переменная (см. Урок 91 - Иерархии переменных Часть 5). Теперь перейдем к разбору модуля - начнем с внутренних функций.

 

int critical = 0;

//захват семафора
void SemaphoreTake(string SEM)
{
  if(GlobalVariableCheck(SEM) == false)
     GlobalVariableSet(SEM, 0);

  while(1==1)
  {
     if(GlobalVariableSetOnCondition(SEM, 1.0, 0.0)) //получили доступ
     {
        critical = 1;
        break;   //выходим из цикла ожидания доступа
     }
     else
     {
        Print("ATTEMPT TO CAPTURE SEMAPHORE \"", SEM, "\" FAILED. SEMAPHORE BUSY. WAITING 0.1 SEC. CURRENT VALUE: ", GlobalVariableGet(SEM));
        Sleep(100);
     }
  }
}

Данная функция:

1. Проверяет существование семафора под именем TRADECONTEXT (имя Вы можете выбирать на свое усмотрение - главное, чтобы одно и то же имя использовалось во всех советниках, работающих на одном терминале)

2. Если семафор не существует, она его создает и инициализирует значением 0 (свободен)

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

4. В случае неудачи, выдает Print() и ждет 0.1 секунду до повторения попытки захвата

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

 

Заметьте, что указанная функция НЕ работает с торговым потоком вовсе. Мы пока только захватили семафор - это сигнал всем остальным советникам, что "Сейчас мы будем некоторое время работать с торговм потоком. Когда мы закончим и торговый поток будет свободен, мы освободим семафор, и это будет Вам сигналом".

 

//возврат семафора
void SemaphoreReturn(string SEM)
{
  GlobalVariableSet(SEM, 0.0);
  critical = 0;
}

Данную функцию необходимо применять после завершения работы с торговым потоком. Она:

1. Возвращает семафору значение 1 (заметьте, что тут уже не нужна атомарная операция, поскольку нет риска вклинивания)

2. Выставляет глабльную (на уровне советника) переменную critical на значение 1 (зачем это нужно я объясню позже)

 

Теперь рассмотрим функции работы с торговым потоком. Они все устроены по одному образу, поэтому мы рассмотрим только SemOrderSend():

 

//вместо OrderSend()
int SemOrderSend(string sem_symbol, int sem_cmd, double sem_volume, double sem_price, int sem_slippage, double sem_stoploss, double sem_takeprofit, string sem_comment="", int sem_magic=0, datetime sem_expiration=0, color sem_arrow_color=CLR_NONE)
{
  int ret;
  SemaphoreTake("TRADECONTEXT");
  ret=OrderSend(sem_symbol, sem_cmd, sem_volume, sem_price, sem_slippage, sem_stoploss, sem_takeprofit, sem_comment, sem_magic, sem_expiration, sem_arrow_color);
  SemaphoreReturn("TRADECONTEXT");
  return(ret);
}

Как должно быть понятно из комментария, для интеграции в код, Вам необходимо везде заменить функцию OrderSend() на данную функцию.

Функция SemOrderSend() устроена очень хитро - она просто-напросто передает все свои входные значения в функцию OrderSend(). Однако, перед вызовом OrderSend() происходит зазват семвафора, а по завершении работы OrderSend() происходит возврат семафора. Вот и все!

 

//данную функцию необходимо обязательно вставить внутрь функции Deinit()
//на случай, если советник завершает работу, пока он в процессе посылки запроса на семафор
void SemaphoreDeinit(string SEM)
{
  if(critical == 1)
  {
     GlobalVariableSet(SEM, 0.0);
  }  
}

И последняя, но немаловажная функция - SemaphoreDeinit()

Это функция подчистки. Иногда, советик может быть выключен принудительно, не успев завршить некоторые операции. Было бы неприлично по отношению к другим советникам не подчистить свои изменения в глобальных переменных, т.к. они общие для всех советников в одном терминале. Это особенно важно при работе с семафорами. Представьте, что советник успешно захватил семафор, но прежде чем он закончил работу с торговым потоком, его отключили. В подобной ситуации, советник не успеет вернуть семафор, и все остальные советники будут думать, что торговый поток до сих пор занят. Сделки совершаться не будут!

 

Добавяляя вызов функции SemaphoreDeinit() в функцию Deinit(), вы предохраняете себя от подобных ситуаций. SemaphoreDeinit() принудительно высвобождает семафор при завершении работы советника. Но как же советник узнает, что семафор занят именно им, а не другим советником в данный момент времени?! Именно тут к нам на вырочку приходит переменная critical: если она имеет значение 1, то советник, действительно сейчас держит семафор и его необходми высвободить.

 

Кстати, алтернативный вариант был бы использовать Magic Number вместо переменной critical. Т.е. в семафор бы записывался Magic Number, а не 1, если семафор занят. Таким образом, глядя на значение семафора можно было бы узнать, кем он захвачен / не захвачен (в случае нулевого значения) в данный момент времени.

 

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

 

post-50854-1404219127,7024_thumb.gif

Рис. 1 - Менеджер глобальных переменных

 

Я разработал приведенный модуль так, чтобы еще больше снизить возможность возникновения подобной ситуации. А именно - как Вы можете заметить семафор захватывается исключительно на время работы с торговым потоком. Между строками SemaphoreTake("TRADECONTEXT"); и SemaphoreReturn("TRADECONTEXT"); всегда только одна строчка. Это сделано для того, чтобы минимизировать время на которое захватывается семафор каждый раз.

 

Если Вы будете переписывать этот модуль под себя, я советую следовать этому же правилу - минимизация периода захвата семафора. Классический пример - market execution. Всем известно, что в случае рыночного исполнения, нельзя указать стоплосс и тейкпрофит при выставлении оредра - их необходимо выставлять отдельно. В связи с этим существует соблазн устроить алгоритм совтеника таким образом, чтобы он захватывал семафор перед выставлением ордера, а высвобождал его только после выставления стоп-лосса и тейк-профита. Это неверный подход! Правильно в случае market execution делать так:

 

- Захватить семафор

- Выставить ордер

- Освободить семафор

- Возможно, некоторый код

- Захватить семафор

- Выставить стоп-лосс и тейк-профит

- Освободить семафор

 

Надеюсь, что касательно атомарных операция и семафоров все понятно, и теперь Вам не страшна Ошибка 146 ("Торговый поток занят").

Я рад, что мы разобрались с этой темой. Если остались вопросы пишите - я обязательно отвечу.

 

До встречи на следующем уроке!

 

© Kirill. [email protected]

Link to post
Share on other sites
  • 1 month later...
Programmer

Урок 100 - Эстетика, стиль и вредные привычки программирования

 

Приветствую, друзья!

Поздравляю всех с юбилейным, сотым уроком MQL4 !!

 

post-50854-1404219475,4992_thumb.jpg

 

Мне очень радостно, что мы наконец-то преодолели этот рубеж, и на сегодням уроке я решли поговорить о немного отвлеченных от самого программирования, но все же очень важных понятиях... Сегодня речь пойдет об эстетике, стиле и вредных привычках в программировании.

 

Начнем с примера. Я уже несколько раз упомянал в нашем курсе OCCC - The International Obfuscated C Code Contest. Это конкурс по самому непонятному коду на C. Например, как Вы думаете, что делает вот этот код?

 

#define			      q [v+a]
#define			     c b[1]
#define			    O 1 q
#define			   o 0 q
#define			  r(v,a\
)v<0&&(			 v*=-1,		a*=-1);
#define			p(v,m,	    s,w)*c==*#v?2 q\
<m?(c++		       ,d=1,3	   q=0,5      q=m,main\
(a+3,		      ,o=o*s	 q,O=O*		 w q)
static		     d,v[99	];main		  (int a,
char**b		    ){d=7;     if(*c?!		  (p(+,3
,4 q+O*		   3,4)p(			   -,(o?3
:(O=1,6		  )),4 q			  -O*3,4)
p(*,4,3		 ,4)p(/				  ,5,4,3)
p((),d,		0+3,0+				 04)*c==
')'?2 q	       <02?(c				++,0):0
:(o=012	      *o+*c-			      '0',c++
,O=1)):	     2 q?3-			   2:printf(
"%d/%d"	    "\n",o		       ,O))return
1;d=a,r    (o,d)r		     (O,d)3 q
=o<O?(4	  q=o,O)		   :(4 q=O,
 o);r(d,		 o)a+=3;O?
			 1:(O=1,2
			q=1);while
			(2 q=o%1 q)a++;v[d]/=O;d[
			v+1]/=O;return main(d,;}

Да-да! Это, действительно, код работающей программы. Более того, это код дробного калькулятора под названием "hamre"! Если этот код C откомпилировать и запустить со следующими параметрами:

 

 

./hamre '-1+4/3*(2+1/(3/2*(7/2-7/3+1/6)))/2'

То на выходе Вы получите ответ: 2/3

 

Удивлены? На сайте конкурса http://www.ioccc.org/ подобных примеров очень и очень много.

 

Конечно, это примеры, доведенные до абсурда - никто в реальной жизни так не программирует. Однако, подбные проекты призваны показать сообществу программистов, что один и тот же корректно составленный код можно написать стильно, а можно написать так, что никто никогда его не поймет. Что приводит нас к следующей часте сегодняшнего урока...

 

Эстетика

 

Википедия определяет это понятие как "философское учение о сущности и формах прекрасного".

 

Программировать эстетично вовсе не значит, что необходимо вкладывать свою душу в каждый блок кода, и смотреть на свои программы, как на произведения искусства. Иногда - да, так бывает; однако в большинстве случаев программирование достаточно строгая и четкая наука... этим нас это занятие и привлекает. Разве не так? Тем не менее, что же означает эстетичное программирование?

 

Эстетичное программирование означает, что при создании программ (скриптов / индикаторов / советников) Вы смотрите не только на выдаваемый результат, видимый конечным пользователем этих программ, но Вы также смотрите на сам код программы. Т.е. Вы думаете о том следующем программисте, кто будет в будущем читать Ваш код. Вот и все! Все настолько просто - пишите код так, чтобы тот, кто будет его читать мог легко его понять. Ведь, кто знает? Быть может, это будете Вы, только два года спустя. Кто из Вас, кто уже пробовал разбирать свой собственный код двухлетней давности, да даже трехмесячной давности, сталкивался с трудностями? Если Вы находили себя в подобной ситуации, значит Вам необходимо поработать над эстетикой своего кода.

 

Примеры как можно улучшить эстетику программирования:

- оставляйте комментарии

- делайте отступы

- используйте подходящие контсрукции

- указывайте возвращаемые значения

- выносите частоиспользуемый код в функции

- придерживайтесь одного и того же стиля

- и пр.

 

Стиль

 

Стиль просто-напросто означает, что у Вы лично предпочитаете некий способ построения / оформления кода. Например, сравните вот эти два блока кода:

 

Код #1

int AnalyseSignal(double FastMA, double SlowMA)
{
  if(FastMA > SlowMA)
  {
     Print("Going UP"); 
     return(1);
  }
  else if (FastMA < SlowMA)
  {
     Print("Going DOWN"); 
     return(-1);
  }
  else
  {
     Print("Neutral"); 
     return(0);
  }
}

 

 

Код #2

int AnalyseSignal(double FastMA, double SlowMA){
  if(FastMA > SlowMA){
     Print("Going UP"); return(1);
  }
  else if (FastMA < SlowMA){
     Print("Going DOWN"); return(-1);
  }
  Print("Neutral"); return(0);
}

 

 

Два приведенных блока, на первый взгляд, смотрятся совершенно различно. Однако, это один и тот же код, стилизованный по разному! Какой стиль больше похож на Ваш?

 

Например, я могу точно сказать, что 99% моих программ использует стиль схожий с Кодом #1. Мне нравится просторный код, где все парные фигурные скобки на одном уровне и тд. Кому-то может нравиться более компактный второй стиль - и в этом ничего страшного нет! Главное, понимать каков именно Ваш стиль, и придерживаться к нему. Тогда Вы упростите себе как написание новых программ, так и чтение своих предыдущих программ, т.к. они все будут написаны в одном стиле.

 

Вредные привычки программирования

 

Все мы обращаем внимание на новые приемы и стили, которые мы встречаем в чужом коде. Особенно на те аспекты кода, которые усложняют его понимание. Попробуем перечислить наиболее часто встречающиеся "вредные привычки".

 

Неинтуитивные имена переменных

 

//использовать MM или нет
bool MM = true; //плохая привычка
bool UseMM = true; //хорошая привычка

 

 

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

 

 

В указанном примере переменная типа bool обзывается именем "MM". Если код содержит 1000 строк, то на 743-й строке никто уже не вспомнит, что означает эта переменная. В случае "UseMM" хотя бы сразу понятно, что эта переменная имеет тип bool.

 

Обилие неиспользуемого кода

 

void AnalyzeSignals(int env, int eic){
  static int ticket = 0;
  int sig = -1;
  bool res;

  if(!allow_multi_trades)
     ticket = RefreshTicket(ticket);

  if(env == OP_BUY && eic == OP_BUY)
     sig = OP_BUY;
  else if(env == OP_SELL && eic == OP_SELL)
     sig = OP_SELL;   


/* //Закрытие при перевороте
  if(ticket != 0)
  {
     OrderSelect(ticket, SELECT_BY_TICKET);      
     int type = OrderType();

     if(sig == OP_BUY && type == OP_SELL)
     {
        res = OrderClose(ticket, OrderLots(), Ask, slip);
        if(!res) {Alert("OrderClose Error: ", GetLastError());}
        else ticket = 0;
     }

     else if(sig == OP_SELL && type == OP_BUY)
     {
        res = OrderClose(ticket, OrderLots(), Bid, slip);
        if(!res) {Alert("OrderClose Error: ", GetLastError());}
        else ticket = 0;
     } 
  }   
*/   

  //Закрытие при перевороте
  if(ticket == 0 || allow_multi_trades == true)
  {   
     ...
  }

 

Если Вы переписали блок кода (как блок "закрытие при перевороте" в примере), или просто решили избавиться от него - сохраните файл под новой версией (чтобы в старой этот блок остался на всякий случай) и удалите ненужный код. Сотни закомментированных строк не упрощают понимание кода.

 

Обобщение алгоритмов

 

Я сам часто допускаю эту ошибку. Вместо того, чтобы писать код, который решает конкретную поставленную задачу, бывает, что я составляю алгоритм таким образом, чтобы он обрабатывал множество различных возможных ситуаций. Из-за этого код разрастается, время потраченную на разработку возрастает экспоненциально. Программирование носит прикладной характер, особенно на FOREX. Это не теоретическая физика, чрезмерно обобщать не нужно.

 

Недостаточное использование функций

 

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

 

 

int start() //хорошая привычка
{
  if(IsNewBar())
  {
     int TX = GetSignal_TX();
     AnalyzeSignals(TX);
  }
  if(UseTrail == true) TrailAllOrders();
  return(0);
}

 

 

Как видите, в данном примере функция start() занимает всего-навсего 5 строк (не считая скобок). Благодаря данному подходу сразу видно, как работает программа:

1. Сначала добывается сигнал, этот код находится в функции GetSignal_TX().

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

3. Первые два шага производятся только при появлении нового бара. Т.е. эта часть советника работает в побаровом режиме. Данный вывод делается на основе названия функции IsNewBar().

4. TrailAllOrders() скорее всего производит перенос стоп-лосса всех ордеров в соответствии с некими правилами. Это предположение поддерживается тем фактом, что указанная функция вынесена за пределы побарового блока и исполняется на каждом тике (как треилинг-стоп и должен).

 

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

 

© Kirill. [email protected]

Edited by Programmer
Link to post
Share on other sites
  • 2 weeks later...
Programmer

Урок 101 - Вирус MQL4

 

Всем привет!

 

Сегодня у нас будет веселый урок программирования. Мы напишем и разберем "вирус" MQL4!

 

Зачем мы это делаем?

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

 

Откройте редактор MetaEditor и создайте новый скрипт. Скопируйте в него следующий код и откомпилируйте:

 

//+------------------------------------------------------------------+
//|                                          WinaApi TEST SCRIPT.mq4 |
//|                                                           Kirill |
//|                                          [email protected] |
//+------------------------------------------------------------------+
#property copyright "Kirill"
#property link      "[email protected]"

#import "user32.dll"
  void     keybd_event(int bVk,int bScan,int dwFlags,int dwExtraInfo);
#import

#define VK_ALT             18    //ALT key
#define VK_F4              115   //F4 key

int start()
{
   keybd_event (VK_ALT, 0, 0, 0);
   keybd_event (VK_F4, 0, 0, 0);
}

 

 

Как видно, этот несложный скрипт зажимает клавиши ALT+F4 и, тем самым, закрывает терминал. Скрипт использует библиотеку WinAPI, чтобы получить доступ к Вашей клавиатуре. Своеобразнй MQL4-вирус :)

 

Осторожно, если будете тестировать! Даже после того, как терминал закрыт, клавиши остаются зажатыми, и чтобы продолжить нормальную работу на компьютере, необходимо отжать клавиши вручную. Это хороший пример того, как WinApi код может оказаться вредоносным далеко за пределами терминала MT4! :acute:

 

Данный урок хорошо иллюстрирует, что в "умелых" руках программирование даже на MQL4 может быть вредоносным. Поэтому остерегайтесь кода из непроверенных источников; особенно, если советник включает в себя DLL, в которую можно запрятать все что угодно.

До встречи на следующем уроке!

 

© Kirill.

[email protected]

Link to post
Share on other sites
  • 1 month later...
Programmer

Урок 102 - CTRL+H (Часть 1)

 

Всем привет.

Хотел затронуть тему редактирования кода MQL4 с помощью встроенного функционала замены текста CTRL+H.

 

Всем знакомы сочетания клавиш CTRL+F и CTRL+H. Однако, не все знают, что применение CTRL+H может привести к нежелаемым изменениям в коде.

 

Простой пример: допустим, Вы хотите изменить переменную as_0 на SymbolString во всем коде. Вы нажимаете CTRL+H, вводите нужные значения, и жмете "Заменить Все", см. изображение ниже



post-50854-1404219784,3851_thumb.jpg

 

Замена прошла успешно, Вы довольны. Но теперь представьте, что в коде есть серия переменных с именами:

  • mae_0 - Moving Average (Exponential), значение на 0-м баре
  • mae_1 - Moving Average (Exponential), значение на 1-м баре
  • mas_0 - Moving Average (Simple), значение на 0-м баре
  • mas_1 - Moving Average (Simple), значение на 1-м баре

 

При нажатии CTRL+H + заменить все, вы замените не только все случаи переменной as_0, а вообще все случае, когда встречается эти три символа as_0 в коде. В результате замены, переменная mas_0 пострадает. Она превратиться в mSymbolString. Конечно, пример немного притянут зауши, но Вы удивитесь насколько часто встречаются подобные ситуации в реалии.

 

Во избежании подобных ситуаций, я рекомендую вместо кнопки "Заменить Все" использовать кнопка "Замена". При таком подходе редактор сначала находит и подсвечивает требуемое выражение, позволяя Вам сначала проверить правильность предстоящей замены. Данный метод занимает больше времени, но, поверьте, он надежней.

 

Надеюсь, этот совет предостережет Вас от ненужных ошибок.

 

© Kirill. [email protected]

Edited by Programmer
Link to post
Share on other sites
Programmer

Урок 103 - CTRL+H (Часть 2)

 

Всем привет.

Коротенький урок, чтобы добить тему CTRL+H.

 

А что будет, если поставить галочки "Только слово целиком" и "Учитывать регистр" как на рисунке ниже? Тогда можно использовать функцию "Заменить Все"?

 

post-50854-1404219801,5438_thumb.jpg

 

Ответ на этот вопрос достаточно прост.

Да, эти параметры уточняют поиск, и поэтому замена становится намного надежней. Тем не менее, все равно существуют ситуации, когда подобное использование CTRL+H приведет к нежелательным заменам. Пример:

 

У Вас есть хороший советник, который используете Вы и Ваши знакомые. В этот советник Вы решили добавить новый модуль принятия решений. У этого модуля будут свои TakeProfit и StopLoss. В связи с этим, Вам необходимо пройтись по коду везде заменить переменные SL и TP, которые Вы использовали раньше, на их новые имена: StopLoss_Module1 и TakeProfit_Module1соответственно. Допустим, что Ваш советник также использует комментарии при выставлении оредров. Комментарии используются для упрощения слежения за действиями советника, и выглядят следующим образом:

 

SL at 40pips TP at 70pips

 

При замене через Ctrl+H + "Заменить Все", код советника измениться так, что теперь комментарий будет выглядеть так:

 

StopLoss_Module1 at 40pipsTakeProfit_Module1at 70pips

 

Но это еще не все. Поскольку в МQL4 ограничение на длину строки комментария 31 символ, то на самом деле итоговый комментарий будет выглядеть вот так:

 

StopLoss_Module1 at 40pipsTake

 

Таким образом, теряется не только эстетика комментария, но и значение TP. Похожие рассуждения можно провести для функции Comment() и меток Label.

 

Данные ошибки не критичны. Тем не менее, я предпочитаю использовать пошаговый CTRL+H для полного контроля того, что заменяется редактором. Лучше потратить лишних 5 минут при замене переменных, чем 20 минут на поиск и исправление неверных замен. К тому же, при пошаговой замене Вы лишний раз пройдетесь по коду, и сможете его проверить на предмет ошибок.

 

До встречи на следующем уроке!

 

© Kirill. [email protected]

Edited by Programmer
Link to post
Share on other sites
  • 1 month later...
Programmer

Урок 104 - Оптимизация и тиковый объем

 

Всем привет.

 

Недавно я оптимизировал одного советника, на скачанных котировках, которые, как я думал, были достоверны. Результаты неплохие. Для полноты картины, решил прооптимизировать еще на котировках из другого источника. И что бы Вы думали? Результаты примерно одинаковы +/- как это обычно бывает. Но вот, что меня удивило, так это скорость исполнения одного теста... На одном и том же промежутке времени он отличалась в разы!

 

post-50854-1404220025,2569_thumb.jpg

 

Последовавшее расследование показало, что две базы котировок имеют существенно различные тиковые обхемы. Если в первом случае он порядка 5~12, то во втором он 30~80. Обратите внимание на изображение ниже. Я проконсультировался со знакомыми, и мне объяснили, что некоторые поставшики ликвидности ограничивают объем своих торговых потоков, чтобы тем самым "сжать" котировки. Зачем это делается, и как это влияет на работу советников - мне пока непонятно, но факт остается фактом.

 

post-50854-1404220025,4067_thumb.jpg

 

Я сверился еще с 3-4 другими источниками котировок. Правильное значение тикового объема на минутных свечах колеблется вблизи значений 30~80, и никак не 5~12.

 

Но все еще не ясно - почему же время тестирования одного и того же советника так сильно отличается? Проверил другие советники: MACD и MA Sample - они тестируются почти одинаково быстро на обоих базах котировок. Разница минимальна. А мой советник сильно зависит от котировок..... т.е. от тикового объема...... что это может значить?

 

Взглянул еще раз в код советникв, и понял, что я зря там оставил все "красивые" комментарии и объекты. Все эти визуальные штучки, которые обновляются на каждом тике занимают очень много процессорного времени. И даже при большой тактовой частоте, задержки значительны, и они умножаются кратно тиковому объему.

 

Из этого опыта я сделал для себя два вывода:

 

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

 

2. Надо удалять комментарии Comment() и объекты Objects (если они не влияют на работу советника) перед оптимизацией советников :D

 

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

 

До встречи на следующем уроке!

 

© Kirill. [email protected]

  • Thanks 1
Link to post
Share on other sites
  • 3 weeks later...
Programmer

Урок 105 - Нормализация цен

 

Приветствую друзья!

 

Кто из нас сталикивался с ошибкой "ERR_INVALID_PRICE 129 Неправильная цена" или "ERR_INVALID_PRICE_PARAM 4107 Неправильный параметр цены для торговой функции" ?

 

Уверен, что почти каждый из нас видели хотя бы одну из этих ошибок. Первая появляется при реальной/демо торговле советника, вторая - только в тестере стратегий.

 

Что же эти ошибки означают? Из названий видно, что ошибки указывают на неверные цены. Например, если торговый поток характеризуется 4х-значными котировками, а советник пытается выставить ордер по 5ти-значной цене, то в ответ он получит одну из указанных ошибок.

 

Какие могут быть причины ненормализованных цен?

1. Разрядность котировок (см. пример выше)

2. Цена является результатом математического расчета. Например, если пятизначную котировку 1.56789 разделить пополам, получим*0.783945 - шесть знаков.

3. Ошибка хранения данных. Очень часто цены Ask и Bid хранят цену с большей точностью, чем сами котировки. Это не значит, что цена точнее, просто на конце могут быть лишние знаки - скорее всего, нули. Например: 1.56789 может храниться в переменной Ask как 1.56789000. Почему это происходит - неясно.

 

Самое интересное, что данную ошибку отследить крайне сложно, поскольку сигнализация цен через Alert() и Print() по умолчанию округляется до 4-го знака. Например, если Bid = 1.43448, то Alert(Bid) выдаст 1.4345. То же самое выдаст и Print(). Единственный способ отследить истинное значение Bid (или любой другой переменной типа double) - это использовать функцию DoubleToStr() с указанием точности рассматриваемой переменной. Например, обратите внимание на следующий код и его результат:

 

Alert("Bid = ", Bid);
Alert("DoubleToStr(Bid) = ", DoubleToStr(Bid,Digits));

 

post-50854-1404220140,6039_thumb.jpg

Рис. 1 - Округление в Alert()

 

Мы немного отвлеклись - вернемся к проблеме. Поскольку полагаться на Ask и Bid нельзя, то следующий код может выдать ошибку и ордер не будет выставлен:

 

OrderSend(Symbol(),OP_BUY,1,Ask,3,Bid-25*Point,Bid+25*Point,"My order #"+counter,16384,0,Green);

 

В таком случае, как же правильно написать этот код? Правильно будет вот так:

 

OrderSend(Symbol(),OP_BUY,1,NormalizeDouble(Ask,Digits),3,NormalizeDouble(Bid-25*Point,Digits),NormalizeDouble(Bid+25*Point,Digits),"My order",77378,0,Green);

 

Как видите, код станосится более громоздким и менее читабельным. Как это исправить? Давайте вспомним, что мы говорили в Уроке 100 "Эстетика и Стиль программирования" о Недостаточном использование функций - программы можно упрощать за счет выноса посторяющихся блоков кода в отдельные функции. Введем функци ND() следующим образом:

 

int start()
{
  ...

  OrderSend(Symbol(),OP_BUY,1,ND(Ask),3,ND(Bid-25*Point),ND(Bid+25*Point),"My order",77378,0,Green);

  ...
}

double ND(double val)
{
  return(NormalizeDouble(val, Digits));
}

Как видно, в итоге, мы сократили не только имя функции нормализации, но еще и избавились от повторяющейся переменной Digits. Код стал более читабельным. Советую копировать функцию ND() во все советники и всегда использовать ее в торговых функциях OrderSend(), OrderModify() и OrderClose(). Подобная практика убережет от опасных ошибок и дорогих последствий.

 

Спешу заметить, что правильно НЕ ND(Bid)-25*Point, а именно ND(Bid-25*Point). Т.е. сначала производятся все вычисления, и только затем мы нормализуем результат.

 

Надеюсь, Вы нашли данный материал полезным.

До встречи на следующем уроке!

 

© Kirill. [email protected]

Link to post
Share on other sites
Programmer

Урок 106 - Симметрия и ассиметрия валютной пары FOREX (Часть 1)

 

Приветсвтую друзья,

 

В ближайщих уроках мы поговрим о симметрии на рынке FOREX и симметрии механических торговых систем. Под симметрией мы будем подразумевать движения вверх (BUY) и вниз (SELL) одной валютной пары.

 

 

post-50854-1404220151,53_thumb.jpg

Рис. 1 - Симметрия и асимметрия глазами художника

 

 

Симметричен ли рынок FOREX ?

 

Уверен, что многие из Вас наслышаны об удивительной "зеркальности" валютного рынка. Действительно, в своей статье "Foreign Exchange Symmetries" Uwe Wystup, генеральный директор немецкой компании MathFinance, рассуждает на тему симметрии рынка FOREX. Wystup говорит, что рынок валют обладает уникальным свойством - для любой котировки любой валютной пары есть зеркально противоположная ей котировка. Например, если пара EUR/USD котируется по цене 1.2500 USD за EUR, то пара USD/EUR котируется в точности по цене 1/X = 0.8000 EUR за USD.

 

Данное свойство валютных пар является по истине уникальной среди инструментов финансовых рынков. Например, акции и облигации не обладают подобной симметрией. Дальнейшее исследование зеркальности котирования валют приводит к очень интересным заключениям, особенно в области теории опционов на валюты: значительно упрощаются модель Блэка — Шоулза, паритет пут-колл, и пр. Но сегодня мы не будем вдаваться в теорию.

 

Что происходит на практике ?

 

Нас интересует что происходит на практике - есть ли симметрия в движениях вверх и вниз каждой отдельно взятой валютной пары? Или же эти движения ассиметричны? И как нам приспособить наши советники под ту или иную ситуацию?

 

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

 

 

post-50854-1404220151,5747_thumb.jpg

Рис. 2 - сравнение графиков FOREX и NYSE

 

 

Верхний график - EURUSD, нижний - IBM. Оба графика взяты за период 2009-2013. Заметны различия? График IBM плавно растет, а когда происходят движения вниз - они резкие и обрывчатые. Плавных трендова вниз нет. График EURUSD как растет, так и падает - движения бывают в обе стороны, причем примерно одинакого характера. Это следует из того, что котировку EURUSD можно записать в виде USDEUR, что просто-напросто перевернет график и при это он должен более-менее сохранить все свои свойства. Ведь нет разницы - торговать EURUSD или USDEUR.

 

Все еще не убеждены? Давайте перевернем оба графика и посмотрим, что произойдет!

 

 

post-50854-1404220151,7053_thumb.jpg

Рис. 3 - перевернутые графики

 

 

Теперь видно? Перевернутый график EURUSD сохранил свои свойства - есть плавные движения вниз и вверх, новый график вполне можно принять за настоящий - потому что он и есть настоящий! Это график пары USDEUR. А вот график IBM кардинально изменился - теперь мы видим только плавный спад и только изредко подскоки вверх.

 

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

 

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

 

© Kirill. [email protected]

Link to post
Share on other sites
Programmer

Урок 107 - Симметрия и ассиметрия валютной пары FOREX (Часть 2)

 

Всем привет,

 

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

 

post-50854-1404220165,7599_thumb.jpg

Рис. 1 - Симметрия и Асимметрия

 

Итак, валютные пары симметричны в том смысле, что их движения вверх и вниз характеризуются одними и теми же свойствами. Однако, не стоит путать - свойства могут быть одними и теми же, но их проявленность в отдельные моменты времени может значительно отличатся. Что это значит? Рассмотрим нижерасположенный график.

 

post-50854-1404220165,8115_thumb.jpg

Рис. 2 - Доминирование тех или иных свойств

 

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

 

Этот пример иллюстрирует, BUY-SELL симметричность валютных пар не является постоянным свойством, проявляющимся в любой момент времени. Скорее, это более общее свойство, характеризующее поведение валют в целом, а достигается оно за счет чередования взаимно-симметричных участков движения цены.

 

Что же это значит для Механических Торговых Систем?

 

Тут вывод очень необычный. Да, рынок FOREX в целом симметричен, но поскольку симметрия достигается за счет чередования движений вверх и вниз, то торговые советники должны быть асимметричны!*Более точно - торговые советники не обязаны быть симметричны - они могут быть как симметричны, так и ассиметричны. Однако поскольку множество вариаций алгоритмов и настроек очень велико, то наиболее вероятно, что любой советник будет более прибыльным, в его ассиметричной вариации. Поэтому проще говорить, что торговые советники должны быть ассиметричны.

 

Что это значит не более простом языке? Это значит, что когда Вы разрабатываете торговую систему или оптимизируете советник, то он вовсе не должен быть одинаковым для Buy и Sell. Например, RSI колеблется от 0 до 100 единиц. Если в Вашей торговой системе сигнал Buy достигается при пересечении RSI уровня 30, то сигнал на Sell вовсе не обязан быть RSI = 70 (100-30), сигнал на Sell может достигаться при RSI = 80 или даже RSI = 90. Суть в том, что параметры для Buy и Sell должны быть совершенно независимы.

 

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

До встречи на следующем уроке!

 

© Kirill. [email protected]

Link to post
Share on other sites
  • 2 weeks later...
Programmer

Урок 108 - Симметрия и ассиметрия валютной пары FOREX (Часть 3)

 

Приветствую друзья,

 

В предыдущем уроке мы развеяли миф о необходимой симметрии советников на рынке форекс. Сегодня мы постараемся более подробно понять почему советники должны быть асимметричны.

 

post-50854-1404220214,205_thumb.gif

Рис. 1 - Пазлы

На рисунке выше изображены пазлы. Сегодня мы представим, что работа на рынке форекс - это разгадывание загадки, или сборка мозайки из пазлов.

 

Конечно, FOREX отличается от мозайки. Хотя бы тем, что мозайку можно собрать только одним образом и никак иначе, а на FOREX можно торговать различными методами и стратегиями, и все равно получать прибыль: в одних случая больше, в других - меньше.

 

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

 

"Причем здесь пазлы?" спросите Вы. Для наглядности допустим, что на оптимизируемом интервале времени у советника существует только 3 набора прибыльных параметров для BUY и 2 - для SELL. Тогда соответствующую "мозайку" можно представить в следующем виде:

 

post-50854-1404220214,2607_thumb.jpg

Рис. 2 - Мозайка

 

Как видите, в этой мозайке (вверху рисунка) недостает один пазл. У отверстия в мозайке три звена вверху и два внизу - это соответствует нашим 3м удачным настройкам BUY и 2м SELL. Как мы только что установили, FOREX отличается от мозайки, т.к. на FOREX может быть много разных правильных ответов. Здесь это отображено тем, что всевозможные мозайки, которые трейдер может поставить в отверстие (внизу рисунка), всегда имеют только одно звено сверху и одно снизу. Это просто означает, что любой советник может иметь только один набор параметров для BUY и один для SELL, что логично.

 

Итак, как мы видим только пазл №2 является симметричным. Он подходит в отверстие, и это значит, что он принесет прибыль как при BUY, так и при SELL. Все трейдеры, которые зададут своему советнику симметричные настройки BUY и SELL (например уровень RSI 70 для BUY и уровень RSI 100-70=30 для SELL) - все в лучшем случае найдут именно этот сет прибыльных настроек (этот пазл). Это верно, потому что других симметричных настроек (решений) нет.

 

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

 

post-50854-1404220214,3293_thumb.jpg

Рис. 3 - Самый прибыльный пазл

 

В нашем примере, самым симметричным оказался пазл №4. Асимметричных сетов намного больше, чем симметричных, и поэтому вероятность того, что самым прибыльным будет симметричный сет, очень мала. Таким образом, трейдеры, которые ищут только симметричные настройки, заведомо ограничивают результаты оптимизации своих советников. Более того, они скорее всего никогда не раскроют истинный потенциал своих торговых стратегий.

 

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

 

До встречи на следующем уроке!

 

© Kirill. [email protected]

Link to post
Share on other sites
  • 1 month later...
Programmer

Урок 109 - Симметрия и ассиметрия валютной пары FOREX (Часть 4)

 

Приветствую друзья,

 

Как я и обещал, сегодня мы обратим внимание на различные методы оптимизации ассиметричных советников.

Как известно, тестер стратегий терминала Метатрейдер 4 одновременно может использовать только одно ядро процессора. Поэтому, даже если в Вашем распоряжении есть 8-ми ядерная машина, оптимизация все равно будет проходить только на одном из этих ядер. Но об этом мы поговорим позже в Курсе MQL4. Сейчас для нас главное - понять, что оптимизацию невозможно бесконечно ускорять за счет улучшения оборуования. Подобному ускорению есть предел. Поэтому необходимо обратить внимание на ускорение оптимизации за счет улучшения алгоритмов.

 

Рассмотрим следующий пример. Допутсим, у нас есть советник, в котором нам необходимо оптимизировать два параметра: A и B.

Пусть параметр A может принимать два различных значения: {a1, a2} - для сделок типа BUY и два: {-a1, -a2} - для сделок типа SELL.

Аналогично, пусть параметр B может принимать значения: {b1,b2,b3} - для сделок типа BUY и значения: {-b1,-b2,-b3} - для cделок типа SELL.

 

Итак:

A_buy: {a1,a2}

A_sell: {-a1,-a2}

B_buy: {b1,b2,b3}

B_sell: {-b1,-b2,-b3}

 

Вариант 1

В случае, если советник устроен симметричным образом, тогда в коде можно прописать A_sell = -A_buy и B_sell = -B_buy. В таком случае, для плоной оптимизации потребуется 2x3 = 6 проходов тестера стратегий.

 

Вариант 2

Однако, как мы уже установили в предыдущих трех уроках, ассиметричные совтеники имеют больше шансов на успех. Поэтому, параметры A_buy и A_sell необходимо оптимизировать раздельно, ровно как и параметры B_buy и B_sell. В данном случае количество проходов резко возрастает: 2x2x3x3 = (2x3)^2 = 36 проходов тестера стратегий.

 

А если у нас не два параметра, а, скажем, четыре. И не 2-3 значения у каждого, а хотя бы 10? Тогда расчет выглядит следущим образом:

Вариант 1: 10x10x10x10 = 10^4 проходов тестера ~ 2.7 часов (если один проход занимает 1 секунду)

Вариант 2: (10x10x10x10)^2 = 10^8 проходов тестера ~ 3.2 года

 

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

 

Конечно, на помощь нам может прийти генетический алгоритм, который в обоих случаях сделает только 10,000 проходов. Однако, во втором случае, качество тестирования все равно будет заметно хуже - поскольку ген. алгоритму необходимо выбрать 10,000 проходов из 100,000,000 потенциальных проходов, вероятность пропустить прибыльные сеты очень и очень велика. Даже для генетического алгоритма, вероятность найти наилучший сет в такой большой выборке всего лишь в несколько раз больше вероятности того, что Вы случайным образом выберете зеленую звездочку на следующей картинке:

 

post-50854-1404220371,4795_thumb.gif

Рис. 1 - Иллюстрация вероятностей

 

Предлагаю Вам немного подумать над изложенным сегодня материалом, а в следующий раз мы обсудим весьма элегантное решение проблемы.

 

До встречи на следующем уроке!

 

© Kirill. [email protected]

Link to post
Share on other sites
Guest
This topic is now closed to further replies.
  • Recently Browsing   0 members

    No registered users viewing this page.


×
×
  • Create New...