Перейти к содержимому

Theme© by Fisana
 

XTrade

Актуальное

Спроси у профи

Заказ советников и роботов

Опытные программисты реализуют ваши идеи в сжатые сроки и по приятной цене, от 10$. Отзывы и подробности

Также на форуме есть тема "Бесплатное написание скриптов", но заказы выполняются редко.

Обучение трейдингу

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



Информер

<a href="http://www.mt5.com/ru/">Форекс портал</a>


Фотография
* * * * * 4 Голосов

Азы программирования на MQL4


  • Авторизуйтесь для ответа в теме
Сообщений в теме: 45

Опрос: Азы программирования на MQL4

Мне эта тема интересна

Вы не можете видеть результаты проса пока не проголосуете.
Голосовать Гости не могут голосовать

#31 OFFLINE   valenok2003

valenok2003

    Пользователи

  • Заблокированные
  • PipPipPipPipPipPipPipPipPipPipPip
  • 606 сообщений
  • Баланс: 0$
  • Пол:Мужчина
  • Город:Россия
  • Интересы:Сергей

Отправлено 17 Декабрь 2010 - 07:56

Операции отношения позволяют производить сравнение некоторых величин. С их помощью можно сравнивать любые величины, начиная от булевых и заканчивая строковыми. Если условие заданное в операции отношения выполнено, то выражение принимает значение true(истина), в противном случае значение выражения - false (ложь). Приведу примеры:


string Txt = "abc";

if(Txt == "abc")  Alert("Строка равна abc");

else   Alert("Строка не равна abc");



string Txt_1 = "abc";

string Txt_2 = "123";

if(Txt_1 != Txt_2)  Alert("Строки не равны");

else   Alert("Строки равны");



double MA_Fast = (здесь вычисляется значение MA_Fast) ;

  double MA_Slow = (здесь вычисляется значение MA_Slow) ;

 if(MA_Fast > MA_Slow)  Alert("Движение вверх");

else   Alert("Движение вниз");



 
 

#32 OFFLINE   valenok2003

valenok2003

    Пользователи

  • Заблокированные
  • PipPipPipPipPipPipPipPipPipPipPip
  • 606 сообщений
  • Баланс: 0$
  • Пол:Мужчина
  • Город:Россия
  • Интересы:Сергей

Отправлено 17 Декабрь 2010 - 09:52

Логические операции позволяют производить анализ нескольких логических отношений.

Например:


string Txt_1 = "abc";

string Txt_2 = "123";

if(Txt_1 == "abc" && Txt_2 == "123")  alert("Все строки равны");

if(Txt_1 == "abc" || Txt_2 == "123")  alert("хотя бы одна строка, да равна");

else alert("ни одна строка не равна"); //оператор имеет отношение к ближайшему оператору if.


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

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

Логических отношений связанных логическими операциями внутри скобок оператора if может быть любое количество.
  • Leonix это нравится

#33 OFFLINE   justas

justas

    Первый выстрел

  • Пользователи
  • PipPip
  • 1 сообщений
  • Баланс: 0$

Отправлено 26 Январь 2011 - 22:11

Здравствуй, Сергей!

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

Еще один момент, возможно не в тему: например
я торгую пару EUR/USD, за доллары купил евро.
Как можно вывести евро, чтобы использовать
их в другой сделке?

Заранее спасибо, удачи!

#34 OFFLINE   valenok2003

valenok2003

    Пользователи

  • Заблокированные
  • PipPipPipPipPipPipPipPipPipPipPip
  • 606 сообщений
  • Баланс: 0$
  • Пол:Мужчина
  • Город:Россия
  • Интересы:Сергей

Отправлено 04 Февраль 2011 - 14:12

Здравствуй, Сергей!

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


Продолжение скоро. Написание обучающих материалов упирается в вопрос вдохновения.

Что касается исполнителя - в разделе BlueDream всё достаточно подробно описано. Если есть вопросы пишите здесь.

Еще один момент, возможно не в тему: например


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

Заранее спасибо, удачи!

Ответ - Купить доллары за евро или что тоже самое - продать евро за доллары.

Хотя вопрос действительно не в тему. Здесь бы я предпочёл увидеть вопросы по вашим первым кодам.

#35 OFFLINE   valenok2003

valenok2003

    Пользователи

  • Заблокированные
  • PipPipPipPipPipPipPipPipPipPipPip
  • 606 сообщений
  • Баланс: 0$
  • Пол:Мужчина
  • Город:Россия
  • Интересы:Сергей

Отправлено 04 Февраль 2011 - 16:48

Способы вывода текстовой информации.

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

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


Alert("текстовая строка заключённая в кавычки"); // не забывайте про точку с запятой в конце выражения



Мы также можем сохранить строку в переменной типа string и затем вывести её в алерте

string Text = "Выводим текстовую строку";

Alert(Text);



Мы также можем составлять строку сообщения из нескольких строк:


string Text1 = "Выводим текстовую строку";

  string Text2 = "n И ещё одну";

Alert(Text1,Text2);

//или так

Alert(Text1+Text2);

//или так

string Txt = Text1+Text2;

 Alert(Txt);
Как видите при сложении строк при помощи знака "+" происходит их объединение (конкатенация). И это очень полезное свойство. Поскольку текст можно собрать из разных кусков сформированных разными функциями программы и вывести в итоге информацию единой строкой.

** в строке string Text2 = "n И ещё одну"; перед символом "n" должна стоять обратная косая черта, местный текстовый редактор не позволяет мне её поставить.

#36 OFFLINE   valenok2003

valenok2003

    Пользователи

  • Заблокированные
  • PipPipPipPipPipPipPipPipPipPipPip
  • 606 сообщений
  • Баланс: 0$
  • Пол:Мужчина
  • Город:Россия
  • Интересы:Сергей

Отправлено 04 Февраль 2011 - 19:11

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

Попробуйте в нашем примере вместо оператора alert() использовать оператор Comment() например:



string Txt = "И эта тоже";

 Comment("Эта информация будет выведена в окно терминала"+Txt);




Правила формирования строк остаются теми же самыми. То-есть, строки можно формировать заранее, сохранять в строковых переменных, складывать и т.п.

Для переноса строк используйте обратный слэш и символ "n" - без пробела между ними.

#37 OFFLINE   valenok2003

valenok2003

    Пользователи

  • Заблокированные
  • PipPipPipPipPipPipPipPipPipPipPip
  • 606 сообщений
  • Баланс: 0$
  • Пол:Мужчина
  • Город:Россия
  • Интересы:Сергей

Отправлено 06 Февраль 2011 - 08:12

Иногда необходимо выводить служ*цензура*ю информацию, которая может быть полезна не только программисту но и продвинутому пользователю. Такая информация по другому называется отладочной. Отладочная информация выводится оператором Print(), строки составляются точно по таким же правилам, как и для операторов Comment() и alert().

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

Пример:


string Txt_Name_Expert = "Советник N1 сообщает: ";

string Txt_Note = "Начали работу!";

Print(Txt_Name_Expert + Txt_Note);





#38 OFFLINE   valenok2003

valenok2003

    Пользователи

  • Заблокированные
  • PipPipPipPipPipPipPipPipPipPipPip
  • 606 сообщений
  • Баланс: 0$
  • Пол:Мужчина
  • Город:Россия
  • Интересы:Сергей

Отправлено 07 Февраль 2011 - 17:37

Подведём итог.

Текстовую информацию мы можем выводить при помощи операторов alert(), Comment(), Print().

Существует ещё одна возможность, но о ней мы поговорим отдельно, когда будем рассматривать объекты.

А сейчас пример и ещё один оператор:


//+------------------------------------------------------------------+
//|                                      Example_4 GS_2011_02_07.mq4 |
//|                      Copyright © 2010, MetaQuotes Software Corp. |
//|                                        http://www.metaquotes.net |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2010, MetaQuotes Software Corp."
#property link      "http://www.metaquotes.net"

#property indicator_chart_window

extern int Switcher = 1;
string Txt_Alert = "Этот текст выводится в Алерте",
       Txt_Expert= "Этот текст выводится в  Log - файле",
       Txt_Window= "Этот текст выводится в комментах";
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
//---- indicators
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()
  {
//---- удаляем последствия работы индикатора
   Comment("");  
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
{
//----
   switch(Switcher)
   {
      case 1:
         Alert(Txt_Alert);
      break;
      case 2:
         Print(Txt_Expert);
      break;
      case 3:
         Comment(Txt_Window);
      break;
      default:
         Alert("Не правильно задана цифра переключателя. Разрешено 1,2 и 3!");
      break;
   }    
//----
   return(0);
}
//+------------------------------------------------------------------+

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

#39 OFFLINE   valenok2003

valenok2003

    Пользователи

  • Заблокированные
  • PipPipPipPipPipPipPipPipPipPipPip
  • 606 сообщений
  • Баланс: 0$
  • Пол:Мужчина
  • Город:Россия
  • Интересы:Сергей

Отправлено 08 Февраль 2011 - 13:40

В последнем примере мы применили новый для нас оператор

switсh()

Это, так называемый, оператор-переключатель.

Его правильная запись выглядит следующим образом:


   switch(ВЫРАЖЕНИЕ)
   {
      case 1:
         alert(Txt_alert);
      break;
      case 2:
         Print(Txt_Expert);
      break;
      case 3:
         Comment(Txt_Window);
      break;
      default:
         alert("Не правильно задана цифра переключателя. Разрешено 1,2 и 3!");
      break;
   }    


ВЫРАЖЕНИЕ- возвращает некоторый результат, этот результат должен быть типа int (это важно).

В зависимости от возвращённого результата будет выполнен тот или иной пункт оператора. В нашем случае это 1, 2 или 3. Пункты выбора могут следовать и не по порядку, например вот так:


 switch(ВЫРАЖЕНИЕ)
   {
      case 2:
         alert(Txt_alert);
      break;
      case 1:
         Print(Txt_Expert);
      break;
      case 3:
         Comment(Txt_Window);
      break;
      default:
         alert("Не правильно задана цифра переключателя. Разрешено 1,2 и 3!");
      break;
   }    

В пунктах выбора case может быть указана константа или символьная константа. Как вы помните 2,1 и 3 - это константы.

Если же ВЫРАЖЕНИЕ не соответствует ни одному пункту case, выполняется пункт default.

**Естественно ВЫРАЖЕНИЕ подразумевает какую-то переменную или функцию и записывается латинскими буквами.

Оператор switсh() исключительно полезен, при осуществлении множественного выбора и , в некоторых случаях, с успехом заменяет оператор if().

#40 OFFLINE   valenok2003

valenok2003

    Пользователи

  • Заблокированные
  • PipPipPipPipPipPipPipPipPipPipPip
  • 606 сообщений
  • Баланс: 0$
  • Пол:Мужчина
  • Город:Россия
  • Интересы:Сергей

Отправлено 08 Февраль 2011 - 21:13

Eщё оператор switсh() удобно использовать как счётчик-переключатель.

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

Тогда вы можете записать так:


//+------------------------------------------------------------------+
//|                                      Example_5 GS_2011_02_07.mq4 |
//|                      Copyright © 2010, MetaQuotes Software Corp. |
//|                                        http://www.metaquotes.net |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2010, MetaQuotes Software Corp."
#property link      "http://www.metaquotes.net"

#property indicator_chart_window

 int Cnt=1; //обычно счёт начинают с нуля


int init()
  {
 //---- 
//----
   return(0);
  }

int deinit()
  {
 //---- 
//----
   return(0);
  }

int start()
{
//----
   switch(Cnt)
   {
      case 1:
           //здесь действия которые повторятся один раз при первом тике
           Cnt = Cnt+1; //увеличиваем счётчик на единицу и логика выполнения программы больше никогда 
           //не приведёт нас в эту точку, если где-нибудь в другом месте кода счётчик не будет приравнен к единице.
      break;
     default: break;
   }    
//----
   return(0);
}


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

В этом случае увеличивать значение переменной счётчика придётся либо в каждом пункте оператора switch(), либо за его пределами, но не перед ним, а после него. Иначе первый пункт (case 1) никогда не будет выполнен.

#41 OFFLINE   Kortizon

Kortizon

    Расстрелял целый магазин

  • Пользователи
  • PipPipPipPip
  • 41 сообщений
  • Баланс: 0$

Отправлено 06 Март 2011 - 15:19

Прекрасная тема. Отличное исполнение.
Респектуем, уважаемый, премного респектуем!

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

Может и другие форумчане захотели бы выложить части кодов для комментов и объяснений их уважаемым
Valenok'ом.....

ПиСи
Может можно бы было хотя бы разобрать вшитые в МТ MACD Sample и Moving Average...
Пояснить их так же, как и предыдущий индюк выше...
Прилепить к ним пару-тройку простых фишек...разобрать их принципы...

#42 OFFLINE   mr.mbb

mr.mbb

    Первый выстрел

  • Новички
  • PipPip
  • 1 сообщений
  • Баланс: 0$

Отправлено 13 Январь 2012 - 23:59

спасибо все написано очень доступно! супер!

#43 OFFLINE   talliy

talliy

    Рвется в бой

  • Свой человек
  • PipPipPipPipPipPip
  • 132 сообщений
  • Баланс: 0$
  • Пол:Мужчина

Отправлено 22 Февраль 2012 - 07:24

Приветствую читателей данной ветки!
Подскажите ответ на вопрос. Необходимо написать в коде тралл профита но без модификации ордеров дабы не сообщать брокеру своих позиций.
Была попытка реализовать через оператор for :

      for (int f = 1; f <= 1000; f++){
      if (li_60 == OP_BUY && Bid >= ld_64 + (0.001 * f) )
      double profitbuy = ld_64 + (0.001 * f );
      if (li_60 == OP_SELL && Ask <= ld_64 - (0.0015* f) )
      double profitsell = ld_64 - (0.001 * f);}
Где li_64 это цена открытия ордера и тралл был бы через каждые 10 пунктов.
Но проблема в том что я не могу понять как зафиксировать цену в переменных profit.

#44 OFFLINE   Alen_T

Alen_T

    Есть ещё порох в пороховницах

  • Специалист
  • PipPipPipPipPipPipPipPipPipPipPipPip
  • 874 сообщений
  • Баланс: 0$
  • Пол:Мужчина

Отправлено 23 Февраль 2012 - 20:58

Приветствую читателей данной ветки!
Подскажите ответ на вопрос. Необходимо написать в коде тралл профита но без модификации ордеров дабы не сообщать брокеру своих позиций.
Была попытка реализовать через оператор for :


      for (int f = 1; f <= 1000; f++){
      if (li_60 == OP_BUY && Bid >= ld_64 + (0.001 * f) )
      double profitbuy = ld_64 + (0.001 * f );
      if (li_60 == OP_SELL && Ask <= ld_64 - (0.0015* f) )
      double profitsell = ld_64 - (0.001 * f);}
Где li_64 это цена открытия ордера и тралл был бы через каждые 10 пунктов.
Но проблема в том что я не могу понять как зафиксировать цену в переменных profit.


double profitbuy; и double profitsell; вынеси в глобальные переменные (объяви их перед функциями - типа init, start).
Тогда они не будут обнуляться при каждом тике.
Далее без цикла сравнивай их разницу их и Ask, Bid c заданным SL и изменяй их (это будет происходить на каждом тике)..
Не забудь сбросить их значения после закрытия ордеров.
  • talliy это нравится
Ты это, заходи, если что...(С)

#45 OFFLINE   talliy

talliy

    Рвется в бой

  • Свой человек
  • PipPipPipPipPipPip
  • 132 сообщений
  • Баланс: 0$
  • Пол:Мужчина

Отправлено 26 Февраль 2012 - 18:11

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


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


АВТООПТИМИЗАТОР ТОРГОВЫХ СОВЕТНИКОВ


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


Установка автооптимизатора


Для выполнения задачи нужно:

  • скопировать MACD Sample_1.mq4 в папку expert установленного и подключенного к интернету терминала MetaTrader.
  • сделать копию папки с установленным в ней терминалом MetaTrader в новое место.
Для удобства оригинал будем называть - Терминалом, а копию - Терминал-тестером. Проверочный тест будем проводить на инструменте EURUSD с периодом H1 с помощью входящего в комплект поставки MT4, но слегка измененного советника MACD Sample_1.mq4.

Изображение


Подготовка Терминал-тестера.


Не забудьте в Терминал-Тестере откомпилировать советник MACD Sample_1.mq4. Для начала запустим клиентский терминал, затем - тестер стратегий и выполним настройку, как показано на скриншоте.

Изображение


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


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

Изображение


Автооптимизация ограничена четырьмя переменными, но нам для проверки в целях сокращения времени оптимизации достаточно и трех. После того как переменные выбраны, сохраним установленные настройки оптимизации в set-файл с именем MACD Sample_1.set. Этот файл должен быть сохранен в папку tester Терминал-Тестера. Далее запустим предварительную оптимизацию и запомним время старта. Это нужно, чтобы можно было вычислить время,
необходимое для автооптимизации с установленными параметрами. После окончания оптимизации вычислим необходимое время ожидания. Затем этот терминал нужно закрыть, так как иначе мы не сможем запустить его программно.


Подготовка эксперта, находящегося в Терминале.

Для этого откроем в редакторе MetaEditor проверочный эксперт MACD Sample_1.mq4 и выполним соответствующие настройки.

- установим время запуска автооптимизации, например в 00:01 каждые сутки.

   	datetime SetHour    = 0;  // Час старта оптимизации; 
        datetime SetMinute  = 1;  // Минута старта оптимизации.

- установим количество дней для оптимизации (должно совпадать с количеством дней предварительной оптимизации):

        int TestDay = 3;


- установим время ожидания окончания оптимизации в минутах, которое мы вычислили ранее, например 4 минуты.

        int TimeOut = 4;

- впишем имя советника:

        string NameMTS = "MACD Sample_1";  // Имя советника

- впишем имя set-файла с настройками:

        // Имя Set файла с установками
        string NameFileSet = "MACD Sample_1.set"; 

- впишем путь к папке с установленным "терминал-тестером", например:

        // Путь к тестеру        
        string PuthTester = "D:\Program Files\......\МТ4";\\ Путь где находится ваш терминал тестер

- установим очередность фильтрации:

   	// Сортировка по Максимальной прибыли
        int Gross_Profit = 1;                      
        // Сортировка по Максимальной прибыльности        
        int Profit_Factor = 2; 	
        // Сортировка по Максимальному матожиданию
        int Expected_Payoff = 3;

- запишем имена переменных для оптимизации:

        string Per1 = "FastEMA";
        string Per2 = "SlowEMA";
        string Per3 = "SignalSMA";
        string Per4 = "";

- далее скопируем приложенный файл auto_optimization.mqh в папку "include";- подключим файл библиотеки в эксперте:

#include <auto_optimization.mqh>

- осталось скопировать указанный ниже код в начало функции start() вашего эксперта. В MACD Sample_1.mq4 он уже есть.

// При тестировании и оптимизации не запускать   
  if(!IsTesting() && !IsOptimization())
    {                
      // Сравнение текущего часа с установленным для запуска
      if(TimeHour(TimeLocal()) == SetHour)
        {
          // Защита от повторного запуска
          if(!StartTest)
            {
              // Сравнение диапазона минут с установленной для запуска
              // минутой
              if(TimeMinute(TimeLocal()) > SetMinute - 1)
                { 	
                  // диапазон нужен в случае если по каким-то причинам 
                  // долго нет нового тика
                  if(TimeMinute(TimeLocal()) < SetMinute + 1)
                    {  
                      // Флаг запуска тестера
                      StartTest    = true;
                      TimeStart    = TimeLocal();
                      Tester(TestDay, NameMTS, NameFileSet, PuthTester, 
                         	TimeOut, Gross_Profit, Profit_Factor, 
                         	Expected_Payoff, Per1, Per2, Per3, Per4);
                    
                    }
                }
            }
        }
    }
    
                        
   FastEMA      = GlobalVariableGet(Per1);
   SlowEMA      = GlobalVariableGet(Per2);
   SignalSMA    = GlobalVariableGet(Per3);
   TrailingStop = GlobalVariableGet(Per4);  
// Если флаг запуска тестера установлен  
  if(StartTest)
    {                                        
      // Если с момента запуска прошло больше установленного времени 
      // ожидания тестирования
      if(TimeLocal() - TimeStart > TimeOut*60)
        {            
          // Обнулим флаг
          StartTest = false;                              
        }
    }[font="Times New Roman"]

Вот и все. После перекомпиляции автооптимизатор можно запускать, причем запускать нужно на том же инструменте и на том же периоде, на котором проводилась предварительная оптимизация, в данном случае на EURUSD периода H1. Для проверки автооптимизатора можно вставить указанный ниже код в функцию int init (), тогда автооптимизатор запустится сразу в момент старта советника.

Tester(TestDay,NameMTS,NameFileSet,PuthTester,TimeOut, Gross_Profit,Profit_Factor, 
   	Expected_Payoff, Per1,Per2,Per3,Per4);

Принцип работы автооптимизатора


Принцип работы автооптимизатора заключается в использовании Терминал-тестера для оптимизации параметров советника, установленного на график в Терминале. Для этого программа пересылает в Терминал-Тестер файл с параметрами оптимизации (optimise.ini) и запускает Терминал-Тестер в режиме оптимизации. Потом копирует полученные результаты "FileReport........htm" обратно в Терминал и отфильтровывает из полученных результатов лучшие значения.

Изображение


Подробнее о работе автооптимизатора.


В установленное время, например, в 00.01, запускается автооптимизатор. Переменные заполняются значениями.

// Путь к терминалу 
string PuthTerminal = TerminalPath() + "\experts\files";
// Имя ini файла для тестера
string FileOptim    = "optimise.ini";
string FileOptim1   = "\optimise.ini";                                  
// Расчет даты старта
datetime DayStart   = TimeLocal()-86400*TestDay;
// Дата начала оптимизации
string DateStart    = TimeToStr(DayStart,TIME_DATE);
// Дата окончания оптимизации
string DateStop 	= TimeToStr(TimeLocal(),TIME_DATE);
// Имя файла отчета тестера
string FileReport   = "FileReport_" + Symbol() + "_" + DateStop + ".htm";
string FileReport1  = "\FileReport_" + Symbol() + "_" + DateStop + ".htm";
// Ограничение на минимальное количество сделок в день
double MinTr        = TestDay - 2;
// Ограничение на максимальное количество сделок в день
double MaxTr        = (60 / Period()*TestDay) + 2;
// Количество попыток скопировать файл отчета
int    KvoPptk      = 10;
// Количество строк для сортировки
int    StepRes      = 12;

Далее в строковый массив записываются параметры ini-файла.

// Подготовим ini-файл для оптимизации
ArrayOpttim[0] = ";optimise strategy tester";         	
// Вкл/Выкл эксперты
ArrayOpttim[1] = "ExpertsEnable = false";
// Наименование файла эксперта
ArrayOpttim[2] = "TestExpert=" + NameMTS;
// Наименование файла с параметрами
ArrayOpttim[3] = "TestExpertParameters=" + NameFileSet;
// Инструмент
ArrayOpttim[4] = "TestSymbol=" + Symbol();
// Период
ArrayOpttim[5] = "TestPeriod=" + Period();
// Режим моделирования
ArrayOpttim[6] = "TestModel=" + 0;
// Пересчитать
ArrayOpttim[7] = "TestRecalculate=false";
// Оптимизация
ArrayOpttim[8] = "TestOptimization=true";
// Использовать дату
ArrayOpttim[9] = "TestDateEnable=true";
// Дата начала тестирования
ArrayOpttim[10] = "TestFromDate=" + DateStart;
// Дата окончания тестирования
ArrayOpttim[11] = "TestToDate=" + DateStop;
// Имя файла отчета
ArrayOpttim[12] = "TestReport=" + FileReport;
// Перезапись файла отчета
ArrayOpttim[13] = "TestReplaceReport=true";
// Закрыть терминал по завершению
ArrayOpttim[14] = "TestShutdownTerminal=true";

Из массива параметры оптимизации переписываются в ini-файл. О формировании ini-файла также можно прочитать в справке клиентского терминала MetaTrader 4 в разделе <Справка - Вызов справки F1 - Сервис - Конфигурация при старте>.

// Запишем данные в ini-файл                
// Выясним размер массива
OptimArraySize = ArraySize(ArrayOpttim);
// Откроем фал для записи
opttim = FileOpen(FileOptim, FILE_CSV|FILE_WRITE, 0x7F);
if(opttim > 0)
  {
    for(int i = 0; i < OptimArraySize; i++)
      {
        // из массива в переменную
        ini = ArrayOpttim[i];                                 	
        // из переменной в файл
        FileWrite(opttim, ini);
      } 
    // закроем файл
    FileClose(opttim);
  }
else
  {
    Print("Не удалось записать данные в ini-файл. Ошибка № ", 
          GetLastError());
    return(0);
  }

После записи параметров в ini-файл подключается входящая в состав Windows библиотека shell32.dll и запускается функция ShellExecuteA.

#import  "shell32.dll"           	//Подключим dll (входит в состав windows)   	
  int ShellExecuteA(int hwnd,string Operation,string 
                    File,string Parameters,string Directory,int ShowCmd); 
#import

Файл с параметрами пересылается в папку Терминал-тестера.

// скопируем ini-файл в песочницу тестера 
copyini = ShellExecuteA(0,"Open","xcopy", "\"" + PuthTerminal + 
                        FileOptim1 + "\" \"" + PuthTester + "\" /y", 
                        "", 3);
// подождем, пока скопируется файл
Sleep(1200);                                                    
if(copyini < 0)
  {
    Print("Не удалось скопировать ini-файл");
    return(0);
  }

Далее запускается тестер и начинает оптимизацию заданных переменных. Пока идет оптимизация, эксперт находится в состоянии ожидания.

// Запустим Тестер 
start = ShellExecuteA(0, "Open", "terminal.exe", FileOptim,
                      PuthTester, 3);
if(start < 0)
  {
    Print("Не удалось запустить тестер");
    return(0);
  }
Comment("Ожидаем окончания оптимизации");
// подождем окончания оптимизации
Sleep(60000*TimeOut);

После завершения оптимизации тестер автоматически записывает результаты в файл отчета. Этот файл копируется в папку с терминалом.

 for(Pptk = 0; Pptk < KvoPptk; Pptk++)
  {                    
    //Запустим цикл попыток копирования файла отчета
    Comment("Попытка № " + Pptk + " скопировать файл отчета");
    ShellExecuteA(0, "Open", "xcopy", "\"" + PuthTester + FileReport1 + 
                  "\" \"" + PuthTerminal + "\" /y", "", 3);
    // подождем, пока скопируется файл
    Sleep(1200);
    // Попытаемся открыть файл отчета
    file = FileOpen(FileReport, FILE_READ, 0x7F);
    if(file < 0)
      {
        // если не удалось, ещё подождем и попробуем снова
        Sleep(60000);
      }                    
    else 
        break;         	
  }
if(file < 0)
  {
    Print("Не удалось скопировать файл отчета");
    return(0);
  }

Затем данные из файла отчета заносятся в строковый массив для дальнейшей обработки.

 // Чтение из файла в массив
// Пока не наступил конец файла - цикл
while(FileIsEnding(file) == false)
  {             	
    // Прочитаем строку из файла отчета
    FileLine = FileReadString(file);
    // Найдем нужную строку и установим точку отсчета
    index = StringFind(FileLine, "title", 20);
    if(index > 0)
      {
        // Увеличиваем размер массива
        ArrayResize(ArrayStrg, NumStr + 1);
        // Записываем в массив строки из файла
        ArrayStrg[NumStr] = FileLine;
        NumStr++;
      }
  }
// Закроем файл
FileClose(file);
// Удалим файл, чтоб не плодить копии
FileDelete(FileReport);
// Установим размер массива по количеству считанных из файла
ArrayResize(ArrayData, NumStr); строк
    

Далее происходит выборка нужных значений из массива

for(text = 0; text < NumStr; text++)
 	{
      select = ArrayStrg[text]; 
    //-------------------------------------------------------------------------
    //   Обработка текста отчета  (Отделяем мух от котлет)                    | 
    //-------------------------------------------------------------------------
    // Позиция Проход 
    ClStep=StringFind(select, "; \">",20)+4;
    // Найдем конец позиции
    ClStepRazm = StringFind(select, "td>", ClStep);
    // Считаем значение
    CycleStep = StringSubstr(select, ClStep, ClStepRazm - ClStep);
    // Позиция Прибыль 
    // Найдем начало позиции
    GrProf = StringFind(select, "", ClStepRazm);
    // Найдем конец позиции
    GrProfRazm = StringFind(select, "td>", GrProf);
    // Считаем значение
    GrossProfit = StringSubstr(select, GrProf+4, GrProfRazm - (GrProf + 4));
    // Позиция Всего Сделок
    // Найдем начало позиции
    TotTrad = StringFind(select, "", GrProfRazm);
    // Найдем конец позиции
    TotTradRazm = StringFind(select, "td>", TotTrad);
    // Считаем значение
    TotalTrades = StringSubstr(select, TotTrad+4, TotTradRazm - 
                           	(TotTrad + 4));
    // Позиция Прибыльность
    // Найдем начало позиции
    ProfFact = StringFind(select, "", TotTradRazm);
    // Найдем конец позиции
    ProfFactRazm = StringFind(select, "td>", ProfFact);
    // Считаем значение
    ProfitFactor = StringSubstr(select, ProfFact + 4, ProfFactRazm - 
                                (ProfFact + 4));
    // Позиция Мат Ожидание 
    // Найдем начало позиции
    ExpPay = StringFind(select, "", ProfFactRazm);
    // Найдем конец позиции
    ExpPayRazm=StringFind(select, "td>", ExpPay);
    // Считаем значение
    ExpectedPayoff = StringSubstr(select, ExpPay+4, ExpPayRazm - 
                                  (ExpPay + 4));
    // Позиции переменных-начиная со второй
    // Найдем начало позиции
    P1 = StringFind(select, Per1, 20);
    // Найдем конец позиции
    P1k = StringFind(select, ";", P1);
    // Считаем Переменную
    Perem1 = StringSubstr(select, P1 + StringLen(Per1) + 1, P1k - 
                          (P1 + 1 + StringLen(Per1)));
    // Найдем начало позиции
    P2 = StringFind(select, Per2, 20);
    // Найдем конец позиции
    P2k = StringFind(select, ";", P2); 
    // Считаем Переменную
    Perem2 = StringSubstr(select, P2 + StringLen(Per2) + 1, P2k - 
                          (P2 + 1 + StringLen(Per2)));
    // Найдем начало позиции
    P3 = StringFind(select, Per3, 20);
    // Найдем конец позиции
    P3k = StringFind(select, ";", P3);
    // Считаем Переменную
    Perem3 = StringSubstr(select, P3 + StringLen(Per3) + 1, P3k - 
                          (P3 + 1 + StringLen(Per3)));
    // Найдем начало позиции
    P4 = StringFind(select, Per4, 20);
    // Найдем конец позиции
    P4k = StringFind(select, ";", P4);
    // Считаем Переменную 
    Perem4 = StringSubstr(select, P4 + StringLen(Per4) + 1, P4k - 
                          (P4 + 1 + StringLen(Per4)));
    Comment("Идет анализ полученных результатов");

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

// Переведем в числовой формат
TotalTradesTransit = StrToDouble(TotalTrades);
GrossProfitTransit = StrToDouble(GrossProfit);
ExpectedPayoffTran = StrToDouble(ExpectedPayoff);
nodubl = true;
if(MinTr < TotalTradesTransit && MaxTr > TotalTradesTransit)
  {                    
    // Отфильтруем по количеству сделок
    PrFactDouble = StrToDouble(ProfitFactor);
    // Убираем 0 в прибыльности для правильного анализа
    if(PrFactDouble == 0)
      {
        PrFactDouble = 1000;
      }

Далее значения фильтруются на наличие дубликатов.

// Отфильтруем данные с одинаковыми значениями
for(Dubl = 0; Dubl <= ResizeArayNew; Dubl++)
  {                    
    // Запустим цикл поиска одинаковых значений
    if(GrossProfitTransit == ArrayData[Dubl][1])
      {          
        // проверим совпадения результатов по максимальной прибыли
        if(TotalTradesTransit == ArrayData[Dubl][2])
          {   	
            // проверим совпадения результатов по количеству сделок
            if(PrFactDouble == ArrayData[Dubl][3])
              {          
                // проверим совпадения результатов по прибыльности
                if(ExpectedPayoffTran == ArrayData[Dubl][4])
                  { 
                    // проверим совпадения результатов по матожиданию
                    nodubl=false;                              
                    // Если все совпало, то поставим флаг совпадения
                  }
              }
          }
      }
  }

Далее подготовленные для сортировки значения записываются в массив.

// Запишем отфильтрованные данные в массив
if(nodubl)
  {
    ArrayData[text][1] = GrossProfitTransit;                                
    ArrayData[text][2] = TotalTradesTransit;
    ArrayData[text][3] = PrFactDouble;
    ArrayData[text][4] = ExpectedPayoffTran;
    ArrayData[text][5] = StrToDouble(Perem1);
    ArrayData[text][6] = StrToDouble(Perem2);
    ArrayData[text][7] = StrToDouble(Perem3);
    ArrayData[text][8] = StrToDouble(Perem4);
    ResizeArayNew++; 
  }

Далее начинается анализ данных в порядке очередности, установленной ранее. Анализ происходит так:


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

// Анализатор
// Принцип анализа - последовательная проверка максимальных 
// значений согласно заданному приоритету фильтрации   
ArrayResize(ArrayTrans, ResizeArayNew - 1);
for(int PrioStep = 1; PrioStep < 4; PrioStep++)
  {
    for(PrCycle = 0; PrCycle < ResizeArayNew; PrCycle++)
      {
        Sort 	= ArrayData[PrCycle][0];
        Prior1   = ArrayData[PrCycle][1];         	
        transit  = ArrayData[PrCycle][2];
        Prior2   = ArrayData[PrCycle][3];         	
        Prior3   = ArrayData[PrCycle][4];         	
        transit1 = ArrayData[PrCycle][5];
        transit2 = ArrayData[PrCycle][6];
        transit3 = ArrayData[PrCycle][7];
        transit4 = ArrayData[PrCycle][8]; 
       	
        if(PrioStep == 1)
          {
            //Подготовимся к 1 сортировке
            if(Gross_Profit ==1)
              {
                SortTrans = Prior1;
              }
            if(Profit_Factor == 1)
              {
                SortTrans = Prior2;
              }
            if(Expected_Payoff == 1)
              {
                SortTrans = Prior3;
              }
          }
        if(PrioStep == 2)
          {
            // Восстановимся
            if(Gross_Profit ==1)
              {
                Prior1 = Sort;
              }
            if(Profit_Factor == 1)
              {
                Prior2 = Sort;
              }
            if(Expected_Payoff == 1)
              {
                Prior3 = Sort;
              } 
            //Подготовимся ко 2 сортировке
            if(Gross_Profit == 2)
              {
                SortTrans = Prior1;
              }
            if(Profit_Factor == 2)
              {
                SortTrans = Prior2;
              }
            if(Expected_Payoff == 2)
              {
                SortTrans = Prior3;
              }
          }
        if(PrioStep == 3)
          {
            // Восстановимся
            if(Gross_Profit == 2)
              {
                Prior1 = Sort;
              }
            if(Profit_Factor == 2)
              {
                Prior2 = Sort;
              }
            if(Expected_Payoff == 2)
              {
                Prior3 = Sort;
              } 
            //Подготовимся к 3 сортировке
            if(Gross_Profit ==3)
              {
                SortTrans = Prior1;
              }
            if(Profit_Factor == 3)
              {
                SortTrans = Prior2;
              }
            if(Expected_Payoff == 3)
              {
                SortTrans = Prior3;
              }
          }          
        ArrayTrans[PrCycle][0] = SortTrans;
        ArrayTrans[PrCycle][1] = Prior1;
        ArrayTrans[PrCycle][2] = transit;
        ArrayTrans[PrCycle][3] = Prior2;
        ArrayTrans[PrCycle][4] = Prior3;
        ArrayTrans[PrCycle][5] = transit1;
        ArrayTrans[PrCycle][6] = transit2;
        ArrayTrans[PrCycle][7] = transit3;
        ArrayTrans[PrCycle][8] = transit4;
      }
    ArraySort(ArrayTrans,StepRes, 0, MODE_DESCEND); // Отсортируем массив
    ArrayResize(ArrayTrans, StepRes);           	// Обрежем лишнее
    for(int CopyAr = 0; CopyAr < StepRes; CopyAr++)
      {
        ArrayData[CopyAr][0] = ArrayTrans[CopyAr][0];
        ArrayData[CopyAr][1] = ArrayTrans[CopyAr][1];
        ArrayData[CopyAr][2] = ArrayTrans[CopyAr][2];
        ArrayData[CopyAr][3] = ArrayTrans[CopyAr][3];
        ArrayData[CopyAr][4] = ArrayTrans[CopyAr][4];         	
        // Per1    Переменная 1
        ArrayData[CopyAr][5] = ArrayTrans[CopyAr][5];              
        // Per2    Переменная 2
        ArrayData[CopyAr][6] = ArrayTrans[CopyAr][6];
        // Per3    Переменная 3
        ArrayData[CopyAr][7] = ArrayTrans[CopyAr][7];
        // Per4    Переменная 4
        ArrayData[CopyAr][8] = ArrayTrans[CopyAr][8];
      }
   StepRes = StepRes / 2;
  }

Отфильтрованные таким образом значения записываются в глобальные переменные. Из глобальных переменных значения подставляются в советник.

 	// Запишем полученные данные в переменные
   double Peremen1 = ArrayTrans[0][5];                     	
   double Peremen2 = ArrayTrans[0][6];
   double Peremen3 = ArrayTrans[0][7];
   double Peremen4 = ArrayTrans[0][8];
   // Если имя переменной указано, то запишем результат в 
   // глобальные переменные
   if(Per1 != "")
 	{
   	GlobalVariableSet(Per1, Peremen1);
 	}         	
   if(Per2 != "")
 	{
   	GlobalVariableSet(Per2,Peremen2);
 	}
   if(Per3 != "")
 	{
   	GlobalVariableSet(Per3,Peremen3);
 	}
   if(Per4 != "")
 	{
   	GlobalVariableSet(Per4,Peremen4);
 	}
   Comment(Per1, " ", Peremen1, "  | ", Per2, " ", Peremen2, "  | ", Per3, 
       	" ", Peremen3, "  | ", Per4, " ", Peremen4);
   Print(Per1, " ", Peremen1, "  | ", Per2, " ", Peremen2, "  | ", Per3, 
     	" ", Peremen3,"  | ",Per4," ",Peremen4);
  }  // Конец функции Все, на этом автооптимизация завершена.

Результат работы автооптимизатора


Результат работы автооптимизатора можно отслеживать по сообщениям появляющимся в верхнем левом углу графика, как показано на скриншоте:

Изображение


Изображение

Изображение

Итоговые значения переменных.



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

Для самостоятельной оценки работы автооптимизатора можно посмотреть сохраненные в процессе работы файлы с промежуточными данными. Тестер сохраняет данные в файл с названием "FileReport_EURUSD_2012.02.12.htm", где символ инструмента и дата подставляются в название файла в зависимости от выбранного инструмента и текущей даты оптимизации. Находится этот файл в папке Терминал-тестера. Эти файлы с отчетами автоматически не удаляются и по ним можно отслеживать изменение параметров.

Изображение


Следующий файл FileTest1.csv сохраняется после фильтрации значений по количеству сделок и удалению дубликатов. Файл сохраняется в папку: D:\Program Files\имя папки терминала\experts\files

Изображение


Далее в файл FileTest2.csv сохраняются значения после каждого шага отсеивания. Файл также сохраняется в папку: D:\Program Files\имя папки терминала\experts\files

Изображение


Из вышеприведенных таблиц видно, как происходит фильтрация полученных значений. Порядок фильтрации был установлен по умолчанию: 1- Gross_Profit, 2- Profit_Factor, 3- Expected_Payoff.

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

 // Ограничение на минимальное количество сделок в день
double MinTr   = TestDay - 2; 
// Ограничение на максимальное количество сделок в день
double MaxTr   = (60 / Period()*TestDay) + 2;
// Количество попыток скопировать файл отчета
int    KvoPptk = 10;
// Количество строк для сортировки
int    StepRes = 12;

Итак, подведем итоги:

Код для вставки в ваш советник:


extern int SetHour   = 0;             	//Час старта оптимизации 
extern int SetMinute = 1;             	//Минута старта оптимизации 
int    TestDay   	= 3;             	//Количество дней для оптимизации 
int    TimeOut   	= 4;             	//Время ожидания окончания оптимизации в минутах
string NameMTS   	= "Expert_Name"; 	//Имя вашего советника
string NameFileSet   = "SetFileName.set"; //Имя Set файла с установками
string PuthTester    = "PathTester";      //Путь к тестеру
//--- Последовательность фильтрации
int    Gross_Profit   = 1;                //Сортировка по Максимальной прибыли
int    Profit_Factor  = 2;                //Сортировка по Максимальной прибыльности
int    Expected_Payoff= 3;                //Сортировка по Максимальному матожиданию
//--имена переменных для оптимизации
string Per1 = "variables_1";
string Per2 = "variables_2";
string Per3 = "variables_3";
string Per4 = "variables_4";
bool StartTest=false;
datetime TimeStart;
//--- Подключение библиотеки автооптимизатора
#include <auto_optimization.mqh>

Код для вставки в эксперт в функцию start()

// При тестировании и оптимизации не запускать   
if(!IsTesting() && !IsOptimization())
  {
    // Сравнение текущего часа с установленным для запуска
    if(TimeHour(TimeLocal()) == SetHour)
      {
        // Защита от повторного запуска
        if(!StartTest)
          {
            // Сравнение диапазона минут с установленной для запуска минутой
            if(TimeMinute(TimeLocal()) > SetMinute - 1)
              { 
                // диапазон нужен в случае если по каким-то причинам долго 
                // нет нового тика
                if(TimeMinute(TimeLocal()) < SetMinute + 1)
                  {
                    TimeStart = TimeLocal();
                    StartTest = true;   // Флаг запуска тестера
                    Tester(TestDay, NameMTS, NameFileSet, PuthTester, TimeOut, 
                       	Gross_Profit, Profit_Factor, Expected_Payoff, Per1, 
                       	Per2, Per3, Per4);
                  }
              }
          }
      }
    variables_1 = GlobalVariableGet(Per1);
    variables_2 = GlobalVariableGet(Per2);
    variables_3 = GlobalVariableGet(Per3);
    variables_4 = GlobalVariableGet(Per4);
  }
// Если флаг запуска тестера установлен
if(StartTest)
  {
    // Если с момента запуска прошло больше установленного времени ожидания 
    // тестирования
    if(TimeLocal() - TimeStart  > TimeOut*60)
      {
        StartTest = false;  // Обнулим флаг
      }
  }

Добавлена измененная библиотека автооптимизатора для 204 Build -а auto_optimization_204.mqh
Соответственно код вызова этой библиотеки будет такой

//--- Подключение библиотеки автооптимизатора
#include <auto_optimization_204.mqh>

Заключение

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

Кроме того, желательно не устанавливать период автооптимизации слишком большим. Предположим, эксперт будет каждые сутки оптимизироваться по 6 - 12 часов. Тогда возникает вопрос, а когда он будет торговать? Иначе говоря, оптимизация не должна быть ради самой оптимизации. Устанавливать периодичность (имеется в виду периодичность запуска автооптимизатора) проведения оптимизации желательно с учетом периода, на котором предполагается торговля советника. То есть, нужно учитывать, что во время запуска Терминал-Тестера происходит подкачка исторических данных и у брокера может просто не быть нужных исторических данных за установленный период. Для проверки описанной в начале статьи гипотезы необходимо наличие круглосуточно подключенного к интернету компьютера и стабильного интернета.

Авторы: MQL4.com , XEON

Разработанные программы автоматической оптимизации расположены в прикрепленных файлах:

Прикрепленные файлы


  • Alen_T это нравится



Количество пользователей, читающих эту тему: 0

0 пользователей, 0 гостей, 0 скрытых пользователей

Copyright © 2016 Your Company Name