Programmer 33 Author Share Posted October 1, 2008 (edited) #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] Edited May 23, 2011 by Programmer Link to post Share on other sites
Programmer 33 Author Share Posted December 24, 2008 Урок 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#. Они называются шаблонными командами. (продолжение в след. посте) 1 Link to post Share on other sites
Programmer 33 Author Share Posted December 24, 2008 (edited) 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] Edited May 23, 2011 by Programmer Link to post Share on other sites
Programmer 33 Author Share Posted July 16, 2009 (edited) Урок 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] Edited May 23, 2011 by Programmer Link to post Share on other sites
Programmer 33 Author Share Posted July 20, 2009 (edited) Урок 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 August 19, 2009 by Programmer Link to post Share on other sites
Programmer 33 Author Share Posted August 19, 2009 (edited) Урок 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 August 19, 2009 by Programmer Link to post Share on other sites
Programmer 33 Author Share Posted October 12, 2010 (edited) Урок 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 - это боевой орк DLL позволяют, например, работать с окнами Windows, что бывает достаточно удобно, а в некоторых случаях и просто необходимо. Какие Вы знаете каналы общения Вашей программы с внешним миром (т.е. не с терминалом MT4)? Правильно - файлы. Но файлы - это работа с жёстким диском, а это медленно - найти диск, дорожку, установить головку на нужный сектор... А котировки всё идут и идут. На Forex иногда необходимо принимать решения молниеоносно! А вот DLL как раз позволяют это сделать! С помощью DLL можно организовать взаимодействие MT4 и того же MathLab, где, поверьте мне, все вычисления происходят в десятки раз быстрее. С помощью DLL можно организвать взаимодействие нескольких советников, или даже нескольких терминалов! Как видите, у DLL много применений. В основном, из-за того, что они доступны очень многим разным видам приложений. Вторая причина, которая, на мой взгляд, была основной при приходе DLL на Forex - ВЗЛОМ. Многие владельцы коммерческих советников и индикаторов хотят защитить свои детища. И ни для кого уже не секрет, что обыкновенные компилки *.ex4 легко превращаются декомпилкой в исходники *.mq4. А вот DLL-ку сломать сложнее. Для этого надо ковыряться в ассемблерном коде. А специалистов, умеющих это делать, среди трейдеров не так уж много. Надеюсь, Вам была понятна краткая справка по миру DLL и его сплетению с миром FOREX. На следующем занятии мы с Вами напишем свою первую DLL-ку. Не пропустите! © Kirill. [email protected] Edited October 15, 2010 by Programmer 1 Link to post Share on other sites
Programmer 33 Author Share Posted October 14, 2010 (edited) Урок 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 Edited October 15, 2010 by Programmer Link to post Share on other sites
Programmer 33 Author Share Posted October 15, 2010 (edited) Откомпилируйте DLL нажатием клавиши F7. Если Вы сделали всё правильно, то Вы не получите никаких предупреждений и сообщений об ошибках. Найдите файл "MyFirstDLL.dll" в подпапке Debug в папке проекта MyFirstDLL (рис. . Рис. 8 - MyFirstDLL.dll Знакомый занчок? Правильно - это тот же самый, что мы рассматривали в прошлом уроке. В ближайшем уроке мы протестируем нашу DLL и научимся импортировать, заложенную в ней ф-ю, в код программ, написанных на MQL4. До встречи! Кирилл. © Kirill. [email protected] Edited October 15, 2010 by Programmer Link to post Share on other sites
Programmer 33 Author Share Posted October 15, 2010 Урок 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] Link to post Share on other sites
Programmer 33 Author Share Posted October 19, 2010 Урок 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] Link to post Share on other sites
Programmer 33 Author Share Posted October 27, 2010 Урок 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 33 Author Share Posted October 28, 2010 Урок 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
Programmer 33 Author Share Posted November 10, 2010 Урок 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. Ведь это ф-ия, которая не принимает никаких параметров. Далеко не уходите! В следующий раз мы разберём преимущества работы на новом баре. © Kirill. [email protected] Link to post Share on other sites
Programmer 33 Author Share Posted November 15, 2010 Урок 30 - Тайна нового бара (часть 2) Всем привет! Насколько Вы помните, в прошлый раз мы рассмотрели код, позволяющий создавать программные блоки, срабатывающие только на открытии нового бара. У меня в институте был знакомый преподаватель, который на любые мои крутые идеи всегда задавал один вопрос "Нафига?". Если я знал ответ, то обсуждение продолжалось. Если нет - то нет Сегодня мы узнаем, зачем может понадобиться модуль, работающий только на новом баре. Итак. Преимущества работы на открытии нового бара 1. Экономия ресурсов Многие торговые системы в своём анализе используют сформировавшиеся сигналы прошлых баров. К данному классу в том числе относится и всё множество "запаздывающих" торговых систем. Алгоритм принятия решений, встроенный в подобные системы в течении жизни текущего бара всегда будет выдавать одно и то же решение. Действительно, анализирется динамика цены и её производных, имевшая место ДО начала текущего бара, следовательно, всё, что происходит НА текущем баре, никак на анализ повлиять не может, т.к. не влияет на прошлое. В данном случае мы сталкиваемся с ситуацией, когда то решение уже принято на первом тике текущего бара, и прогон алгоритма вновь и вновь не несёт в себе никакого содержательного смысла. 2. Упрощение жизни программиста Иногда возникают следующие ситуации: Вы запрограммировали какое-то условие и хотите, чтобы оно проверялось только один раз на каждом баре. Например: если на предыдущем баре MA( > MA(16) и одновременно RSI(5) > 80, то BUY. Если Вы просто вставите это условие в код, то в случае его выполнения, у Вас откроется столько сделок BUY, сколько было тиков в текущем баре (т.к. ф-я start() срабатывает на каждом тике). Значит, Вам необходимо внести ограничени - проверять данное условие только один раз на каждом баре. Вместо того, чтобы заводить счётчик, затем сбрасывать его на каждом новом баре, можно просто воспльзоваться кодом, который мы рассмотрели в прошлом уроке, и заставить советника работать только на первом тике каждого нового бара. 3. Ускорение оптимизации Многие сталкивались с длительной оптимизацией. Думаю, все согласятся, что оптимизация по "всем тикам" является наиболее качественной, но и соответственно, самой долгой. Можно ждать ДНЯМИ пока она завершится. В то время, как оптимизация по "контрольным точкам" намного быстрее. А оптимизация по "ценам открытия" вообще летает. Рис 1 - типы оптимизации В связи с этим, я предлагаю Вам следующий вариант. Прежде чем оптимизировать советник прогоните его несколько раз по "всем тикам" и по "ценам открытия". Если результаты в большинстве случаев приблизительно совпадают, это означает, что советнику достаточно одного тика из каждого бара, чтобы правильно работать. Поэтому Вы можете оптимизировать его по "ценам открытия", а найденные лучшие настройки перепроверить на "всех тиках". Быть может, после оптимизации Вам больше понравится вариант данного советника, работающий только по "ценам открытия", - тогда смело вставляйте модуль контроля нового бара и у Вас будет новый советник. Надеюсь, Вы нашли данный урок полезным. До встречи! © Kirill. [email protected] 1 Link to post Share on other sites
Programmer 33 Author Share Posted November 23, 2010 Урок 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] Link to post Share on other sites
Programmer 33 Author Share Posted November 27, 2010 Урок 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 Author Share Posted November 29, 2010 Урок 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); } (См. продолжение) Link to post Share on other sites
Programmer 33 Author Share Posted November 29, 2010 (продолжение: Урок 33 - Введение в WinAPI ) Разбор кода: Первым делом мы импортируем Windows-функцию keybd_event из ДЛЛ-ки user32.dll. Затем с помощью дескриптора #define вводим интуитивно-понятные обозначения для констант, используемых упомянтой функцией. Основной код начинается с переменной SecondsToRefresh, которая задаёт частоту обновления графика. Функция, которая используется для обновления графика называется Refresh(). В качестве входного параметра она принимает заданное количество секунд между двумя последовательными обновлениям. Функция сравнивает время последнего обновления с текущим временем и производит новое обновление, если это необходимо. Каждые SecondsToRefresh секунд программа "нажимает" сочетание клавиш CTRL+C, тем самым открывая меню графика, после чего "нажимает" клавишу R - чтобы выбрать команду обновления графика из всплывающего меню! Внимание: Программа посылает все свои команды в активное окно Windows!! И если у Вас открыт не тот график, то будет обновляться не тот график. А если у Вас активен, например, блокнот, то каждые 60 секунд в нём будет появляться новый символ "R". Так что аккуратно! На самом деле, автообновление графика, хоть и полезно, не столь заметно. Я специально включил достаточно много различных клавиш в код выше, чтобы Вы могли поиграться с ним и сделать что-нибудь более наглядное. Например, попробуйте слелать так, чтобы каждые 60 секунд Ваша программа меняла таймфрейм активного графика с H1 на M5 и обратно! Это намного менее полезно, зато будет очень весело!! С помощью WinAPI можно открывать себе подобные лазейки сплошь и рядом! Урок достаточно сложный, но те, кто в нём разберётся до конца, найдут много полезной информации для себя. Увидимся на следующем уроке! © Kirill. [email protected] Link to post Share on other sites
Programmer 33 Author Share Posted December 7, 2010 Урок 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] Link to post Share on other sites
Programmer 33 Author Share Posted December 9, 2010 Урок 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] 1 Link to post Share on other sites
Programmer 33 Author Share Posted January 5, 2011 Урок 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 33 Author Share Posted January 5, 2011 Далее, я скомпилировал этот исходный код, получил исполняемый файл, который я декомпилировал и получил следующее: Результат декомпиляции: 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] Link to post Share on other sites
Programmer 33 Author Share Posted February 1, 2011 Урок 37 - Объекты (часть 1) Всех приветствую! Сегодня мы рассмотрим полезные индикативные элементы терминала МТ4 - объекты. Объекты - это вспомогатильные графические элементы, используемые при визуальном анализе графиков. Например, линии поддержки и сопротивления - это объекты, уровни Фибоначчи - это объект, канал Ганна - объект, самый обыкновенный прямоугольник, который вы рисуете на графике, - объект, и даже текстовая метка - тоже объект. Ниже несколько примеров ручной работы с объектами. Рис.1 - Выбор объекта Рис.2 - Нанесение объекта на график Рис.3 - Меню свойств объекта Теперь Вы скажите: "О! Так мы знаем, что такое объект! Но тогда причём здесь программирование?". Действительно, поскольку объекты помогают человеку визуально воспринимать график, они не предназначены для использования программами (советниками/индикаторами), но советники и индикаторы могут вполне успешно их наносить на график, тем самым поясняя человеку, что в данный момент диктует внутренний алгоритм анализа данного советника или индикатора. Таким образом, с точки зрения программирования, объекты - это очень удобный и, в прямом смысле, наглядный способ общения программы и пользователя. Программа может наносить объект на график, модифицировать его (изменять цвет/расположение/длину и др. параметры) и убирать его с графика. Ещё программа может обращаться к объекты и узнавать его текущие параметры, например - координаты. Некоторые программисты воспользовались этим для организации двухстороннего общения программы и пользователя! Многим известна надстройка для визуальной торговли "Autograph" от Сергея Ковалёва. В ней как раз и используется такой принцип. Поясню. Мы уже давно заучили, что настройки советника надо вводить в поле настроек при запуске советника или индикатора, и если изменть настройки по ходу его работы, то произойдёт перезапуск программы. Однако, как мы теперь знаем, советники и индикаторы могут считывать параметры объектов на графике. Например, если у Вас на графике есть горизонтальная линия поддержки и советник знает, что при её пробитии надо продавать, Вы можете двигать эту линию мышкой вверх и вниз и тем самым изменять параметр советника. Таким образом, объеты позволяют осуществлять интерактивное общение пользователя и программы! Указанное выше применение объектов применяется только в специфических случаях, в основном, объекты используются для визуального анализа. Надеюсь, что теперь у Вас есть понимание того, что такое объет и как его можно использовать. В следующем уроке мы рассмотрим функции работы с объектами. До встречи! © Kirill. [email protected] Link to post Share on other sites
Programmer 33 Author Share Posted February 3, 2011 Урок 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
Recommended Posts