English Русский 中文 Español Deutsch 日本語
preview
Técnicas do MQL5 Wizard que você deve conhecer (Parte 21): Testando com Dados do Calendário Econômico

Técnicas do MQL5 Wizard que você deve conhecer (Parte 21): Testando com Dados do Calendário Econômico

MetaTrader 5Sistemas de negociação | 11 outubro 2024, 16:06
164 0
Stephen Njuki
Stephen Njuki

Introdução

Damos continuidade à série sobre Expert Advisors montados pelo Wizard, analisando como as notícias do calendário econômico poderiam ser integradas em um Expert Advisor durante o teste, seja para confirmar uma ideia ou construir um sistema de trade mais robusto, graças em grande parte a este artigo. Esse artigo faz parte de uma série, sendo o primeiro, e, portanto, encorajo os leitores a lê-lo e segui-lo. No entanto, nosso foco aqui é estritamente em como os Expert Advisors montados pelo Wizard podem se beneficiar dessas ferramentas do IDE MQL5. Para novos leitores, há artigos introdutórios aqui e aqui sobre como desenvolver e montar Expert Advisors com o MQL5 Wizard.

Os dados econômicos podem ser a fonte de uma vantagem competitiva em um sistema de trade, já que tendem a se basear mais nos ‘fundamentos’ dos ativos, em oposição aos ‘técnicos’ que são mais comuns na forma de indicadores tradicionais, indicadores personalizados e outras ferramentas de ação de preço. Esses ‘fundamentos’ podem incluir taxas de inflação, taxas de juros dos bancos centrais, taxas de desemprego, dados de produtividade e uma série de outros pontos de notícias que geralmente têm um impacto significativo nos preços dos ativos, como evidenciado pela volatilidade sempre que há um lançamento. O mais famoso desses dados provavelmente é o relatório de empregos não-agrícolas (non-farm payroll), que é divulgado quase toda primeira sexta-feira de cada mês. Além disso, certamente há outros dados importantes que não recebem a devida atenção e são, portanto, ignorados por muitos traders. É por isso que testar estratégias com base nesses dados econômicos pode ajudar a descobrir alguns desses fatores e, assim, proporcionar uma vantagem ao trader prospectivo.

Os bancos de dados SQLite podem ser criados dentro do IDE MetaEditor e, como são repositórios de dados, teoricamente, deveríamos ser capazes de usá-los como uma fonte de dados para um Expert Advisor, de modo que atuem como buffers de indicadores. Além disso, eles podem armazenar os dados econômicos localmente, permitindo testes offline e também o uso no caso de a fonte dos dados de notícias ser corrompida por razões desconhecidas, o que é um risco constante, pois alguns (ou inevitavelmente a maioria) dos pontos de dados ficam desatualizados. Portanto, neste artigo, exploramos como os bancos de dados SQLite podem ser usados para arquivar notícias do Calendário Econômico, de modo que os Expert Advisors montados pelo Wizard possam usá-los para gerar sinais de trade.


Limitações Atuais e Soluções

Há um porém. Além da incapacidade de ler dados do calendário econômico no strategy tester, a partir dos meus testes ao ler bancos de dados dentro do strategy tester, parece haver uma limitação semelhante. No momento da redação deste texto, pode ser um erro de codificação da minha parte, mas ao tentar ler os dados do banco de dados com este código:

//+------------------------------------------------------------------+
//| Read Data
//+------------------------------------------------------------------+
double CSignalEconData::Read(string DB, datetime Time, string Event)
{  double _data = 0.0;
//--- create or open the database in the common terminal folder
   ResetLastError();
   int _db_handle = DatabaseOpen(DB, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE);
   if(_db_handle == INVALID_HANDLE)
   {  Print("DB: ", DB, " open failed with err: ", GetLastError());
      return(_data);
   }
   string _sql =
      "  SELECT ACTUAL " +
      "  FROM "   +
      "  ( "   +
      "  SELECT ACTUAL "   +
      "  FROM PRICES "  +
      "  WHERE DATE <= '" + TimeToString(Time) + "' "  +
      "  AND EVENT = '" + Event + "'  "  +
      "  ORDER BY DATE DESC   "  +
      "  LIMIT 1  "  +
      "  )  ";
   int _request = DatabasePrepare(_db_handle, _sql);
   if(_request == INVALID_HANDLE)
   {  Print("request failed with err: ", GetLastError());
      DatabaseClose(_db_handle);
      return(_data);
   }
   while(DatabaseRead(_request))
   {  //--- read the values of each field from the obtained entry
      ResetLastError();
      if(!DatabaseColumnDouble(_request, 0, _data))
      {  Print(" DatabaseRead() failed with err: ", GetLastError());
         DatabaseFinalize(_request);
         DatabaseClose(_db_handle);
      }
   }
   return(_data);
}

Resulta no erro 5601, com a mensagem de que a tabela que estou tentando acessar não existe! No entanto, ao executar o script SQL exato, tanto no IDE do banco de dados do MetaEditor quanto em um script anexado a um gráfico, não encontro esses problemas, já que o resultado esperado é retornado. Portanto, pode ser um descuido da minha parte, onde há algum código extra que preciso incluir para que isso funcione no strategy tester OU o acesso a bancos de dados no strategy tester não é permitido. O chatbot do Service Desk não pode ajudar!

Então, o que poderíamos fazer nessa situação? Claramente há benefícios em arquivar dados econômicos em um banco de dados, localmente, como mencionado acima, então seria uma pena não avançar mais testando e desenvolvendo Expert Advisors com base nisso. A solução que proponho é exportar os dados econômicos para um arquivo CSV e ler isso durante o strategy tester.

Apesar de contar e usar arquivos CSV como uma solução alternativa neste caso, eles enfrentam vários desafios e limitações se alguém pensar que eles poderiam substituir bancos de dados. Alguém poderia argumentar que, em vez de exportar os dados para um banco de dados e depois para um arquivo CSV, por que não exportá-los diretamente para o arquivo CSV? Bem, aqui está o motivo.

Arquivos CSV são muito menos eficientes em armazenar dados do que bancos de dados. Isso é demonstrado por vários fatores, sendo o principal a integridade e validação dos dados. Os bancos de dados aplicam verificações de integridade e restrições por meio de chaves primárias e estrangeiras, enquanto os arquivos CSV claramente não têm essa capacidade. Em segundo lugar, com base nisso, o desempenho e a escalabilidade são pontos fortes dos bancos de dados graças à indexação, onde grandes conjuntos de dados podem ser pesquisados com muita eficiência, enquanto arquivos CSV sempre dependerão de pesquisa linear, o que pode ser muito lento quando confrontado com grandes volumes de dados.

Terceiro, o acesso simultâneo é integrado na maioria dos bancos de dados, o que permite o acesso em tempo real para vários usuários, enquanto os arquivos CSV não conseguem lidar com isso. Além disso, os bancos de dados fornecem acesso seguro com recursos que incluem autenticação de usuário, controle de acesso baseado em funções e criptografia. Arquivos CSV, por padrão, não fornecem segurança, o que torna difícil proteger dados sensíveis.

Além disso, os bancos de dados fornecem ferramentas automatizadas para backup e recuperação, o que os arquivos CSV não fazem; bancos de dados suportam consultas complexas que usam joins e manipulações com SQL para uma análise completa, enquanto arquivos CSV precisariam de scripts de terceiros para atingir a mesma capacidade. Os bancos de dados fornecem conformidade ACID para suas transações, o que os arquivos CSV não fazem.

Além disso, os bancos de dados também suportam normalização, o que reduz a redundância de dados, permitindo assim um armazenamento mais compacto e eficiente, com menos duplicidade, enquanto a estrutura plana inerente dos arquivos CSV tende a gerar muita redundância. Os bancos de dados também suportam versionamento (o que é importante, pois muitos dados podem ser atualizados ao longo do tempo), um recurso essencial para auditorias, e os arquivos CSV não oferecem isso. Arquivos CSV são propensos a corromper dados em atualizações de dados e enfrentam desafios na gestão de estruturas de dados complexas. Há muitas outras vantagens cruciais dos bancos de dados sobre os arquivos CSV, no entanto, vamos manter nossa lista apenas nessas para destacar os benefícios. Cada um desses benefícios mencionados pode desempenhar um papel crucial na curadoria de dados econômicos para análise e estudo, especialmente ao longo de períodos prolongados, algo que seria impraticável com arquivos CSV.

Antes de exportar dados para um arquivo CSV para o acesso pelo strategy tester, devemos construir o banco de dados e carregar os dados econômicos nele, então é isso que abordamos a seguir.


Construindo o Banco de Dados SQLite

Para construir nosso banco de dados SQLite, usaremos um script. O código está listado abaixo:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
//| Sourced from: https://www.mql5.com/en/articles/7463#database_functions
//| and here: https://www.mql5.com/en/docs/database/databasebind
//+------------------------------------------------------------------+
void OnStart()
{
//--- create or open a database
   string _db_file = __currency + "_econ_cal.sqlite";
   int _db_handle = DatabaseOpen(_db_file, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE);
   if(_db_handle == INVALID_HANDLE)
   {  Print("DB: ", _db_file, " open failed with code ", GetLastError());
      return;
   }
   else
      Print("Database: ", _db_file, " opened successfully");
	...

        ...

}

Este código foi principalmente obtido aqui, com algumas modificações. Criar um banco de dados é feito através de um handle, semelhante à declaração de um handle de leitura ou gravação de arquivos. Estamos criando um banco de dados para cada par de moedas, o que admito ser uma abordagem ineficiente e muito difícil de gerenciar. Uma abordagem melhor seria ter todos esses pontos de dados econômicos em um único banco de dados, mas não fui diligente o suficiente para fazer isso e peço desculpas. O leitor pode corrigir essa abordagem. Uma vez criado nosso handle, precisaríamos verificar se ele é válido antes de continuar. Se for válido, isso indica que temos um banco de dados em branco e, portanto, podemos prosseguir para criar a tabela para armazenar nossos dados. Estamos nomeando nossa tabela como "prices" porque, neste artigo, focaremos apenas no setor de eventos do calendário do tipo 'preços do setor'. Este é um setor abrangente que inclui não apenas dados da taxa de inflação, mas também índices de preços ao consumidor e ao produtor, sendo o foco, pois estamos desenvolvendo uma classe de sinal personalizada que baseia suas condições de compra ou venda nas taxas de inflação relativas do par de moedas negociado. Muitas abordagens alternativas poderiam ser tomadas para desenvolver esses sinais de condição de compra e venda, e esta rota escolhida aqui é provavelmente uma das mais simples. Ao criar a tabela, assim como na maioria dos objetos de banco de dados, começamos verificando se ela já existe e, caso sim, ela seria excluída (drop) para que possamos criar a tabela que vamos preencher e usar. A listagem que faz isso está abaixo:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
{
        ...


//--- if the PRICES table exists, delete it
   if(DatabaseTableExists(_db_handle, "PRICES"))
   {  //--- delete the table
      if(!DatabaseExecute(_db_handle, "DROP TABLE PRICES"))
      {  Print("Failed to drop table PRICES with code ", GetLastError());
         DatabaseClose(_db_handle);
         return;
      }
   }
//--- create the PRICES table
   if(!DatabaseExecute(_db_handle, "CREATE TABLE PRICES("
                       "DATE           TEXT            ,"
                       "FORECAST       REAL            ,"
                       "ACTUAL         REAL            ,"
                       "EVENT          TEXT);"))
   {  Print("DB: ", _db_file, " create table failed with code ", GetLastError());
      DatabaseClose(_db_handle);
      return;
   }
//--- display the list of all fields in the PRICES table
   if(DatabasePrint(_db_handle, "PRAGMA TABLE_INFO(PRICES)", 0) < 0)
   {  PrintFormat("DatabasePrint(\"PRAGMA TABLE_INFO(PRICES)\") failed, error code=%d at line %d", GetLastError(), __LINE__);
      DatabaseClose(_db_handle);
      return;
   }

        ...
}

Nossa tabela criada terá 4 colunas, sendo elas a coluna ‘DATE’, que será do tipo texto e registrará quando a notícia econômica foi divulgada, a coluna ‘FORECAST’ para o ponto de dado econômico previsto, que será do tipo real, a coluna ‘ACTUAL’, que também será do tipo real e incluirá o dado econômico real para aquela data, e, finalmente, a coluna ‘EVENT’, que será do tipo texto e ajudará a rotular adequadamente esse ponto de dado, já que, em qualquer data para uma determinada moeda, podemos ter vários pontos de dados dentro da categoria de preços do setor de eventos. Assim, o tipo de rótulo usado para cada ponto de dado corresponderá ao código do evento do dado. Isso ocorre porque, ao recuperar os dados do calendário econômico, usamos a função ‘CalendarValueHistoryByEvent’ para retornar os valores de notícias do calendário que estão vinculados a eventos específicos. Cada um desses eventos tem um código descritivo em forma de string, e são esses códigos que atribuímos aos nossos dados ao armazená-los no banco de dados. A listagem da função ‘Get’, que recupera esses dados do calendário econômico, está abaixo:

//+------------------------------------------------------------------+
//| Get Currency Events
//+------------------------------------------------------------------+
bool Get(string Currency, datetime Start, datetime Stop, ENUM_CALENDAR_EVENT_SECTOR Sector, string &Data[][4])
{  ResetLastError();
   MqlCalendarEvent _event[];
   int _events = CalendarEventByCurrency(Currency, _event);
   printf(__FUNCSIG__ + " for Currency: " + Currency + " events are: " + IntegerToString(_events));
//
   MqlCalendarValue _value[];
   int _rows = 1;
   ArrayResize(Data, __COLS * _rows);
   for(int e = 0; e < _events; e++)
   {  int _values = CalendarValueHistoryByEvent(_event[e].id, _value, Start, Stop);
      //
      if(_event[e].sector != Sector)
      {  continue;
      }
      printf(__FUNCSIG__ + " Calendar Event code: " + _event[e].event_code + ", belongs to sector: " + EnumToString(_event[e].sector));
      //
      _rows += _values;
      ArrayResize(Data, __COLS * _rows);
      for(int v = 0; v < _values; v++)
      {  //
         printf(__FUNCSIG__ + " Calendar Event code: " + _event[e].event_code + ", for value: " + TimeToString(_value[v].period) + " on: " + TimeToString(_value[v].time) + ", has... ");
         //
         Data[_rows - _values + v - 1][0] = TimeToString(_value[v].time);
         //
         if(_value[v].HasForecastValue())
         {  Data[_rows - _values + v - 1][1] = DoubleToString(_value[v].GetForecastValue());
         }
         if(_value[v].HasActualValue())
         {  Data[_rows - _values + v - 1][2] = DoubleToString(_value[v].GetActualValue());
         }
         //
         Data[_rows - _values + v - 1][3] = _event[e].event_code;
      }
   }
   return(true);
}

Usamos um array multidimensional de strings chamado ‘_data’ para recuperar os dados do calendário econômico, e sua segunda dimensão corresponde ao número de colunas da tabela ‘PRICES’, que usaremos para armazenar os dados, o que significa que suas linhas são iguais em número às linhas de dados que inseriremos na tabela ‘PRICES’. Para acelerar o carregamento de dados do nosso array para a tabela, primeiro usamos as funções ‘DatabaseTransactionBegin()’ e ‘DatabaseTransactionCommit()’ para, respectivamente, iniciar e terminar as operações de gravação de dados. Isso é explicado aqui, no artigo já referenciado acima, como uma rota mais eficiente em oposição a trabalhar sem essas funções. Em segundo lugar, usamos a função data bind para realmente gravar os dados do array no banco de dados. Como nossas colunas de dados correspondem à tabela de destino, esse processo também é relativamente simples e muito eficiente, apesar de ser um pouco longo, conforme mostrado na listagem abaixo:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
//| Sourced from: https://www.mql5.com/en/articles/7463#database_functions
//| and here: https://www.mql5.com/en/docs/database/databasebind
//+------------------------------------------------------------------+
void OnStart()
{

        ...

//--- create a parametrized _sql_request to add _points to the PRICES table
   string _sql = "INSERT INTO PRICES (DATE,FORECAST,ACTUAL,EVENT)"
                " VALUES (?1,?2,?3,?4);"; // _sql_request parameters
   int _sql_request = DatabasePrepare(_db_handle, _sql);
   if(_sql_request == INVALID_HANDLE)
   {  PrintFormat("DatabasePrepare() failed with code=%d", GetLastError());
      Print("SQL _sql_request: ", _sql);
      DatabaseClose(_db_handle);
      return;
   }
//--- go through all the _points and add them to the PRICES table
   string _data[][__COLS];
   Get(__currency, __start_date, __stop_date, __event_sector, _data);
   int _points = int(_data.Size() / __COLS);
   bool _request_err = false;
   DatabaseTransactionBegin(_db_handle);
   for(int i = 0; i < _points; i++)
   {  //--- set the values of the parameters before adding a data point
      ResetLastError();
      string _date = _data[i][0];
      if(!DatabaseBind(_sql_request, 0, _date))
      {  PrintFormat("DatabaseBind() failed at line %d with code=%d", __LINE__, GetLastError());
         _request_err = true;
         break;
      }
      //--- if the previous DatabaseBind() call was successful, set the next parameter
      if(!DatabaseBind(_sql_request, 1, _data[i][1]))
      {  PrintFormat("DatabaseBind() failed at line %d with code=%d", __LINE__, GetLastError());
         _request_err = true;
         break;
      }
      if(!DatabaseBind(_sql_request, 2, _data[i][2]))
      {  PrintFormat("DatabaseBind() failed at line %d with code=%d", __LINE__, GetLastError());
         _request_err = true;
         break;
      }
      if(!DatabaseBind(_sql_request, 3, _data[i][3]))
      {  PrintFormat("DatabaseBind() failed at line %d with code=%d", __LINE__, GetLastError());
         _request_err = true;
         break;
      }
      //--- execute a _sql_request for inserting the entry and check for an error
      if(!DatabaseRead(_sql_request) && (GetLastError() != ERR_DATABASE_NO_MORE_DATA))
      {  PrintFormat("DatabaseRead() failed with code=%d", GetLastError());
         DatabaseFinalize(_sql_request);
         _request_err = true;
         break;
      }
      else
         PrintFormat("%d: added data for %s", i + 1, _date);
      //--- reset the _sql_request before the next parameter update
      if(!DatabaseReset(_sql_request))
      {  PrintFormat("DatabaseReset() failed with code=%d", GetLastError());
         DatabaseFinalize(_sql_request);
         _request_err = true;
         break;
      }
   } //--- done going through all the data points
//--- transactions status
   if(_request_err)
   {  PrintFormat("Table PRICES: failed to add %s data", _points);
      DatabaseTransactionRollback(_db_handle);
      DatabaseClose(_db_handle);
      return;
   }
   else
   {  DatabaseTransactionCommit(_db_handle);
      PrintFormat("Table PRICES: added %d data", _points);
   }


        ...

}

Com os dados inseridos na tabela ‘PRICES’, agora precisamos criar um arquivo CSV a partir do nosso banco de dados, uma vez que o acesso ao usar o strategy tester parece estar inibido. Para recapitular, nossa função ‘Read()’, que contém o SQL usado para ler o banco de dados, pode ser executada perfeitamente dentro do MetaEditor, como mostrado na imagem abaixo:

m_ed_sql

Além disso, se anexarmos o script ‘sql_read’ (o código-fonte completo está abaixo) a qualquer gráfico com entradas semelhantes de tempo e consultarmos o banco de dados USD, obtemos o mesmo resultado, o que implica que não há problemas com o banco de dados no ambiente do IDE do MetaEditor ou no terminal MT5. Veja a imagem do log abaixo:

t_lg_sql

A anexação e execução do script significa que um Expert Advisor anexado a um gráfico pode ser capaz de ler os valores do banco de dados sem nenhum problema. No entanto, para nossos propósitos agora, não podemos ler os valores do banco de dados ao executar o strategy tester, e isso significa que precisamos contar com arquivos CSV.


Exportando Dados do Calendário Econômico

Para exportar dados para um arquivo CSV, usamos simplesmente uma das funções internas ‘DatabaseExport()’, como mostrado no código abaixo:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
{

...

//--- save the PRICES table to a CSV file
   string _csv_file = "PRICES.csv";
   if(DatabaseExport(_db_handle, "SELECT * FROM PRICES", _csv_file,
                     DATABASE_EXPORT_HEADER | DATABASE_EXPORT_INDEX | DATABASE_EXPORT_QUOTED_STRINGS, ";"))
      Print("Database: table PRICES saved in ", _csv_file);
   else
      Print("Database: DatabaseExport(\"SELECT * FROM PRICES\") failed with code", GetLastError());
//--- close the database file and inform of that
   DatabaseClose(_db_handle);
   PrintFormat("Database: %s created and closed", _db_file);
}


Essa abordagem é a rota menos intensiva em código, já que, por exemplo, se fossemos primeiro selecionar os dados em um objeto (como um array) e depois fazer um loop por todos os valores do array e salvá-los em uma string separada por vírgulas para exportá-los, alcançaríamos o mesmo resultado, mas tenho quase certeza de que, além do trabalho adicional de codificação, o método que adotamos aqui tem um tempo de execução muito mais curto do que a abordagem com laço for. Isso pode ser porque o SQLite é uma biblioteca baseada na linguagem C, e o MQL5 também é amplamente baseado em C.

Nosso design de tabela do banco de dados, para ‘PRICES’, não possui uma chave primária explícita, e essas chaves com grandes conjuntos de dados são importantes para criar índices que tornam os bancos de dados uma ferramenta rápida e poderosa. O que pode ser feito como modificação nesta tabela é adicionar uma coluna de incremento automático que sirva como chave primária ou emparelhar as colunas ‘EVENT’ e ‘DATE’ para serem ambas chaves primárias, já que, do ponto de vista do design, os valores combinados em ambas as colunas serão únicos em todas as linhas de dados. A ambiguidade dos códigos adotados para rotular os eventos, conforme armazenados na coluna ‘EVENT’, exige uma diligência extra para garantir que o ponto de dado de interesse seja o que você realmente está recuperando.

Por exemplo, neste artigo, estamos focando no par GBPUSD, o que significa que as duas moedas de interesse são GBP e USD. (Observe que evitamos o EUR devido aos múltiplos pontos de dados, não apenas da área do euro, mas também de seus países membros!) Se olharmos os códigos de eventos para dados de inflação dessas moedas menos ambíguas, para GBP temos ‘cpi-yy’ e para USD temos ‘core-pce-price-index-yy’. Lembre-se de que há outros códigos de inflação ano a ano para USD que não consideraremos, portanto, deve-se ter cuidado ao fazer uma seleção. Além disso, essa rotulagem não é padronizada, o que significa que, ao longo dos anos, ou até mesmo em menos tempo, ela pode ser revisada, e qualquer sistema automatizado precisará ter seu código atualizado. Isso aponta para a ideia de se ter uma rotulagem personalizada com uma verificação de validação de dados do calendário para garantir que os dados corretos sejam codificados corretamente, mas, como mencionado, seria necessário um pouco de inspeção humana de tempos em tempos, já que a codificação pode mudar a qualquer momento.


Classe de Sinal do MQL5

Estamos usando arquivos CSV para isso, como mencionado, e embora isso deva ser um processo simples, a formatação ANSI e UTF8 pode apresentar alguns desafios ao ler esses dados, caso alguém não esteja ciente de suas diferenças. Adotamos uma função padrão de leitor de CSV de aqui para ler os dados CSV exportados, e eles são carregados na função que inicializa os indicadores para cada moeda. A moeda de margem (GBP) e a moeda de lucro (USD). Ao fazer isso, há limitações ao ler grandes arquivos CSV, pois eles sobrecarregam a RAM. Uma possível solução seria particionar o arquivo CSV por tempo, de forma que, na inicialização, apenas um dos arquivos seja carregado e, quando seu ponto de dado mais recente estiver muito desatualizado para o tempo atual no strategy tester, esse arquivo seja ‘liberado’ e um novo arquivo CSV seja carregado.

Essas soluções alternativas abordam problemas que não existiriam se o acesso ao banco de dados no strategy tester fosse possível. Portanto, nossa classe de sinal, já que não está lendo de um banco de dados, simplesmente receberá como entradas os nomes dos arquivos CSV para a moeda de margem e a moeda de lucro. Com nossa classe de sinal, o único buffer de série que usaremos será a classe ‘m_time’ e, estritamente falando, nem precisaríamos do buffer, já que o tempo atual é suficiente. No entanto, ele é usado aqui para obter o tempo no índice zero. A recuperação dos valores do calendário com base no arquivo CSV carregado é feita pela função ‘Read’, cujo código está abaixo:

//+------------------------------------------------------------------+
//| Read Data
//+------------------------------------------------------------------+
double CSignalEconData::Read(datetime Time, SLine &Data[])
{  double _data = 0.0;
   int _rows = ArraySize(Data);
   _data = StringToDouble(Data[0].field[1]);
   for(int i = 0; i < _rows; i++)
   {  if(Time >= StringToTime(Data[i].field[0]))
      {  _data = StringToDouble(Data[i].field[1]);
      }
      else if(Time < StringToTime(Data[i].field[0]))
      {  break;
      }
   }
   return(_data);
}

É iterativo, já que usa um laço for. Entretanto, se tivéssemos sido capazes de acessar esses mesmos dados de um banco de dados indexado, a mesma operação seria executada muito mais rapidamente. Em pequenos conjuntos de dados, como o usado neste artigo, essa diferença de desempenho pode ser ignorada, mas à medida que o tamanho do conjunto de dados aumenta com mais dados históricos sendo analisados, o caso para ler o SQLite dentro do strategy tester se torna mais forte.

A função de leitura é chamada tanto para a moeda de margem quanto para a moeda de lucro, e retorna as taxas de inflação mais recentes. Nosso sinal é gerado simplesmente com base no tamanho relativo dessas taxas. Se a moeda de margem tiver uma taxa de inflação mais alta do que a moeda de lucro, venderemos o par. Se, por outro lado, a taxa de inflação da moeda de margem for menor, iremos comprar. Essa lógica é mostrada abaixo como parte da função ‘LongCondition()’:

//+------------------------------------------------------------------+
//| "Voting" that price will grow.                                   |
//+------------------------------------------------------------------+
int CSignalEconData::LongCondition(void)
{  int result = 0;
   m_time.Refresh(-1);
   double _m = Read(m_time.GetData(0), m_margin_data);
   double _p = Read(m_time.GetData(0), m_profit_data);
   if(_m < _p)
   {  result = int(100.0 * ((_p - _m) / _p));
   }
   return(result);
}


Se executarmos o Expert Advisor sem nenhuma otimização, usando as primeiras configurações padrão após montá-lo com o wizard e compilá-lo, obteremos os seguintes resultados:

s1


r1


c1

A inflação é claramente um determinante na tendência dos pares de moedas. Os dados de inflação que utilizamos são divulgados mensalmente, portanto, nosso período de teste também é mensal. No entanto, isso não precisa necessariamente ser o caso, já que, em intervalos de tempo menores, a mesma posição pode ser mantida enquanto se busca pontos de entrada mais precisos ou melhores. O par testado é GBPUSD.


Conclusão

Resumindo, os bancos de dados SQLite introduzem muitos benefícios e vantagens na forma como permitem armazenar e organizar conjuntos de dados personalizados. Os Dados do Calendário Econômico, divulgados em eventos de notícias importantes, são um desses conjuntos de dados que podem ser arquivados para análises futuras, ajudando a entender quais são os principais fatores que impulsionam a ação do mercado. Uma estratégia muito simples, como a abordada neste artigo, que foca na inflação, pode fazer toda a diferença em um sistema que também utiliza indicadores técnicos. Como sempre, isso não é um conselho de investimento, e o leitor é encorajado a realizar sua própria diligência antes de adotar qualquer uma das ideias compartilhadas neste artigo ou série.


Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/14993

Arquivos anexados |
ed_r1.mq5 (7.01 KB)
db_calendar_r1.mq5 (8.06 KB)
SignalWZ_21_r1.mqh (10.79 KB)
Do básico ao intermediário: Array (I) Do básico ao intermediário: Array (I)
Este é um artigo de transição entre o que foi visto até agora, para uma nova etapa de estudos. O pré-requisito para conseguir entender este artigo é ter compreendido os artigos anteriores. O conteúdo exposto aqui, visa e tem como objetivo, pura e simplesmente a didática. De modo algum deve ser encarado como sendo, uma aplicação cuja finalidade não venha a ser o aprendizado e estudo dos conceitos mostrados.
Ganhe uma Vantagem sobre Qualquer Mercado (Parte II): Previsão de Indicadores Técnicos Ganhe uma Vantagem sobre Qualquer Mercado (Parte II): Previsão de Indicadores Técnicos
Você sabia que podemos obter mais precisão ao prever certos indicadores técnicos do que ao prever o preço subjacente de um símbolo negociado? Junte-se a nós para explorar como aproveitar essa percepção para melhores estratégias de negociação
Técnicas do MQL5 Wizard que você deve conhecer (Parte 22): GANs Condicionais Técnicas do MQL5 Wizard que você deve conhecer (Parte 22): GANs Condicionais
Redes Generativas Adversariais são uma combinação de Redes Neurais que treinam entre si para obter resultados mais precisos. Adotamos o tipo condicional dessas redes ao buscarmos uma possível aplicação na previsão de séries temporais financeiras dentro de uma Classe de Sinais de Expert.
Data Science e Machine Learning (Parte 23): Por que o LightGBM e o XGBoost superam muitos modelos de IA? Data Science e Machine Learning (Parte 23): Por que o LightGBM e o XGBoost superam muitos modelos de IA?
Essas técnicas avançadas de árvores de decisão com boosting de gradiente oferecem desempenho superior e flexibilidade, tornando-as ideais para modelagem financeira e trading algorítmico. Aprenda como aproveitar essas ferramentas para otimizar suas estratégias de trading, melhorar a precisão preditiva e ganhar uma vantagem competitiva nos mercados financeiros.