Como criar e testar símbolos de ativos MOEX personalizados no MetaTrader 5

Dmitrii Troshin | 12 fevereiro, 2019

Introdução

Dois tipos básicos de mercados financeiros são os mercados de bolsa de valores e de balcão, também podemos desfrutar da negociação OTC Forex usando ferramentas modernas do MetaTrader e MetaEditor, que estão sendo constantemente melhoradas, bem como da automação das negociações. Estas ferramentas nos permitem testarmos diversos algoritmos de negociação usando dados históricos.

Que tal usar nossas próprias idéias para fazermos as negociações? Alguns terminais de negociação possuem linguagens de programação integradas, por exemplo, o popular terminal Transaq apresenta a linguagem de programação ATF (Advanced Trading Facility), mas, claro, não pode ser comparado com a linguagem MQL5, pois não possui nenhuma funcionalidade de teste de estratégia. Uma boa solução para obter dados da bolsa e otimizar algoritmos de negociação é o testador de estratégia do MetaTrader.

Isso pode ser feito através da criação de símbolos personalizados, o processo de criação é descrito em detalhes no artigo "criando e testando símbolos personalizados no MetaTrader 5". Tudo o que precisa é obter os dados no formato CSV (TXT) e importar o histórico de preços seguindo as etapas descritas no artigo.

Isso seria fácil, se não fosse pela diferença nos formatos de dados. Por exemplo, vamos considerar o popular website russo relacionado ao mercado financneiro, "finam.ru". Cotações podem ser baixadas aqui:



Exportando cotações da bolsa de Moscou


Quais formatos de dados são fornecidos pelo Finam:




Formatos de datas disponíveis: "yyyymmdd", "yymmdd", "ddmmyy", "dd/mm/yy", "mm/dd/yy". Nosso formato:



O formato "yyyy.mm.dd" é o que precisamos e não está disponível, então, o "finam.ru" fornece uma grande variedade de formatos, mas não tem o que precisamos. 

Além disso, há muitos outros recursos da bolsa e formatos fornecidos por outros sites que também podem ser inadequados. Precisamos de uma certa ordem de dados, no entanto, as cotações podem ser armazenadas em uma ordem diferente, por exemplo: Abertura, Fechamento, Máxima e Mínima. 

Portanto, nossa tarefa é converter dados fornecidos em uma ordem aleatória e de diferentes formatos, para um formato acessível, de forma que a MetaTrader 5 possa para receber dados de qualquer recurso. Então criaremos um símbolo personalizado com base nos dados recebidos, usando as ferramentas MQL5, o que nos permitirá realizar testes.

Existem algumas dificuldades relacionadas à importação das cotações.

As negociações suportam "Spread", "Ask" e "Bid". No entanto, todos esses valores existem apenas no "momento", na Profundidade do Mercado (Book de Ofertas), depois disso, apenas o preço da transação é escrito independentemente do seu preço de execução, ou seja, Ask ou Bid. Precisamos do valor do spread para o terminal, então é adicionado um spread fixo aqui, porque é impossível restaurar o spread da Profundidade de Mercado, se o spread for essencial, você poderá simular isso de alguma forma. Um dos métodos é descrito no artigo "modelando série temporais usando símbolos personalizados de acordo com as leis de distribuição especificadas". Alternativamente, você pode escrever uma função simples, apresentando a dependência do spread na volatilidade como "Spread = f(High-Low)".

Ao trabalhar com timeframes, o uso de um spread fixo é bastante aceitável, o erro será insignificante em grandes períodos, no entanto, a modelagem do spread é importante para os ticks. Formato dos ticks na bolsa:


Nosso formato:



Aqui precisamos definir o ASK e o BID, além do ÚLTIMO PREÇO. Os dados são classificados com precisão de milissegundos, a bolsa fornece apenas um fluxo de preço. Os dados da primeira página parecem mais como dividir um grande lote em partes. Em termos de Forex não existe ticks, pode ser Bid, Ask ou Bid e Ask ao mesmo tempo, além do que, precisamos classificar artificialmente as ofertas pelo tempo e adicionar milissegundos.

Logo, este artigo não lida com a importação de dados, mas com modelagem de dados, assim como o artigo mencionado acima, portanto, para não enganar você, decidi não publicar um aplicativo de importação de ticks, operando de acordo com o princípio ASK=BID(+spread)=LAST. O spread é significativo quando se trabalha com milissegundos, portanto, nos testes, precisamos escolher um método de modelagem apropriado.

Depois de corrigido o código de importação de tick, levará alguns minutos, basta substituir a estrutura MqlRates por MqlTick. A função CustomRatesUpdate() precisa ser substituída pelo CustomTicksAdd().

O próximo ponto está relacionado com a incapacidade de considerar todos os possíveis formatos de dados. Por exemplo, os números podem ser escritos separados, tipo "1 000 000", ou com uma vírgula usada em vez de um ponto para o separador decimal, como "3,14". Ou pior ainda - quando o separador de dados e o separador decimal são um ponto ou uma vírgula (como distinguir entre eles). Apenas os formatos mais comuns são considerados aqui, se for preciso lidar com um formato fora do padrão, você terá que processá-lo sozinho.

Também não existe histórico de ticks na bolsa - apenas fornece volumes das transações, portanto, neste artigo, usamos o volume de troca =VOL=TICKVOL.

O artigo está dividido em duas partes, a primeira parte apresenta a descrição do código, permite que você se familiarize com o código, para mais tarde editá-lo nas operações com formatos de dados que não são padrão. A segunda parte contém um guia passo a passo (manual do usuário), destina-se àqueles que não estão interessados em programação, mas precisam apenas usar a funcionalidade implementada. Se você opera com formatos de dados padrão (em particular, use o site "finam.ru" como fonte), você pode prosseguir imediatamente para a parte 2.

Parte 2. Descrição do Código

Apenas uma parte do código é descrita aqui, o código completo encontra-se nos arquivos anexos.

Primeiro, vamos inserir os parâmetros necessários, como a posição dos dados na string, os parâmetros do arquivo, o nome do símbolo, etc.

input int SkipString        =1;                               // O número de strings para ignorar
input string mark1          ="Time position and format";      // Tempo
input DATE indate           =yyyymmdd;                        // Formato da data
input TIME intime           =hhdmmdss;                        // Formato da hora
input int DatePosition      =1;                               // Posição da data
input int TimePosition      =2;                               // Posição do tempo
//------------------------------------------------------------------+
input string mark2          ="Price data position";           // Preço
input int OpenPosition      =3;                               // Posição da abertura do preço
input int HighPosition      =4;                               // Posição da máxima do preço
input int LowPosiotion      =5;                               // Posição da mínima do preço 
input int ClosePosition     =6;                               // Posição do fechamento do preço 
input int VolumePosition    =7;                               // Posição do volume
input string mark3          ="File parameters";               // Arquivo
//-------------------------------------------------------------------+
input string InFileName     ="sb";                            // Nome do Arquivo de Origem
input DELIMITER Delimiter   =comma;                           // Separador
input CODE StrType          =ansi;                            // Tipo da string
input string mark4          ="Other parameters";              // Outros parâmetros 
//-------------------------------------------------------------------+
input string spread         ="2";                             // Spread fixo em pontos
input string Name           ="SberFX";                        // O nome do símbolo que você está criando


Enumerações são criadas para alguns dados, por exemplo, para o formato da data e hora:

enum DATE
{
yyyycmmcdd, // yyyy.mm.dd
yyyymmdd,   // yyyymmdd
yymmdd,     // yymmdd
ddmmyy,     // ddmmyy   
ddslmmslyy, // dd/mm/yy
mmslddslyy  // mm/dd/yy
// Outros formatos devem ser adicionados aqui
};

enum TIME
{
hhmmss,     // hhmmss
hhmm,       // hhmm
hhdmmdss,   // hh:mm:ss
hhdmm       // hh:mm
// Outros formatos devem ser adicionados aqui
};


Se o formato exigido não estiver disponível, adicione-o.

Então abra o arquivo fonte, para uma edição correta de dados formatados, sugiro salvá-los em arquivo CSV, ao mesmo tempo, os dados devem ser gravados na estrutura MqlRates para permitir a criação automática do símbolo personalizado.

// Abertura do arquivo
    
  int out =FileOpen(InFileName,FILE_READ|StrType|FILE_TXT);
  if(out==INVALID_HANDLE)
  {
   Alert("Failed to open the file for reading");
   return; 
  }
// Abra o arquivo
  int in =FileOpen(Name+"(f).csv",FILE_WRITE|FILE_ANSI|FILE_CSV);
  if(in==INVALID_HANDLE)
  {
   Alert("Failed to open the file for writing");
   return; 
  }
  //---Inserir string de legendas  
  string Caption ="<DATE>\t<TIME>\t<OPEN>\t<HIGH>\t<LOW>\t<CLOSE>\t<TICKVOL>\t<VOL>\t<SPREAD>";
  FileWrite(in,Caption);
//-----------------------------------------------------------
string fdate="",ftime="",open="";
string high="",low="",close="",vol="";
int left=0,right=0;

string str="",temp="";
for(int i=0;i<SkipString;i++)
   {
   str =FileReadString(out);
   i++;
   }
MqlRates Rs[];
ArrayResize(Rs,43200,43200);  //43200 minutos em um mês
datetime time =0;


O arquivo fonte deve ser salvo no diretório MQL5/Files. A variável externa SkipString apresenta o número de linhas no cabeçalho do arquivo para saltar, para usar espaços e tabulações como separadores, abrimos o arquivo com a flag FILE_TXT. 

Então precisamos extrair dados da string, a localização é determinada nos parâmetros de entrada, a numeração começa em 1, vamos usar cotações de ações do Sberbank como exemplo.



Aqui a posição da data é 1, do tempo é 2, etc. SkipString=1.

Para analisar a string, poderíamos usar a função StringSplit(), mas é melhor desenvolver nossas próprias funções para um monitoramento mais adequado dos erros no arquivo fonte. A análise de dados pode ser adicionada a essas funções, ao passo que usando o código StringSplit() seria mais fácil, a primeira função que localiza os limites de dados recebe a string, o separador e a posição. Os limites são gravados para as variáveis "a" e "b" que são conferidas pela referência.

//---Pesquisar limites de posição de dados---------------------------+
bool SearchBorders(string str,int pos,int &a,int &b,DELIMITER delim)
{
// Variáveis Auxiliares
int left=0,right=0;
int count=0;
int start=0;
string delimiter="";
//-------------------------------------------------------------------+

switch(delim)
{
case comma : delimiter =",";
   break; 
case tab : delimiter ="/t";
   break;
case space : delimiter =" ";
   break;
case semicolon : delimiter =";";
   break;
}

while(count!=pos||right!=-1)
   {
   right =StringFind(str,delimiter,start);
      
   if(right==-1&&count==0){Print("Data errada");return false;} //Incorrect data
   
   if(right==-1)
      {
      right =StringLen(str)-1;
      a =left;
      b =right;
      break;
      }
   
   count++;
      if(count==pos)
      {
      a =left;
      b =right-1;
      return true;
      }
   left =right+1;
   start =left;
   }

return true;
}


Agora, usando StringSubstr(), vamos obter dados apropriados. Os valores recebidos devem ser convertidos para o formato desejado, para esse propósito, vamos escrever as funções de conversão de data e hora. Por exemplo, aqui está a função de conversão de data:

//---Formatação da data----------------------------------------------+
//2017.01.02
string DateFormat(string str,DATE date)
{

string res="";
string yy="";
 
switch(date)
  { 
   case yyyycmmcdd :  //Nosso formato
      res =str;
      if(StringLen(res)!=10)res=""; // Verificando o formato da data
   case yyyymmdd :
      res =StringSubstr(str,0,4)+"."+StringSubstr(str,4,2)+"."+StringSubstr(str,6,2);
      if(StringLen(res)!=10)res=""; // Verificando o formato da data    
      break;
   case yymmdd :
      yy =StringSubstr(str,0,2);
      if(StringToInteger(yy)>=70)
         yy ="19"+yy;
      else
         yy ="20"+yy;
      res =yy+"."+StringSubstr(str,2,2)+"."+StringSubstr(str,4,2);
      if(StringLen(res)!=10)res=""; // Verificando o formato da data  
      break;
//--Outros formatos (o código completo está no arquivo)--
//Adicionar análise de outros formatos, se necessário      
   default :
      break; 

  }

return res;
}


Se o formato requesitado não estiver disponível (por exemplo, uma data como 01 de janeiro de 18), ele deve ser adicionado. Aqui é feita uma verificação se os dados recebidos correspondem ao formato exigido (no caso de um erro no arquivo fonte)  if(StringLen(res)!=10) res=""; Eu entendo que isso não é uma verificação completa, mas a análise de dados não é uma tarefa fácil, então um programa separado seria necessário para uma análise mais detalhada. Em caso de erro, a função retorna res =, "" e a linha apropriada é então ignorada.

A seguinte conversão é fornecida para formatos do tipo ddmmyy, em que o ano é escrito como dois dígitos. Valores >=70 são convertidos para 19yy, valores menores são convertidos para 20yy.

Após a conversão de formato, gravamos dados para variáveis apropriadas e compilamos uma string final.

while(!FileIsEnding(out))
   {
   str =FileReadString(out);
   count++;
//---fdate-----------------------------
   if(SearchBorders(str,DatePosition,left,right,Delimiter))
   {
   temp =StringSubstr(str,left,right-left+1);
   fdate =DateFormat(temp,indate);
   if(fdate==""){Print("Erro na string   ",count);continue;}
   }
   else {Print("Erro na string   ",count);continue;}
//---Outros dados são tratados da mesma forma


Se for encontrado um erro nas funções SearchBorders, DateFormat ou TimeFormat, a string será ignorada e seu número de sequência será gravado usando a função Print(). Todas as enumerações e funções de conversão de formato estão localizadas em um arquivo de inclusão separado, FormatFunctions.mqh. 

Então a string resultante é formada e escrita, os dados são atribuídos a elementos apropriados da estrutura MqlRates.

//-------------------------------------------------------------------+
   str =fdate+","+ftime+","+open+","+high+","+low+","+close+","+vol+","+vol+","+Spread;
   FileWrite(in,str);
//---Preechendo o MqlRates ------------------------------------------+
   Rs[i].time               =time;
   Rs[i].open               =StringToDouble(open);
   Rs[i].high               =StringToDouble(high);
   Rs[i].low                =StringToDouble(low);
   Rs[i].close              =StringToDouble(close);
   Rs[i].real_volume        =StringToInteger(vol);
   Rs[i].tick_volume        =StringToInteger(vol);
   Rs[i].spread             =int(StringToInteger(Spread));
   i++;
//-------------------------------------------------------------------+   
   }


Depois de ler todas as strings, o array dinâmico recebe seu tamanho final e os arquivos são fechados:

   ArrayResize(Rs,i);
   FileClose(out);
   FileClose(in);

Agora tudo está pronto para criar um símbolo personalizado, também temos um arquivo CSV, que pode ser facilmente editado diretamente no MetaEditor. Com base no arquivo CSV, podemos criar símbolos personalizados usando métodos padrão do terminal MetaTrader 5.



Criando um símbolo personalizado usando o MQL5

Agora que todos os dados foram preparados, precisamos apenas adicionar o símbolo personalizado.

   CustomSymbolCreate(Name);
   CustomRatesUpdate(Name,Rs);

As cotas são importadas usando a função CustomRatesUpdate(), significa que o programa pode ser usado para a criação de símbolos e também para a adição de novos dados. Se o símbolo já existir, CustomSymbolCreate(), retornará -1 (menos um) e a execução do programa continuará, assim as cotações serão atualizadas através da função CustomRatesUpdate(). O símbolo é exibido na janela Observação do Mercado, destacado em verde.



Agora podemos abrir o gráfico para garantir que tudo funcione corretamente:


O gráfico EURUSD


Definição das especificações (propriedades do símbolo)

Ao testar um símbolo, podemos precisar configurar suas características (especificações). Escrevi um arquivo separado de inclusão, "Specification", que habilita a edição adequada das propriedades do símbolo, neste arquivo, as propriedades do símbolo são definidas na função SetSpecifications(). Todas as propriedades de símbolo das enumerações ENUM_SYMBOL_INFO_INTEGER, ENUM_SYMBOL_INFO_DOUBLE , ENUM_SYMBOL_INFO_STRING são coletadas aqui.

void SetSpecifications(string Name)
   {  
//---Propriedades Gerais-------------------------------------
//   CustomSymbolSetInteger(Name,SYMBOL_CUSTOM,true);                       //bool - Uma indicação de que o símbolo é personalizado
//   CustomSymbolSetInteger(Name,SYMBOL_BACKGROUND_COLOR,clrGreen);         // A cor de fundo usada para o símbolo no Market Watch
// Outras propriedades "Integer"
//---Propriedades duplas ---------------------------------------------------   
//   CustomSymbolSetDouble(Name,SYMBOL_BID,0);                              // Bid, o melhor preço pelo qual um símbolo pode ser vendido 
//  CustomSymbolSetDouble(Name,SYMBOL_BIDHIGH,0);                           // Maior Bid por dia
//   CustomSymbolSetDouble(Name,SYMBOL_BIDLOW,0);                           // Menor Bid por dia
// Outras propriedades "Double"
//---Propriedades da String-----------------------------------------------+
//   CustomSymbolSetString(Name,SYMBOL_BASIS,"");                           // O nome do ativo subjacente ao símbolo personalizado
//   CustomSymbolSetString(Name,SYMBOL_CURRENCY_BASE,"");                   // Moeda base do símbolo
//   CustomSymbolSetString(Name,SYMBOL_CURRENCY_PROFIT,"");                 // Lucro da moeda
// Outras propriedades da string
}


Esta função é executada após a função CustomSymbolCreate. Não se sabe de antemão que tipo de símbolo é este: futuros, ações ou opções. A maioria das propriedades não são necessárias e são comentadas, apenas algumas das linhas não são comentadas no código-fonte:

   CustomSymbolSetInteger(Name,SYMBOL_CUSTOM,true);                       // bool - Uma indicação de que o símbolo é personalizado
   CustomSymbolSetInteger(Name,SYMBOL_BACKGROUND_COLOR,clrGreen);         // A cor de fundo usada para o símbolo na Observação do Mercado
   CustomSymbolSetInteger(Name,SYMBOL_SELECT,true);                       // bool - Uma indicação de que o símbolo está selecionado na Observação do Mercado
   CustomSymbolSetInteger(Name,SYMBOL_VISIBLE,true);                      // bool - Uma indicação de que o símbolo é exibido na Observação do Mercado


Os seguintes parâmetros, que são as características mais necessárias, não são comentados para fins de teste: volume mínimo, passo do volume, passo do preço, tamanho do ponto, essas características são típicas das ações do Sberbank. O conjunto de propriedades e suas características diferem para diferentes símbolos.

   CustomSymbolSetDouble(name,SYMBOL_POINT,0.01);               // O valor de um ponto
   CustomSymbolSetDouble(name,SYMBOL_VOLUME_MIN,1);             // Volume mínimo para uma ordem
   CustomSymbolSetDouble(name,SYMBOL_VOLUME_STEP,1);            // Passo da alteração do volume mínimo
   CustomSymbolSetInteger(name,SYMBOL_DIGITS,2);                // int - Número de casas decimais
   CustomSymbolSetInteger(name,SYMBOL_SPREAD,2);                // int - Valor do spread em pontos 
   CustomSymbolSetInteger(name,SYMBOL_SPREAD_FLOAT,false);      // bool - Uma indicação de spread flutuante
   CustomSymbolSetDouble(name,SYMBOL_TRADE_TICK_SIZE,0.01);	// Alteração mínima de preço


Essa seria uma boa abordagem, se não tivéssemos que recompilar o código toda vez que é necessário a definição das propriedades desejadas. Seria mais conveniente se isso pudesse ser feito apenas com a inserção dos parâmetros desejados, portanto, tive que mudar a abordagem. As propriedades do símbolo serão fornecidas em um simples arquivo de texto, "Specifications.txt", que pode ser editado manualmente para cada novo símbolo, isto não requer a recompilação do código-fonte.

É mais adequado editar o arquivo de texto no MetaEditor, principalmente porque o MetaEditor fornece destaque dos parâmetros e dados. As propriedades são escritas no seguinte formato:




Os dados são separados por vírgulas, as strings são analisadas da seguinte forma:

   while(!FileIsEnding(handle))
     {
     str =FileReadString(handle);
//--- Saltando linhas ----------------------+     
     if(str=="") continue;
     if(StringFind(str,"//")<10) continue;
//------------------------------------------+     
     sub =StringSplit(str,u_sep,split);
     if(sub<2) continue;
     SetProperties(SName,split[0],split[1]);
     }


Uma linha é transposta se estiver vazia ou existe o símbolo de comentário "//" no começo (posição<10), a string é dividida em substrings usando a função StringSplit(). Depois disso, as strings são conferidas pela função SetProperties(), onde as propriedades dos símbolos são definidas. A estrutura do código da função:

void SetProperties(string name,string str1,string str2)
   {
   int n =StringTrimLeft(str1);
       n =StringTrimRight(str1);
       n =StringTrimLeft(str2);
       n =StringTrimRight(str2);
       
   if(str1=="SYMBOL_CUSTOM")
      {
      if(str2=="0"||str2=="false"){CustomSymbolSetInteger(name,SYMBOL_CUSTOM,false);}
      else {CustomSymbolSetInteger(name,SYMBOL_CUSTOM,true);}
      return;
      }
   if(str1=="SYMBOL_BACKGROUND_COLOR")
      {
      CustomSymbolSetInteger(name,SYMBOL_BACKGROUND_COLOR,StringToInteger(str2));
      return;
      }
   if(str1=="SYMBOL_CHART_MODE")
      {
      if(str2=="SYMBOL_CHART_MODE_BID"){CustomSymbolSetInteger(name,SYMBOL_CHART_MODE,SYMBOL_CHART_MODE_BID);}
      if(str2=="SYMBOL_CHART_MODE_LAST"){CustomSymbolSetInteger(name,SYMBOL_CHART_MODE,SYMBOL_CHART_MODE_LAST);}
      return;
      }
//--- Outras propriedades de símbolo
}


Se o usuário deixar espaços ou tabulações ao editar, mais duas funções são adicionadas: StringTrimLeft()e StringTrimRight().

O código completo está disponível no arquivo de inclusão, "PropertiesSet.mqh".

Agora todas as propriedades do símbolo são definidas através do arquivo de texto anexo. Você pode verificar as duas variantes de código que estão anexadas abaixo, a primeira variante requer a configuração de propriedade por meio de um arquivo de inclusão comentado.


Interface

Para uma edição correta do código, as configurações são determinadas usando parâmetros de entrada. Se não há nada para editar, podemos pensar na interface, desenvolvi um painel de entradas para a versão final:


Sobre o código do painel. O conjunto padrão de controles dos seguintes arquivos de inclusão é usado aqui:

#include <Controls\Dialog.mqh>
#include <Controls\Label.mqh>
#include <Controls\Button.mqh>
#include <Controls\ComboBox.mqh>

Um manipulador de eventos foi criado para o botão OK.

//+------------------------------------------------------------------+ 
//| Manipulação de eventos                                           | 
//+------------------------------------------------------------------+ 
EVENT_MAP_BEGIN(CFormatPanel) 
ON_EVENT(ON_CLICK,BOK,OnClickButton) 
EVENT_MAP_END(CAppDialog)

void CFormatPanel::OnClickButton(void) 
  { 
// Programa descrito acima
  } 

Agora, quase todo o código do programa descrito acima é movido para este manipulador de eventos, parâmetros externos tornam-se variáveis locais.

long SkipString        =1;                              // O número de strings para ignorar
DATE indate           =yyyymmdd;                        // Formato da data
TIME intime           =hhdmmdss;                        // Formato da hora
int DatePosition      =1;                               // Posição de data
int TimePosition      =2;                               // Posição do tempo
// Outros parâmetros

A função Create() é gravada para cada controle, portanto os valores necessários são colocados na lista de controles após sua execução. Por exemplo, o seguinte controle é feito para o formato da data:

//-----------Formato da Data ComboBox--------------------------------+     
    if(!CreateComboBox(CDateFormat,"ComDateFormat",x0,y0+h+1,x0+w,y0+2*h+1))
     {
      return false;
     }
   CDateFormat.ListViewItems(6);
   CDateFormat.AddItem(" yyyy.mm.dd",0);
   CDateFormat.AddItem(" yyyymmdd",1);
   CDateFormat.AddItem(" yymmdd",2);
   CDateFormat.AddItem(" ddmmyy",3);
   CDateFormat.AddItem(" dd/mm/yy",4);
   CDateFormat.AddItem(" mm/dd/yy",5);
   CDateFormat.Select(1);
     }

Esses valores retornão dos campos de entrada para as variáveis correspondentes:

long sw;  
SkipString =StringToInteger(ESkip.Text());

sw =CDateFormat.Value();
switch(int(sw))
{
   case 0 :indate =yyyycmmcdd;
      break;
   case 1 :indate =yyyymmdd;
      break;
   case 2 :indate =yymmdd;
      break;
   case 3 :indate =ddmmyy;
      break;
   case 4 :indate =ddslmmslyy;
      break;
   case 5 :indate =mmslddslyy;
      break;                
}
//Outras variáveis

 

Esta versão tem uma implementação maior, então se você precisar editar o código, você deve trabalhar com a versão de entrada.


Parte 2. Guia Passo-a-Passo

Esta parte apresenta uma descrição passo a passo das ações necessárias para a criação de um símbolo personalizado da bolsa de valores. Este guia pode ser usado quando as cotações disponíveis tiverem formatos padrão, não tendo a necessidade de editar o código. Por exemplo: as cotações coletadas no site "finam.ru", se as cotações estiverem em alguns formatos fora do padrão, você deve editar o código descrito na Parte 1.

Portanto, temos um arquivo fonte com cotações de um instrumento financeiro da bolsa, caso tenhamos obtido no site "Finam", conforme descrito no início do artigo. Não se esqueça de que precisamos das cotações do timeframe de um minuto.

Duas opções de importação de dados são descritas no artigo. Você pode usar o script CreateCustomSymbol e o Expert Advisor CreateSymbolPanel, ambos executam exatamente o mesmo com o painel de entradas. Por exemplo, vamos considerar a operação com o painel de entradas, nos exemplos colocados aqui, usamos cotações das ações do Sberbank da Bolsa de Moscou. As cotações estão no arquivo "sb.csv", anexo abaixo.

1. Arranjo de arquivos

Primeiro de tudo, precisamos salvar o arquivo das cotações em MQL5/Files, isto está conectado com o conceito de programação MQL5, pois as operações com arquivos são rigorosamente controladas por razões de segurança. O método mais fácil de encontrar o diretório desejado é abri-lo no MetaTrader, na janela Navegador, clique com o botão direito do mouse na pasta Arquivos e selecione "Abrir pasta" no menu de contexto.



O arquivo fonte dos dados deve ser salvo nessa pasta (os locais dos arquivos anexos estão descritos no final do artigo). O arquivo agora pode ser aberto no MetaEditor.



Adicione Specifications.txt à mesma pasta. Define as propriedades do símbolo.

2. Entradas

O próximo passo é determinar o formato e a posição dos dados, selecionar as propriedades do arquivo e definir o nome do nosso símbolo personalizado. O exemplo de como os campos podem ser preenchidos é mostrado abaixo:



Os dados devem ser transferidos para o painel. O spread fixo em pontos é usado nesta versão, spread flutuante não é modelado, portanto você deve inserir aqui um valor apropriado de spread.


Escreva o nome completo do arquivo, incluindo a extensão.

Antes de clicar em "OK", especifique os símbolo necessários, eles estão disponíveis no arquivo Specifications.txt que deve ser colocado no MQL5/Files.

É muito interessante editar o arquivo de texto no MetaEditor, a principal razão são os destaques dos dados suportado no MetaEditor. Se você não conseguir entender nenhuma das propriedades, passe o cursor sobre elas e pressione F1.



As propriedades são destacadas em vermelho e os valores são mostrados em verde. As propriedades comentadas (//) que não são usadas são mostradas em verde, observe que as vírgulas são usadas para separação de dados. Não exclua propriedades durante a edição, para evitar erros, você deve manter o formato existente.

Para editar propriedades, remova os comentários que lhe interessa (remova "//") e defina o valor apropriado, um conjunto mínimo de propriedades estão disponíveis em arquivo anexado: passo do preço, valor de ponto, lote mínimo, etc.

Todas essas características (no arquivo fonte) são necessárias para as ações do Sberbank na Bolsa de Valores de Moscou. Diferentes características são necessárias para outros instrumentos financeiros, portanto, é necessário editar as propriedades. 

O conjunto mínimo necessário de propriedades está localizado no início do arquivo.

Normalmente, os preços das ações têm duas casas decimais (SYMBOL_DIGITS), enquanto o valor do ponto é igual a 0,01 rublos. O número de casas decimais nos preços dos mercados futuros das ações é 0 e o valor do ponto é 1 rublo. Veja as especificações em moex.com.

Depois de definir todas as propriedades necessárias, clique em OK. O símbolo personalizado criado aparecerá na janela do navegador, no meu exemplo, está destacado em verde.


Abra o gráfico para verificar:



Tudo está correto, agora o símbolo personalizado pode ser testado no Testador de Estratégia.

As configurações do símbolo personalizado são iguais ao símbolo padrão, um ponto importante aqui é configurar corretamente as especificações dos símbolos.

Como exemplo, vamos testar qualquer Expert Advisor padrão disponível no terminal (aqui a Média Móvel) e utilizar nossos dados:

 


Tudo funciona como esperado. Se você precisar adicionar novas cotações ou alterar as propriedades, basta repetir as ações descritas para o símbolo já existente. Se as especificações não foram alteradas, clique em OK sem editar as propriedades.


Arquivos

Os arquivos anexos devem ser adiconados no seu computador, nas pastas relacionadas a seguir: