Jump to content

Recommended Posts

Programmer

#include <stdlib.mqh>

Дальше в нашем коде мы будем использовать функцию ErrorDescription(), которая возвращает строковое описание по коду произошедшей ошибки. Тело этой функции находится в файле "stdlib.mqh", так что нам надо его включить в нашу программу, используя директиву #include.

Это означает, что отныне всё содержание файла "stdlib.mqh" является частью нашей программы, и мы спокойно можем пользоваться функцией ErrorDescription.

 

extern double TakeProfit=250;
extern double StopLoss=35;

Тут мы объявили две внешние переменные, которые пользователь может задать в окошке настроек скрипта (рис. 3) или же он может оставить значения по умолчанию.

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

 

int start()
{
//----

  int total,cnt,err;

  total = OrdersTotal();

  ...

}

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

Мы объявили три переменные integer, используя однострочное объявление нескольких переменных. Затем мы присвоили значение, возвращаемое функцией OrdersTotal(), перменной total. Как Вы помните, функция OrdersTotal() возвращает общее количество открытых и отложенных ордеров.

 

   for(cnt=0;cnt<total;cnt++)
  {

  ...

  }

Мы запускаем цикл, в котором переменная цикла cnt, при выполнение очередного шага цикла, будет увеличиваться на 1. Т.е. переменная cnt меняется от 0 включительно до total не включительно, т.е. последнее значение cnt будет total-1. Это потому что ордеров всего total, а их индексация начинается с 0.

 

OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);

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

 

(Пожалуйста, пересмотрите урок 15 - торговые функции).

 

      if(OrderType()==OP_BUY) // long position is opened
     {

     ...

     }

Функция OrderType() возвращает тип открытого ордера, который может принимать одно из значений:

 

OP_BUY, OP_SELL, OP_BUYLIMIT, OP_BUYSTOP, OP_SELLLIMIT, OP_SELLSTOP

 

В вышеуказанном if условии мы осуществляем проверку того, что данный ордер является ордером на покупку. Если это так, то будет выполнен if-блок.

 

OrderModify(OrderTicket(),OrderOpenPrice(),Bid-Point*StopLoss,Bid+Point*TakeProfit,0,Green);

Итак, рассматриваемая сделка является открытой сделкой BUY. Значит, мы хотим выставить ей указанные пользователем StopLoss и TakeProfit. Для этого мы используем функцию OrderModify() со следующими параметрами:

 

ticket: Тикет данного ордера нам предоставит функция OrderTicket().

 

price: Цену открытия нам предоставит функция OrderOpenPrice().

Замечание: Этот параметр используется для модификации цены открытия отложенных ордеров, и для открытых ордеров можно передавать 0.

 

stoploss: Здесь мы выставляем стоп-лосс на уровень равный разнице тек. цены bid и произведения StopLoss * Point. Т.е. стоп-лосс выставляем на StopLoss пунктов ниже тек. цены bid.

 

takeprofit: Здесь мы выставляем тейк-профит на уровень равный сумме тек. цены bid и произведения TakeProfit * Point. Т.е. тейк-профит выставляем на TakeProfit пунктов выше тек. цены bid.

 

expiration: У открытых ордеров не может быть срока истечения. Поэтому мы передаём 0.

 

arrow_color: Мы выбрали зелёную стрелку. Вы помните почему ;)

 

err=GetLastError();
Print("error(",err,"): ",ErrorDescription(err));
Sleep(1000);

Мы присваиваем значение, возвращаемое функцией GetLastError(), переменной err.

Функция GetLastError() возвращает код последней произошедшей ошибки после выполнения какого-либо оператора.

 

(OrderModify() в нашем случае).

 

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

 

Затем мы используем функцию Sleep() чтобы дать нашему скрипту и терминалу передохнуть одну секунду (1000 милисекунд).

 

Замечание: Функция Sleep() откладывает дальнейшее выполнение программы на указанное время в милисекундах.

 

      if(OrderType()==OP_SELL) // short position is opened
     {
        OrderModify(OrderTicket(),OrderOpenPrice(),Ask+Point*StopLoss,Ask-Point*TakeProfit,0,Red);
        err=GetLastError();
        Print("error(",err,"): ",ErrorDescription(err));
        Sleep(1000);
     }

У Вас уже достаточно опыта, чтобы с этим куском самим разобраться. Думаю, Вам это будет даже интересней.

 

return(0);

Пришло время завершить работу нашего скрипта, используя функцию return().

 

Теперь откомпилируем наш первый скрипт.

 

Нажмите F5. Как только скрипт откомпилирован, он появится в окошке Навигатор в категории Скрипты (см. рис. 4).

 

Замечание: Код, который на создал помощник и который мы модифицировали, хранится в файле "My_fist_Script.mq4". Этот файл лежит в папке: "MetaTrader\experts\scripts\". Все скрипты необходимо класть в эту папку и компилировать перед использованием в терминале.

 



Рис. 4 - Местонахождение скрипта в терминале

 

Для исполнения скрипта просто щёлкните по нему дважды или же перетащите на график.

Во вкладке "Входные параметры" можно изменять значения переменных TakeProfit и StopLoss.

 

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

 

© Kirill. [email protected]

post-50854-1404215535,7611_thumb.gif

Edited by Programmer
Link to post
Share on other sites
  • 2 months 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

Урок 19 - Работа с шаблонами

 

Мы уже создали наш первый индикатор, советник и скрипт в предыдущих уроках.

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

Сегодня мы создадим индикатор, основанный на встроенном MACD-шаблоне.

 

Ну что ж? Начнём!

 

Что такое MQL4-шаблоны?

 

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

 

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

 

Взгляд изнутри:

 

Прежде, чем создавать наш первый шаблонный индикатор, мы посмотрим на сам файл-шаблон.

 

Замечание: Файлы шаблонов храняться в папке MetaTrader\experts\templates\ и имеют расширение ".mqt".

 

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

 

<expert>
type=INDICATOR_ADVISOR
separate_window=1
used_buffers=2
<param>
type=int
name=FastEMA
value=12
</param>
<param>
type=int
name=SlowEMA
value=26
</param>
<param>
type=int
name=SignalSMA
value=9
</param>
<ind>
color=Silver
type=DRAW_HISTOGRAM
</ind>
<ind>
color=Red
</ind>
</expert>
#header#
#property copyright "#copyright#"
#property link      "#link#"

#indicator_properties#
#extern_variables#
#mapping_buffers#
//---- indicator buffers
double ExtSilverBuffer[];
double ExtRedBuffer[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
 {
  #buffers_used#;
//---- drawing settings
  #indicators_init#
//----
  SetIndexDrawBegin(1,SignalSMA);
  IndicatorDigits(5);
//---- indicator buffers mapping
  SetIndexBuffer(0, ExtSilverBuffer);
  SetIndexBuffer(1, ExtRedBuffer);
//---- name for DataWindow and indicator subwindow label
  IndicatorShortName("MACD("+FastEMA+","+SlowEMA+","+SignalSMA+")");
//---- initialization done
  return(0);
 }
//+------------------------------------------------------------------+
//| Moving Averages Convergence/Divergence                           |
//+------------------------------------------------------------------+
int start()
 {
  int limit;
  int counted_bars=IndicatorCounted();
//---- check for possible errors
  if(counted_bars<0) return(-1);
//---- last counted bar will be recounted
  if(counted_bars>0) counted_bars--;
  limit=Bars-counted_bars;
//---- macd counted in the 1-st buffer
  for(int i=0; i<limit; i++)
     ExtSilverBuffer[i]=iMA(NULL,0,FastEMA,0,MODE_EMA,PRICE_CLOSE,i)-iMA(NULL,0,SlowEMA,0,MODE_EMA,PRICE_CLOSE,i);
//---- signal line counted in the 2-nd buffer
  for(i=0; i<limit; i++)
     ExtRedBuffer[i]=iMAOnArray(ExtSilverBuffer,Bars,SignalSMA,0,MODE_SMA,i);
//---- done
  return(0);
 }
//+------------------------------------------------------------------+

Если Вы взглянете на этот код, то заметите, что это обычный MQL4-код с двумя нововведениями.

Первое - это конструкции, похожие на HTML-теги. Например: <expert> и <param>. Они называются шаблонными тегами.

Второе - это конструкции, похожие на MQL4-директивы. Например: #header# и #link#. Они называются шаблонными командами.

 

(продолжение в след. посте)

  • Downvote 1
Link to post
Share on other sites
Programmer

MQL4 шаблонные теги

 

Шаблон начинается с тегов, похожих на HTML и XML теги.

Теги всегда идут парами. Есть открывающий тег, есть закрывающий. Закрывающий тег имеет слеш сразу после первой своей скобки. Например <expert> - открывающий, </expert> - закрывающий.

Текст между тегами называется элементом.

 

Например:

 

<param>
type=int
name=FastEMA
value=12
</param>

В MQL4-шаблоне возможны три типа тегов:

 

1. Тег expert

Это основной тег, и другие два могут быть только элементами этого тега.

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

 

В теге expert возможны следующие элементы:

 

type: Тип программы. Возможные значения: EXPERT_ADVISOR, INDICATOR_ADVISOR, SCRIPT_ADVISOR.

 

seperate_window: Тип окна графика.

 

used_buffers: Кол-во используемых буферов.

 

ind_minimum: Минимальное значение в окне индикатора.

 

ind_maximum: Максимальное значение в окне индикатора.

 

2. Тег param

Этот тег используется для создания внешних переменных. Для каждой переменной мы используем свой тег param.

 

В теге param возможны следующие элементы:

 

type: Тип данных данной переменной.

 

name: Имя переменной.

 

value: Значение поумолчанию для данной переменной.

 

3. Тег ind

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

 

В теге ind возможны следующие элементы:

 

type: Форма рисования для индикатора. Возможные значения: DRAW_LINE, DRAW_HISTOGRAM, DRAW_SECTION, DRAW_ARROW.

 

color: Цвет индикатора.

 

arrow: Символ, для отображения в индикаторе DRAW_ARROW. Шрифт "Windings" используется для оттображения этих символов.

 

 

MQL4 шаблонные команды:

 

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

 

Замечание: MetaEditor читает эти строчки и заменяет их там, где находит.

 

Список шаблонных-команд:

 

#header#: Эта строчка будет заменена header-блоком программы - комментарии с названием файла, именем автора и названием компании.

 

#copyright#: Эта строчка будет заменена названием Вашей компании.

 

#link#: Эта строчка будет заменена ссылкой на Ваш сайт.

 

#indicator_properties#: Эта строчка будет заменена свойствами индикатора.

 

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

 

#buffers_used#: Эта строчка будет заменена количетсвом используемых буферов, если такие имеются.

 

#indicators_init#: Эта строчка будет заменена инициализацией индикаторов.

 

 

Создание нашего MACD-шаблонного индикатора

 

Теперь давайте создадим этот шаблонный индикатор. Нажимаем: File->New или просто Ctrl+N. Таким образом мы вызвали нашего старого доброго помощника.

Но в этот раз мы выберем опцию "Генерация по шаблону". И из выпадающего списка надо выбрать "Indicator - MACD".

 



Рис. 1 - Помощник. 1й шаг

 

Затем нажимаем кнопку "Далее". Переходим на шаг 2.

MetaEditor заполнил поля "Автор" и "Ссылка" (значения он взял из реестра Windows) и оставил пустым поле "Имя", в котрое мы введём MACD_From_Template.

 

MetaEditor прочёл файл-шаблон и заполнил список внешних переменных и их значений поумолчанию.

 



Рис. 2 - Помощник. 2й шаг

 

Нажатие кнопки "Далее" переносит нас на шаг 3, в котором находятся парам етры индикатора.

 



Рис. 3 - Помощник. 3й шаг

 

А теперь нажмите "Готово", и Вы увидите магию шаблонов.

 

Что у нас есть?

 

Помощник создал для нас индикатор на основе шаблона MACD.

Код готов к компиляции и запуску:

 

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

#property indicator_separate_window
//---- input parameters
extern int       FastEMA=12;
extern int       SlowEMA=26;
extern int       SignalSMA=9;
//---- indicator buffers
double ExtSilverBuffer[];
double ExtRedBuffer[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
 {
//---- additional buffers are used for counting
  IndicatorBuffers(2);
//---- drawing settings
//----
  SetIndexDrawBegin(1,SignalSMA);
  IndicatorDigits(5);
//---- indicator buffers mapping
  SetIndexBuffer(0, ExtSilverBuffer);
  SetIndexBuffer(1, ExtRedBuffer);
//---- name for DataWindow and indicator subwindow label
  IndicatorShortName("MACD("+FastEMA+","+SlowEMA+","+SignalSMA+")");
//---- initialization done
  return(0);
 }
//+------------------------------------------------------------------+
//| Moving Averages Convergence/Divergence                           |
//+------------------------------------------------------------------+
int start()
 {
  int limit;
  int counted_bars=IndicatorCounted();
//---- check for possible errors
  if(counted_bars<0) return(-1);
//---- last counted bar will be recounted
  if(counted_bars>0) counted_bars--;
  limit=Bars-counted_bars;
//---- macd counted in the 1-st buffer
  for(int i=0; i<limit; i++)
     ExtSilverBuffer[i]=iMA(NULL,0,FastEMA,0,MODE_EMA,PRICE_CLOSE,i)-iMA(NULL,0,SlowEMA,0,MODE_EMA,PRICE_CLOSE,i);
//---- signal line counted in the 2-nd buffer
  for(i=0; i<limit; i++)
     ExtRedBuffer[i]=iMAOnArray(ExtSilverBuffer,Bars,SignalSMA,0,MODE_SMA,i);
//---- done
  return(0);
 }
//+------------------------------------------------------------------+

Откомпилируйте код, и наслаждайтесь своим новым индикатором MACD.

С наступающим!

 

© Kirill. [email protected]

post-50854-1404215535,8277_thumb.gif

post-50854-1404215535,8501_thumb.gif

post-50854-1404215535,8716_thumb.gif

Edited by Programmer
Link to post
Share on other sites
  • 6 months later...
Programmer

Урок 20 - Массивы

 

Всем привет!

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

 

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

Сегодня мы раскроем тайны массивов в целом и в MQL4 в частности.

 

Что такое массив?

 

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

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

Нумерация индексов начинается не с 1, как мы привыкли, а с 0. Так что индекс первого элемента массива - это 0, второго элемента - это 1, третьего - это 2 и т.д.

 

Замечание: Если в каком-либо языке программирования нумерация индексов начинается с 1, то такие массивы называются 1-индексированными, но в MQL4 и большинстве языков программирования индексация массивов начинается с 0 (0-индексированные массивы).

 



Рис. 1 - Одномерный массив из 10 элементов

 

Измерения массивов.

 

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

 

Создание массива.

 

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

 

В MQL4 массивы создаются следующим образом:

 

int myarray[50];  

В данном коде мы объявили одномерный массив.

int - ключевое слово, означает, что массив - массив из integer'ов.

myarray - имя массива - его идентификатор.

[] - эти скобки говорят компилятору, что мы объявляем массив, а не переменную типа int.

50 - размер массива.

 

Теперь у нас есть одномерный массив из 50 int'ов.

 

 

double myarray[5][40];  

В данном коде мы объявили двумерный массив.

Данный двумерный массив состоит из пяти одномерных массивов, каждый из которых состоит из 40 double'ов.

 

Инициализация массива.

 

Инициализация массива (или переменной) означает присвоение значения в строке объявления.

Код ниже инициализирует одномерный массив из 6 integer'ов.

 

int myarray [6] = {1,4,9,16,25,36};
Список элементов массива должен быть заключён в фигурные скобки {}, а затем присвое массиву.

 

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

 

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

 

int myarray [6] = {1,4,9,16,25}; //5 элементов, а размер массива 6
Alert ("myarray[0] " + myarray[0]);
Alert ("myarray[1] " +myarray[1]);
Alert ("myarray[2] " +myarray[2]);
Alert ("myarray[3] " +myarray[3]);
Alert ("myarray[4] " +myarray[4]);
Alert ("myarray[5] " +myarray[5]); 

Данный код заполнит массив так, как указано на рис. 2.

 



Рис. 2 - распечатанные элементы массива

 

 

Присвоение значений элементам массива.

 

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

Второй способ - присвоение значений элементам массива по индексам.

 

Например:

 

int myarray [6] ;
myarray[0] = 1;
myarray[1] = 4;
myarray[2] = 9;
myarray[3] = 16;
myarray[4] = 25;
myarray[5] = 36;

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

 

Ещё пример:

 

int mysourcearray [6] = {1,4,9,16,25,36};
int mycopyarray[6] ;
mycopyarray[0] = mysourcearray [0];
mycopyarray[1] = mysourcearray [1];
mycopyarray[2] = mysourcearray [2];
mycopyarray[3] = mysourcearray [3];
mycopyarray[4] = mysourcearray [4];
mycopyarray[5] = mysourcearray [5];

А здесь мы использовали один массив для присвоения значений другому массиву.

 

 

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

 

Доступ к элементам массива.

 

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

 

Например:

 

Alert ("The first element at myarray is: " + myarray[0]);
Alert ("The second element at myarray is: " + myarray[1]);
Alert ("The third element at myarray is: " + myarray[2]);
Alert ("The fourth element at myarray is: " + myarray[3]);
Alert ("The fifth element at myarray is: " + myarray[4]);
Alert ("The sixth element at myarray is: " + myarray[5]); 

В данном коде myarray[0] - это первый эелемент массива, myarray[1] - второй, и т.д. Последний элемент массива - это myarray[5].

 

Например, для доступа к третьему элементу массива можно написать такой код:

 

int third_element;
third_element = myarray[2];
Alert(third_element);  //9 

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

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

 

© Kirill. [email protected]

post-50854-1404215535,8926_thumb.gif

post-50854-1404215535,9063_thumb.gif

Edited by Programmer
Link to post
Share on other sites
Programmer

Урок 21 - Функции для работы с массивами

 

Всем привет!

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

Сегодня мы рассмотрим все эти ф-ии одну за другой. Всего их 15 штук.

 

1 - ArrayBsearch()

 

Синтаксис:

int ArrayBsearch( double array[], double value, int count=WHOLE_ARRAY, int start=0, int direction=MODE_ASCEND)

 

Описание:

Возвращает индекс первого найденного элемента в первом измерении массива.

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

Функция не может применяться к массивами строк и таймсериям (исключение составляет таймсерия времени открытия бара).

 

Замечание: двоичный поиск обрабатывает только сортированные массивы. Для сортировки числового массива используется функция ArraySort().

 

Параметры:

array[] - Числовой массив для поиска.

value - Значение для поиска.

count - Количество элементов для поиска. По умолчанию, ищется в целом массиве.

start - Начальный индекс для поиска. По умолчанию, поиск начинается с первого элемента.

direction - Направление поиска. Может быть любой из следующих величин:

MODE_ASCEND поиск в направлении возрастания,

MODE_DESCEND поиск в направлении убывания.

 

 

2 - ArrayCopy()

 

Синтаксис:

int ArrayCopy( object&dest[], object source[], int start_dest=0, int start_source=0, int count=WHOLE_ARRAY)

 

Описание:

Копирует один массив в другой. Массивы должны иметь одинаковый тип. Массивы типа double[], int[], datetime[], color[], и bool[], можно копировать как массивы одного типа.

Возвращает количество скопированных элементов.

 

Параметры:

dest[] - Массив-приемник.

source[] - Массив-источник.

start_dest - Начальный индекс для приемного массива. По умолчанию, стартовый индекс - 0.

start_source - Начальный индекс для исходного массива. По умолчанию, стартовый индекс - 0.

count - Количество элементов, которые нужно скопировать. По умолчанию, весь массив (WHOLE_ARRAY).

 

 

3 - ArrayCopyRates()

 

Синтаксис:

int ArrayCopyRates( double&dest_array[], string symbol=NULL, int timeframe=0)

 

Описание:

Копирует в двухмерный массив, вида RateInfo[][6], данные баров текущего графика и возвращает количество скопированных баров, либо -1 в случае неудачи. Первое измерение содержит количество баров. Второе измерение имеет 6 элементов со значениями:

 

0 - время (time),

1 - цена открытия (open),

2 - наименьшая цена (low),

3 - наивысшая цена (high),

4 - цена закрытия (close),

5 - объём (volume).

 

 

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

 

Замечания: обычно массив используется, чтобы передать данные в DLL функцию.

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

 

Параметры:

dest_array[] - Ссылка на двумерный массив.

symbol - Наименование инструмента (символ валютной пары).

timeframe - Период. Может быть любым значением из перечисленных периодов.

 

 

4 - ArrayCopySeries()

 

Синтаксис:

int ArrayCopySeries( double&array[], int series_index, string symbol=NULL, int timeframe=0)

 

Описание:

Копирует массив-таймсерию в пользовательский массив и возвращает количество скопированных элементов.

 

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

 

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

 

Замечание: если series_index - MODE_TIME, то передаваемый в функцию массив должен иметь тип datetime[].

 

Параметры:

array[] - Ссылка на одномерный числовой массив.

series_index - Идентификатор массива-таймсерии. Должен быть одним из перечисленных идентификаторов таймсерий.

symbol - Наименование инструмента (символ валютной пары).

timeframe - Период графика (таймфрейм). Может быть любым значением из перечисленных периодов.

 

 

5 - ArrayDimension()

 

Синтаксис:

int ArrayDimension( object array[])

 

Описание:

Возвращает ранг многомерного массива.

 

Параметры:

array[] - Массив, для которого будет возвращен ранг.

 

 

6 - ArrayGetAsSeries()

 

Синтаксис:

bool ArrayGetAsSeries( object array[])

 

Описание:

Возвращается TRUE, если массив организован как таймсерия (элементы массива индексируются от последнего к первому), иначе возвращается FALSE.

 

Параметры:

array[] - Проверяемый массив.

 

 

7 - ArrayInitialize()

 

Синтаксис:

int ArrayInitialize( double&array[], double value)

 

Описание:

Устанавливает все элементы числового массива в одну величину. Возвращает количество инициализированных элементов.

 

Замечание: не рекомендуется инициализировать индексные буферы в функции init() пользовательских индикаторов, так как они инициализируются автоматически "пустым значением" при распределении и перераспределении буферов.

 

Параметры:

array[] - Числовой массив, который нужно инициализировать.

value - Новая величина, которая нужно установить.

 

 

8 - ArrayIsSeries()

 

Синтаксис:

bool ArrayIsSeries( object array[])

 

Описание:

Возвращается TRUE, если проверяемый массив является массивом-таймсерией (Time[],Open[],Close[],High[],Low[] или Volume[]), иначе возвращается FALSE.

 

Параметры:

array[] - Проверяемый массив.

 

 

9 - ArrayMaximum()

 

Синтаксис:

int ArrayMaximum( double array[], int count=WHOLE_ARRAY, int start=0)

 

Описание:

Поиск элемента с максимальным значением. Функция возвращает позицию максимального элемента в массиве.

 

Параметры:

array[] - Числовой массив, в котором производится поиск.

count - Количество элементов для поиска.

start - Начальный индекс для поиска.

 

 

10 - ArrayMinimum()

 

Синтаксис:

int ArrayMinimum( double array[], int count=WHOLE_ARRAY, int start=0)

 

Описание:

Поиск элемента с минимальным значением. Функция возвращает позицию минимального элемента в массиве.

 

Параметры:

array[] - Числовой массив, в котором производится поиск.

count - Количество элементов для поиска.

start - Начальный индекс для поиска.

 

 

11 - ArrayRange()

 

Синтаксис:

int ArrayRange( object array[], int range_index)

 

Описание:

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

 

Параметры:

array[] - Проверяемый массив

range_index - Индекс измерения.

 

 

12 - ArrayResize()

 

Синтаксис:

int ArrayResize( object&array[], int new_size)

 

Описание:

Устанавливает новый размер в первом измерении массива. При успешном выполнении функция возвращает количество всех элементов, содержащихся в массиве после изменения размера, в противном случае возвращает -1, и массив не меняет размеры.

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

 

Параметры:

array[] - Массив для изменения размеров.

new_size - Новый размер для первого измерения.

 

 

13 - ArraySetAsSeries()

 

Синтаксис:

bool ArraySetAsSeries( double&array[], bool set)

 

Описание:

Устанавливает направление индексирования в массиве. Значение параметра set TRUE устанавливает направление индексирования в обратном порядке, то есть, последний элемент имеет нулевой индекс. Значение FALSE устанавливает нормальное направление индексирования. Функция возвращает предыдущее состояние.

 

Параметры:

array[] - Числовой массив для установки.

set - Направление индексирования массива.

 

 

14 - ArraySize()

 

Синтаксис:

int ArraySize( object array[])

 

Описание:

Возвращает количество элементов массива. Для одномерного массива значение, возвращаемое функцией ArraySize, равно значению ArrayRange(array,0).

 

Параметры:

array[] - Массив любого типа.

 

 

15 - ArraySort()

 

Синтаксис:

int ArraySort( double&array[], int count=WHOLE_ARRAY, int start=0, int sort_dir=MODE_ASCEND)

 

Описание:

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

 

Параметры:

array[] - Числовой массив для сортировки.

count - Количество элементов для сортировки.

start - Начальный индекс.

sort_dir - Направление сортировки массива. Это может быть любым из следующего величин

MODE_ASCEND - сортировка в порядке возрастания

MODE_DESCEND - сортировка в порядке убывания.

 

© Kirill. [email protected]

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

Урок 22 - Глобальные переменные

 

Здравствуйте, товарищи!

Сегодня мы будем изучать глобальные переменные языка MQL4 и набор функций для работы с ними.

 

Что же такое глобальные переменные?

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

 

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

 

Насколько полезны глобальные переменные?

Основная задача глобальных переменных - обеспечить внутритерминальную связь между различными МТС.

 

Например, допустим Вы создали два советника:

 

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

И продаёт, когда скользящая средняя за последние 10 дней пересекает скользящую среднюю за последние 80 дней вниз.

 

Второй работает на часовом графике EURUSD и покупает, когда скользящая средняя за последние 10 часов пересекает скользящую среднюю за последние 80 часов вверх.

И продаёт, когда скользящая средняя за последние 10 часов пересекает скользящую среднюю за последние 80 часов вниз.

 

Вы не хотите, чтобы эти два советника принимали конфликтующие решения - Вы не хотите, чтобы первый советник продавал EURUSD в то время, как второй советник покупает EURUSD.

 

При этом ни один из советников не подозревает о действиях другого. Как же им общаться?

Они могут общаться посредством записи/чтения в/из глобальных переменных.

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

 

Ещё один пример использования глобальных переменных.

Пусть у Вас есть советник, торгующий с одинаковыми настройками на 10 графиках. Чтобы изменить одну какую-то настройку (например, вместо периода 10 поставить период 25), Вам придётся перебрать все 10 графиков и 10 раз изменить эту настройку. Но если советники используют общие глобальные переменные для хранения настроек, то Вам придётся сделать это всего лишь один раз! Лень - двигатель прогресса ;)

 

Функции для работы с глобальными переменными.

В языке MQL4 существует 8 функций для работы с глобальными переменными. Вот эти ф-ии:

 

1 - GlobalVariableCheck()

 

Синтаксис:

bool GlobalVariableCheck( string name)

 

Описание:

Возвращает значение TRUE, если глобальная переменная существует, иначе возвращает FALSE.

Чтобы получить информацию об ошибке, необходимо вызвать функцию GetLastError().

 

Параметры:

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

 

 

2 - GlobalVariableDel()

 

Синтаксис:

bool GlobalVariableDel( string name)

 

Описание:

Удаляет глобальную переменную. При успешном удалении функция возвращает TRUE, иначе FALSE. Чтобы получить информацию об ошибке, необходимо вызвать функцию GetLastError().

 

Параметры:

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

 

 

3 - GlobalVariableGet()

 

Синтаксис:

double GlobalVariableGet( string name)

 

Описание:

Возвращает значение существующей глобальной переменной или 0 в случае ошибки. Чтобы получить информацию об ошибке, необходимо вызвать функцию GetLastError().

 

Параметры:

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

 

 

4 - GlobalVariableName()

 

Синтаксис:

string GlobalVariableName( int index)

 

Описание:

Функция возвращает имя глобальной переменной по порядковому номеру в списке глобальных переменных. Чтобы получить информацию об ошибке необходимо вызвать функцию функцию GetLastError().

 

Параметры:

index - Порядковый номер в списке глобальных переменных. Должен быть большим или равным 0 и меньшим, чем GlobalVariablesTotal().

 

 

5 - GlobalVariableSet()

 

Синтаксис:

datetime GlobalVariableSet( string name, double value)

 

Описание:

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

 

Параметры:

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

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

 

 

6 - 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 - Значение для проверки текущего значения глобальной переменной.

 

 

7 - GlobalVariablesDeleteAll()

 

Синтаксис:

int GlobalVariablesDeleteAll( string prefix_name=NULL)

 

Описание:

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

 

Параметры:

prefix_name - Префикс имени удаляемых глобальных переменных.

 

 

8 - GlobalVariablesTotal()

 

Синтаксис:

int GlobalVariablesTotal( )

 

Описание:

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

 

Параметры:

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

 

© Kirill. [email protected]

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

Урок 23 - Ваша первая DLL (часть 1)

 

Всем привет!

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

 

С недавнего времени всё чаше слышу испуганные восклицания трейдеров "У этого советника есть DLL!", "А куда положить DLL?", "Зачем нужны эти DLL вообще?" и т.д. Что объединяет все эти фразы? Правильно! Загадочное и пугающее слово "D-L-L". Я решил написать пару уроков на эту тему.

Итак, начнём!

 

Что же такое DLL?

 

DLL (англ. Dynamic-link library — динамически подключаемая библиотека) — понятие операционных систем Microsoft Windows и IBM OS/2; динамическая библиотека, позволяющая многократное применение различными программными приложениями. K DLL относятся также элементы управления ActiveX и драйверы. В мире UNIX аналогичные функции выполняют т. н. shared objects («разделяемые объекты»).

Wikipedia.

 

По сути DLL - это такой же код, с тем лишь отличием, что он не является испольняемым. Вы можете более подробно узнать про историю возникновения DLL, их первоначальное назначение и DLL-hell на википедии: http://ru.wikipedia.org/wiki/DLL

 

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

 

В отличие от Forex, в мире программирования применение DLL является широко распространённой практикой. Вы используете DLL каждый день сами того не подозревая! Хотите убедиться? Откройте папку C:\Windows\System32 и посмотрите. Все эти файлы, на которых изображены две шестерёнки - это DLL.

 



 

Рис. 1 - DLL в Windows

 

Вы спросите: как же такое явление как DLL пришло на FOREX? Зачем?

 

Причин несколько.

Во-первых, программная среда MQL4 является сильно урезанной по отношению к тому же visual C, не говоря о C++. Банальное отсутствие WinAPI уже о многом говорит. Внедрение DLL позволяет использовать различные ф-ии, недоступные рядовому MQL4-программисту. DLL переносят Вас на следующую ступень развития. С этой точки зрения, есть забавная аналогия: если MQL4-программист - это рабочий гоблин, то MQL4-программист, использующий DLL - это боевой орк :D

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

Какие Вы знаете каналы общения Вашей программы с внешним миром (т.е. не с терминалом MT4)? Правильно - файлы. Но файлы - это работа с жёстким диском, а это медленно - найти диск, дорожку, установить головку на нужный сектор... А котировки всё идут и идут. На Forex иногда необходимо принимать решения молниеоносно! А вот DLL как раз позволяют это сделать! С помощью DLL можно организовать взаимодействие MT4 и того же MathLab, где, поверьте мне, все вычисления происходят в десятки раз быстрее.

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

 

Вторая причина, которая, на мой взгляд, была основной при приходе DLL на Forex - ВЗЛОМ.

Многие владельцы коммерческих советников и индикаторов хотят защитить свои детища. И ни для кого уже не секрет, что обыкновенные компилки *.ex4 легко превращаются декомпилкой в исходники *.mq4. А вот DLL-ку сломать сложнее. Для этого надо ковыряться в ассемблерном коде. А специалистов, умеющих это делать, среди трейдеров не так уж много.

 

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

На следующем занятии мы с Вами напишем свою первую DLL-ку. Не пропустите!

 

© Kirill. [email protected]

post-50854-1404214279,0992_thumb.gif

Edited by Programmer
  • Downvote 1
Link to post
Share on other sites
Programmer

Урок 24 - Ваша первая DLL (часть 2)

 

Приветствую!

Сегодня мы напишем нашу первую DLL'ку. В бой!

 

Инструменты, которые Вам понадобятся.

 

DLL Вы можете написать во многих разных средах. Но у меня под рукой сейчас Microsoft Visual Studio 6, поэтому мы будем работать в ней, а писать мы будем на Visual C++.

Создадим DLL, которая выводит окошко с фразой "Hello World!".

 



Рис. 1 - Microsoft Visual C++ 6.0

 

 

Hello World!

 

1 - Открываем Visual C++ (рис. 2)

 



Рис. 2 - Окошко Microsoft Visual C++

 

 

2- Выбираем меню: File -> New. Всплывает диалоговое окно, показанное на рис. 3

 



Рис. 3 - Диалоговое окно

 

 

3 - Необходимо выбрать "MFC AppWizard (dll)" и вбить имя проекта в поле "Project Name" (рис. 4) и нажать "OK".

Замечание: В принципе, можно выбрать "Win32 Dynamic-Link Library" вместо "MFC AppWizard (dll)", но это будет означать, что Вы не сможете использовать тип строк "CString". Cstring - это тип строк в MFC, который заметно упрощает жизнь.

 



Рис. 4 - MFC dll

 

 

4 - Всплывёт ещё один диалог, который попросит Вас ввести дополнительные опции. Оставьте все опции по умолчанию и просто нажмите "Finish" (рис. 5). Затем всплывёт информационное окно - просто нажмите "OK".

 



Рис. 5 - Опции проекта

 

 

5 - Поздравляю! Теперь у Вас есть новый проект под названием MyFirstDLL (или как Вы его назвали), в котором можно начать писать нашу первую DLL. Откройте файл "MyFirstDLL.cpp" и взгляните на него (рис. 6).

 



Рис. 6 - MyFirstDLL.cpp

 

 

Сейчас мы добавим немного кода в этот файл:

 

#define MT4_EXPFUNC __declspec(dllexport)

Эту строчку необходимо вставить сразу после строчек:

 

#include "stdafx.h"
#include "MyFirstDLL.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

А код:

 

MT4_EXPFUNC void __stdcall Hello(char* say)
{
   MessageBox(NULL,say,"(c) StockProgrammer",NULL);
}

Добавьте в самый конец файла MyFirstDLL.cpp

 

7 - Теперь у нас есть ф-я "Hello", которая принимает строчку для отображения и ничего не возвращает на выходе (void).

Но чтобы мир узнал о нашей ф-ии мы должны указать её название в .def файле.

Теперь откройте файл "MyFirstDLL.def" (рис. 7) и добавьте следующую строчку в самый конец файла:

 

Hello



Рис. 7 - MyFirstDLL.def

 

 

post-50854-1404214292,0125_thumb.gif

post-50854-1404214292,0983_thumb.gif

post-50854-1404214292,1906_thumb.gif

post-50854-1404214292,305_thumb.gif

post-50854-1404214292,3884_thumb.gif

post-50854-1404214292,4733_thumb.gif

post-50854-1404214292,5652_thumb.gif

Edited by Programmer
Link to post
Share on other sites
Programmer

Откомпилируйте DLL нажатием клавиши F7. Если Вы сделали всё правильно, то Вы не получите никаких предупреждений и сообщений об ошибках. Найдите файл "MyFirstDLL.dll" в подпапке Debug в папке проекта MyFirstDLL (рис. 8).

 



Рис. 8 - MyFirstDLL.dll

 

 

Знакомый занчок? Правильно - это тот же самый, что мы рассматривали в прошлом уроке.

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

 

До встречи!

Кирилл.

 

© Kirill. [email protected]

post-50854-1404214292,6492_thumb.gif

Edited by Programmer
Link to post
Share on other sites
Programmer

Урок 25 - Ваша первая DLL (часть 3)

 

Привет, программисты!

Думаю, раз Вы дошли до этого урока, я Вас могу смело так называть ;)

В предыдущем уроке мы пошагово построили свою первую DLL, а в этом уроке мы будем её тестировать!

 

Тестируем нашу MyFirstDLL.dll

 

Создайте скрипт MQL4 с названием "Hello.mq4" - надеюсь, Вы помните, как это делается. Если нет - перечитайте урок 18.

Запрограммируйте скрипт следующим образом:

 

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

#import "MyFirstDLL.dll"
  void Hello(string say);
#import

int start()
{
  Hello ("Hello World!");
  return(0);
}

Нажмите F5 и откомпилируйте скрипт.

Открываем терминал. Далее, в настройках терминала необходимо разрешить импорт DLL: в меню "Сервис" выбрать пункт "Настройки", далее во вкладке "Советники" необходимо отметить галочкой пункт "Разрешить импорт DLL".

 

Запускаем наш скрипт.



Рис. 1 - Окошко Windows

 

Воиля! Что мы видим?! Скрипт вызвал окошко Windows, которое никакими стандартными методами MQL4 не вызвать. Заголовок окошка © StockProgrammer (это я) а сообщение - Hello World!

Вот так вот! Кстати, хочу обратить Ваше внимание на то, что теперь авторские права, выраженные в виде заголовка окошка © StockProgrammer - без взлома DLL убрать никто не сможет. Это к вопросу, который мы обсуждали в первом уроке данного цикла, о том, зачем нужны DLL.

 

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

Кирилл.

 

© Kirill. [email protected]

post-50854-1404214293,4992_thumb.gif

Link to post
Share on other sites
Programmer

Урок 26 - Скриншот (часть 1)

 

Всем привет!

 

Сегодня мы начнём изучать очень важную часть MQL4 - ф-ю ScreenShot(), и в следуюшем уроке мы создадим весьма полезный советник.

Для начала рассмотрим, что такое скриншот в самом MetaTrader.

 

MetaTrader Screen Shot

В торговом терминале Вы можете легко сохранить любой график со всеми нанесёнными на него индикаторами.

Просто шёлкните правой кнопкой мыши на нужный график и выбирите пункт меню "Сохранить как рисунок".

 



Рис. 1 - всплывающее меню

 

При выборе данной опции Вам предлагается три варианта дальнейших действий:

 



Рис. 2 - выбор области для сохранения

 

1. "Активная рабочая область" - будет сделан снимок всего терминала MetaTrader. Это то же самое, что нажать ALT + PrintScreen в Windows.

2. "Активный график (как есть)" - данная опция сохранит график в том виде, который сейчас перед Вами.

3. "Активный график AAA x BBB pixels" - позволяет указать высоту и ширину картинки.

 

Затем Вас попросят выбрать папку для сохранения изображения. Доступно два формата: BMP и GIF.

 



Рис. 3 - выбор папки и типа изображения

 

Сохранение Screen Shot в MQL4

Сохранение снимков терминала в MQL$ немного сложнее. Но плюсом является то, что всё автоматизировано и происходит намного быстрее.

Для сохранения скрин-шотов в MQL4 нужна всего лишь одна ф-я - это WindowScreenShot().

 

WindowScreenShot()

 

Синтаксис:

bool WindowScreenShot( string filename, int size_x, int size_y, int start_bar=-1, int chart_scale=-1, int chart_mode=-1)

 

Описание:

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

Скриншот сохраняется в папке каталог_терминала\experts\files (каталог_терминала\tester\files в случае тестирования эксперта) или ее подпапках.

 

Параметры:

filename - Имя файла для скриншота.

size_x - Ширина скриншота в пикселах.

size_y - Высота скриншота в пикселах.

start_bar - Номер первого видимого бара на скриншоте. Если указано значение 0, то скриншот снимается с текущего первого видимого бара. Если значение не указано, или указано отрицательное значение, то делается скриншот конца графика с учётом правого отступа.

chart_scale - Масштаб графика, выводимого на скриншот. Может принимать значение от 0 до 5. Если значение не указано, или указано отрицательное значение, то используется текущий масштаб графика.

chart_mode - Вид отображения графика. Может принимать значения: CHART_BAR (0 - последовательность баров), CHART_CANDLE (1 - японские свечи), CHART_LINE (2 - линия по ценам закрытия). Если значение не указано, или указано отрицательное значение, то график выводится в своем текущем виде.

 

Пример:

  int lasterror=0;
 //---- тестер закрыл одну или несколько позиций
 if(IsTesting() && ExtTradesCounter<TradesTotal())
   {
    //---- снимем скриншот для проверки
    if(!WindowScreenShot("shots\\tester"+ExtShotsCounter+".gif",640,480))
       lasterror=GetLastError();
    else ExtShotsCounter++;
    ExtTradesCounter=TradesTotal();
   }

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

До встречи!

Кирилл.

 

© Kirill. [email protected]

post-50854-1404214307,5522_thumb.gif

post-50854-1404214307,6219_thumb.gif

post-50854-1404214307,6813_thumb.gif

Link to post
Share on other sites
Programmer

Урок 27 - Скриншот (часть 2)

 

Приветствую!

В предыдущем уроке мы рассмотрели ф-ю ScreenShot() и я обещал показать код, который бы её грамотно утилизировал.

 

Когда-то я писал советника, который почему всегда работал немножко не так. Где-то была ошибка, но я никак не мог её отловить. И поэтому, я решил вставить в код ф-ю, которая бы делала изображение графика в определённые моменты. Это помогло мне найти ошибку.

 

Итак, посмотрим.

 

int start()
{
    .....
   TakeScreenShot("OPEN_BUY");
    .....
}

void TakeScreenShot(string type)
{
  int count = 1;

  if(!GlobalVariableCheck("ssc"))
  {
   GlobalVariableSet("ssc",1);
   count = 1;
  }
  else
  {
     count = GlobalVariableGet("ssc") + 1;
     GlobalVariableSet("ssc",count); 
  }
  string filename = "MyEA\\" + "MyEA_" + Symbol() + "_" + type + "_" + DoubleToStr(count,0) + ".gif";
  ScreenShot(filename,640,480);
}

 

Как же эта ф-я работает?

 

Функция TakeScreenShot() просто делает следуюшее:

1 - Она использует глобальную переменную ssc, чтобы запомнить сколько уже было сделано скриншотов. Зачем? Чтобы нумеровать все снимки.

2 - Она делает снимки тек. графика каждый раз, когда Вы её вызываете.

3 - Она сохраняет все файлы в одну папку "MyEA".

 

Когда вызывать ф-ю TakeScreenShot()?

 

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

 

Я надеюсь, что это была полезная статья и код Вы тоже найдёте полезным!

 

© Kirill. [email protected]

Link to post
Share on other sites
Programmer

Урок 28 - Загадка магического числа

 

Всем привет!

Из-за огромного количества вопросов я решил посвятить отдельный урок магическиму числу Magic Number. Данный вопрос является насущным почти для каждого forex-программиста.

 

Что такое Magic Number и для чего оно нужно?

 

Magic Number является одним из параметров (опциональным параметром) ф-ии OrderSend():

 

int OrderSend( string symbol, int cmd, double volume, double price, int slippage, double stoploss, double takeprofit, string comment=NULL, int magic=0, datetime expiration=0, color arrow_color=CLR_NONE)

 

Ф-ия OrderSend() используется для открытия рыночных и отложенных ордеров. Параметр Magic (9й параметр) является целочисленным (integer) параметром-идентификатором. Используя Magic-номера, Вы можете отличать ордера, которые открыл данный советник от ордеров открытых другими советниками и ручных ордреов. Для иллюстрации значимости Magic Number для программиста мы рассмотрим пример.

 

 

Пример использования Magic Number

 

Наш пример - это советник, который открывает 4 ордера на покупку (без всякого сигнала - это всего лишь пример) и закрывает их, когда суммарная прибыль достигает 100 единиц валюты депозита или суммарный убыток достигает -100 единиц валюты депозита .

 

Именно Magic Number позволит нашему советнику определять открыли ли он какой-то конкретный ордер или этот ордер был открыт другим советником или вручную.

 

Код:

 

extern int      MagicNumber = 101030;

extern double    Profit = 100;
extern double    Loss = -100;

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

int deinit() 
{
  return(0);
}
int start()
 {
  int cnt, ticket, total,n;

  if(Bars<100) {Print("bars less than 100"); return(0);}

  total  = OrdersTotal();

  if(total < 1) 
    {
        OrderSend(Symbol(),OP_BUY,1,Ask,5,0,Ask+100*Point,"MagicNumber demo",MagicNumber,0,Green);
        OrderSend(Symbol(),OP_BUY,1,Ask,5,0,Ask+100*Point,"MagicNumber demo",MagicNumber,0,Green);
        OrderSend(Symbol(),OP_BUY,1,Ask,5,0,Ask+100*Point,"MagicNumber demo",MagicNumber,0,Green);
        OrderSend(Symbol(),OP_BUY,1,Ask,5,0,Ask+100*Point,"MagicNumber demo",MagicNumber,0,Green);
        return(0);
    }

  ProfitLossMonitor();    

  return(0);
 }


void ProfitLossMonitor()
{
     int total  = OrdersTotal();
     double MyCurrentProfit=0;
     for (int cnt = 0 ; cnt < total ; cnt++)
     {
        OrderSelect(cnt,SELECT_BY_POS,MODE_TRADES);
        if (OrderMagicNumber() == MagicNumber)
           MyCurrentProfit += OrderProfit();
     }

     if(MyCurrentProfit>=Profit)
        CloseAll();

     if(MyCurrentProfit<=Loss)
        CloseAll();

}

void CloseAll()
{
     int total  = OrdersTotal();
     for (int cnt = total-1 ; cnt >= 0 ; cnt--)
     {
        OrderSelect(cnt,SELECT_BY_POS,MODE_TRADES);
        if (OrderMagicNumber() == MagicNumber)
           if(OrderType()==OP_BUY)
              OrderClose(OrderTicket(),OrderLots(),Bid,5,Violet);
           if(OrderType()==OP_SELL)   
              OrderClose(OrderTicket(),OrderLots(),Ask,5,Violet);
     }
}

 

 

Разбор кода

 

int MagicNumber = 101030;

 

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

 

Затем советник открывает 4 ордера на покупку, если они ещё не открыты, указывая в них свой MagicNumber. После чего запускается ф-я ProfitLossMonitor(), которая следит за суммарной прибылью и убытком. Но как же советник знает, какие именно ордера ему надо проверять? Как он отделяет свои ордера от чужих ордеров? Вот тут и вступает в игру MagicNumber. Перебирая все открытые ордера советник проверяет их Magic-номера:

 

if (OrderMagicNumber() == MagicNumber)

 

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

 

Когда суммарная прибыль равняется 100$ или суммарный убыток равняется -100$, советник вызывает ф-ю CloseAll(), которая закрывает все открытые советником ордера. И опять, советник выделяет свои ордера, используя переменную MagicNumber.

 

 

Заключение

 

Вы можете себе представить работу подобных советников без Magic-номеров? Как бы они считали суммарную прибыль или убыток своих ордеров? Как бы они закрывали именно свои ордера?

 

Именно для этого и нужны Magic-номера.

До скорых встреч!

 

© Kirill. [email protected]

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

Урок 29 - Тайна нового бара (часть 1)

 

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

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

 

 

Когда образовывается новый бар?

 

Образование нового бара (красный на рис. 1) начинается тогда, когда завершается текущий бар (желтый на рис. 1). Этот момент зависит от таймфрейма графика, на котором работает советник. Например, на M15 новый бар образовывается каждые 15 минут, а на D1 - только в начале каждого дня.

 



Рис 1 - текущий и будущий новый бар

 

 

Как программно определить образование нового бара?

 

Код:

 

bool NewBar()
{
  static datetime lastbar = 0;
  datetime curbar = Time[0];
  if(lastbar!=curbar)
  {
     lastbar=curbar;
     return (true);
  }
  else
  {
     return(false);
  }
}  

 

Данный код весьма легко понять. При вызове ф-ии NewBar() происходит запись значения Time[0] - времени открытия текущего бара - в переменную curbar. Переменная lastbar является статической и хранит время открытия последнего нового бара. Советник сравнивает значения curbar и lastbar. Значения будут разные только в том случае, если терминал MT4 начал новый бар, т.е. время открытия текущего бара стала не равно сохранённому в переменной lastbar значению. В этом случае ф-я определяет открытие нового бара и записывает значение Time[0] в переменную lastbar.

 

На различных форекс-форумах Вы найдёте различные вариации данного кода. С моей точки зрения это самый простой вариант, а как многие уже, наверное, поняли из моих уроков - в программировании моя философия звучит так: "чем проще, тем лучше" ;)

 

 

Как заставлять часть кода работать только при наступлении нового бара?

 

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

 

int start()
 {    
  .......  
  if (NewBar() == true)
  {
     Print("New Bar");
  }   
 .......
 }

 

PS: помните про пустые скобки после слова NewBar. Ведь это ф-ия, которая не принимает никаких параметров.

 

Далеко не уходите! :D

В следующий раз мы разберём преимущества работы на новом баре.

 

© Kirill. [email protected]

post-50854-1404214424,9275_thumb.gif

Link to post
Share on other sites
Programmer

Урок 30 - Тайна нового бара (часть 2)

 

Всем привет!

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

У меня в институте был знакомый преподаватель, который на любые мои крутые идеи всегда задавал один вопрос "Нафига?". Если я знал ответ, то обсуждение продолжалось. Если нет - то нет :D

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

Итак.

 

Преимущества работы на открытии нового бара

 

1. Экономия ресурсов

Многие торговые системы в своём анализе используют сформировавшиеся сигналы прошлых баров. К данному классу в том числе относится и всё множество "запаздывающих" торговых систем. Алгоритм принятия решений, встроенный в подобные системы в течении жизни текущего бара всегда будет выдавать одно и то же решение. Действительно, анализирется динамика цены и её производных, имевшая место ДО начала текущего бара, следовательно, всё, что происходит НА текущем баре, никак на анализ повлиять не может, т.к. не влияет на прошлое.

 

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

 

2. Упрощение жизни программиста

Иногда возникают следующие ситуации: Вы запрограммировали какое-то условие и хотите, чтобы оно проверялось только один раз на каждом баре. Например: если на предыдущем баре MA(8) > MA(16) и одновременно RSI(5) > 80, то BUY. Если Вы просто вставите это условие в код, то в случае его выполнения, у Вас откроется столько сделок BUY, сколько было тиков в текущем баре (т.к. ф-я start() срабатывает на каждом тике). Значит, Вам необходимо внести ограничени - проверять данное условие только один раз на каждом баре. Вместо того, чтобы заводить счётчик, затем сбрасывать его на каждом новом баре, можно просто воспльзоваться кодом, который мы рассмотрели в прошлом уроке, и заставить советника работать только на первом тике каждого нового бара.

 

3. Ускорение оптимизации

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

 



Рис 1 - типы оптимизации

 

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

 

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

До встречи!

 

© Kirill. [email protected]

post-50854-1404214446,4155_thumb.gif

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

Урок 31 - Строковые функции

 

Всех приветствую! Я заметил, что у многих пользователей MQL4 возникают проблемы со строками. С числами всё в порядке, а вот использование слов в программировании вызывает затруднение :) Сегодня мы это исправим!

 

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

 

Строковые переменные - String

 

Переменная типа String - это набор символов, заключённых в двойные кавычки.

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

Символ NULL - это спец. символ (в таблице ASCII ему соответсвует число 0), используемый для обозначения конца строки.

На рис. 1 схематически изображено, как хранится в памяти строка "hello".

 



Рис. 1 - Символы в массиве

 

MQL4 ограничивает размер строки 255 символами.

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

 

Ключевое слово string используется для созания переменных типа String.

 

Например:

 

string str1 = "Hello world! StockProgrammer here!”;

string str2 = "Copyright © \"StockProgrammer\".";  //Notice the use of (") character.

string str3 = "1234567890";

Теперь перейдём к изучению функций MQL4, предназначенных исключительно для работы со строковыми переменными:

 

1 - StringConcatenate()

 

Синтаксис:

string StringConcatenate( ...)

 

Описание:

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

 

Параметры преобразуются в строки по тем же правилам, что и в функциях Print(), Alert() и Comment(). Возвращаемая строка получается в результате конкатенации строк, преобразованных из параметров функции.

 

Функция StringConcatenate() работает быстрее и экономнее по памяти, чем связывание строк при помощи операций сложения (+).

 

 

Параметры:

... - Любые значения, разделенные запятыми.

 

 

2 - StringFind()

 

Синтаксис:

int StringFind( string text, string matched_text, int start=0)

 

Описание:

Поиск подстроки. Возвращает номер позиции в строке, с которой начинается искомая подстрока, либо -1, если подстрока не найдена.

 

Параметры:

text - Строка, в которой производится поиск.

matched_text - Искомая подстрока.

start - Позиция в строке, с которой должен быть начат поиск.

 

 

3 - StringGetChar()

 

Синтаксис:

int StringGetChar( string text, int pos)

 

Описание:

Возвращает значение символа, расположенного в указанной позиции строки.

 

Параметры:

text - Строка.

pos - Позиция символа в строке. Может быть от 0 до StringLen(text)-1.

 

 

4 - StringLen()

 

Синтаксис:

int StringLen( string text)

 

Описание:

Возвращает число символов в строке.

 

Параметры:

text - Строка для вычисления длины.

 

 

5 - StringSetChar()

 

Синтаксис:

string StringSetChar( string text, int pos, int value)

 

Описание:

Возвращает копию строки с измененным значением символа в указанной позиции.

 

Параметры:

text - Строка для изменения.

pos - Позиция символа в строке. Может быть от 0 до StringLen(text).

value - Символьный код ASCII.

 

 

6 - StringSubstr()

 

Синтаксис:

string StringSubstr( string text, int start, int length=0)

 

Описание:

Извлекает подстроку из текстовой строки, начинающейся c указанной позиции.

Функция возвращает копию извлеченной подстроки, если возможно, иначе возвращается пустая строка.

 

Параметры:

text - Строка, из которой должна быть извлечена подстрока.

start - Начальная позиция подстроки. Может быть от 0 до StringLen(text)-1.

length - Длина извлекаемой подстроки. Если значение параметра меньше или равно 0 либо параметр не задан, то будет извлекаться подстрока, начиная с указанной позиции и до конца строки.

 

 

7 - StringTrimLeft()

 

Синтаксис:

string StringTrimLeft( string text)

 

Описание:

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

 

Параметры:

text - Строка, которая будет срезана слева.

 

 

8 - StringTrimRight()

 

Синтаксис:

string StringTrimRight( string text)

 

Описание:

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

 

Параметры:

text - Строка, которая будет срезана справа.

 

 

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

 

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

 

До встречи!

© Kirill. [email protected]

post-50854-1404214498,153_thumb.gif

Link to post
Share on other sites
Programmer

Урок 32 - Типы ордеров OrderSend

 

Добрый день друзья!

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

 

Поехали!

 

OP_BUY

 

Ask

Bid-StopLoss

Bid+TakeProfit

 

Вы открываете BUY по текущей цене Ask валютной пары!

Вы выставляете StopLoss ПОД (-) текущей ценой Bid!

Вы выставляете TakeProfit НАД (+) текущей ценой Bid!

 

Пример:

 

OrderSend(Symbol(),OP_BUY,Lots,Ask,slippage,Bid-StopLoss*Point,Bid+TakeProfit*Point,”comment”,0,0,Green);

 

 

OP_SELL

 

Bid

Ask+StopLoss

Ask-TakeProfit

 

Вы открываете SELL по текущей цене Bid валютной пары!

Вы выставляете StopLoss НАД (+) текущей ценой Ask!

Вы выставляете TakeProfit ПОД (-) текущей ценой Ask!

 

Пример:

 

OrderSend(Symbol(),OP_SELL,Lots,Bid,slippage,Ask+StopLoss*Point,Ask-TakeProfit*Point, ”comment”,0,0,Green);

 

 

OP_BUYLIMIT

 

Ask-Level

Bid-Level-StopLoss

Bid-Level+TakeProfit

 

Вы открываете BUYLIMIT по будущему ценовому уровню ПОД текущей ценой Ask валютной пары!

Вы выставляете StopLoss ПОД (-) под уровнем Bid-Level!

Вы выставляете TakeProfit НАД (+) под уровнем Bid-Level!

 

Пример:

 

OrderSend(Symbol(),OP_BUYLIMIT,Lots,Ask-Level*Point,slippage,(Bid-Level*Point)-StopLoss*Point,( Bid-Level*Point)+TakeProfit*Point,”comment”,0,0,Green);

 

 

OP_BUYSTOP

 

Ask+Level

Bid+Level-StopLoss

Bid+Level+TakeProfit

 

Вы открываете BUYSTOP по будущему ценовому уровню НАД текущей ценой Ask валютной пары!

Вы выставляете StopLoss ПОД (-) под уровнем Bid+Level!

Вы выставляете TakeProfit НАД (+) под уровнем Bid+Level!

 

Пример:

 

OrderSend(Symbol(),OP_BUYSTOP,Lots,Ask+Level*Point,slippage,(Bid+Level*Point)-StopLoss*Point,( Bid+Level*Point)+TakeProfit*Point,”comment”,0,0,Green);

 

 

OP_SELLLIMIT

 

Bid+Level

Ask+Level+StopLoss

Ask+Level-TakeProfit

 

Вы открываете SELLLIMIT по будущему ценовому уровню НАД текущей ценой Bid валютной пары!

Вы выставляете StopLoss НАД (+) под уровнем Ask+Level!

Вы выставляете TakeProfit ПОД (-) под уровнем Ask+Level!

 

Пример:

 

OrderSend(Symbol(),OP_SELLLIMIT,Lots,Bid+Level*Point,slippage,(Ask+Level*Point)+StopLoss*Point,(Ask+Level*Point)-TakeProfit*Point,”comment”,0,0,Green);

 

 

OP_SELLSTOP

 

Bid-Level

Ask-Level+StopLoss

Ask-Level-TakeProfit

 

Вы открываете SELLSTOP по будущему ценовому уровню ПОД текущей ценой Bid валютной пары!

Вы выставляете StopLoss НАД (+) под уровнем Ask-Level!

Вы выставляете TakeProfit ПОД (-) под уровнем Ask-Level!

 

Пример:

 

OrderSend(Symbol(),OP_SELLSTOP,Lots,Bid-Level*Point,slippage,(Ask-Level*Point)+StopLoss*Point,(Ask-Level*Point)-TakeProfit*Point,”comment”,0,0,Green);

 

Уверен, что теперь всё встанет на свои места!

 

До следующего урока!

© Kirill. [email protected]

Link to post
Share on other sites
Programmer

Урок 33 - Введение в WinAPI

 

Всем привет!

Сегодня будет весьма инетересный урок. Мы немного познакомимся с WinAPI.

WinAPI - это базовый набор функций для прогнраммирования приложений Windows. Сильно вдаваться в эту тему мы не будем - мы ведь не разработчики ПО. Однако, некоторые функции могут оказаться полезными при программировании программ даже на MQL4. Поскольку язык MQL4 весьма ограничен, некоторые задачи выполнимы только непосредственно через работу с окнами Windows.

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

 

Мы разберём пример импользования WinAPI для автообновления активного графика с определённой частотой. Для решения данной задачи наша программа будет посылать в терминал MT4 коды клавиатурных символов. Поэтому мы сначала хотим определиться, как мы обновляем график, когда делаем это руками.

 

Мы идём в меню графика и нажимаем кнопку "Обновить". Это то же самое, что нажать CTRL+C а потом R. См. Рис1.

 



Рис. 1 - Ручное обновление графика

 

 

Теперь посмотрим, как молжно перепрограммировать обыкновенный MACD индикатор в MACD индикатор, который автоматически обновляет график каждые 60 секунд.

 

Код:

 

//+------------------------------------------------------------------+
//|                                             AutoRefreshChart.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 KEYEVENTF_EXTENDEDKEY          0x0001
#define KEYEVENTF_KEYUP                0x0002

#define VK_0   48
#define VK_1   49
#define VK_2   50
#define VK_3   51
#define VK_4   52
#define VK_5   53
#define VK_6   54
#define VK_7   55
#define VK_8   56
#define VK_9   57
#define VK_A   65
#define VK_B   66
#define VK_C   67
#define VK_D   68
#define VK_E   69
#define VK_F   70
#define VK_G   71
#define VK_H   72
#define VK_I   73
#define VK_J   74
#define VK_K   75
#define VK_L   76
#define VK_M   77
#define VK_N   78
#define VK_O   79
#define VK_P   80
#define VK_Q   81
#define VK_R   82
#define VK_S   83
#define VK_T   84
#define VK_U   85
#define VK_V   86
#define VK_W   87
#define VK_X   88
#define VK_Y   89
#define VK_Z   90

#define VK_LBUTTON         1     //Left mouse button
#define VK_RBUTTON         2     //Right mouse button
#define VK_CANCEL          3     //Control-break processing
#define VK_MBUTTON         4     //Middle mouse button (three-button mouse)
#define VK_BACK            8     //BACKSPACE key
#define VK_TAB             9     //TAB key
#define VK_CLEAR           12    //CLEAR key
#define VK_RETURN          13    //ENTER key
#define VK_SHIFT           16    //SHIFT key
#define VK_CONTROL         17    //CTRL key
#define VK_MENU            18    //ALT key
#define VK_PAUSE           19    //PAUSE key
#define VK_CAPITAL         20    //CAPS LOCK key
#define VK_ESCAPE          27    //ESC key
#define VK_SPACE           32    //SPACEBAR
#define VK_PRIOR           33    //PAGE UP key
#define VK_NEXT            34    //PAGE DOWN key
#define VK_END             35    //END key
#define VK_HOME            36    //HOME key
#define VK_LEFT            37    //LEFT ARROW key
#define VK_UP              38    //UP ARROW key
#define VK_RIGHT           39    //RIGHT ARROW key
#define VK_DOWN            40    //DOWN ARROW key
#define VK_PRINT           42    //PRINT key
#define VK_SNAPSHOT        44    //PRINT SCREEN key
#define VK_INSERT          45    //INS key
#define VK_DELETE          46    //DEL key
#define VK_HELP            47    //HELP key
#define VK_LWIN            91    //Left Windows key (Microsoft® Natural® keyboard)
#define VK_RWIN            92    //Right Windows key (Natural keyboard)
#define VK_APPS            93    //Applications key (Natural keyboard)
#define VK_SLEEP           95    //Computer Sleep key
#define VK_NUMPAD0         96    //Numeric keypad 0 key
#define VK_NUMPAD1         97    //Numeric keypad 1 key
#define VK_NUMPAD2         98    //Numeric keypad 2 key
#define VK_NUMPAD3         99    //Numeric keypad 3 key
#define VK_NUMPAD4         100   //Numeric keypad 4 key
#define VK_NUMPAD5         101   //Numeric keypad 5 key
#define VK_NUMPAD6         102   //Numeric keypad 6 key
#define VK_NUMPAD7         103   //Numeric keypad 7 key
#define VK_NUMPAD8         104   //Numeric keypad 8 key
#define VK_NUMPAD9         105   //Numeric keypad 9 key
#define VK_MULTIPLY        106   //Multiply key
#define VK_ADD             107   //Add key
#define VK_SEPARATOR       108   //Separator key
#define VK_SUBTRACT        109   //Subtract key
#define VK_DECIMAL         110   //Decimal key
#define VK_DIVIDE          111   //Divide key
#define VK_F1              112   //F1 key
#define VK_F2              113   //F2 key
#define VK_F3              114   //F3 key
#define VK_F4              115   //F4 key
#define VK_F5              116   //F5 key
#define VK_F6              117   //F6 key
#define VK_F7              118   //F7 key
#define VK_F8              119   //F8 key
#define VK_F9              120   //F9 key
#define VK_F10             121   //F10 key
#define VK_F11             122   //F11 key
#define VK_F12             123   //F12 key
#define VK_F13             124   //F13 key
#define VK_NUMLOCK         144   //NUM LOCK key
#define VK_SCROLL          145   //SCROLL LOCK key
#define VK_LSHIFT          160   //Left SHIFT key
#define VK_RSHIFT          161   //Right SHIFT key
#define VK_LCONTROL        162   //Left CONTROL key
#define VK_RCONTROL        163   //Right CONTROL key
#define VK_LMENU           164   //Left MENU key
#define VK_RMENU           165   //Right MENU key

//---- indicator settings
#property  indicator_separate_window
#property  indicator_buffers 2
#property  indicator_color1  Silver
#property  indicator_color2  Red
//---- indicator parameters
extern int FastEMA=12;
extern int SlowEMA=26;
extern int SignalSMA=9;
extern int SecondsToRefresh = 60;
//---- indicator buffers
double     ind_buffer1[];
double     ind_buffer2[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
 {
//---- drawing settings
  SetIndexStyle(0,DRAW_HISTOGRAM,STYLE_SOLID,3);
  SetIndexDrawBegin(1,SignalSMA);
  IndicatorDigits(MarketInfo(Symbol(),MODE_DIGITS)+1);
//---- indicator buffers mapping
  if(!SetIndexBuffer(0,ind_buffer1) && !SetIndexBuffer(1,ind_buffer2))
     Print("cannot set indicator buffers!");
//---- name for DataWindow and indicator subwindow label
  IndicatorShortName("MACD("+FastEMA+","+SlowEMA+","+SignalSMA+")");
  SetIndexLabel(0,"MACD");
  SetIndexLabel(1,"Signal");
//---- initialization done
  return(0);
 }
//+------------------------------------------------------------------+
//| Moving Averages Convergence/Divergence                           |
//+------------------------------------------------------------------+
int start()
 {
  int limit;
  int counted_bars=IndicatorCounted();
//---- check for possible errors
  if(counted_bars<0) return(-1);
//---- last counted bar will be recounted
  if(counted_bars>0) counted_bars--;
  limit=Bars-counted_bars;
//---- macd counted in the 1-st buffer
  for(int i=0; i<limit; i++)
     ind_buffer1[i]=iMA(NULL,0,FastEMA,0,MODE_EMA,PRICE_CLOSE,i)-iMA(NULL,0,SlowEMA,0,MODE_EMA,PRICE_CLOSE,i);
//---- signal line counted in the 2-nd buffer
  for(i=0; i<limit; i++)
     ind_buffer2[i]=iMAOnArray(ind_buffer1,Bars,SignalSMA,0,MODE_SMA,i);
//---- done
  Refresh(SecondsToRefresh);
  return(0);
 }

void Refresh(int seconds = 60)
{
   datetime cur = CurTime();
   static datetime last = 0;

   if (last == 0) {last = cur;}

   if (cur > last + seconds)
   {
     last = cur;

     SendKey (VK_MENU);
     SendKey (VK_C);
     ReleaseKey(VK_MENU);
     ReleaseKey(VK_C);
     SendKey (VK_R);
     ReleaseKey(VK_R);

   }
}

void SendKey(int key,bool release = false)
{
  keybd_event (key, 0, 0, 0);
  if(release)
      keybd_event (key, 0, KEYEVENTF_KEYUP, 0);

}
void ReleaseKey(int key)
{
  keybd_event (key, 0, KEYEVENTF_KEYUP, 0);
}

(См. продолжение)

post-50854-1404214525,5437_thumb.gif

Link to post
Share on other sites
Programmer

(продолжение: Урок 33 - Введение в WinAPI )

 

Разбор кода:

 

Первым делом мы импортируем Windows-функцию keybd_event из ДЛЛ-ки user32.dll. Затем с помощью дескриптора #define вводим интуитивно-понятные обозначения для констант, используемых упомянтой функцией.

 

Основной код начинается с переменной SecondsToRefresh, которая задаёт частоту обновления графика.

 

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

 

Каждые SecondsToRefresh секунд программа "нажимает" сочетание клавиш CTRL+C, тем самым открывая меню графика, после чего "нажимает" клавишу R - чтобы выбрать команду обновления графика из всплывающего меню!

 

Внимание:

 

Программа посылает все свои команды в активное окно Windows!!

И если у Вас открыт не тот график, то будет обновляться не тот график. А если у Вас активен, например, блокнот, то каждые 60 секунд в нём будет появляться новый символ "R". Так что аккуратно!

 

На самом деле, автообновление графика, хоть и полезно, не столь заметно. Я специально включил достаточно много различных клавиш в код выше, чтобы Вы могли поиграться с ним и сделать что-нибудь более наглядное. Например, попробуйте слелать так, чтобы каждые 60 секунд Ваша программа меняла таймфрейм активного графика с H1 на M5 и обратно! Это намного менее полезно, зато будет очень весело!! :D

 

С помощью WinAPI можно открывать себе подобные лазейки сплошь и рядом!

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

 

Увидимся на следующем уроке!

© Kirill. [email protected]

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

Урок 34 - Защита MQL4-программ (часть 1)

 

Всем привет!

 

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

 

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

 

Итак, поехали!

 



Рис. 1 - Замок

 

Исходники V.S. исполняемы файлы

 

Чтобы быть уверенным, что все в курсе дела, немного освежу Вашу память.

Существуют два типа файлов MQL4, которые используются в терминале MetaTrader4 - это исходники (*.mq4 и *.mqh) и исполняемые файлы (*.ex4).

 

Исходники - это обычные текстовые файлы с изменённым расширением. Когда Вы что-то изменяете в MetaEditor'е, Вы работаете именно с исходниками. По секрету Вам скажу, что файлы с расширением *.mq4 Вы можете открывать блокнотом ;)

 

Исполняемые файлы - это файлы, получаемые в результате компиляции исходников. Именно эти файлы используются терминалом при вызове соответствующих индикаторов или советников.

 

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

 

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

 

 

У них есть декомпилятор! Что же делать?

 

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

 

Сложно объяснить в двух словах - принцип работы декомпилятора мы рассмотрим через один урок, а пока зададим себе вопрос: "Если пользователь потенциально может превратить исполняемый файл в исходный код, как нам защищать наши файлы?"

 

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

 

Ответ прост. Помните серию уроков 23-25 ? Помните, что я сказал в самом конце, после этой картинки:

 



Рис. 2 - Окошко Windows

 

Воиля! Что мы видим?! Скрипт вызвал окошко Windows, которое никакими стандартными методами MQL4 не вызвать. Заголовок окошка © StockProgrammer (это я) а сообщение - Hello World!

Вот так вот! Кстати, хочу обратить Ваше внимание на то, что теперь авторские права, выраженные в виде заголовка окошка © StockProgrammer - без взлома DLL убрать никто не сможет. Это к вопросу, который мы обсуждали в первом уроке данного цикла, о том, зачем нужны DLL.

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

 

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

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

 

До встречи!

© Kirill. [email protected]

post-50854-1404214567,5847_thumb.jpg

post-50854-1404214567,6238_thumb.gif

Link to post
Share on other sites
Programmer

Урок 35 - Защита MQL4-программ (часть 2)

 

Всем привет!

В предыдущем уроке мы рассмотрели, как устроена защита MQL4-программ в общем, и Вы узнали куда надо её прятать, чтобы Ваши программы было не так просто взламывать. Куда? Правильно - в DLL-ки.

Сегодня мы рассмотрим конкретные методы защиты.

 

Виды (идеи) защиты программ:

 

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

 

 

Защита паролем

 

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

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

 

Пример:

 

int start()
 {
extern string Please_Enter_Password = "0";

int start()
{
   if (password != "I LOVE FOREX!") //именно этот пароль должен ввести пользователь
   {
       Alert ("Wrong password!");
       return (0);  
   }
  //тут вставьте код Вашей программы!
}  

 

В вышеприведённом коде строка "I LOVE FOREX!" является паролем, который будет вшит в исполняемый файл.

 

 

Ограничение срока действия

 

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

 

Пример:

 

int start()
 {
  string expire_date = "2006.31.06"; //вшитая дата экспирации
  datetime e_d = StrToTime(expire_date);

  if (CurTime() >= e_d)
  {
     Alert ("The trial version has expired!");
     return(0);
  }
  //тут вставьте код Вашей программы!
  return(0);
 }

 

 

Ограничение по номеру счёта

 

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

 

Пример:

 

int start()
 {

 int hard_accnt = 11111; //вшитый номер счёта пользователя
 int accnt = AccountNumber();

  if (accnt != hard_accnt)
  {
     Alert ("You can not use this account (" + DoubleToStr(accnt,0) + ") with this program!");
     return(0);
  }
  //тут вставьте код Вашей программы!
  return(0);
 }

 

 

Ограничение по типу счёта

 

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

 

Пример:

 

int start()
 {

 bool demo_account = IsDemo();

  if (!demo_account) //промерка того, что программа запущена на демо-счёте
  {
     Alert ("You can not use the program with a real account!");
     return(0);
  }
  //тут вставьте код Вашей программы!
  return(0);
 }

 

 

На этом на сегодня всё!

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

 

До встречи!

© Kirill. [email protected]

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

Урок 36 - Защита MQL4-программ (часть 3)

 

Приветствую!

Сегодня мы разберёмся, как устроен:

 

Декомпилятор

 

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

Википедия.

Декомпиля?ция — процесс воссоздания исходного кода декомпилятором.

Википедия.

 

Далее в Википедии идёт речь про удачность декомпиляции...

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

 

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

 

Давайте посмотрим на данную концепцию на примере:

 

Исходный код:

 

//+------------------------------------------------------------------+
//|                                                  MACD Sample.mq4 |
//|                      Copyright © 2005, MetaQuotes Software Corp. |
//|                                       [url="https://alpariforum.com/redirector.php?url=http%3A%2F%2Fwww.metaquotes.net%2F"]MetaTrader 5 Trading Platform / MetaQuotes Software Corp.[/url] |
//+------------------------------------------------------------------+

extern double TakeProfit = 50;
extern double Lots = 0.1;
extern double TrailingStop = 30;
extern double MACDOpenLevel=3;
extern double MACDCloseLevel=2;
extern double MATrendPeriod=26;

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int start()
 {
  double MacdCurrent, MacdPrevious, SignalCurrent;
  double SignalPrevious, MaCurrent, MaPrevious;
  int cnt, ticket, total;
// initial data checks
// it is important to make sure that the expert works with a normal
// chart and the user did not make any mistakes setting external 
// variables (Lots, StopLoss, TakeProfit, 
// TrailingStop) in our case, we check TakeProfit
// on a chart of less than 100 bars
  if(Bars<100)
    {
     Print("bars less than 100");
     return(0);  
    }
  if(TakeProfit<10)
    {
     Print("TakeProfit less than 10");
     return(0);  // check TakeProfit
    }
// to simplify the coding and speed up access
// data are put into internal variables
  MacdCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,0);
  MacdPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,1);
  SignalCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,0);
  SignalPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,1);
  MaCurrent=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,0);
  MaPrevious=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,1);

  total=OrdersTotal();
  if(total<1) 
    {
     // no opened orders identified
     if(AccountFreeMargin()<(1000*Lots))
       {
        Print("We have no money. Free Margin = ", AccountFreeMargin());
        return(0);  
       }
     // check for long position (BUY) possibility
     if(MacdCurrent<0 && MacdCurrent>SignalCurrent && MacdPrevious<SignalPrevious &&
        MathAbs(MacdCurrent)>(MACDOpenLevel*Point) && MaCurrent>MaPrevious)
       {
        ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,0,Ask+TakeProfit*Point,"macd sample",16384,0,Green);
        if(ticket>0)
          {
           if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES)) Print("BUY order opened : ",OrderOpenPrice());
          }
        else Print("Error opening BUY order : ",GetLastError()); 
        return(0); 
       }
     // check for short position (SELL) possibility
     if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious && 
        MacdCurrent>(MACDOpenLevel*Point) && MaCurrent<MaPrevious)
       {
        ticket=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,0,Bid-TakeProfit*Point,"macd sample",16384,0,Red);
        if(ticket>0)
          {
           if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES)) Print("SELL order opened : ",OrderOpenPrice());
          }
        else Print("Error opening SELL order : ",GetLastError()); 
        return(0); 
       }
     return(0);
    }
  // it is important to enter the market correctly, 
  // but it is more important to exit it correctly...   
  for(cnt=0;cnt<total;cnt++)
    {
     OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
     if(OrderType()<=OP_SELL &&   // check for opened position 
        OrderSymbol()==Symbol())  // check for symbol
       {
        if(OrderType()==OP_BUY)   // long position is opened
          {
           // should it be closed?
           if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious &&
              MacdCurrent>(MACDCloseLevel*Point))
               {
                OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet); // close position
                return(0); // exit
               }
           // check for trailing stop
           if(TrailingStop>0)  
             {                 
              if(Bid-OrderOpenPrice()>Point*TrailingStop)
                {
                 if(OrderStopLoss()<Bid-Point*TrailingStop)
                   {
                    OrderModify(OrderTicket(),OrderOpenPrice(),Bid-Point*TrailingStop,OrderTakeProfit(),0,Green);
                    return(0);
                   }
                }
             }
          }
        else // go to short position
          {
           // should it be closed?
           if(MacdCurrent<0 && MacdCurrent>SignalCurrent &&
              MacdPrevious<SignalPrevious && MathAbs(MacdCurrent)>(MACDCloseLevel*Point))
             {
              OrderClose(OrderTicket(),OrderLots(),Ask,3,Violet); // close position
              return(0); // exit
             }
           // check for trailing stop
           if(TrailingStop>0)  
             {                 
              if((OrderOpenPrice()-Ask)>(Point*TrailingStop))
                {
                 if((OrderStopLoss()>(Ask+Point*TrailingStop)) || (OrderStopLoss()==0))
                   {
                    OrderModify(OrderTicket(),OrderOpenPrice(),Ask+Point*TrailingStop,OrderTakeProfit(),0,Red);
                    return(0);
                   }
                }
             }
          }
       }
    }
  return(0);
 }
// the end.

Link to post
Share on other sites
Programmer

Далее, я скомпилировал этот исходный код, получил исполняемый файл, который я декомпилировал и получил следующее:

 

Результат декомпиляции:

 

extern double TakeProfit = 50.0;
extern double Lots = 0.1;
extern double TrailingStop = 30.0;
extern double MACDOpenLevel = 3.0;
extern double MACDCloseLevel = 2.0;
extern double MATrendPeriod = 26.0;

int start() {
  int l_ticket_52;
  if (Bars < 100) {
     Print("bars less than 100");
     return (0);
  }
  if (TakeProfit < 10.0) {
     Print("TakeProfit less than 10");
     return (0);
  }
  double l_imacd_0 = iMACD(NULL, 0, 12, 26, 9, PRICE_CLOSE, MODE_MAIN, 0);
  double l_imacd_8 = iMACD(NULL, 0, 12, 26, 9, PRICE_CLOSE, MODE_MAIN, 1);
  double l_imacd_16 = iMACD(NULL, 0, 12, 26, 9, PRICE_CLOSE, MODE_SIGNAL, 0);
  double l_imacd_24 = iMACD(NULL, 0, 12, 26, 9, PRICE_CLOSE, MODE_SIGNAL, 1);
  double l_ima_32 = iMA(NULL, 0, MATrendPeriod, 0, MODE_EMA, PRICE_CLOSE, 0);
  double l_ima_40 = iMA(NULL, 0, MATrendPeriod, 0, MODE_EMA, PRICE_CLOSE, 1);
  int l_ord_total_56 = OrdersTotal();
  if (l_ord_total_56 < 1) {
     if (AccountFreeMargin() < 1000.0 * Lots) {
        Print("We have no money. Free Margin = ", AccountFreeMargin());
        return (0);
     }
     if (l_imacd_0 < 0.0 && l_imacd_0 > l_imacd_16  && l_imacd_8 < l_imacd_24 && MathAbs(l_imacd_0) >  MACDOpenLevel * Point && l_ima_32 > l_ima_40) {
        l_ticket_52 = OrderSend(Symbol(), OP_BUY, Lots, Ask, 3, 0, Ask + TakeProfit * Point, "macd sample", 16384, 0, Green);
        if (l_ticket_52 > 0) {
           if (OrderSelect(l_ticket_52, SELECT_BY_TICKET, MODE_TRADES)) Print("BUY order opened : ", OrderOpenPrice());
        } else Print("Error opening BUY order : ", GetLastError());
        return (0);
     }
     if (l_imacd_0 > 0.0 && l_imacd_0 < l_imacd_16  && l_imacd_8 > l_imacd_24 && l_imacd_0 >  MACDOpenLevel * Point && l_ima_32 < l_ima_40) {
        l_ticket_52 = OrderSend(Symbol(), OP_SELL, Lots, Bid, 3, 0, Bid - TakeProfit * Point, "macd sample", 16384, 0, Red);
        if (l_ticket_52 > 0) {
           if (OrderSelect(l_ticket_52, SELECT_BY_TICKET, MODE_TRADES)) Print("SELL order opened : ", OrderOpenPrice());
        } else Print("Error opening SELL order : ", GetLastError());
        return (0);
     }
     return (0);
  }
  for (int l_pos_48 = 0; l_pos_48 < l_ord_total_56; l_pos_48++) {
     OrderSelect(l_pos_48, SELECT_BY_POS, MODE_TRADES);
     if (OrderType() <= OP_SELL && OrderSymbol() == Symbol()) {
        if (OrderType() == OP_BUY) {
           if (l_imacd_0 > 0.0 && l_imacd_0 < l_imacd_16  && l_imacd_8 > l_imacd_24 && l_imacd_0 >  MACDCloseLevel * Point) {
              OrderClose(OrderTicket(), OrderLots(), Bid, 3, Violet);
              return (0);
           }
           if (TrailingStop <= 0.0) continue;
           if (Bid - OrderOpenPrice() <= Point * TrailingStop) continue;
           if (OrderStopLoss() >= Bid - Point * TrailingStop) continue;
           OrderModify(OrderTicket(), OrderOpenPrice(), Bid - Point * TrailingStop, OrderTakeProfit(), 0, Green);
           return (0);
        }
        if (l_imacd_0 < 0.0 && l_imacd_0 > l_imacd_16  && l_imacd_8 < l_imacd_24 && MathAbs(l_imacd_0) >  MACDCloseLevel * Point) {
           OrderClose(OrderTicket(), OrderLots(), Ask, 3, Violet);
           return (0);
        }
        if (TrailingStop > 0.0) {
           if (OrderOpenPrice() - Ask > Point * TrailingStop) {
              if (OrderStopLoss() > Ask + Point * TrailingStop || OrderStopLoss() == 0.0) {
                 OrderModify(OrderTicket(), OrderOpenPrice(), Ask + Point * TrailingStop, OrderTakeProfit(), 0, Red);
                 return (0);
              }
           }
        }
     }
  }
  return (0);
}

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

 

На последок, я покажу Вам как выглядет наиболее распространённый декомпилятор для MQL4. Выкладывать не буду, но вот картинка:

 



Рис. 1 - Декомпилятор

 

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

© Kirill. [email protected]

post-50854-1404214734,8186_thumb.gif

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

Урок 37 - Объекты (часть 1)

 

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

Сегодня мы рассмотрим полезные индикативные элементы терминала МТ4 - объекты.

 

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

 



Рис.1 - Выбор объекта

 

 

Рис.2 - Нанесение объекта на график

 

 

Рис.3 - Меню свойств объекта

 

Теперь Вы скажите: "О! Так мы знаем, что такое объект! Но тогда причём здесь программирование?". Действительно, поскольку объекты помогают человеку визуально воспринимать график, они не предназначены для использования программами (советниками/индикаторами), но советники и индикаторы могут вполне успешно их наносить на график, тем самым поясняя человеку, что в данный момент диктует внутренний алгоритм анализа данного советника или индикатора. Таким образом, с точки зрения программирования, объекты - это очень удобный и, в прямом смысле, наглядный способ общения программы и пользователя.

 

Программа может наносить объект на график, модифицировать его (изменять цвет/расположение/длину и др. параметры) и убирать его с графика. Ещё программа может обращаться к объекты и узнавать его текущие параметры, например - координаты. Некоторые программисты воспользовались этим для организации двухстороннего общения программы и пользователя! Многим известна надстройка для визуальной торговли "Autograph" от Сергея Ковалёва. В ней как раз и используется такой принцип. Поясню. Мы уже давно заучили, что настройки советника надо вводить в поле настроек при запуске советника или индикатора, и если изменть настройки по ходу его работы, то произойдёт перезапуск программы. Однако, как мы теперь знаем, советники и индикаторы могут считывать параметры объектов на графике. Например, если у Вас на графике есть горизонтальная линия поддержки и советник знает, что при её пробитии надо продавать, Вы можете двигать эту линию мышкой вверх и вниз и тем самым изменять параметр советника. Таким образом, объеты позволяют осуществлять интерактивное общение пользователя и программы!

 

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

 

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

В следующем уроке мы рассмотрим функции работы с объектами.

 

До встречи!

© Kirill. [email protected]

post-50854-1404214898,9597_thumb.gif

post-50854-1404214899,0423_thumb.gif

post-50854-1404214899,0788_thumb.gif

Link to post
Share on other sites
Programmer

Урок 38 - Объекты (часть 2)

 

Всем привет!

Мы уже познакомились с объектами и знаем, как с ними работать вручную. Но как с работать программно? Сегодня мы рассмотрим функции работы с объектами в языке MQL4.

 

1 - ObjectCreate()

 

Синтаксис:

bool ObjectCreate( string name, int type, int window, datetime time1, double price1, datetime time2=0, double price2=0, datetime time3=0, double price3=0)

 

Описание:

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

Объекты с типом OBJ_LABEL игнорируют координаты. Используйте функцию ObjectSet() для установки свойств OBJPROP_XDISTANCE и OBJPROP_YDISTANCE.

Замечания: нумерация подокон графика (если на графике есть подокна с индикаторами) начинается с 1. Главное окно графика есть всегда и имеет индекс 0.

Координаты должны передаваться парами - время и цена. Для примера, объекту OBJ_VLINE требуется только время, но также нужно передать и цену (любое значение).

 

Параметры:

name - Уникальное имя объекта.

type - Тип объекта. Может быть любым из типов объектов.

window - Индекс окна, в которое будет добавлен объект. Индекс окна должен быть большим или равным 0 и меньшим, чем WindowsTotal().

time1 - Время первой координаты.

price1 - Цена первой координаты.

time2 - Время второй координаты.

price2 - Цена второй координаты.

time3 - Время третьей координаты.

price3 - Цена третьей координаты.

 

 

2 - ObjectDelete()

 

Синтаксис:

bool ObjectDelete( string name)

 

Описание:

Удаление объекта с указанным именем. При успешном удалении функция возвращает TRUE, иначе FALSE.

Чтобы получить дополнительную информацию об ошибке, необходимо вызвать функцию GetLastError().

 

Параметры:

name - Имя удаляемого объекта.

 

 

3 - ObjectDescription()

 

Синтаксис:

string ObjectDescription( string name)

 

Описание:

Функция возвращает описание объекта. Для объектов типа OBJ_TEXT и OBJ_LABEL возвращается текст, отображаемый этими объектами.

Чтобы получить информацию об ошибке, необходимо вызвать функцию GetLastError().

См. также ObjectSetText().

 

Параметры:

name - Имя объекта.

 

 

4 - ObjectFind()

 

Синтаксис:

int ObjectFind( string name)

 

Описание:

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

 

Параметры:

name - Имя искомого объекта.

 

 

5 - ObjectGet()

 

Синтаксис:

double ObjectGet( string name, int prop_id)

 

Описание:

Функция возвращает значение указанного свойства объекта. Для получения информации об ошибке необходимо вызвать функцию GetLastError().

См. также ObjectSet().

 

Параметры:

name - Имя объекта.

prop_id - Идентификатор свойства объекта. Может быть любым из значений списка свойств объекта.

 

 

6 - ObjectGetFiboDescription()

 

Синтаксис:

string ObjectGetFiboDescription( string name, int index)

 

Описание:

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

Для получения информации об ошибке необходимо вызвать функцию GetLastError().

См. также ObjectSetFiboDescription().

 

Параметры:

name - Имя объекта Фибоначчи.

index - Индекс уровня Фибоначчи (0-31).

 

 

7 - ObjectGetShiftByValue()

 

Синтаксис:

int ObjectGetShiftByValue( string name, double value)

 

Описание:

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

См. также ObjectGetValueByShift().

 

Параметры:

name - Имя объекта.

value - Значение цены.

 

 

8 - ObjectGetValueByShift()

 

Синтаксис:

double ObjectGetValueByShift( string name, int shift)

 

Описание:

Функция вычисляет и возвращает значение цены для указанного бара (смещение относительно текущего бара). Значение цены вычисляется при помощи линейного уравнения по первой и второй координатам. Применяется для трендовых линий и аналогичных объектов. Для получения информации об ошибке необходимо вызвать функцию GetLastError().

См. также ObjectGetShiftByValue().

 

Параметры:

name - Имя объекта.

shift - Номер бара.

 

 

9 - ObjectMove()

 

Синтаксис:

bool ObjectMove( string name, int point, datetime time1, double price1)

 

Описание:

Изменение одной из координат объекта на графике. Объекты могут иметь от одной до трех точек привязки в зависимости от типа объекта. Функция возвращает TRUE в случае успеха, иначе FALSE. Для получения дополнительной информации об ошибке необходимо вызвать функцию GetLastError().

Нумерация координат объекта начинается с 0.

 

Параметры:

name - Имя объекта.

point - Индекс координаты (0-2).

time1 - Новое значение времени.

price1 - Новое значение цены.

 

 

10 - ObjectName()

 

Синтаксис:

string ObjectName( int index)

 

Описание:

Функция возвращает имя объекта по порядковому номеру в списке объектов. Для получения дополнительной информации об ошибке необходимо вызвать функцию функцию GetLastError().

 

Параметры:

index - Порядковый номер в списке объектов. Должен быть большим или равным 0 и меньшим, чем ObjectsTotal().

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...