English 中文 Español Deutsch 日本語 Português
Взаимодействие между MеtaTrader 4  и MATLAB Engine (виртуальная машина MATLAB)

Взаимодействие между MеtaTrader 4 и MATLAB Engine (виртуальная машина MATLAB)

MetaTrader 4Примеры | 30 марта 2009, 08:19
3 173 36
Andrey Emelyanov
Andrey Emelyanov

Введение

В настоящее время MetaTrader 4 и математический пакет MATLAB успели получить высокую оценку пользователей за многие свои положительные характеристики, в том числе за «гибкость» при создании сложных вычислительный систем. Математический пакет MATLAB имеет три основных способа связи с вешними приложениями, но лишь один из них рекомендован – использование виртуального рабочего стола (машины) MATLAB Engine, данный способ гарантирует полную совместимость со всем пакетом MATLAB. Многие программисты избегают данный способ из-за следующих причин:

  • Многие считают его медленным. Да, если сравнивать с методом прямого вызова функций из Dll-библиотек MATLAB. Основная задержка происходит в начале работы, при вызове виртуальной машины, из-за вызова многочисленных библиотек, которые загружаются в виртуальное адресное пространство вызвавшего процесса (в нашем случае MetaTrader 4).
  • Переносимость проекта. Да, при переносе проекта на другой компьютер необходимо таскать все Dll-библиотеки MATLAB, но и при прямом вызове тоже, a также знать «родственные связи» последних, т.е. очередность запуска!
  • Обязательное знание С++ или Fortran. Ну на этот вопрос отвечу так: если знаешь MQL4, то выучить С++ не сложно и наоборот.

Почему я рекомендую этот способ:

  1. Самый надежный и независимый от версии MATLAB способ взаимодействия с внешними программами. Вы можете поменять версию MATLAB, а ваши индикаторы и советники этого не заметят, это считаю главным преимуществом.
  2. Относительно быстрый способ разработки. Нет необходимости в отладчиках, а написать «Dll-обертку» не составит труда.
  3. «Общий рабочий стол» для нескольких индикаторов и/или советников. Это свойство считаю полезным в тех случаях, когда необходимо принять решение на основе данных от нескольких индикаторов или же при реализации пирамидальной сделки.

В данной статье описывается способ связи MetaTrader 4 и MATLAB версии 7.4.0 (R2007a) через «Dll-обертку» написанную на Borland C++ Builder 6. Программистам, предпочитающим продукцию Microsoft, придется самим адаптировать примеры под свой компилятор (удачи Вам в нелегком труде!).

I . Постановка задачи

Прежде всего надо разобраться, с чего же начать проектирование? Разобьем процесс разработки на три части:

  1. Разработка M-функции в MATLAB, реализующей расчет индикатора/советника.
  2. Разработка «Dll-обертки» для связи MATLAB и MetaTrader 4.
  3. Разработка MQL программы.

II. Разработка M -функции

Это, наверное, самый интересный и продолжительный процесс, который включает в себя следующие действия:

1. Предварительный экспорт данных из MetaTrader 4 в MATLAB.

На рисунках подробно показан процесс ручного экспорта данных в MATLAB. После окончания экспорта в рабочем столе MATLAB будут созданы переменные.

2. Поиск верных формул, диапазона параметров формул, и т.п.

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

3. Создание M-функции в MATLAB.

Создание функции для программиста, знакомого с С++ и/или MQL4, не составляет особых проблем, особенно если учитывать тот факт, что все переменные имеют один тип данных «матрица». Т.е. несущественно и незначимо явно определять переменную как массив или многомерный массив, за вас сделает это язык. Так же считаю неважным процесс подбора типа данных, я лично использую всегда mxREAL, да, возможно, расходуется больше памяти, но зато никакой путаницы. Ну а более подробную справку можно получить в литературе № 1, 2. В приведенном примере реализован фильтр высоких частот.

III. Разработка «Dll-обертки»

На данном пункте остановимся подробно, так как он ВАЖЕН как воздух. Итак, любая Dll-библиотека позднего связывания должна удовлетворять следующим условиям:

  • Иметь внутренние функции для сбора «мусора» и очистки памяти после своей работы.
  • Быть по возможности многопоточной, т.е. поддерживать работу более одного потока одновременно.
  • Располагаться в определенных каталогах, см. ниже расположение файлов проекта.

Основными внешними функциями «Dll-обертки» являться API-интерфейс MATLAB Engine и одна функция стандартной библиотеки ввода/вывода С++. API-интерфейс MATLAB Engine прост и лаконичен и содержит всего восемь функций:

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

int exitCode = engClose(Engine *pEng) – функция закрытия рабочего стола, pEng указатель на «дескриптор» рабочего стола, возвращает значение, но его смысл неважен, т.к. данная функция вызывается при закрытии Dll и роли особой не играет, вернет число «пользователей» стола MATLAB.

mxArray *mxVector = mxCreateDoubleMatrix(int m, int n, int ComplexFlag) – функция создает матрицу для рабочего стола MATLAB, возвращает указатель на переменную матрицу. Нужна для создания переменной совместимой с MATLAB. Обычные массивы данных и/или простые типы данных в MATLAB отправлять нельзя!

mxArray *mxVector – указатель на переменную матрицу;

int m – кол-во рядов;

int n – кол-во колонок;

ComplexFlag – тип комплексного числа, всегда mxREAL для правильной работы с MetaTrader 4.

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(…). Когда получаете/записываете переменную типа mxArray , для извлечения/вставки переменной простого типа(int, double...) используйте данную функцию.

double *p – указатель на массив типа double;

mxArray *mxVector – указатель на переменную матрицу.

int = engEvalString(Engine *pEng, char *Command) – функция отправки команды рабочему столу. Команда в строке Command будет выполнена рабочим столом MATLAB.

Engine *pEng – указатель на “дескриптор” рабочего стола;

char *Command – команда для MATLAB, строка тип char.

Функция для работы с памятью только одна:

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

ВНИМАНИЕ: следите за размерностью массивов, они должны либо совпадать, либо массив pIn должен быть больше pOut.

Требования к экспортным функциям «Dll-обертки»

Для того чтобы MetaTrader мог использовать Matlab, необходимо написать функции-переходники. Рассмотрим требования к проектированию данных функций. Любая функция, которая будет вызвана из MetaTrader 4, должна быть __stdcall – т.е. передача параметров через стек, функция чистит стек. Расмотрим, как объявить функцию:

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

extern "C" __declspec(dllexport) - говорит компилятору С++, что функция внешняя, записывается в таблице экспорта.

<тип_переменной> - тип возвращаемой переменной, может быть: void, bool, int, double, составные типы и указатели передавать нельзя, см. ниже;

__stdcall – соглашение о передаче параметров в функцию и обратно;

Funcion – имя Вашей функции;

<тип> <имя> - тип и имя входной переменной, максимальное количество переменных - 64.

А вот прототип определения функции, также обращаем внимание на __stdcall

bool __stdcall Funcion (<тип> <имя>)

{

//……

}

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

LIBRARY NameDll

EXPORTS

NameFunctionA

NameFunctionB

LIBRARY – служебное слово, указывает на имя Dll.

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

NameFunctionA, NameFunctionB – имена функций Dll.

Но есть ограничения, накладываемые MQL: т.к. в данном языке нет указателей, значит, нет динамической памяти, а значит нельзя передавать из DLL библиотеки массивы, структуры и т.п. Но кто сказал, что нельзя записывать данные в массивы MetaTrader переданные функции по ссылке? Никто не запрещает Вам писать результат в массив, который создал MetaTrader, и указатель которого получила Ваша Dll, но массив должен быть определенного размера, и не может быть индикаторной линией (это ограничение, по-видимому, связано со специфичной организацией памяти в MetaTrader 4).

Теперь, зная как написать и какие функции вызвать, рассмотрим типовой алгоритм «Dll-обертки»:

1. Запуск MATLAB Engine с помощью функции engOpen() при первом обращении к DLL;

2. Получение данный из MetaTrader и возврат обратно, Dll-функция;

2.1. Создание переменных функцией mxCreateDoubleMatrix();

2.2. Копирование данных в переменную mxVector, функции memcpy() и mxGetPr();

2.3. Передача переменных в рабочий стол MATLAB, функция engPutVariable();

2.4. Передача формулы/команды в рабочий стол MATLAB, функция engEvalString();

2.5. Получение ответа из рабочего стола MATLAB, функция engGetVariable();

2.6. Возврат значения в MetaTrader, функции memcpy() и mxGetPr();

3. Закрытие MATLAB Engine с помощью функции engClose(), удаление всех переменных mxDestroyArray() при выгрузке DLL из адресного пространства процесса MetaTrader.

Имея подробный план местности, составим скелет «Dll-обертки»:

/*---------------------------------------------------------------------------
** Библиотеки + *.lib + *.def:
** libeng.lib** libmx.lib
** libmex.lib** имя_проекта.def
*/
#include <windows.h>#include <memory.h>#include "engine.h"
//---------------------------------------------------------------------------
extern "C" __declspec(dllexport)<тип_переменной>__stdcall Funcion(<тип><имя>);
//---------------------------------------------------------------------------
int WINAPI DllEntryPoint(HINSTANCE hinst,unsigned long reason,void *lpReserved)
  {
   /*    
** причина вызова DLL    
*/
   switch(reason)
     {
      case DLL_PROCESS_ATTACH:
         /*            
** DLL загружена в адресное пространство процесса            
*/
         break;
      case DLL_PROCESS_DETACH:
         /*            
**DLL выгружена из адресного пространства процесса            
*/
         break;
     }
   return TRUE;
  }
//---------------------------------------------------------------------------
bool __stdcall Funcion(<тип><имя>)
  {
   ……
  }
//---------------------------------------------------------------------------

Сборка проекта

На рисунке показано, как добавлять библиотеки и def файлы в проект:

Вот список файлов, необходимых проекту “Dll-обертки”:

  1. libeng.lib – расположен в паке: \Program Files\MATLAB\R2007a\extern\lib\win32\borland\
  2. libmx.lib – расположен в паке: \Program Files\MATLAB\R2007a\extern\lib\win32\borland\
  3. libmex.lib – расположен в паке: \Program Files\MATLAB\R2007a\extern\lib\win32\borland\
  4. имя_проекта.def – этот файл создаем в «блокноте», как описано выше.

Файл engine.h копируем из папки \Program Files\MATLAB\R2007a\extern\\include в папку \Program Files\Borland\CBuilder6\Include, чтобы каждый раз не указывать путь компилятору.

Внимание: Даны указания для сборки проекта только в Borland C++ Builder 6!

IV. Разработка MQL программы

Будем рассматривать вопросы, связанные только с объявлением функций “Dll-обертки” и передачей параметров. Итак, чтобы объявить функции, необходима следующая языковая конструкция:

#import "HighPass.dll"

void ViewAnsFilter();

bool TestDllFilter();

bool AdaptiveHighFilter(double& nInVector[], int nSizeVector, double nSizeWind, double dAmplit);

void MakeBuffFilter(int nSize);

void DestrBuffFilter();

#import

где:

#import "HighPass.dll" – ключевое слово и имя dll библиотеки;

void MakeBuffFilter(int nSize); - имя функции, тип возвращаемого значения, имя и тип передаваемого значения.

Обратите внимание! При передаче массивов используют “[]”, знак амперсанд “&” – необходим, если dll пишет ответ в этот массив данных! Других способов передачи массивов из внешних программ в MQL 4 не предусмотрено! Передаваемый массив должен быть определенного размера и не может быть индикаторным массивом!

V. Расположение файлов

После сборки всего проекта необходимо правильно разместить файлы проекта:

*.dll и *.m - файлы библиотеки и m-функции в каталог \Program Files\MetaTrader\experts\libraries;

, а *.mql располагается в обычном месте, т.е. если индикатор - в папке indicators, если советник - в папке experts, если скрипт - в папке scripts.

ВНИМАНИЕ: При запуске индикатора или советника может появиться предупреждение:

В таком случае необходимо подождать 5-10 секунд до появления в панели задач Console Matlab и нажать «повторить».

P.S. У меня ноутбук с 512 ОЗУ, Celeron M 2100, задержек при работе фильтра не наблюдал, кол-во графиков 5, с суммарным буфером 500 х 8 х 5 = 20 000 байт. Так что- выбор за Вами! Лично я его уже сделал. Если появятся задержки, то в MATLAB легко можно организовать распределенную вычислительную систему, иными словами запустить несколько рабочих столов на разных PC, объединенных в локальную сеть.

Литература

  1. Встроенная справка по MATLAB.
  2. «Matlab 5.х вычисления, визуализация, программирование.» Н.Н. Мартынов.
  3. «C++ Builder 6. Справочное пособие.» А.Я. Архангельский.
  4. Встроенная справка по MQL4.

Заключение

В данной статье рассмотрели основы разработки "Dll-обертки" для связывания MetaTrader 4 c математическим пакетом MATLAB. Вопросы обеспечения работы нескольких индикаторов и/или советников остался без внимания, это будет раскрыто в следующей статье. В прикрепленном файле находиться стандартный индикатор MACD, усовершенствованный за счет использования фильтра высоких частот.

Прикрепленные файлы |
highpas.rar (10.19 KB)
highpasFull.zip (355.1 KB)

Другие статьи автора

Последние комментарии | Перейти к обсуждению на форуме трейдеров (36)
dak
dak | 23 июн. 2009 в 15:57

Выложил адаптированный проект сюда http://neurotrading.ru/forum/20-56-1#909

С уважением Дмитрий

ivan
ivan | 25 нояб. 2009 в 06:51

Вопрос. Насколько "коряво", по сравнению с предложенным методом, будет делать .exe файлы из нужных Matlab-овских функций с помощью Matlab Compiler и затем обращаться к exe-шникам посредством "командной строки"  (http://www.metatrader4.com/forum/1476) из mql4 кода? Или это будет очень медленно?

Andrey Emelyanov
Andrey Emelyanov | 25 нояб. 2009 в 10:27
qomment:

Вопрос. Насколько "коряво", по сравнению с предложенным методом, будет делать .exe файлы из нужных Matlab-овских функций с помощью Matlab Compiler и затем обращаться к exe-шникам посредством "командной строки" (http://www.metatrader4.com/forum/1476) из mql4 кода? Или это будет очень медленно?


Вопрос больше системного характера, не желе касающегося MatLab и MetaTrader'а... С точки зрения ОС Windows файлы с расширением *.exe и *.dll различаются только наличием логической единицы у Dll по смещению 05Eh в файле относительно заголовка "PE\0\0", но есть еще одна тонкость, Dll грузиться в адресное пространство вызываемого процесса, в нашем случае MT4/MT5, а *.exe создает свой отдельный процесс и свое отдельное адресное пространство, естественно этот факт не может остаться незамеченным и комп тормозит, а если учесть тот факт, что данная программа будет постоянно загружаться-выгружать в память PC это будет очень накладно по времени.

Если бы MT4/MT5 был создан для Linux, то это было бы делом вкуса, создавать DLL или отдельную программу, в Linux есть расширенное понятие канал(Pipe), в отличии от Win32. Тут уж нечего не поделаешь, такова политика фирмы Micro$oft - изоляция программ их девиз.

albert-mql
albert-mql | 29 янв. 2012 в 06:54
Дмитрий, выложите, пожалуйста, адаптированный проект под VS2008 в эту статью. Ваша ссылка больше не работает. Спасибо.
Dmitriy Voevodkin
Dmitriy Voevodkin | 4 мар. 2018 в 12:41

Если нет возможности углубляться в Си и т.п. вот вариант попроще.

https://www.mql5.com/ru/forum/228342#comment_6712127

Конструктор трейдера: Украшение индикаторов Конструктор трейдера: Украшение индикаторов
Основные задачи при раскрашивании индикаторов, их решение и автоматизация.
MetaTrader для работы на фондовом рынке - легко! MetaTrader для работы на фондовом рынке - легко!
В данной статье поднимается проблема автоторговли на фондовом рынке. Приводится пример интеграции MetaTrader и QUIK. Описаны преимущества MT для решения данной задачи, приводится пример торгового робота, способного выполнять операции на ММВБ.
Alert и Comment для внешних индикаторов Alert и Comment для внешних индикаторов
В практической работе трейдер иногда сталкивается с такой ситуацией: нужно получить «alert» или текстовое сообщение на экране монитора, (в окне графика) сообщение или информацию о появившемся сигнале от какого-либо индикатора. В статье приводится пример вывода информации о графических объектах, созданных сторонним индикатором.
Эффективные алгоритмы усреднения с минимальным лагом и их использование в индикаторах и экспертах Эффективные алгоритмы усреднения с минимальным лагом и их использование в индикаторах и экспертах
В статье изложены авторские разработки пользовательских функций для более качественного по сравнению с обычным усреднением сглаживания: JJMASeries(), JurXSeries(), JLiteSeries(), ParMASeries(), LRMASeries(), T3Series() и MASeries(). В ней автор расматривает горчую замену этих функций в индикаторах в помощью обращения к функции SmoothXSeries().