Интеграция MetaTrader 4 с MS SQL-сервером

Yuriy Zaytsev | 26 мая, 2008

Введение

Использование интеграции с другими продуктами открывает дополнительные возможности в трейдинге.


Способов применения может быть множество, приведу некоторые из них.

Вы можете собирать тики и передавать их в MS SQL SERVER для дальнейшего анализа. Имея большую историю тиков, можно собрать любой период начиная от самого минимального кванта времени до самого нестандартного периода. Имея реальные тиковые котировки, желающие могут отлаживать стратегии зависимые от тиковых данных - известные как пипсовщики.

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

К примеру, можно выгрузить всю историю из History Center терминала в MS SQL, и не хранить историю в MT4, тем самым разгрузив терминал от хранения большой истории.

Вы можете выполнять расчеты нейросети, используя хранимые котировки в MS SQL SERVER: к примеру STATISTICA - 7,8 позволяют загружать котировки из SQL, можно решить в реальном времени, передавая сигналы сети в MT4.

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


При реализации проекта использовалось программное обеспечение


Минимум который необходимо инсталировать:

1 MS SQL SERVER 2000 Developer
2 MDAC 7

Я отлаживал на MDAC 7, возможно на более низких версиях тоже все будет работать. Если вы не собираетесь компилировать DLL, то инсталяция или наличие Visual C++ 6.0 не обязательно, можете использовать готовую DLL. Но я жестко прописал в ней имя пользователя, название DSN, соединения - их в этом случае прийдется повторить. Я не буду подробно описывать как инсталировать MS SQL SERVER или Visual C++ 6.0, это выходит за рамки темы.

После инсталяции ПО необходимо создать DSN,

dsn=MT4_SQL_BASE;", "yuraz", "qwerty"


Рассмотрим пример реализации приема тиков в MS SQL

Все опыты проводились с MS SQL SERVER 2000 Developer. На Visual C++ 6.0 была создана YZMSSQLExpertSample.DLL, используется метод доступа к MS SQL через ADO. Необходимо инсталлировать MDAC 7 или MDAC 8. Опишу лишь примеры создания процедур и таблиц. Минимально что нам потребуется - это создать на MS SQL базу, таблицы, процедуры; рассмотрим таблицу и процедуры для работы с тиковыми котировками. Далее функционал можно нарастить.


В MS SQL необходимо создать базу и таблицы, я сделал новую базу MT4TRADE. Далее в ней необходимо создать таблицы:

MT4TICK - таблица тиков

//-----------------------------------------------------------------------------------
//
//  Структура базы MT4TICK
//
//    idc             - Формируется автоматически, уникальный номер записи
//    ServerDateTime  - Заполняется автоматически, при добавлении записи
//                        Серверное локальное время - время помещения котировки в таблицу 
//                        ( не имеет ничего общего с датой в временем - которые передает МТ)
//                        это время которое отсчитывает сам сервер - оно будет совпадать с временем
//                        машины на котором запущен сервер. 
//--- 
//    iDateTime       - дата и время в формате MT4, передается из MT4
//    sSymbol         - инструмент
//    cAsk            - котировка Ask
//    cBid            - Котировка Bid
//
CREATE TABLE [dbo].[MT4TICK] (
    [idc] [bigint] IDENTITY (1, 1) NOT NULL ,
    [ServerDateTime] [datetime] NULL ,
    [iDateTime] [bigint] NULL ,
    [sSymbol] [char] (6) COLLATE SQL_Latin1_General_CP1251_CI_AS NULL ,
    [cAsk] [numeric](18, 4) NULL ,
    [cBid] [numeric](18, 4) NULL 
) ON [PRIMARY]
GO
 
--- Пропишем автоматическое заполнение поля ServerDateTime датой и временем сервера MS SQL
ALTER TABLE [dbo].[MT4TICK] ADD 
    CONSTRAINT [DF_MT4TICK_ServerDateTime] DEFAULT (getdate()) FOR [ServerDateTime]
GO

Процедура приема тика и записи в таблицу выглядит так:

// 
//    @RetCode int out          --- используется для возврата
//   ,@psSymbol char(6)         --- инструмент
//   ,@piDateTime  bigint       --- дата и время прихода тика
//   ,@pdAsk  float             --- Ask 
//   ,@pdBid  float             --- Bid
//  
//   Процедура просто возвращает 0 
//     если проанализировать код возврата в MQL4 то можно понять что  котировка дошла до процедуры и записана в таблицу
//     
//
CREATE PROCEDURE dbo.YZ_MT4_TICK
    @RetCode int out          
   ,@psSymbol char(6)
   ,@piDateTime  bigint
   ,@pdAsk  float
   ,@pdBid  float
AS
insert into  MT4TICK   ( sSymbol, iDateTime, cAsk, cBid   )  values  ( @psSymbol , @piDateTime,  @pdAsk ,  @pdBid  )
select @RetCode=0
return @RetCode

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

@RetCode - при передаче из DLL он не несет функционала, он служит лишь для получения кода завершения.

Настройка MS SQL SERVER окончена. Скрипт по созданию типовой конфигурации прилагается.


Немного пофантазируем, возможные решения и плюсы.

Мы можем создать хранилище данных, помещать в него и извлекать информацию. Тем самым мы можем разгрузить терминал от функции хранения истории котировок в самом MT4. Теперь история котировок находится в SQL сервере в хранилище и мы можем оперировать этой информацией с большой скоростью извлекать ее в другие приложения. Мы можем использовать данные для аналитики в НЕЙРОННЫХ пакетах, большинство из которых умеет работать с хранилищами SQL.

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

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

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





Исходные тексты

Код DLL

//+------------------------------------------------------------------+
//|                                              Sample DLL for MQL4 |
//|                 Copyright c 2004-2006, MetaQuotes Software Corp. |
//|                                        https://www.metaquotes.net |
//+------------------------------------------------------------------+
 
//+------------------------------------------------------------------+
//
//  YURAZ 2008  YZMSSQLExpertSample
//
//  Пример  DLL интеграция  MT4  с MS SQL 2000
//
//  ADO  MS SQL SERVER
//
//  используемое программное обеспечение
//
//  VISUAL C++ 6 , SP5 ,  MDAC 7 ,  MS SQL2000 + SP4
//
//+------------------------------------------------------------------+
  
#define WIN32_LEAN_AND_MEAN  // Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
//----
#define MT4_EXPFUNC __declspec(dllexport)
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
#pragma pack(push,1)
 
struct RateInfo
  {
   unsigned int      ctm;
   double            open;
   double            low;
   double            high;
   double            close;
   double            vol;
   double            vol1;
   double            vol2;
   double            vol3;
   double            vol4;
   double            vol5;
 
  };
 
#pragma pack(pop)
   
struct MqlStr
  {
   int               len;
   char             *string;
  };
  
static int CompareMqlStr(const void *left,const void *right);
   
static int SQLexecProcedure( char *nprc );
static int SQLexecProcedureSignal( char *sSymbol, char* sProcedure );
// static int _YZSQLsqlstrinsql( char *Symbol , unsigned int DateTime , double Ask, double Bid, char *NamePrc );
static int _YZSQLprocedure  ( char *sSymbol, unsigned int pDateTime, double Ask, double Bid, char *NamePrc );
static int _YZSQLprocedureHISTORYPut(char *Symbol,unsigned int Period, unsigned int DateTime,double Open,
                        double High,double  Low, double Close ,double Volume, unsigned int Bar ,char *Procedure);
 
                                                                                                             
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
BOOL APIENTRY DllMain(HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
  {
//----
   switch(ul_reason_for_call)
     {
      case DLL_PROCESS_ATTACH:
      case DLL_THREAD_ATTACH:
      case DLL_THREAD_DETACH:
      case DLL_PROCESS_DETACH:
         break;
     }
//----
   return(TRUE);
  }
 
 // укладываем тики в MS SQL
 // вызываем процедуру как SQL строку  передавая параметры   "exec YZ_MT4_TICK ?,?,?,?"
/*
  MT4_EXPFUNC int  __stdcall SQLsqlstringTickPut(char *Symbol,unsigned int DateTime,double Ask,double Bid,char *sSQLstring)
 {
    int ccc =  _YZSQLsqlstrinsql( Symbol  , DateTime ,  Ask , Bid  , sSQLstring  );
    return(ccc);
 }
*/
 
 // вызываем как процедуру передавая параметры
  MT4_EXPFUNC int  __stdcall SQLProcedureTickPut(char *Symbol,unsigned int DateTime,double Ask,double Bid,char *Procedure)
 {
    int ccc =  _YZSQLprocedure( Symbol  , DateTime ,  Ask , Bid  ,Procedure );
    return(ccc);
 }
 
 // уложить в историю MS SQL конкретную свечу
 MT4_EXPFUNC int  __stdcall SQLProcedureHistoryPut(char *Symbol,unsigned int Period , unsigned int DateTime,
                double Open,double High,double  Low, double Close ,double Volume,unsigned int Bar ,char *Procedure)
 {
    int ccc  =  _YZSQLprocedureHISTORYPut(Symbol,Period,DateTime,Open,High,Low,Close,Volume,Bar,Procedure);
    return(ccc);
 }
 
 
 // вызываем процедуру sProcedure
 // 
 // возврат -1 ошибка
 //
 MT4_EXPFUNC int  __stdcall SQLProcedureGetInt(char *sProcedure)
 {
        int Ret =    SQLexecProcedure( sProcedure );
     return((int)Ret);
 }
 
 
 MT4_EXPFUNC int  __stdcall SQLProcedureGetSignal  (char *sSymbol, char *sProcedure)
 {
     
     int Ret =    SQLexecProcedureSignal( sSymbol, sProcedure );
     return((int)Ret);
 }
  
//////////////////////////////////
#include "stdafx.h"
#include <stdio.h>
#import "C:\Program Files\Common Files\System\ado\msado20.tlb" \
        rename("EOF","ADOEOF") rename("BOF","ADOBOF")
 
using namespace ADODB;
  
inline void TESTHR(HRESULT x) { if FAILED(x) _com_issue_error(x); };
 
// метод вызова процедуры
int _YZSQLprocedure( char *sSymbol, unsigned int pDateTime, double Ask, double Bid,   char *NamePrc )
{
  
   HRESULT hr = S_OK;
   _CommandPtr pCmd = NULL;
   _ConnectionPtr pConnection = NULL;
   _bstr_t strMessage, strAuthorID;
 
       ::CoInitialize(NULL);
 
   long codRet = -1;
 
   try {
      
      _ParameterPtr Par1;
      _ParameterPtr Par2;
      _ParameterPtr Par3;
      _ParameterPtr Par4;
      _ParameterPtr Par5;
 
      TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
      hr = pConnection->Open("dsn=MT4_SQL_BASE;", "yuraz", "qwerty", adConnectUnspecified);
      pConnection->CursorLocation = adUseClient;
      TESTHR(pCmd.CreateInstance(__uuidof(Command)));
      pCmd->CommandText = NamePrc;  // имя  процедуры
      pCmd->CommandType = adCmdStoredProc;
     
      Par1 = pCmd->CreateParameter( _bstr_t("@P1"), adInteger,   adParamOutput,0,    codRet );
      pCmd->Parameters->Append( Par1 );
      Par1 = pCmd->CreateParameter("@psSymbol",adChar, adParamInput, strlen(sSymbol) ,sSymbol );
         pCmd->Parameters->Append(Par1);
      Par2 = pCmd->CreateParameter("@piDateTime", adDouble , adParamInput, sizeof(double) ,  (double)pDateTime );
      pCmd->Parameters->Append(Par2);
      Par3 = pCmd->CreateParameter("@pdAsk", adDouble, adParamInput, 4, Ask );
      pCmd->Parameters->Append(Par3);
      Par4 = pCmd->CreateParameter("@pdBid", adDouble, adParamInput, 4, Bid );
      pCmd->Parameters->Append(Par4);
 
      
      pCmd->ActiveConnection = pConnection;
      int hr = pCmd->Execute( 0, 0, adCmdStoredProc );
      if( FAILED(hr) )
      {
                codRet = -1;
      }
      else
      {
         Par1 = pCmd->Parameters->GetItem(_bstr_t("@P1"));     // получим из процедуры
         codRet = Par1->GetValue();
      }
   }
   catch(_com_error  ) {
       //
       // при необходимости обработаем ошибку исполнения
          //
       codRet = -1;
 
   }
   if (pConnection)
      if (pConnection->State == adStateOpen)
         pConnection->Close();
 
   ::CoUninitialize();
    return((int)codRet);
}
 
 
 
// поместить в историю Symbol , Period . DateTime, Open , High , Low , Close , Value , Bar
int _YZSQLprocedureHISTORYPut(char *pSymbol,unsigned int pPeriod, unsigned int pDateTime,double pOpen,double pHigh,
                                        double  pLow, double pClose ,double pVolume, unsigned int pBar ,char *pProcedure )
{
 
 
        HRESULT hr = S_OK;
   _CommandPtr pCmd = NULL;
   _ConnectionPtr pConnection = NULL;
   _bstr_t strMessage, strAuthorID;
 
       ::CoInitialize(NULL);
 
   long codRet = -1;
 
   try {
 
      _ParameterPtr ParReturn; // 
      _ParameterPtr Par1; // SYMBOL
      _ParameterPtr Par2; // PERIOD
      _ParameterPtr Par3; // DATETIME
      _ParameterPtr Par4; // OPEN
      _ParameterPtr Par5; // HIGH
      _ParameterPtr Par6; // LOW
      _ParameterPtr Par7; // CLOSE
      _ParameterPtr Par8; // VOLUME
      _ParameterPtr Par9; // BAR
 
 
      TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
      hr = pConnection->Open("dsn=MT4_SQL_BASE;", "yuraz", "qwerty", adConnectUnspecified);
      pConnection->CursorLocation = adUseClient;
      TESTHR(pCmd.CreateInstance(__uuidof(Command)));
      pCmd->CommandText = pProcedure;  // имя  процедуры
      pCmd->CommandType = adCmdStoredProc;
     
      ParReturn = pCmd->CreateParameter( _bstr_t("@P1"), adInteger,   adParamOutput,0,    codRet );
      pCmd->Parameters->Append( ParReturn );
 
      Par1 = pCmd->CreateParameter("@psSymbol",adChar, adParamInput, strlen(pSymbol) ,pSymbol );
         pCmd->Parameters->Append(Par1);
 
      Par2 = pCmd->CreateParameter("@piDateTime", adDouble , adParamInput, sizeof(double) ,  (double)pPeriod );
      pCmd->Parameters->Append(Par2);
 
      Par3 = pCmd->CreateParameter("@piDateTime", adDouble , adParamInput, sizeof(double) ,  (double)pDateTime );
      pCmd->Parameters->Append(Par3);
 
      Par4 = pCmd->CreateParameter("@pdOpen", adDouble, adParamInput, 4, pOpen );
      pCmd->Parameters->Append(Par4);
 
      Par5 = pCmd->CreateParameter("@pdHigh", adDouble, adParamInput, 4, pHigh );
      pCmd->Parameters->Append(Par5);
 
      Par6 = pCmd->CreateParameter("@pdLow", adDouble, adParamInput, 4, pLow );
      pCmd->Parameters->Append(Par6);
 
      Par7 = pCmd->CreateParameter("@pdClose", adDouble, adParamInput, 4, pClose );
      pCmd->Parameters->Append(Par7);
      
      Par8 = pCmd->CreateParameter("@pdVolume", adDouble, adParamInput, 4, pVolume );
      pCmd->Parameters->Append(Par8);
 
      Par9 = pCmd->CreateParameter("@piBar", adDouble , adParamInput, sizeof(double) ,  (double)pBar );
      pCmd->Parameters->Append(Par9);
 
 
      pCmd->ActiveConnection = pConnection;
      int hr = pCmd->Execute( 0, 0, adCmdStoredProc );
      if( FAILED(hr) )
      {
                codRet = -1;
      }
      else
      {
         ParReturn = pCmd->Parameters->GetItem(_bstr_t("@P1"));     // получим из процедуры
         codRet = ParReturn->GetValue();
      }
   }
   catch(_com_error  ) {
       //
       // при необходимости обработаем ошибку исполнения
          //
       codRet = -1;
 
   }
   if (pConnection)
      if (pConnection->State == adStateOpen)
         pConnection->Close();
 
   ::CoUninitialize();
    return((int)codRet);
}
 
 
//
// вернем значение возвращаемое процедурой
//
int  SQLexecProcedure( char *nprc )
{
   
    HRESULT hr = S_OK;
   _CommandPtr pcmd = NULL;
   _ConnectionPtr pConnection = NULL;
   _bstr_t strMessage, strAuthorID;
 
       ::CoInitialize(NULL);
 
   long codRet = -1;
 
   try {
      TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
      hr = pConnection->Open("dsn=MT4_SQL_BASE;", "yuraz", "qwerty", adConnectUnspecified);
      pConnection->CursorLocation = adUseClient;
      TESTHR(pcmd.CreateInstance(__uuidof(Command)));
      pcmd->CommandText = nprc;  // имя  процедуру
      pcmd->CommandType = adCmdStoredProc;
     
      _ParameterPtr  pParm1 = pcmd->CreateParameter( _bstr_t("@P1"), adInteger,   adParamOutput,0,    codRet );
      pcmd->Parameters->Append( pParm1 );
      pcmd->ActiveConnection = pConnection;
      int hr = pcmd->Execute( 0, 0, adCmdStoredProc );
      if( FAILED(hr) )
      {
                codRet = -1;
      }
      else
      {
         pParm1 = pcmd->Parameters->GetItem(_bstr_t("@P1"));     // получим из процедуры
         codRet = pParm1->GetValue();
      }
   }
   catch(_com_error  ) {
       //
       // при необходимости обработаем ошибку исполнения
          //
       codRet = -1;
 
   }
   if (pConnection)
      if (pConnection->State == adStateOpen)
         pConnection->Close();
 
   ::CoUninitialize();
    return((int)codRet);
}
 
//
//
//
int  SQLexecProcedureSignal( char *sSymbol, char* sProcedure )
{
   
    HRESULT hr = S_OK;
   _CommandPtr pcmd = NULL;
   _ConnectionPtr pConnection = NULL;
   _bstr_t strMessage;
   _bstr_t strAuthorID;
 
       ::CoInitialize(NULL);
 
   long codRet = 0;
 
   try {
      TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
      hr = pConnection->Open("dsn=MT4_SQL_BASE;", "yuraz", "qwerty", adConnectUnspecified);
      pConnection->CursorLocation = adUseClient;
      TESTHR(pcmd.CreateInstance(__uuidof(Command)));
      pcmd->CommandText = sProcedure;  // имя  процедуру
      pcmd->CommandType = adCmdStoredProc;
 
      _ParameterPtr pParm1 = pcmd->CreateParameter("@psSymbol",adChar, adParamInput, strlen(sSymbol) ,sSymbol );
      pcmd->Parameters->Append(pParm1);
      _ParameterPtr pParm2 = pcmd->CreateParameter( _bstr_t("@P1"), adInteger,   adParamOutput,0,    codRet );
      pcmd->Parameters->Append( pParm2 );
      pcmd->ActiveConnection = pConnection;
      int hr = pcmd->Execute( 0, 0, adCmdStoredProc );
            if( FAILED(hr) )
            {
                bool bSuccess = false;
            }
            pParm2 = pcmd->Parameters->GetItem(_bstr_t("@P1"));     // получим из процедуры
            codRet = pParm2->GetValue();
 
//             printf("\n [%d] \n",codRet );       // ПОЛУЧАЕМ из процедуры
   }
   catch(_com_error  ) {
       //
       // при необходимости обработаем ошибку исполнения
          //
   }
   if (pConnection)
      if (pConnection->State == adStateOpen)
         pConnection->Close();
 
   ::CoUninitialize();
    return((int)codRet);
}

Пример вызова из MQL4 -

// Помещаю с сокращениями комментариев для простоты восприятия, в прикрепленных файлах комментарии полные
 
//+------------------------------------------------------------------+
//|                                                                  |
//|                 Copyright c 1999-2006, MetaQuotes Software Corp. |
//|                                         http://www.metaquotes.ru |
//|                                                YZMSSQLSample.mq4 |
//|                                                   Yuriy Zaitsev  |
//+------------------------------------------------------------------+
// Пример интеграции с MS SQL                                        |
//+------------------------------------------------------------------+
 
#property copyright "YURAZ Copyright(C) 2008"
#property link      "yzн @ mail.ru"
 
//+------------------------------------------------------------------+
// Библиотека функций DLL 
//+------------------------------------------------------------------+
#import "YZMSSQLExpertSample.dll"
// Выполнение каких либо действий на MS SQL сервере, вызывается процедура
SQLProcedureGetInt 
// Сбор тиков
int SQLProcedureTickPut( string,  int , double , double ,string );  
 
int Prc = 0;
int init()
  {
 
//
// SQLProcedureGetInt Функция вызвав определенную процедуру , 
// вернет в MT4 int значение к примеру параметры
// сохраненные на MS SQL сервере , сформронанные иным программным обеспечением
//
   Prc =     SQLProcedureGetInt ("YZ_MT4_T1"); 
   return(0);
  }
 
int start()
  {
   int a;
 
   int RetCode = SQLProcedureTickPut( Symbol(), TimeCurrent() , Ask, Bid ,"YZ_MT4_TICK"); // вызываем процедуру сбора тиков
   Print(" SQLProcedureTickPut (YZ_MT4_NEWDAY)"+ RetCode );
 
// Как пример:
//    на сервере MS SQL возможно формировать сигналы  сформированные с помощью стороннего программного обеспечения
//    нейронных сетей 
//    других программных продуктов 
//
/*
   int Signal = SQLProcedureGetSignal (Symbol() , "YZ_MT4_SIGNAL" ); // процедура MS SQL , вернет сигнал
   Print(" SQLProcedureGetSignal (Symbol() , YZ_MT4_SIGNAL )"+ Signal );
   if ( Signal == OP_BUY )
   {
       // процедура вернула сигнал,  рекомендует покупку
   }
   if ( Signal == OP_SELL )
   {
       // процедура вернула сигнал,  рекомендует продажу
   }
*/
 
   return(0);
  }

Скрипт по загрузки истории в MS SQL сервер:

//
// YURAZ  2008 yzh @ mail.ru
//
// скрипт загрузки истории в MS SQL
// перегрузить всю историю по всем парам  по всем ТАЙМФРЕМАМ
// в MS SQL
//
 
#import "YZMSSQLExpertSample.dll"
int SQLProcedureHistoryPut( string,  int  , int, double , double ,double , double ,double ,int,  string );  
 
static int mPeriod[8]={PERIOD_M1,PERIOD_M5,PERIOD_M15,PERIOD_M30,PERIOD_H1,PERIOD_H4,PERIOD_D1,PERIOD_W1,PERIOD_MN1};
 
void start()
{
    PutHistor("EURUSD"); 
    PutHistor("USDCHF"); 
    Comment(" ЗАГРУЗКА  ПРОИЗВЕДЕНА " );
}
 
void PutHistor(string sSymbol)
{
  for ( int iPeriod = 0; iPeriod <= 8 ; iPeriod++ )
  {
     int pPERIOD_XX = mPeriod[iPeriod];
      int Bar = iBars(sSymbol,pPERIOD_XX ); // получим глубину истории по данному тф
 // не организован прогресс бар 
     for ( int iBar = Bar; iBar >= 0 ; iBar--)
     {
     Comment( "ЖДИТЕ  Период "+pPERIOD_XX+" СИМВОЛ "+sSymbol+" БАРОВ "+iBar );
       double   o = iOpen  (sSymbol,pPERIOD_XX,iBar);
       double   h = iHigh  (sSymbol,pPERIOD_XX,iBar);
       double   l = iLow   (sSymbol,pPERIOD_XX,iBar);
       double   c = iClose (sSymbol,pPERIOD_XX,iBar);
       double   v = iVolume(sSymbol,pPERIOD_XX,iBar);
       datetime d = iTime  (sSymbol,pPERIOD_XX,iBar);
       int RetCode = SQLProcedureHistoryPut( sSymbol,pPERIOD_XX,d,o,h,l,c,v,iBar, "YZ_MT4_HISTORY");
       // Print ( " YZ_MT4_HITSRY "+RetCode);
      }
  }
}

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

Лучшее решение - это выгрузка котировок в текстовый файл и загрузка в MS SQL через IMPRT EXPORT DTS. По времени загрузка минутки 1999-2008 занимает несколько минут по инструменту.
При выгрузке в текстовый файл не выгружается индекс бара. Если принять решение, что индексом бара будет просто порядковый номер строки, то нас ожидает проблема пропущенных баров, и при изменении - перезагрузки номера выгруженных баров в MS SQL и MT4 могут не совпасть. Пока я не решил эту проблему, но думаю, что ее решение вполне возможно перезагрузкой истории после качественного обновления истории в самом MT4.


Описание списка прикрепленных файлов

CreateSQLallDate.txt (9.0 Kb)

- Скрипт в формате SQL как пример создания баз, таблиц, процедур на MS SQL сервере

SQLGETHISTORY.mq4 (1.4 Kb)

- Скрипт для загрузки истории в MS SQL

YZMSSQLExpertSample.rar (89.9 Kb)

- проект DLL

YZMSSQLSample.mq4 (13.1 Kb) -

Вешается как советник на график инструмента, тики которого необходимо собирать, вешать можно на любой таймфрейм.


Заключение

Интеграция с другими продуктами расширяет функциональность MetaTrader 4, позволяет более качественно распределить задачи и функции автоматизированной торговой системы.