English 中文 Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Взаимодействие MetaTrader 5 и MATLAB

Взаимодействие MetaTrader 5 и MATLAB

MetaTrader 5Примеры | 26 июля 2010, 19:28
10 123 16
Andrey Emelyanov
Andrey Emelyanov

Введение

После выхода в свет моей первой статьи "Взаимодействие между MetaTrader 4 и MATLAB Engine (виртуальная машина MATLAB)" она не осталась не замеченной MQL-сообществом, некоторые читатели даже смогли самостоятельно перенести проект с Borland на VS2008 (1Q2W3E4R5T). Но время неутомимо бежит вперед и MetaTrader 4, как это ни печально, уходит в прошлое, уступая место своему преемнику MetaTrader 5 с языком MQL5, в котором появились указатели и динамическая память. 

Благодаря этим нововведениям появилась возможность написания универсальной библиотеки связи с виртуальной машиной MATLAB Engine и напрямую связать сгенерированные MATLAB библиотеки с MetaTrader 5, о чем и пойдет речь в данной статье. Эта статья является логическим продолжением предыдущей, и более детально рассматривает проблему связки MetaTrader 5 с MATLAB.

Чтобы материал данной статьи был более понятен неподготовленным читателям, разобьем статью на три части: теоретическая, справочная, практическая части. В теоретической части ознакомимся с типами данных, принятых в MQL5 и MATLAB, а также способах их взаимного преобразования. В справочной части будем изучать языковые конструкции и синтаксис функций, необходимых для создания DLL. А в практической части разберем "подводные камни" данной связки.

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

1. Теория

1.1 Типы данных в MQL5 и MATLAB

1.1.1 Простые типы данных

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

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

MQL5
Размер в байтах
Минимальное значение
Максимальное значение
 MATLAB
char
1
-128
127
Array int8/char
uchar
1
0
255
Array int8/char
bool
1
0(false)
1(true)
Array logical
short
2
-32768
32767
Array int16
ushort
2
0
65535
Array int16
int
4
-2147483648
2147483647
Array int32
uint
4
0
4294967295
Array int32
long 8
-9223372036854775808
9223372036854775807 Array int64
ulong 8
0
18446744073709551615
Array int64
float 4
1.175494351e-38
3.402823466e+38
Array single
double
8
2.225073858507201e-308
1.7976931348623158e+308
Array double

Таблица 1. Типы данных в MQL5 и MATLAB

Есть лишь одно существенное различие: переменные в MQL5 могут быть простыми и составными (сложными), а в MATLAB все переменные многомерные (сложные) – т.е. матрицы. Данное различие необходимо всегда помнить!

1.1.2 Сложные типы данных

В MQL5 существует 4 типа сложных данных: массивы, строки (string), структуры, классы. Сложные типы данных состоят из некоторого набора простых типов данных, объединенных в блок памяти определенной длины. При работе с такими данными всегда необходимо знать либо размер блока памяти в байтах, либо количество элементов (исключением, пожалуй, являются классы). Для нас представляет интерес только массивы и строки (string), т.к. передавать в MATLAB классы и структуры MQL5 смысла нет.  

При передаче массивов любого типа необходимо узнать: тип (размерность) и количество элементов, используя функцию ArraySize(). Особое внимание следует уделить индексации в MetaTrader 5 - обычно она задом наперед (т.е. первый элемент содержит более свежие данные, чем последующие), но этот факт необходимо проверять с помощью функции ArrayIsSeries(). А в MATLAB принята следующая индексация: первый элемент содержит более старые данные, чем последующие - поэтому необходимо "переворачивать" массивы перед посылкой в MATLAB, если флаг AS_SERIES = TRUE. На основании изложенного выше, договоримся:

  • "Переворот" массива производить "невидимо" для программ MQL5, за исключением массивов типа char и 2-мерных массивов - их оставляем без изменений;
  • Все массивы, получаемые из MATLAB, "невидимо" переворачивать, а флагу AS_SERIES присваивать значение TRUE, за исключением массивов типа char и 2-мерных массивов - их оставляем без изменений;
  • У любого массива MQL5-программы, созданного в соответствии с индексацией "задом наперед", флаг AS_SERIES должен содержать значение TRUE, за исключением массивов типа char и 2-мерных массивов — их оставляем без изменений.  

Но это не единственное ограничение при работе с массивами. При работе с многомерными массивами, правильнее сказать с матрицами, особенно с теми, которые получаем из MATLAB, введем ограничение на не более чем 2-мерные массивы. При этом флаг направления AS_SERIES  не может быть TRUE, а значит такие массивы не "переворачиваются".  

Также не следует забывать, что строки в MQL5 не являются массивами символов типа char, поэтому при передаче строк возникает небольшая проблема: в MQL5 принято кодировать строки в Unicode, а в MATLAB принята кодировка ANSI, поэтому перед тем, как передать строку, следует её преобразовать в массив символов ANSI с помощью функции StringToCharArray(). И наоборот, когда получаем из MATLAB массив символов, преобразовываем его функцией CharArrayToString() (см. табл. 2). Для того, чтобы не путаться, договоримся: все строки в MQL5-программе храним в Unicode, никаких массивов типа char.

1.2 Сопоставление типов данных MQL5 и MATLAB

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

 MQL5 
Аналог в MatLab
char 
uchar
Array char
bool
Array logical
short
ushort
int
uint
Array int32
long
ulong
Array int64*
float
double
Array double
string
Array char, через функции StringToCharArray() <=> CharArrayToString()

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

Таблица 2. Сопоставление типов данных в MQL5 и MATLAB

Теперь, когда мы ознакомились с типами данных, принятых в MQL5 и MATLAB, выяснили какие "подводные камни" нас поджидают при передаче данных и как грамотно их обходить, осталось выяснить API интерфейс MATLAB Engine и ознакомиться с компилятором MATLAB версии 4.

2. Справочник по API MATLAB Engine, компилятору MATLAB версии 4 и библиотеке ввода/вывода C++

Данный раздел познакомит вас с наиболее важными функциями API MATLAB Engine, c особенностями компилятора MATLAB версии 4, а также рядом полезных функций из стандартной библиотеки ввода/вывода C++. Итак, начнем.

2.1 API MATLAB Engine и MCR функции

MATLAB Engine — внешний интерфейс, позволяющий сторонним программам использовать рабочий стол MATLAB, обеспечивает полную функциональную работу всех пакетов MATLAB без ограничений.

Хотя и в документации не указано, но с точки зрения системного программиста — это просто виртуальная машина, такая как PHP, MySQL…, поддерживающая простой и относительно быстрый способ обмена данных между MetaTrader 4/5 и MATLAB.  

Данный способ связки внешних программ с пакетом MATLAB рекомендован разработчиками. Интерфейс состоит из шести функций:

Engine *pEng = engOpen(NULL) — функция вызова рабочего стола MATLAB, параметр всегда NULL, возвращает указатель на дескриптор рабочего стола.

int exitCode = engClose(Engine *pEng) — функция закрытия рабочего стола, возвращает число оставшихся пользователей стола MATLAB, где:

  • Engine  *pEng — указатель на дескриптор рабочего стола.  

mxArray *mxVector = mxCreateDoubleMatrix(int m, int n, int ComplexFlag) — функция создает переменную (матрицу) рабочего стола MATLAB, возвращает указатель на переменную (матрицу), где:

  • mxArray *mxVector — указатель на переменную матрицу;  
  • int m — кол-во рядов;  
  • int n — кол-во колонок;  
  • ComplexFlag — тип комплексного числа, для MetaTrader 4/5 mxREAL.
void = mxDestroyArray(mxArray *mxVector) — функция уничтожает матрицу MATLAB, необходима для очистки памяти, где:
  • mxArray *mxVector — указатель на переменную матрицу.  
int = engPutVariable(Engine *pEng, char *Name, mxArray *mxVector) — функция отправки переменной в рабочий стол. Необходимо не только создавать переменные типа mxArray, но еще отправлять их в MATLAB, где:
  • Engine *pEng — указатель на дескриптор рабочего стола;  
  • char *Name — имя переменной в столе MATLAB, тип char;  
  • mxArray *mxVector — указатель на переменную матрицу.  
mxArray *mxVector = engGetVariable(Engine *pEng, char *Name) — функция получения переменной из рабочего стола - функция, обратная предыдущей. Принимать можно только переменные типа mxArray, где:
  • mxArray *mxVector — указатель на переменную матрицу;  
  • Engine *pEng — указатель на дескриптор рабочего стола;  
  • char *Name — имя переменной в столе MATLAB, тип char.  
double *p = mxGetPr(mxArray *mxVector) — функция получает указатель на массив значений, используется для копирования данных совместно с memcpy (см. 2.3 Стандартная  библиотека ввода/вывода С++), где:
  • double *p — указатель на массив типа double;  
  • mxArray *mxVector — указатель на переменную матрицу.  
int = engEvalString(Engine *pEng, char *Command) — функция отправки команды рабочему столу, которая будет выполнена рабочим столом MATLAB, где:
  • Engine *pEng — указатель на дескриптор рабочего стола;  
  • char *Command — команда для MATLAB, строка тип char.  

Наверное, вы заметили, что API MATLAB Engine позволяет создавать структуры mxArray, только для типа double, однако это ограничение никак не повлияет на Ваши возможности, но отразится на алгоритме работы Вашей библиотеки.

MCR (MCR instance) — особая библиотека пакета MATLAB, обеспечивающая работу автономных приложений/общедоступных библиотек, сгенерированных средой MATLAB на любом компьютере. Стоит отметить, что даже если у вас стоит полный пакет MATLAB, все равно требуется установить библиотеку MCR, запустив файл: MCRInstaller.exe (расположенный в каталоге: <MATLAB>\toolbox\compiler\deploy\win32). Итак, перед вызовом любой функции общедоступной библиотеки, созданной средой MATLAB, необходимо вызвать функцию инициализации MCR:
 
bool = mclInitializeApplication(const char **option, int count) – возвращает true в случае успешного запуска MCR, иначе false, где:

  • const char **option — строка опций, такая же как в mcc – R; обычно NULL  
  • int count — размер строки опций; обычно 0.

При завершении работы общедоступной библиотеки, необходимо вызвать:
bool = mclTerminateApplication(void) — возвращает true, в случае успешного закрытия MCR.

2.2 Компилятор MATLAB версии 4

Компилятор MATLAB позволяет из M-функций создавать:  

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

Компилятор поддерживает большинство команд и пакетов MATLAB, однако не весь. Полный список ограничений можно найти на сайте производителя MATLAB. Данный способ позволяет создать "программно-независимую связку" MetaTrader 5 и MATLAB, однако в отличие от MATLAB Engine, требует хорошей подготовки программиста и глубокого знания компиляции.

Компилятор MATLAB требует наличия хотя бы одного из следующих С/С++ - компиляторов:

  • Lcc C (обычно идет в поставке с MATLAB), является только С–компилятором;  
  • Borland C++ версии 5.3, 5.4, 5.5, 5.6;
  • Microsoft Visual C/C++ версии 6.0, 7.0, 7.1.

Компилятор MATLAB версии 4, в отличие от своих предшественников, генерирует только код интерфейса (обертки), т.е. не занимается трансляцией m-функции в двоичный или С/С++ код, но при этом создает специальный файл по технологии Component Technology File (CTF), который содержит объединения различных пакетов, необходимых для обеспечения работы m-функции. Попутно компилятор MATLAB зашифровывает данный файл уникальным (неповторяющимся) 1024-битным ключом.

А теперь рассмотрим алгоритм работы компилятора MATLAB версии 4, т.к. незнание данного вопроса приведет к многочисленным глупым ошибкам на этапе компиляции:

  1. Анализ зависимостей — на данном этапе определяются все функции, MEX-файлы, P-файлы от, которых зависят компилированные m-функции;  
  2. Создание архива — создается файл CTF, он зашифрован и сжат;  
  3. Генерация объектного кода обертки – на данном этапе создаются все исходные тексты, необходимые для создания компоненты:
    • код интерфейса C/C++ для m-функций, указанных в командной строке (NameFile_main.c);
    • файл компоненты (NameFile_component.dat), который содержит всю информацию, необходимую для исполнения m-кода (включая ключи шифрования и пути, сохраненные в CTF-файле);  
  4. С/С++ трансляция. На этом шаге компилируется созданные С/С++ файлы из кода в объектные файлы;
  5. Соединение (линковка). Заключительный этап сборки проекта.

Теперь, когда мы ознакомились с алгоритмом поведения компилятора MATLAB, осталось ознакомиться с ключами, чтобы иметь перед собой подробный план действий при использовании компилятора (mcc):   

Ключ
Назначение
    a filename
 Добавление файла filename в архив, определяет какие файлы будут добавлены в архив CTF
     l
 Макрос, который генерирует библиотеку функций
    N
 Очистить все пути, кроме минимального необходимого набора каталогов
    p <directory>
 Добавление пути трансляции в соответствии с порядком. Требует наличия ключа –N
    R -nojvm
 Отмена опции MCR (MATLAB Component Runtime, компонент времени выполнения, см. справку по MATLAB)
    W
 Управляет созданием оберток функций
    lib
 Создание функции инициализации и завершения
    main
 Создание POSIX оболочки функции main()
    T
 Определяет стадию вывода
    codegen
 Создает код обертки для автономного приложения
    compile:exe
 То же, что codegen
    compile:lib
 Создать код обертки общедоступной библиотеки DLL
    link:exe
 То же, что и compile:exe, плюс линковка
    link:lib
 То же, что и compile:lib, плюс линковка

Таблица 3. Ключи компилятора Matlab mcc (версия 4)

В таблице 3 приведены основные ключи, которые могут понадобится при решении типовых задач. За более подробной справкой обращайтесь к MATLAB через команды help mcc или doc mcc.

Осталось ознакомиться только с линковщиком MATLAB, ниже перечислены основные ключи (mbuild):

 Ключ
Назначение
 -setup
 В интерактивном режиме определение файла опций компилятора для использования по умолчанию в будущих вызовах mbuild
 -g
 Создание программы с отладочной информацией. Добавляет в конец файла DEBUGFLAGS
 -O
 Оптимизация объектного кода

Таблица 4. Ключи линковщика Matlab mbuild (версия 4)

В таблице 4 перечислены основные ключи, за более подробной информацией обращайтесь через команды help mbuild или doc mbuild.

2.3 Стандартная  библиотека ввода/вывода С++

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

void *pIn = memcpy (void *pIn, void *pOut, int nSizeByte) – функция копирования (клонирования) переменной/массива из pOut в pIn размером nSizeByte байт, где:

  • void *pIn — указатель на массив, куда производиться копирование;  
  • void *pOut — указатель на массив, откуда производиться копирование;  
  • int nSizeByte — размер копируемых данных, не должен превышать размера массива pIn, иначе возникнет ошибка доступа к памяти.  

3. Практическая часть

Теперь, когда мы окончательно разобрались с теоретической частью, мы можем приступить к реализации связки MetaTrader 5 & MATLAB.

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

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

3.1 Разработка универсальной библиотеки связи MetaTrader 5 & MATLAB Engine

Данный способ связки нельзя назвать элегантным и быстрым, но зато это самый надежный и охватывающий весь пакет MATLAB. Конечно, нельзя не упомянуть о быстроте разработки конечной модели. Суть разработки сводится к написанию универсальной библиотеки-обертки для связи MetaTrader 4/5 & MATLAB Engine, после чего скрипт/индикатор/эксперт MetaTrader 4/5 может управлять виртуальным рабочим столом MATLAB. Причем весь программно-математический алгоритм может находиться в MQL-программе и храниться в виде строк, чем можно воспользоваться при защите интеллектуальной собственности (подробности см. в статье "Защищайтесь, господа разработчики!") и также может располагаться в самостоятельных файлах m-функций или P-функций, хранящихся в каталоге < MetaTrader 5>\MQL5\Libraries.  

Возможные области применения такой связки:

  • для проверки или демонстрации "математических моделей/идей" — без написания сложных программ (защиту интеллектуальной собственности можно организовать, как в MQL-программе, так и средствами пакета MATLAB — применение P-функций);  
  • для написания сложных математических моделей использующих все возможности пакета MATLAB;
  • всем тем, кто не собирается распространять свои скрипты/индикаторы/эксперты.  

Приступим, я надеюсь что вы прочитали для начала 1.1 Типы данных в MQL 5 и MatLab, 1.2 Сопоставление типов данных MQL 5 и MATLAB, 2.1 API MATLAB Engine и 2.3 Стандартная  библиотека ввода/вывода C/С++, т.к. останавливаться и разбирать это уже не будем. Внимательно ознакомьтесь со следующей блок-схемой, которая отображает алгоритм будущей библиотеки:  

Рис. 1. Блок-схема алгоритма библиотеки

Рис. 1. Блок-схема алгоритма библиотеки

Как видно на рис. 1, библиотека состоит из трех основных блоков, рассмотрим их назначение:

  • БЛОК-MQL5, предварительная подготовка данных отправляемых/получаемых:  
    • Переворот массивов;
    • Преобразование типов;
    • Преобразование кодировок строк;
  • БЛОК-C/C++:
    • обеспечивает преобразование массивов в структуру mxArray;
    • обеспечивает передачу команд MATLAB Engine;
  • БЛОК-MATLAB Engine — вычислительная система;  

Теперь разберемся с алгоритмами, начнем по порядку, с БЛОКА-MQL5, как внимательный читатель уже понял, речь пойдет о реализации того, о чем писалось в разделе 1.1 Типы данных в MQL 5 и MatLab. Если вы его пропустили, то не поймете, зачем все это нужно.

Алгоритм функций mlInput <тип_переменной> практически одинаков, и разберем его работу на функции mlInputDouble(), которая обеспечивает ввод переменных типа double в виртуальную машину MATLAB.

Вот её прототип:

bool mlInputDouble(double &array[],int sizeArray, string NameArray), где:

  • array — ссылка на переменную или массив, тип double;
  • sizeArray — размер массива array (количество элементов массива, не байты!); 
  • NameArray — строка, содержащая уникальное имя переменной для виртуальной машины MATLAB (имя должно соответствовать требованиям MATLAB).

Алгоритм:

  1. Перевод строки NameArray в массив char с помощью функции StringToCharArray();
  2. Проверяем с помощью функции ArrayIsSeries() тип индексации. Если тип индексации обычный — передаем значение функции mlxInputDouble();
    ИНАЧЕ индексация массива тайм-серия:
    "переворачиваем" массив и передаем значение функции mlxInputDouble();
  3. Завершаем работу функции, передаем возвращаемое значение функции mlxInputDouble();

Алгоритм функций mlGet <тип_переменной> тоже практически одинаков. Разберем его на примере функции mlGetDouble(), которая возвращает переменную типа double из виртуальной машины MATLAB.

Её прототип: 

int mlGetDouble(double &array[],int sizeArray, string NameArray), где:

  • array — ссылка на переменную или массив, тип double;
  • sizeArray — размер массива array (количество элементов массива, не байты!); 
  • NameArray — строка, содержащая уникальное имя переменной для виртуальной машины MATLAB.

Алгоритм:

  1. Перевод строки NameArray в массив char, с помощью функции StringToCharArray();   
  2. Выясняем размер массива с помощью функции mlxGetSizeOfName();
    • ЕСЛИ размер БОЛЬШЕ НУЛЯ, выделить массив-приемник необходимого размера с помощью функции ArrayResize(), получить данные mlxGetDouble(), вернув размер массива;
    • ЕСЛИ размер НУЛЬ вернуть ошибку, т.е. значение нуль.  

Вот и все! Отличие mlGetInt() и mlGetLogical() в том, что производят "теневое" преобразование типов из double -> int/bool, для чего указанные функции создают временный буфер памяти в своем теле. Данная мера вынужденная, т.к. к сожалению API интерфейс MATLAB не позволяет создавать структуры mxArray для типов данных, отличных от double. Однако это не означает, что MATLAB оперирует исключительно double.

С Блоком-C/C++ все значительно проще  – он должен обеспечивать трансляцию данных с типа double в структуру mxArray, что осуществляется с помощью функций mxCreateDoubleMatrix(), mxGetPr() и memcpy(). После чего с помощью функции engPutVariable()  он передает данные в виртуальную машину MATLAB, а при извлечении данных использует функцию  engGetVariable(). И опять же, обратите внимание на функции с приставками Int и Logical — как видно на БЛОК-СХЕМЕ, напрямую не общаются с MATLAB, а используют функции: mlxInputDouble/mlxGetDouble и mlxInputChar(). Алгоритм поведения их прост: вызов функции mlxInputDouble/mlxGetDouble — ввод/вывод значений как double(!), и отправка "теневой" команды MATLAB на преобразование типа данных через функцию mlxInputChar().

С Блоком MATLAB Engine все еще проще, он обеспечивает выполнение лишь математических функций. Его поведение напрямую зависит от Ваших команд и от Ваших m/p-функций.  

Теперь, когда все "тонкости" реализации проекта ясны, пора разобраться со сборкой проекта.

Любая подобная сборка начинается с создания главной библиотеки — в нашем случае это БЛОК-С/С++. Для этого в любом ANSI-текстовом редакторе (Блокнот, Bred, и т.п.) создадим файл с расширением DEF. Желательно, чтобы имя данного файла состояло из латинских символов, без пробелов и знаков, иначе вы"услышите" от своего компилятора много лестных "слов" в свой адрес…  Данный файл обеспечивает неизменность ваших функций, в случае его отсутствия компилятор С/С++ придумает свои "экзотические имена" экспортным функциям.

Данный файл содержит: LIBRARY — управляющие слово "имя библиотек", LibMlEngine —  имя библиотеки, и второе управляющее слово EXPORTS — дословно "экспорт", после этого слова идут имена функций. Как вы, наверное, поняли, имена экспортных функций не могут быть написаны русским шрифтом, иметь пробелы и знаки. Вот текст файла DllUnit.def из архива MATLABEngine.zip:  

LIBRARY LibMlEngine
EXPORTS
mlxClose
mlxInputChar
mlxInputDouble
mlxInputInt
mlxInputLogical
mlxGetDouble
mlxGetInt
mlxGetLogical
mlxGetSizeOfName
mlxOpen

Итак, первый файл проекта есть, теперь открываем проводник и заходим в папку <MATLAB>\extern\include. Копируем файл engine.h (заголовочный файл виртуальной машины MATLAB) в каталог, где собираем проект (не сделаете этого, будете указывать путь к этому файлу вручную на этапе компиляции).

Теперь пора перейти к созданию БЛОКА-С/С++. Весь текст программы в статье писать не будем, т.к. данный файл есть в архиве MATLABEngine.zip под именем DllUnit.cpp и хорошо комментирован. Но стоить отметить, что функции все таки лучше создавать __stdcall — т.е. передача параметров через стек, функция чистит стек. Данный стандарт "родной" для API Win32/64.

Рассмотрим, как объявить функцию:

  extern "C" __declspec(dllexport) <тип_переменной> __stdcall Function(<тип> <имя>);

  1. extern "C" __declspec(dllexport) — говорит компилятору С++, что функция внешняя.  
  2. <тип_переменной> — тип возвращаемой переменной, может быть: void, bool, int, double, составные типы (известные не только Dll, но и вызывающей программе) и указатели;
  3.  __stdcall — соглашение о передаче параметров в функцию и обратно, стандарт для API Win32/64;  
  4. Function — имя Вашей функции;  
  5. <тип> <имя> — тип и имя входной переменной, максимальное количество переменных - 64.

Более подробно данная тема раскрывается в статье "Как за 10 минут написать DLL библиотеку для MQL5 и обмениваться данными?".

Сборка БЛОКА-С/С++: для этого необходимо подключить стандартную библиотеку ввода/вывода и добавить в проект следующие файлы (команда в Вашем компиляторе: Project->Add Project):

  1. DllUnit.def
  2. В каталоге <MATLAB>\extern\lib\<win32/64>\<компилятор>\, где:
    <MATLAB> — главный каталог MATLAB;
    <win32/64> — либо папка win32 для 32 битных ОС, либо win64 для 64 битных ОС;
    <компилятор> — папка "borland" – Borland C/C++ ver. 5-6, папка "microsoft" - Microsoft Visual C++:  
    • libeng.lib
    • libmx.lib

У многих возник наверно вопрос "У меня компилятор другой версии или нет такого компилятора в списке! (Очень редко данных файлов вообще нет)". Давайте рассмотрим процесс ручного создания общедоступных библиотек. Будет рассмотрено, как это делается в Visual C++ и Borland C++:

  1. открываем FAR’ом папку <MATLAB>\bin\<win32/64>, где:
    <MATLAB> — главный каталог MATLAB;
    <win32/64> — либо папка win32 для 32 битных ОС, либо win64 для 64 битных ОС;  
  2. Если Borland C++: команда implib libeng.lib libeng.dll ; так же для libmx.dll
  3. Если Visual C++: команда lib libeng.dll ; так же для libmx.dll
  4. Если другой компилятор: любой компилятор любого языка программирования должен иметь в своем составе данную утилиту, обычно данная консольная программа располагается <каталог_компилятора>\bin\*lib*.exe — по-английски звучит название так: "Library Manager".

Кстати, забыл предупредить, не вздумайте делать 64-битные LIB для 32-битного компилятора, сначала узнайте в справке на компилятор о поддержки 64-битной адресации, если таковой нет, либо ищите 32-битные DLL MATLAB, либо меняйте С/С++ компилятор. Приступаем к компиляции, после которой у нас получается библиотека, которую необходимо разместить в каталоге: Каталог_терминала\MQL5\Libraries.

Теперь можно приступить БЛОКУ-MQL. Для этого запустим MetaEditor, выберем команду "Создать" и выполним действия, как показано на рисунке:  

Рис. 2. Мастер MQL5: Создание библиотеки

Рис. 2. Мастер MQL5: Создание библиотеки

Рис. 3. Мастер MQL5: Общие параметры библиотеки

Рис. 3. Мастер MQL5: Общие параметры библиотеки

Теперь, когда Мастер MQL5 создал шаблон, приступим к его редактированию:

1. Необходимо описать импорт функций:

//+------------------------------------------------------------------+
//| ОБЪЯВЛЕНИЕ ИМПОРТИРУЕМЫХ ФУНКЦИЙ                                 |
//+------------------------------------------------------------------+
#import "LibMlEngine.dll"
void   mlxClose(void);                        //void – означает: не передаем никаких параметров!
bool   mlxOpen(void);                         //void – означает: не передаем и не получаем никаких параметров!
bool   mlxInputChar(char &CharArray[]);       //char& CharArray[] – означает: передаем ссылку!
bool   mlxInputDouble(double &dArray[],
                      int sizeArray,
                      char &CharNameArray[]);
bool   mlxInputInt(double &dArray[],
                   int sizeArray,
                   char &CharNameArray[]);
bool   mlxInputLogical(double &dArray[],
                       int sizeArray,
                       char &CharNameArray[]);
int    mlxGetDouble(double &dArray[],
                    int sizeArray,
                    char &CharNameArray[]);
int    mlxGetInt(double &dArray[],
                 int sizeArray,
                 char &CharNameArray[]);
int    mlxGetLogical(double &dArray[],
                     int sizeArray,
                     char &CharNameArray[]);
int    mlxGetSizeOfName(char &CharNameArray[]);
#import    

Обратите внимание, что передавать "указатели" в MQL 5 можно двумя способами:

  • void NameArray[] ;   // При таком способе передачи из массива можно только читать данные. Однако, если такую ссылку попытаться использовать для "редактирования содержимого", возникнет ошибка доступа к памяти (в лучшем для вас случае, MetaTrader 5 "терпеливо промолчит", обработав ошибку незаметно в SEH-фрейме, а вот мы свой SEH-фрейм НЕ ПИСАЛИ, поэтому можем даже причину ошибки не узнать);
  • void& NameArray[] ; // При таком способе передачи над массивом можно осуществлять не только операцию чтения, но и даже операцию редактирования содержимого, но при этом размеры массива должны быть сохранены.

Если функция не принимает, либо не передает параметров, всегда указывать тип void.

2. Описывать все функции БЛОКА-MQL нет смысла, т.к. исходный текст есть в архиве MATLABEngine.zip под именем MatlabEngine.mq5.

Поэтому рассмотрим особенности объявления и определения внешних функций в MQL5:

bool mlInputChar(string array)export
{
//… тело функции
}

Как видно в примере, объявление и определение функции объединены. В данном случае мы объявляем функцию с именем mlInputChar()  внешней (export), возвращающей значение типа bool  и принимающей строку array в качестве параметра. Осталась только компиляция…

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

Для этого напишем простой проверочный скрипт (либо возьмем его из архива MATLABEngine.zip, файл: TestMLEngine.mq5).

Код скрипта простой и хорошо комментирован:

#property copyright "2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com/ru"
#property version   "1.00"
#import "MatlabEngine.ex5"
bool mlOpen(void);
void mlClose(void);
bool mlInputChar(string array);
bool mlInputDouble(double &array[],
                   int sizeArray,
                   string NameArray);
bool mlInputInt(int &array[],
                int sizeArray,
                string NameArray);
int mlGetDouble(double &array[],
                string NameArray);
int mlGetInt(int &array[],
             string NameArray);
bool mlInputLogical(bool &array[],
                    int sizeArray,
                    string NameArray);
int mlGetLogical(bool &array[],
                 string NameArray);
int mlGetSizeOfName(string strName);
#import
void OnStart()
  {
// Динамичские буфера, для вывода из MATLAB
   double dTestOut[];
   int    nTestOut[];
   bool   bTestOut[];
// Переменные для ввода в среду MATLAB
   double dTestIn[] = {   1,     2,    3,     4};
   int    nTestIn[] = {   9,    10,   11,    12};
   bool   bTestIn[] = {true, false, true, false};
   int nSize=0;
// Имена переменных и командная строка
   string strComm="clc; clear all;"; // командная строка - очистить экран и переменные
   string strA     = "A";            // переменная A
   string strB     = "B";            // переменная B
   string strC     = "C";            // переменная C
/*
   ** 1. ЗАПУСК DLL
   */
   if(mlOpen()==true)
     {
      printf("Matlab загружен");
     }
   else
     {
      printf("Matlab ERROR! Ошибка загрузки.");
      mlClose();
      return;
     }
/*
   ** 2. ПЕРЕДАЧА КОМАНДНОЙ СТРОКИ
   */
   if(mlInputChar(strComm)==true)
     {
      printf("Комадная строка переданна в Matlab");
     }
   else printf("ERROR! Ошибка при передачи строки");
/*
   ** 3. ПЕРЕДАЧА ПЕРЕМЕННОЙ ТИПА DOUBLE
   */
   if(mlInputDouble(dTestIn,ArraySize(dTestIn),strA)==true)
     {
      printf("Переменная типа double передана в MATLAB");
     }
   else printf("ERROR! При передачи строки типа double");
/*
   ** 4. ПОЛУЧЕНИЕ ПЕРЕМЕННОЙ ТИПА DOUBLE
   */
   if((nSize=mlGetDouble(dTestOut,strA))>0)
     {
      int ind=0;
      printf("Переменная A, тип: double получена в MATLAB, размером = %i",nSize);
      for(ind=0; ind<nSize; ind++)
        {
         printf("A = %g",dTestOut[ind]);
        }
     }
   else printf("ERROR! Переменная типа double не получена");
/*
   ** 5. ПЕРЕДАЧА ПЕРЕМЕННОЙ ТИПА INT
   */
   if(mlInputInt(nTestIn,ArraySize(nTestIn),strB)==true)
     {
      printf("Переменная типа int передана в MATLAB");
     }
   else printf("ERROR! При передачи строки типа int");
/*
   ** 6. ПОЛУЧЕНИЕ ПЕРЕМЕННОЙ ТИПА INT
   */
   if((nSize=mlGetInt(nTestOut,strB))>0)
     {
      int ind=0;
      printf("Переменная B, тип: int получена в MATLAB, размером = %i",nSize);
      for(ind=0; ind<nSize; ind++)
        {
         printf("B = %i",nTestOut[ind]);
        }
     }
   else printf("ERROR! Переменная типа int не получена");
/*
   ** 7. ПЕРЕДАЧА ПЕРЕМЕННОЙ ТИПА BOOL
   */
   if(mlInputLogical(bTestIn,ArraySize(bTestIn),strC)==true)
     {
      printf("Переменная типа bool передана в MATLAB");
     }
   else printf("ERROR! При передачи строки типа bool");
/*
   ** 8. ПОЛУЧЕНИЕ ПЕРЕМЕННОЙ ТИПА BOOL
   */
   if((nSize=mlGetLogical(bTestOut,strC))>0)
     {
      int ind=0;
      printf("Переменная C, тип: bool получена в MATLAB, размером = %i",nSize);
      for(ind=0; ind<nSize; ind++)
        {
         printf("C = %i",bTestOut[ind]);
        }
     }
   else printf("ERROR! Переменная типа bool не получена");
/*
   ** 9. ЗАВЕРШЕНИЕ РАБОТЫ
   */
   mlClose();
  }

Как видно из скрипта, мы поочередно вводим значения, после чего получаем их же. При этом, в отличие от MetaTrader 4, где требовалось заранее знать размер буфера на стадии проектирования, в MetaTrader 5 это не нужно, т.к. мы используем исключительно динамические буферы.

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

3.2 Технические рекомендации по сборке/использовании DLL-библиотек, сгенерированных компилятором MATLAB версии 4

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

Для примера возьмем простой индикатор — скользящую среднюю (SMA) — и "немного" модернизируем его, добавив Нейронно-Сетевой Фильтр (тип НС: GRNN), позволяющий сглаживать помехи "белого шума" (случайные всплески). Новый индикатор назовем NeoSMA, а НС-фильтр GRNNFilter.  

Итак, мы получили две m-функции, из которых мы хотим создать одну DLL-библиотеку, причем которую можно вызывать из MetaTrader 5.

Теперь вспомним, что MetaTrader 5 ищет DLL-библиотеки в следующих каталогах:

  • <каталог_терминала>\MQL5\Libraries;  
  • Директория, из которой запущен клиентский терминал — <terminal_dir>;  
  • Текущая директория;
  • Системная директория <windows_dir>\SYSTEM32;  
  • Директория, в которую установлена операционная система — <windows_dir>;  
  • Директории, перечисленные в системной переменной окружения PATH.

Поэтому перемещаем в один из указанных каталогов две m-функции (NeoSMA.m и GRNNFilter.m), где и будем производить сборку DLL-библиотеки. Обращаю ваше внимание на данный факт перемещения, т.к. это сделано не случайно. Внимательный читатель уже знает особенность компилятора MATLAB, которая заключается в том, что он сохраняет значения путей при компиляции (см.  раздел "2.2 Компилятор MATLAB версии 4").

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

  1. Введем в командной строке MATLAB команду: mbuild –setup;
  2. Введем ‘y’ для подтверждения поиска совместимых С/С++ - компиляторов установленных в системе;
  3. Выберем стандартный С-компилятор Lcc-win32 C;
  4. Подтвердим правильность выбора компилятора, введем ‘y’.

Рис. 4. Компиляция проекта

Рис. 4. Компиляция проекта


Теперь мы готовы перейти к процессу компиляции m-функций.

Для этого введем команду:

mcc -N -W lib:NeoSMA -T link:lib  NeoSMA.m GRNNFilter.m

Поясним ключи:

-N                                     —  ввели для того, чтобы компилятор откинул все лишние пути
-W lib:NeoSMA                   —  ключ говорит компилятору, что имя библиотеки NeoSMA
-T link:lib                           —  ключ сообщает компилятору, что создается общедоступная библиотека, с линкованием
NeoSMA.m и GRNNFilter.m  —  имена m-функций

Итак, давайте разберемся, что же создал компилятор:

  • mccExcludedFiles.log  —  файл журнала, где записывается действия компилятора;
  • NeoSMA.c  —  С версия библиотеки (содержит С-код обертки);  
  • NeoSMA.ctf  —  файл CTF (см. раздел 2.2 Компилятор MATAB версии 4);  
  • NeoSMA.h  —  заголовочный файл (файл содержит объявления библиотек, функций, констант);  
  • NeoSMA.obj  —  объектный файл (файл-сырец содержащий машинный и псевдокод);  
  • NeoSMA.exports  —  имена экспортных функций;  
  • NeoSMA.dll  —  Dll библиотека, для позднего связывания;  
  • NeoSMA.lib  —  Dll библиотека, для использования в проектах С/С++;  
  • NeoSMA_mcc_component_data.с  —  С версия компоненты (используется для совместимости с CTF- файлом, содержит пути и т.п.);  
  • NeoSMA_mcc_component_data.obj  —  объектная версия компоненты (файл-сырец содержащий машинный и псевдокод);

Итак, давайте разберемся с DLL библиотекой, а именно с её внутренним строением. Она состоит (только основные функции) из:

  1. Главной функции любой DLL-библиотеки BOOL WINAPI DllMain(), которая, согласно спецификации Microsoft, обрабатывает события происходящие с DLL: загрузка DLL в адресное пространство процесса, создание нового потока, уничтожение потока и выгрузка из памяти Dll;  
  2. Служебных функций инициализации/деинициализации DLL: BOOL <NameLib>Initialize(void)/void <NameLib>Terminate(void) — необходимы для запуска/выгрузки среды окружения Math Work перед началом использования библиотечных функций и в конце их использования;
  3. Экспортных m-функций – void mlf<NameMfile>(int <number_of_return_values>, mxArray **<return_values>, mxArray *<input_values>,…), где:
    • <number_of_return_values> — число возвращаемых переменных (не путать с размером массива и т.п.);
    • mxArray **<return_values> — адрес структуры mxArray куда возвратят результат работы m-функции;
    • mxArray *<input_values> — указатель на структуру mxArray входной переменной m-функции.
     

Как вы уже сами увидели, экспортные m-функции содержат адреса и указатели на структуру mxArray, и напрямую вызвать такие функции из MetaTrader 5 нельзя, т.к. MetaTrader 5 не поймет такой тип данных. Описывать в MetaTrader 5 структуру mxArray не имеет никакого здравого смысла, т.к. разработчики MATLAB  не гарантируют её неизменность во времени, даже в пределах одной версии продукта, поэтому необходимо написать простенькую DLL-переходник.

Её блок-схема показана ниже:

Рис. 5. Блок-схема DLL-переходника

Рис. 5. Блок-схема DLL-переходника

Она очень похожа на правую часть DLL-библиотеки для MATLAB Engine, поэтому её алгоритм разбирать не будем, чтобы не повторяться, а перейдем сразу к коду. Для этого в вашем компиляторе С/С++ создаем два маленьких файла:  

nSMA.cpp (из архива DllMatlab.zip):  

#include <stdio.h>
#include <windows.h>
/* Включение заголовочного файла MCR и заголовочного файла библиотеки*/
#include "mclmcr.h"
#include "NEOSMA.h"
/*---------------------------------------------------------------------------
** Глобальные функции DLL(внешние)
*/
extern "C" __declspec(dllexport) bool __stdcall IsStartSMA(void);
extern "C" __declspec(dllexport) bool __stdcall nSMA(double *pY,  int  nSizeY,
                                                     double *pIn, int nSizeIn,
                                                     double   dN, double dAd);
/*---------------------------------------------------------------------------
** Глобальные переменные
*/
mxArray *TempY;
mxArray *TempIn;
mxArray *TempN;
mxArray *TempAd;
bool bIsNeoStart;
//---------------------------------------------------------------------------
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
    switch(reason)
    {
        case DLL_PROCESS_ATTACH:
         bIsNeoStart = false;
         TempY  = 0;   //Обнулили указатели на буфера
         TempN  = 0;
         TempIn = 0;
         TempAd = 0;
         break;
        case DLL_PROCESS_DETACH:
         NEOSMATerminate();
         //Удаляем старые данные, перед выходом из DLL
         if(TempY  != NULL) mxDestroyArray(TempY);
         if(TempN  != NULL) mxDestroyArray(TempN);
         if(TempIn != NULL) mxDestroyArray(TempIn);
         if(TempAd != NULL) mxDestroyArray(TempAd);
         mclTerminateApplication();
    }
    return 1;
}
//---------------------------------------------------------------------------
bool __stdcall IsStartSMA(void)
{
 if(bIsNeoStart == false)
 {
  if(!mclInitializeApplication(NULL,0) )
  {
   MessageBoxA(NULL, (LPSTR)"Can't start MATLAB MCR!",
               (LPSTR) "MATLAB DLL: ERROR!", MB_OK|MB_ICONSTOP);
   return false;
  }else
   {
    bIsNeoStart = NEOSMAInitialize();
   };
 };
 return bIsNeoStart;
}
//---------------------------------------------------------------------------
bool __stdcall nSMA(double *pY, int nSizeY, double *pIn, int nSizeIn, double dN, double dAd)
{
   /*
   ** Создаем буферы
   */
   if(TempN == NULL){ TempN = mxCreateDoubleMatrix(1, 1, mxREAL);}
   else
   {
     mxDestroyArray(TempN);
     TempN= mxCreateDoubleMatrix(1, 1, mxREAL);
   };
   if(TempIn == NULL){ TempIn = mxCreateDoubleMatrix(1, nSizeIn, mxREAL);}
   else
   {
     mxDestroyArray(TempIn);
     TempIn= mxCreateDoubleMatrix(1, nSizeIn, mxREAL);
   };
   if(TempAd == NULL){ TempAd = mxCreateDoubleMatrix(1, 1, mxREAL);}
   else
   {
     mxDestroyArray(TempAd);
     TempAd= mxCreateDoubleMatrix(1, 1, mxREAL);
   };
   /*
   ** Создание данных для обработки
   */
   memcpy((char *)mxGetPr(TempIn), (char *) pIn, (nSizeIn)*8);
   memcpy((char *)mxGetPr(TempN), (char *) &dN, 8);
   memcpy((char *)mxGetPr(TempAd), (char *) &dAd, 8);
   /*
   ** Передача и получение ответа от m-функции
   */
   if(mlfNeoSMA(1, (mxArray **)TempY, (mxArray *)TempIn, (mxArray *)TempN
      , (mxArray *)TempAd) == false) return false;
   /*
   ** Возвращаем вычисленный вектор из m-функции, очищаем буфера
   */
   memcpy((char *) pY, (char *)mxGetPr(TempY), (nSizeY)*8);
   mxDestroyArray((mxArray *)TempY);  TempY  = 0;
   mxDestroyArray((mxArray *)TempN);  TempN  = 0;
   mxDestroyArray((mxArray *)TempIn); TempIn = 0;
   mxDestroyArray((mxArray *)TempAd); TempAd = 0;
   return true;
}

nSMA.def (из архива DllMatlab.zip):

LIBRARY nnSMA
EXPORTS
IsStartSMA
nSMA


Собираем проект в вашем компиляторе С/С++: для этого необходимо подключить стандартную библиотеку ввода/вывода и добавить в проект следующие файлы (команда в вашем компиляторе: Project->Add Project):

  1. nSMA.def
  2. В каталоге <MATLAB>\extern\lib\<win32/64>\<компилятор>\, где:
    <MATLAB> — главный каталог MATLAB; 
    <win32/64> — либо папка win32 для 32 битных ОС, либо win64 для 64 битных ОС;
    <компилятор> — папка "borland" – Borland C/C++ ver. 5-6, папка "microsoft" - Microsoft Visual C++ (у меня файлы для 6 версии):  
    • libmx.lib
    • mclmcr.lib
  3. NeoSMA.lib — создаем самостоятельно (см. раздел 3.1 Разработка универсальной библиотеки связи MetaTrader 5 & MATLAB Engine).  

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

Вот список файлов и пути размещения на целевой машине:

  • MCRInstaller.exe                    любой каталог (установщик MCR)
  • extractCTF.exe                      любой каталог (для установщика MCR)
  • MCRRegCOMComponent.exe  любой каталог (для установщика MCR)
  • unzip.exe                              любой каталог (для установщика MCR)
  • NeoSMA.dll                           Каталог_терминала\MQL5\Libraries
  • NeoSMA.ctf                           Каталог_терминала\MQL5\Libraries
  • nnSMA.dll                             Каталог_терминала\MQL5\Libraries

Как уже догадались многие продвинутые программисты, целесообразно использовать программы-установщики (SETUP), их достаточное количество в Internet, в том числе есть и бесплатные продукты.

Осталось протестировать данную DLL в MetaTrader 5. Для этого этого напишем простой скрипт (TestDllMatlab.mq5 из архива DllMatlab.zip):

#property copyright "2010, MetaQuotes Software Corp."
#property link      "nav_soft@mail.ru"
#property version   "1.00"
#import "nnSMA.dll"
bool  IsStartSMA(void);
bool  nSMA(double  &pY[],
           int    nSizeY,
           double &pIn[],
           int   nSizeIn,
           double     dN,
           double   dAd);
#import
datetime    Time[];    // дин. массив координат времени
double      Price[];   // дин. массив цены
double      dNeoSma[]; // дин. массив цены
void OnStart()
  {
   int ind=0;
// запуск Dll
   if(IsStartSMA()==true)
     {
      //--- Создание, заполнение массивов
      CopyTime(Symbol(),0,0,301,Time);   // временной массив + 1
      ArraySetAsSeries(Time,true);       // получение времени графика
      CopyOpen(Symbol(),0,0,300,Price);  // ценовой массив
      ArraySetAsSeries(Price,true);      // получение цен открытий
      ArrayResize(dNeoSma,300,0);        // резервирование места под ответ функции
                                         // получение данных
      if(nSMA(dNeoSma,300,Price,300,1,2)==false) return;
      // уточняем ориентацию массива
      ArraySetAsSeries(dNeoSma,true);
      // отрисовываем данные на графике
      for(ind=0; ind<ArraySize(dNeoSma);ind++)
        {
         DrawPoint(IntegerToString(ind,5,'-'),Time[ind],dNeoSma[ind]);
        }
     }
  }
//+------------------------------------------------------------------+
void DrawPoint(string NamePoint,datetime x,double y)
  {  // Готовность: 100%. Функция отрисовки данных на графике. Рисование стрелками.
// Основные свойства объекта графика
   ObjectCreate(0,NamePoint,OBJ_ARROW,0,0,0);
   ObjectSetInteger(0, NamePoint, OBJPROP_TIME, x);        // координата по времени х
   ObjectSetDouble(0, NamePoint, OBJPROP_PRICE, y);        // координата по цене y
// Дополнительные свойства объекта графика
   ObjectSetInteger(0, NamePoint, OBJPROP_WIDTH, 0);       // толщина линии
   ObjectSetInteger(0, NamePoint, OBJPROP_ARROWCODE, 173); // тип стрелки
   ObjectSetInteger(0, NamePoint, OBJPROP_COLOR, Red);     // цвет стрелки
  }
//+------------------------------------------------------------------+

Заключение

Итак, мы ознакомились с тем как создать универсальную библиотеку для связи MetaTrader 5 & MATLAB, и как подключить DLL-библиотеку созданную средой MATLAB. Но остались ещё неосвещенные интерфейсы взаимодействия MetaTrader 5 & MATLAB, их освещать не имеет смысла, т.к. в данная статья "не резиновая". Рассматриваемый вопрос освещен в полном объеме, т.к. выбраны самые эффективные способы взаимодействия, не требующие создания особых видов "переходников". Хотя вы можете пойти и "другим путем", например по технологии .NET — МetaTrader 5. Экспорт котировок в .NET приложение, используя WCF сервисы.

У многих возник вопрос: какой же способ связки выбрать из описываемых в статье? Ответ прост – оба, т.к. на этапе проектирования/отладки математической модели скорость не нужна, однако нужно, чтобы вся мощь MATLAB была задействована без "особых производственных затрат" на программирование. Тут, естественно, поможет MATLAB Engine. Однако, когда математическая модель будет отлажена и готова к использованию, потребуется скорость, многозадачность (работа индикатора и/или MTC на нескольких ценовых графиках) – тут без сомнения поможет DLL-библиотека, созданная средой MATLAB.

Но все вышесказанное не обязывает вас этому следовать, ответ на этот вопрос должен каждый дать самостоятельно, опираясь в первую очередь на отношение "затраты на программирование" к масштабности проекта (количество пользователей индикатора и/или MTC). Нет никакого смысла создавать Dll в среде MATLAB для одного, двух пользователей (легче на двух компьютерах установить MATLAB).  

У многих читателей, кто мало знаком с MATLAB, наверно есть вопрос: зачем все это? Ведь MQL5 и так имеет математические функции! Применение MATLAB позволит вам без особых усилий реализовать свои математические идеи, вот лишь неполный список возможностей:  

  • динамический алгоритм нечеткой логики в индикаторе и/или МТС;  
  • динамический генетический алгоритм в МТС (динамический тестер стратегий);
  • динамический нейросетевой алгоритм в индикаторе и/или МТС;  
  • трехмерные индикаторы;
  • моделирование нелинейных систем управления;

Так что все в ваших руках, и не забывайте: "МАТЕМАТИКА БЫЛА И БУДЕТ ЦАРИЦЕЙ НАУК", а пакет MATLAB — ваш научный калькулятор.

Литература

   1. Встроенная справка по MATLAB.

   2. Встроенная справка по MQL5.
   3. Н. Н. Мартынов, А. П. Иванов. MATLAB 5.x. Вычисления. Визуализация. Программирование.
   4. М. Л. Подкур, П. Н. Подкур, Н. К. Смоленцев. Программирование в среде Borland C++ Builder с математическими библиотеками MATLAB C/C++.
   5. А.Я. Архангельский. C++ Builder 6. Справочное пособие.
   6. Дж. Рихтер. Windows для профессионалов. Создание эффективных Win32-пpилoжeний с учетом специфики 64-разрядной версии Windows.

Прикрепленные файлы |
dllmatlab.zip (956.8 KB)
matlabengine.zip (671.62 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (16)
arbitrageur
arbitrageur | 7 апр. 2014 в 14:27

Такое конечно гуано это статья.

 Во первых почему статья под x32, когда у всех уже x64

Борландовские dll есть только в древней версии Матлаба, в студии под x 64 Ниче не компилируется даже если все настроить в студии на x64 Матлабовские библиотеки.  Папка метатрейдера со скриптами находится не в папке МТ а хз где. Без борландовских x32 библиотек ничего не компилируется, т.е. с библиотеками x64 - х.

 

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

 

FO 0 16:30:22.029 Scripts script TestMLEngine (GBPUSD,H1) loaded successfully

LQ 2 16:30:22.047 Scripts initializing of TestMLEngine (GBPUSD,H1) failed

NG 0 16:30:22.047 Scripts script TestMLEngine (GBPUSD,H1) removed 

arbitrageur
arbitrageur | 7 апр. 2014 в 14:27
Zhigool:

А если при запуске скрипта  в журнале

"2013.11.13 16:49:28 Scripts script TestMLEngine (EURUSD,H1) removed

 2013.11.13 16:49:28 Scripts script TestMLEngine (EURUSD,H1) loaded successfully"

и всё... в чем может быть проблема?

такая же ерунда

 

Многие рекомендуют запускать МТ от имени администратора, ставить только x32 все - начиная от МТ и заканчивая Матлабом. 

arbitrageur
arbitrageur | 7 апр. 2014 в 14:36
H_F_M:

Доброго дня) У меня возникла еще проблема - в папке<MATLAB>\extern\lib\win32\ почему-то не обнаруживается каталога "Borland", хотя стоит Borland C++ Builder 6. Соответственно файлы libeng.lib и libmx.lib покдлючить к проекту тоже не получается. На команду "implib libeng.lib libeng.dll" Far выдает следующее:

 )

 А папки Borland как не было, так и нет... Может кто подскжет, как с это бедой бороться? Или хотя бы эти два файлика скинет?:-) 

Это гуано есть только в 2007 Лабе версии X32 !!!
arbitrageur
arbitrageur | 15 апр. 2014 в 12:39
arbitrageur:
Это гуано есть только в 2007 Лабе версии X32 !!!
Если кому-то нужна скомпилированная  64битная(!) dll  для 64 битного(!) матлаба и 64 битного МТ пишите в почту этого сайта, цена 40$
DmitryAE
DmitryAE | 16 авг. 2015 в 14:42
скомпилированная есть бесплатно (пишите в личку для x64) но возникает т.н. dll hall когда не находятся куча dll... перекомпиловал матлабом dll под x64 с stdcall статьи другого https://www.mql5.com/en/articles/1567 автора на эту тему намного качественнее...  в этой статье ничего про возможные баги вообще не написано и скриншота что что-то у автора запустилось нет...
Интервью с Николаем Косициным: мультивалютные эксперты менее рискованны (ATC 2010) Интервью с Николаем Косициным: мультивалютные эксперты менее рискованны (ATC 2010)
Мы побеседовали с Николаем Косициным о его разработках. Он считает мультивалютники перспективным направлением и занимается разработкой именно таких экспертов. На чемпионатах Николай также выступает только с мультивалютниками. Именно его советник стал единственным мультивалютником, который вышел в победители Чемпионата за все время проведения соревнования.
Конкурс советников внутри советника Конкурс советников внутри советника
С помощью виртуальной торговли можно создать адаптивный советник, который будет выполнять включение/отключение сделок на реальном рынке. Соберите несколько стратегий в одном эксперте! Ваш мультисистемный советник будет автоматически выбирать торговую стратегию, которой стоит торговать на реальном рынке по результатам успешности виртуальных сделок. Такой метод позволяет снизить просадку и увеличить прибыльность Вашей работы на рынке. Экспериментируйте и делитесь результатами с другими! Думаю, будет интересно многим узнать о вашем портфеле стратегий.
Цветные индикаторы - создание и применение Цветные индикаторы - создание и применение
Речь в данной статье пойдет о возможностях для создания цветных индикаторов и раскрашивания индикаторов уже существующих. С переходом на MQL5 появилась возможность представлять информацию в удобном для глаза виде. Теперь не обязательно накидывать кучу графиков с разными индикаторами и с линейкой высматривать уровни RSI и Stochastic, можно просто раскрасить свечи в разные цвета в зависимости от показаний индикаторов.
Оптимальный метод подсчета объема совокупной позиции по заданному магическому номеру Оптимальный метод подсчета объема совокупной позиции по заданному магическому номеру
В статье рассматривается проблема необходимости подсчета совокупной позиции по заданному символу и магическому номеру. Предложенный метод подсчета объема позиции в процессе работы загружает только минимально необходимую часть истории сделок. В процессе же самой работы обработка происходит только по последним сделкам. Дополнительно рассматривается метод формирования уникальных имен глобальных переменных.