Пишем библиотеку DLL для Metastock с нуля.Часть2

В этой статье будут подробно рассмотрены наша функция (часть1), правила получения данных из Metastock«а, их обработки и возврата результата обратно в Metastock. Эта информация поможет избежать ошибок в работе MSX DLL.Наша функция имеет вид: DLL_EXPORT BOOL __stdcall Price (const MSXDataRec *a_psBasic, const MSXDataInfoRecArgsArray *a_psArrayArgs, const MSXNumericArgsArray *a_psNumericArgs, const MSXStringArgsArray *a_psStringArgs, const MSXCustomArgsArray *a_psCustomArgs, MSXResultRec *a_psResult) , где*a_psBasic — указатель на структуру MSXDataRec (все доступные данные о ценных бумагах);*a_psArray — указатель на структуру MSXDataInfoRecArgsArray (аргументы массивов);*a_psNumeric — указатель на структуру MSXNumericArgsArray (числовые аргументы);*a_psString — указатель на структуру MSXStringArgsArray (строковые аргументы);*a_psCustom — указатель на структуру MSXCustomArgsArray (Custom-аргументы);*a_psResult — указатель на структуру MSXResultRec (результат обработки данных возвращаемый в Metastock);*a_psValue — указатель на структуру MSXDataInfoRec (все числовые данные, используемые в расчетах).Все структуры описываются в файле MSXStruc.h.Хранение данных и расчеты Все числовые данные, используемые в расчетах индикаторов, сохраняются в структурах, известных как массивы данных. Массивы данных используются для хранения данных цен (например, Open, High, Low, и т.д.), числовых констант и результатов расчета индикаторов. Когда MetaStock поставляет данные о ценах бумагах и аргументах функций MSX DLL, эти данные используются как массивы. Когда функция, вычисленная MSX DLL, возвращает результаты индикатора в MetaStock, то результат возвращается в виде массива данных.Массивы данных реализованы в структуре MSXDataInfoRec и имеют три основных компонента: • Data elements — элементы данных.• First valid index — начальная точка отсчета (Fvi).• Last valid index — конечная точка отсчета (Lvi).Элементы данных являются фактическими числовыми значения, связанными с массивом данных. Эти значения сохраняются в массиве в виде float значений. Первый и последний допустимый индекс используются для определения, какие элементы данных содержат достоверные значения. Все элементы данных между первым и последним допустимым индексом (включительно) содержат достоверные данные. Все элементы вне этого диапазона имеют неопределенные значения и должны игнорироваться для всех расчетов.Массив данных считается «пустым», если FviИли, если результат 100 — периодной скользящей средней применяется к массиву данных цен ЦБ, который содержит меньше 100 элементов, тогда получаем пустой массив данных.Первый и последний допустимые индексы очень важны во время расчета индикатора. Расчеты всегда должны быть ограничены элементами данных, содержащихся между ними. Два важных момента следует понимать, чтобы правильно установить Fvi и Lvi индексы для возвращаемого массива данных: • Всегда ограничивать расчеты в промежутке допустимых диапазонов всех используемых входных массивов данных.• Fvi и Lvi в результате расчета должны сохранять свои позиции по отношению к значениям всех массивов входных данных.Структура MSXDataRec содержит семь массивов данных: • sOpen, sHigh, sLow, sClose, sVol, sOI, sInd, хранящих свои значения в MSXDataInfoRec структуре. Эти массивы данных хранят все необходимые ценовые данные ЦБ.Некоторые из этих массивов могут быть пустым, если ЦБ не имеет данных для этого поля цен (например, Open Interest).• Кроме того, в структуре MSXDataRec содержится указатель на массив из структуры MSXDateTime.Этот массив содержит информацию о дате и времени для каждой точки данных. Если функции расчета необходим доступ к дате и времени для N-го бара инструмента, надо ссылаться на N-ый элемент массива psDate. Обратите внимание, что этот массив отличается от такого массива данных как sHigh, sLow, и т.п.• Массив данных sInd содержит данные для индикатора, выбранного пользователем. В случае пользовательского индикатора этот массив данных будет содержать значение для объекта диаграммы, к которой индикатор был прикреплен. Если не выбран участок sInd массив данных будет пустым.• Обратите внимание, что расположение данных в этих массивах синхронизированы с N-м элементом каждого элемента массива, соответствующего временного периода.• Массив данных sClose всегда содержит максимальное количество элементов данных. Все остальные массивы данных содержат количество элементов <= sClose.iLastValid.• Настройки iFirstValid и iLastValid в массиве данных sClose очень важны.Обычно количество элементов в этом массиве определяет максимальное количество элементов данных, хранящихся в других массивах цен. Это важно для определения количества допустимых элементов, содержащихся в массиве psDate. Например, если поле sClose.iFirstValid = 100 и поле sClose.iLastValid = 200, вы можете быть уверены, что массив psDate содержит действительные данные на промежутке от psDate [100] до psDate [200].• После того, как расчет выполнится, значение iLastValid в a_psResult -> psResultArray никогда не должно быть больше, чем значение iLastValid массива данных sClose.• iFirstValid и iLastValid из sClose должны быть использованы для определения того, сколько значений доступно для хранения всех данных массивов. Для хранения всех массивов выделяется достаточно памяти, только до точки данных sClose.iLastValid. Данные возвращаемые в a_psResult — > psResultArray от MSX DLL должны уложиться в эти же ограничения по хранению.• Начало массива данных a_psResult→psResultArray возвращаемого из MSX DLL никогда не должно быть меньше sClose.iFirstValid. Конец массива данных a_psResult — >psResultArray никогда не должны быть больше, чем sClose.iLastValid.

Что необходимо помнить • Функции расчета никогда не должны изменять любые входящие аргументы, за исключением результирующей записи (a_psResult). Входящие аргументы определяются как 'const', где это возможно, в предоставленных шаблонах, чтобы гарантировать, что не произойдет незаконных изменений.• Обязательно установите a_psResult → psResultArray → iFirstValid и a_psResult — > psResultArray — > iLastValid до возвращения из вашей функции.• Если ваша функция возвращает MSX_ERROR, что указывает на внутреннюю ошибку, убедитесь, что вы скопировали в расширенную строку ошибки, причину описывающую ошибку a_psResult — >pszExtendedError.• Никогда не устанавливайте a_psResult — > psResultArray — > iFirstValid меньше sClose.iFirstValid.• Никогда не устанавливайте a_psResult — > psResultArray — > iLastValid больше sClose.iLastValid. Запись в a_psResult — > psResultArray → pfValue за значение sClose.iLastValid вызовет перепонение памяти в MetaStock и аварийному завершению работы программы.• Обязательно проверьте iFirstValid и iLastValid любых аргументов MSXDataInfoRec или a_psBasic, которые вы собираетесь использовать. Никогда не думайте, что данные будут доступны в любом массиве данных. Если данные не доступны для вашей функции для обработки, установитеa_psResult — > psResultArray — > iFirstValid = 0 иa_psResult — >psResultArray — > iLastValid = -1, чтобы указать, что нет достоверных данных для возвращения массива. Этот метод позволяет избежать аварийного завершения работы программы Metastock.Изменяем код В соответствии с вышеизложенным материалом немного изменим код нашей функции. Добавим Fvi и Lvi, а также исключение в случае получения поврежденного массива на выходе нашей функции. DLL_EXPORT BOOL __stdcall Price (const MSXDataRec *a_psBasic, const MSXDataInfoRecArgsArray *a_psArrayArgs, const MSXNumericArgsArray *a_psNumericArgs, const MSXStringArgsArray *a_psStringArgs, const MSXCustomArgsArray *a_psCustomArgs, MSXResultRec *a_psResult)

{ BOOL l_bRtrn = MSX_SUCCESS; for (int i= a_psBasic →sClose.iFirstValid; i<= a_psBasic ->sClose.iLastValid; i++) a_psResult→psResultArray→pfValue[ i ] = a_psBasic →sClose.pfValue[ i ];

// Задаем начало и конец массива. a_psResult→psResultArray→iFirstValid = a_psBasic→sClose.iFirstValid; a_psResult→psResultArray→iLastValid = a_psBasic→sClose.iLastValid;

// Если данные не доступны, возвращаем пустой массив. if (l_bRtrn!= MSX_SUCCESS) { strncpy (a_psResult→szExtendedError, «Error: Corrupted Result Array.», sizeof (a_psResult→szExtendedError)-1); a_psResult→psResultArray→iFirstValid = 0; a_psResult→psResultArray→iLastValid = -1; } return l_bRtrn; } Вывод в файл Для вывода наших данных обязательно подключите заголовочный файл stdio.h, в котором объявлен специальный тип данных — структура FILE.• #include Комментарии Для организации работы с файлами, программе надо использовать указатели на файлы. Для создания файловой переменной-указателя используется оператор типа: FILE *file (объявление потока). Чтобы можно было обращаться к файлу его необходимо открыть. Функция fopen () открывает для использования поток, связывает файл с данным потоком, возвращает указатель FILE на данный поток и имеет следующий вид: • file = fopen («путь к файлу», «режим работы файла»).Режим «w» используется для записи в файл. Запись в файл осуществляет функция fprintf (): • fprintf (file,[строка формата], [список переменных, констант]).Функция fclose () используется для закрытия потока, ранее открытого с помощью fopen ().• fclose (file)Вызов fclose () освобождает блок управления файлом, связанный с потоком, и делает его доступным для повторного использования.

Давайте выведем следующие данные: имя ЦБ, период ('D’aily, 'W’eekly, 'M’onthly, 'Q’uarterly, 'I’ntraday), индекс, наш индикатор, время и дату в файл. DLL_EXPORT BOOL __stdcall Price (const MSXDataRec *a_psBasic, const MSXDataInfoRecArgsArray *a_psArrayArgs, const MSXNumericArgsArray *a_psNumericArgs, const MSXStringArgsArray *a_psStringArgs, const MSXCustomArgsArray *a_psCustomArgs, MSXResultRec *a_psResult) { BOOL l_bRtrn = MSX_SUCCESS; FILE *file; file = fopen («D:\\example.txt», «w»); for (int i= a_psBasic →sClose.iFirstValid; i<= a_psBasic ->sClose.iLastValid; i++) { a_psResult→psResultArray→pfValue[ i ] = a_psBasic →sClose.pfValue[ i ]; if (file) fprintf (file,»%-10s %2c %5u %12.4f %5u %10u\n», a_psBasic →pszSecurityName, a_psBasic →iPeriod, i, double (a_psResult→psResultArray→pfValue[i]), a_psBasic →psDate[i].lTime/1000, a_psBasic →psDate[i].lDate); } fclose (file); // Задаем начало и конец массива. a_psResult→psResultArray→iFirstValid = a_psBasic→sClose.iFirstValid; a_psResult→psResultArray→iLastValid = a_psBasic→sClose.iLastValid;

// Если данные не доступны, возвращаем пустой массив. if (l_bRtrn!= MSX_SUCCESS) { strncpy (a_psResult→szExtendedError, «Error: Corrupted Result Array.», sizeof (a_psResult→szExtendedError)-1); a_psResult→psResultArray→iFirstValid = 0; a_psResult→psResultArray→iLastValid = -1; } return l_bRtrn; } В результате у меня на одноминутном графике фьючерса на индекс РТС получилось следующее: example.txt SP_RTS_1m I 1 112310.0000 1725 20140417 SP_RTS_1m I 2 112320.0000 1726 20140417 SP_RTS_1m I 3 112130.0000 1727 20140417 … SP_RTS_1m I 497 117150.0000 1336 20140418 SP_RTS_1m I 498 117170.0000 1337 20140418 SP_RTS_1m I 499 117200.0000 1338 20140418 SP_RTS_1m I 500 117190.0000 1339 20140418 С функциями без аргументов, надеюсь, мы разобрались. В следующей статье мы рассмотрим создание функций с различными аргументами.

© Habrahabr.ru