Conselhos de um programador profissional (Parte II): armazenamento e troca de parâmetros entre um EA, scripts e programas externos
Sumário
- Introdução
- Locais de armazenamento de parâmetros
- Variáveis globais do terminal
- Objetos gráficos
- Comentários de ordens
- Arquivos de texto
- Configurações do aplicativo
- Parâmetros dos analisadores
- Transmissão parâmetros de script para um Expert Advisor
- Transmissão de parâmetros para programas externos
- Recepção de parâmetros vindos de programas externos
- Transmissão de parâmetros para um smartphone
- Conclusão
Introdução
Este artigo se concentrará nos parâmetros que podem ser restaurados após reiniciar (fechar) o terminal. Na verdade, todos os exemplos apresentados são partes funcionais do código do meu projeto Cayman.
Locais de armazenamento de parâmetros
Exemplos de parâmetros
- Tempo de barra zero. Por exemplo, para detectar um padrão de vela, é lógico avaliá-lo uma vez depois que uma nova barra aparecer num determinado período.
- Parâmetros do nível de negociação. Por exemplo, destacamos um nível de negociação, usamos um script para definirmos o tipo e tamanho da negociação em caso de rompimento. O script transmite os parâmetros para o Expert Advisor. O Expert Advisor cria um analisador de nível. O analisador "é habilitado" somente depois que uma nova barra aparece no período gráfico especificado.
- Preferências do usuário. Por exemplo, cor, regras de negociação, métodos de plotagem, etc. É lógico defini-los uma vez, por exemplo, no arquivo de configurações.
- Variáveis globais do terminal
- Objetos gráficos
- Comentários de ordens
- Arquivos de texto
Locais de armazenamento | Tipo | Área de visibilidade | Tempo de vida |
---|---|---|---|
Variáveis globais do terminal | double | Todos os gráficos | 4 semanas após o último acesso |
Objetos gráficos | Qualquer. String <= 63 caracteres | Gráfico atual | Vida útil do gráfico |
Comentários de ordens | Strings <= 23 caracteres | Todos os gráficos | Vida útil do terminal |
Arquivos de texto | Qualquer. Sem restrições | Todos os gráficos | Tempo de vida do arquivo |
Variáveis globais do terminal
As variáveis globais do terminal estão disponíveis em qualquer gráfico. O escopo pode ser limitado incluindo componentes como ChartId, Symbol, Period no nome da variável. No entanto, você não pode "argumentar" contra o tipo de variável. O texto não pode ser salvo.
No entanto, há um truque - compactar/descompactar valores inteiros. Como é sabido, o double ocupa 8 bytes (64 bits). Vou mostrar com um exemplo como armazenar vários valores inteiros numa variável. O mais importante é determinar a profundidade de bits de seus valores máximos.
// ----------------------------------------------------------------------------- // Exemplo de compactação/descompactação de valores inteiros de/para | // uma variável global usando operações bit a bit | // ----------------------------------------------------------------------------- void OnStart() { int value10 = 10; // max = 255 (8 casas decimais) int value20 = 300; // max = 65535 (16 casas decimais) bool value30 = true; // max = 1 (1 casas decimais) // empacotamos os valores em 25 bits (8+16+1) // 39 bits permanecem livres (64-25) ulong packedValue = (value10 << 17) + // reservamos local (16+1) para value20, value30 (value20 << 1) + // reservamos local (1) para value30 value30; // salvamos a variável global string nameGVar = "temp"; GlobalVariableSet(nameGVar, packedValue); // lemos a variável global packedValue = (ulong)GlobalVariableGet(nameGVar); // descompactamos os valores // 0xFF, 0xFFFF, 0x1 - máscaras de bits de valores máximos int value11 = (int)((packedValue >> 17) & 0xFF); int value21 = (int)((packedValue >> 1) & 0xFFFF); bool value31 = (bool)(packedValue & 0x1); // comparamos os valores if (value11 == value10 && value21 == value20 && value31 == value30) Print("OK"); else PrintFormat("0x%X / 0x%X /0x%X / 0x%X", packedValue, value11, value21, value31); }
Objetos gráficos
Hmm, será que podemos armazenar parâmetros de script em objetos gráficos? Por que não? Definimos a propriedade do objeto OBJPROP_PRICE = 0, portanto, o objeto se tornará "invisível", mas acessível programaticamente. Para maior confiabilidade, podemos salvar tal objeto no modelo do gráfico. Lógica de acesso ao parâmetro: há um objeto - extraímos os parâmetros, não há objeto - definimos os valores padrão.
Comentários de ordens
O comprimento máximo do comentário da ordem é de 23 caracteres. O que pode ser armazenado lá? Por exemplo, SOP/H1/SS/C2/Br/Br/Br. Onde (da esquerda para a direita)
- SOP — quem envia a ordem (SOP – script SendOrderByPlan)
- H1 — período de formação da ordem (H1)
- SS — tipo de ordem (SS – Sell Stop)
- C2 — algoritmo de fechamento da ordem
- Br — tendência em D1 (Br – Bear)
- Br — tendência em H4 (Br – Bear)
- Br — tendência no período de formação da ordem (Br – Bear)
Por que isso é necessário? Por exemplo, quando o histórico de trades é analisado ou quando uma ordem pendente é acionada, extraio o valor do algoritmo de fechamento e crio um analisador de stop virtual AnalyzerVirtSL que fechará o próprio trade sob certas condições.
Arquivos de texto
Esta é talvez a maneira mais confiável e versátil de armazenar parâmetros de recuperação. Uma vez que depuramos as classes de acesso, +podemos usá-las em qualquer lugar e sempre.
Configurações do aplicativo
Parte do arquivo de configurações do aplicativo AppSettings.txt
# ------------------------------------------------------------------- # Configurações de EA e script #Codificação de arquivo = UCS-2 LE com BOM (obrigatório!!!) // isto é unicode # ------------------------------------------------------------------- TimeEurWinter = 10:00 # horário de inverno do início da sessão europeia (horário do servidor) TimeEurSummer = 09:00 # horário de verão do início da sessão europeia (horário do servidor) ColorSessionEur = 224,255,255 # cor da sessão europeia ColorSessionUsd = 255,240,245 # cor da sessão americana NumberColorDays =10 # número de dias destacados (sessões)
Classe AppSettings.mqh
#property copyright "Copyright 2020, Malik Arykov" #property link "malik.arykov@gmail.com" #property strict #include <Cayman/Params.mqh> // nomes dos parâmetros do aplicativo #define APP_TIME_EUR_SUMMER "TimeEurSummer" #define APP_TIME_EUR_WINTER "TimeEurWinter" #define APP_TIME_TRADE_ASIA "TimeTradeAsia" #define APP_COLOR_SESSION_EUR "ColorSessionEur" #define APP_COLOR_SESSION_USD "ColorSessionUsd" #define APP_NUMBER_COLOR_DAYS "NumberColorDays" // ----------------------------------------------------------------------------- // Configurações gerais do Expert Advisor e dos Scripts | // ----------------------------------------------------------------------------- class AppSettings { private: Params *m_params; public: // são instalados no arquivo AppSettings.txt string TimeEurSummer; // horário de verão do início da sessão europeia string TimeEurWinter; // horário de inverno do início da sessão europeia string TimeTradeAsia; // hora do fim da negociação do corredor asiático color ColorSessionEur; // cor da sessão europeia color ColorSessionUsd; // cor da sessão americana int NumberColorDays; // número de dias destacados // são instalados programaticamente string PeriodTrends; // período de cálculo das tendências (D1,H4) string TradePlan; // direção do trading (plano curto) bool IsValid; // parâmetros válidos // métodos AppSettings(); ~AppSettings() { delete m_params; }; void Dump(string sender); }; // ----------------------------------------------------------------------------- // Construtor | // ----------------------------------------------------------------------------- AppSettings::AppSettings() { IsValid = true; m_params = new Params(); m_params.Load(PATH_APP_SETTINGS); if (m_params.Total() == 0) { PrintFormat("%s / ERROR: Arquivo inválido / %s", __FUNCTION__, PATH_APP_SETTINGS); IsValid = false; return; } TimeEurWinter = m_params.GetValue(APP_TIME_EUR_WINTER); TimeEurSummer = m_params.GetValue(APP_TIME_EUR_SUMMER); TimeTradeAsia = m_params.GetValue(APP_TIME_TRADE_ASIA); ColorSessionEur = StringToColor(m_params.GetValue(APP_COLOR_SESSION_EUR)); ColorSessionUsd = StringToColor(m_params.GetValue(APP_COLOR_SESSION_USD)); NumberColorDays = (int)StringToInteger(m_params.GetValue(APP_NUMBER_COLOR_DAYS)); } // ----------------------------------------------------------------------------- // Imprimir os parâmetros das configuraçõ | // ----------------------------------------------------------------------------- void AppSettings::Dump(string sender) { PrintFormat("sender=%s / %s", sender, PATH_APP_SETTINGS); PrintFormat("%s = %s", APP_TIME_EUR_WINTER, TimeEurWinter); PrintFormat("%s = %s", APP_TIME_EUR_SUMMER, TimeEurSummer); PrintFormat("%s = %s", APP_TIME_TRADE_ASIA, TimeTradeAsia); PrintFormat("%s = %s / %s", APP_COLOR_SESSION_EUR, ColorToString(ColorSessionEur), ColorToString(ColorSessionEur, true)); PrintFormat("%s = %s / %s", APP_COLOR_SESSION_USD, ColorToString(ColorSessionEur), ColorToString(ColorSessionEur, true)); PrintFormat("%s = %i", APP_NUMBER_COLOR_DAYS, NumberColorDays); }
Características
Coloquei a declaração da classe AppSettings no arquivo Uterminal.mqh, que é anexado via #include ao Expert Advisor e a qualquer script.
extern AppSettings *gAppSettings; // configurações do aplicativo
Esta solução permite:
- Inicializar gAppSettings uma vez em qualquer lugar
- Usar gAppSettings numa instância de qualquer classe (em vez de passá-lo como parâmetro)
Parâmetros dos analisadores
O Expert Cayman gerencia vários analisadores, como AnalyzerTrend, AnalyserLevel, AnalyserVirtSL. Cada analisador está vinculado a um período gráfico específico. Isto quer dizer que a análise é iniciada apenas no momento em que uma nova barra aparece no período especificado. Os parâmetros do analisador são armazenados num arquivo de texto com as strings Key = Value. Por exemplo, o analisador de nível de negociação em H4 armazena seus parâmetros no arquivo Files\Cayman\Params\128968168864101576\exp_05_Lev607A160E_H4.txt
- Cayman — nome do projeto
- Params — subdiretório com parâmetros do analisador
- 128968168864101576 — ID do gráfico // IntergerToString(ChartID())
- exp_05_Lev607A160E_H4.txt — nome do arquivo com parâmetros do analisador —
- exp — prefixo
- 05 — tipo de analisador
- Lev607A160E — nome do analisador (nível de negociação)
- H4 — período rastreado.
Darei o conteúdo do arquivo com comentários (arquivo real — sem comentários)
// parâmetros do nível de negociação nameObj=Lev607A160E // nome do nível de negociação kindLevel=1 // tipo de nível (1 - resistência) riskValue=1.00 // volume do trade durante o rompimento de nível (1) riskUnit =1 // unidade de medida do volume do trade (1 -% dos fundos para garantia) algClose =2 // algoritmo de fechamento de trade (2 - duas barras de correção) ticketNew=0 // ticket do trade aberto durante o rompimento de nível ticketOld=0 // ticket para fechamento de trade durante o rompimento de nível profits=0// lucro planejado em pontos losses=0 // perda planejada em pontos // parâmetros do analisador symbol=EURUSD // nome do símbolo period=16388 // período do analisador (H4) time0Bar=1618603200 // tempo de barra zero (seg.) typeAnalyser=5 // tipo de analisador colorAnalyser=16711935 // cor dos resultados do analisador resultAnalyser=Lev607A160E, H4, 20:00, RS // resultado do analisador
Existe uma Analyser Analyser que pode salvar e restaurar os parâmetros de qualquer analisador. Quando o EA é reiniciado (por exemplo, ao alternar o período gráfico), os analisadores restauram seus parâmetros a partir dos devidos arquivos de texto. Além disso, se não chegar a hora de uma nova barra, a análise não é reiniciada. Os resultados do analisador (reseultAnalyser, colorAnalyser) calculados na barra anterior são exibidos nos comentários do Expert Advisor.
Transmissão parâmetros de script para um Expert Advisor
O script SetTradeLevel permite definir os parâmetros do nível de negociação. Um único objeto (linha reta, linha de tendência ou retângulo) é destacado no gráfico. O script SetTradeLevel encontra o objeto selecionado (nível de negociação) e define seus parâmetros.
Em seguida, o script salva os parâmetros no arquivo Files\Cayman\Params\128968168864101576\exp_05_Lev607A160E_H4.txt e envia o comando e o caminho para o arquivo através da função SendCommand
// ----------------------------------------------------------------------------- // Enviar os parâmetros de nível ao EA | // ----------------------------------------------------------------------------- NCommand SendCommand() { // carregamos os parâmetros de nível (se houver) Params *params = new Params(); string speriod = UConvert::PeriodToStr(_Period); params.Load(PREFIX_EXPERT, anaLevel, gNameLev, speriod); // definimos o comando NCommand cmd = (gKindLevel == levUnknown) ? cmdDelete : (params.Total() > 0) ? cmdUpdate : cmdCreate; // salvamos os parâmetros params.Clear(); params.Add(PARAM_NAME_OBJ, gNameLev); params.Add(PARAM_TYPE_ANALYSER, IntegerToString(anaLevel)); params.Add(PARAM_PERIOD, IntegerToString(_Period)); params.Add(PARAM_KIND_LEVEL, IntegerToString(gKindLevel)); params.Add(PARAM_RISK_VALUE, DoubleToString(gRiskValue, 2)); params.Add(PARAM_RISK_UNIT, IntegerToString(gRiskUnit)); params.Add(PARAM_ALG_CLOSE, IntegerToString(gAlgClose)); params.Add(PARAM_TICKET_OLD, IntegerToString(gTicketOld)); params.Add(PARAM_PROFITS, IntegerToString(gProfits)); params.Add(PARAM_LOSSES, IntegerToString(gLosses)); params.Save(); // enviamos um comando para EA params.SendCommand(cmd); delete params; return cmd; }
A função params.SendCommand(cmd) é
// ----------------------------------------------------------------------------- // Enviar um comando ao EA | // ----------------------------------------------------------------------------- void Params::SendCommand(NCommand cmd) { string nameObj = NAME_OBJECT_CMD; ObjectCreate(0, nameObj, OBJ_LABEL, 0, 0, 0); ObjectSetString(0, nameObj, OBJPROP_TEXT, m_path); ObjectSetInteger(0, nameObj, OBJPROP_ZORDER, cmd); ObjectSetInteger(0, nameObj, OBJPROP_TIMEFRAMES, 0); }
O EA cada tick (OnTick) por meio da função CheckExpernalComman() verifica a existência de um objeto denominado NAME_OBJECT_CMD. Se estiver presente, o comando e o caminho para o arquivo com os parâmetros do analisador são lidos e o objeto é imediatamente excluído. Em seguida, o Expert Advisor procura um analisador funcional pelo nome do arquivo. Se cmd == cmdDelete, o analisador é removido. Se cmd == cmdUpdate, são atualizados os parâmetros do analisador desde o arquivo. Se cmd == cmdNew, novo analisador é criado com parâmetros desde o arquivo.
Texto completo da classe Params, que encapsula a lógica para usar arquivos de parâmetros (strings Key=Value).
#property copyright "Copyright 2020, Malik Arykov" #property link "malik.arykov@gmail.com" #include <Arrays/ArrayString.mqh> #include <Cayman/UConvert.mqh> #include <Cayman/UFile.mqh> // ----------------------------------------------------------------------------- // Classe de parâmetros (strings key=value com comentários #) | // ----------------------------------------------------------------------------- class Params { private: string m_path; // caminho ao arquivo de parâmetros NCommand m_cmd; // comando para o EA CArrayString *m_items; // matriz de pares {key=value} int Find(string key); public: Params(); ~Params() { delete m_items; }; void Clear() { m_items.Clear(); }; int Total() { return m_items.Total(); }; string Path() { return m_path; }; CArrayString *Items() { return m_items; }; void Add(string line) { m_items.Add(line); }; bool Add(string key, string value); string GetValue(string key); void Load(string prefix, int typeAnalyser, string nameObj, string speriod); void Load(string path); void Save(); void SendCommand(NCommand cmd); NCommand TakeCommand(); void Dump(string sender); }; // ----------------------------------------------------------------------------- // Construtor padrão | // ----------------------------------------------------------------------------- Params::Params() { m_items = new CArrayString(); } // ----------------------------------------------------------------------------- // Adicionar o par chave=valor | // ----------------------------------------------------------------------------- bool Params::Add(string key, string value) { int j = Find(key); string line = key + "=" + value; if (j >= 0) { // atualizamos m_items.Update(j, line); return false; } else { // adicionamos m_items.Add(line); return true; } } // ----------------------------------------------------------------------------- // Obter o valor do parâmetro segundo a chave | // ----------------------------------------------------------------------------- string Params::GetValue(string key) { // buscamos a chave int j = Find(key); if (j < 0) return NULL; // não há chave // verificamos o separador string line = m_items.At(j); j = StringFind(line, "="); if (j < 0) { // não = PrintFormat("%s / ERROR: string inválida %s", __FUNCTION__, line); return NULL; } // retornamos o valor return UConvert::Trim(StringSubstr(line, j + 1)); } // ----------------------------------------------------------------------------- // Encontrar o valor do parâmetro segundo a chave | // ----------------------------------------------------------------------------- int Params::Find(string key) { int index = -1; for (int j = 0; j < m_items.Total(); j++) { if (StringFind(m_items.At(j), key) == 0) { index = j; break; } } return index; } // ----------------------------------------------------------------------------- // Carregar os parâmetros | // ----------------------------------------------------------------------------- void Params::Load(string prefix, int typeAnalyser, string nameObj, string speriod) { string nameFile = StringFormat("%s%02i_%s_%s.txt", prefix, typeAnalyser, nameObj, speriod); m_path = StringFormat("%s%s/%s", PATH_PARAMS, IntegerToString(ChartID()), nameFile); if (FileIsExist(m_path)) Load(m_path); } // ----------------------------------------------------------------------------- // Carregar os parâmetros | // ----------------------------------------------------------------------------- void Params::Load(string path) { m_path = path; if (!FileIsExist(m_path)) return; //PrintFormat("%s / %s", __FUNCTION__, m_path); string text = UFile::LoadText(m_path); if (text == NULL) return; // dividimos o texto em strings string line, lines[]; int numLines = StringSplit(text, DLM_LINE, lines); for (int j = 0; j < numLines; j++) { line = lines[j]; // removemos comentários int k = StringFind(line, "#"); if (k == 0) continue; // toda a string é um comentário if (k > 0) line = StringSubstr(line, 0, k); // adicionamos uma string não vazia if (line != "") m_items.Add(line); } } // ----------------------------------------------------------------------------- // Salvar parâmetros | // ----------------------------------------------------------------------------- void Params::Save() { string text = ""; for (int j = 0; j < m_items.Total(); j++) { text += m_items.At(j) + "\n"; } // substituímos o arquivo existente UFile::SaveText(text, m_path, true); } // ----------------------------------------------------------------------------- // Enviar um comando ao EA | // ----------------------------------------------------------------------------- void Params::SendCommand(NCommand cmd) { string nameObj = NAME_OBJECT_CMD; ObjectCreate(0, nameObj, OBJ_LABEL, 0, 0, 0); ObjectSetString(0, nameObj, OBJPROP_TEXT, m_path); ObjectSetInteger(0, nameObj, OBJPROP_ZORDER, cmd); ObjectSetInteger(0, nameObj, OBJPROP_TIMEFRAMES, 0); } // ----------------------------------------------------------------------------- // Receber o comando desde o script | // ----------------------------------------------------------------------------- NCommand Params::TakeCommand() { string nameObj = NAME_OBJECT_CMD; if (ObjectFind(0, nameObj) < 0) return cmdUnknown; m_path = ObjectGetString(0, nameObj, OBJPROP_TEXT); m_cmd = (NCommand)ObjectGetInteger(0, nameObj, OBJPROP_ZORDER); ObjectDelete(0, nameObj); Load(m_path); return m_cmd; } // ----------------------------------------------------------------------------- // |Despejo de parâmetros | // ----------------------------------------------------------------------------- void Params::Dump(string sender) { for (int j = 0; j < m_items.Total(); j++) { PrintFormat("%s / %s", sender, m_items.At(j)); } }
Para fãs de MQL5: ao alterar o tipo m_items para CHashMap, o código das funções Add, GetValue, Find será reduzido significativamente. Mas a classe Params também é usada em MQL4. Além disso, a velocidade de acesso aos parâmetros não é importante neste caso. Porque os parâmetros são lidos uma vez para inicializar as variáveis locais. Por que não refiz a classe para CHashMap para MQL5? Provavelmente por ter trabalhado muito tempo num banco. Os desenvolvedores de software financeiro têm uma frase: se funciona, não toque nele! ;-)
Transmissão de parâmetros para programas externos
A unidade de intercâmbio entre sistemas diferentes é um arquivo json. Costumava ser um arquivo xml. As principais vantagens dos arquivos json são:
- Facilidade de criação (geração / formatação)
- Excelente suporte em todos os idiomas de alto nível
- Legibilidade
Por exemplo, existe uma classe Bar com os campos m_time, m_open, m_high, m_low, m_close, m_body. Onde m_body é a cor do candle: branco, preto ou doji. A classe Bar tem um método ToJson() que gera uma string json
string Bar::ToJson() { return "{" + "\n\t\"symbol\":\"" + _Symbol + "\"," + "\n\t\"period\":" + IntegerToString(_Period) + "," + "\n\t\"digits\":" + IntegerToString(_Digits) + "," + "\n\t\"timeBar\":\"" + TimeToStr(m_time) + "\"," + "\n\t\"open\":" + DoubleToString(m_open, _Digits) + "," + "\n\t\"high\":" + DoubleToString(m_high, _Digits) + "," + "\n\t\"low\":" + DoubleToString(m_low, _Digits) + "," + "\n\t\"close\":" + DoubleToString(m_close, _Digits) + "," + "\n\t\"body\":" + IntegerToString(m_body) + "," + "\n}"; }
Isso pode ser feito por meio de StringFormat, mas haverá dificuldades com a reorganização ou exclusão de valores. É possível remover a formatação “\n\t”, uma vez que existem muitos serviços de formatação json online. Um deles é JSON Parser. Basta depurar a obtenção de um json válido uma vez e usar a função bar.ToJson() sem hesitação.
Um programa externo, por exemplo em C#, pode converter qualquer arquivo json num objeto. Como transferir um arquivo json desde MQL? É muito simples. Solte (salve) o arquivo json, por exemplo, no diretório do terminal Files/Json. Um programa externo monitora este diretório em busca de novos arquivos. Após encontrar um arquivo, ele o lê, converte-o num objeto e imediatamente exclui o arquivo (para que ele não fique a fazer nada) ou o move para o arquivo (para estatísticas).
Recepção de parâmetros vindos de programas externos
É desnecessário anexar a biblioteca json (Deus me livre de fazer esse bicho de sete cabeças) aos programas MQL. É mais fácil transferir arquivos de texto com strings Key=Value. Para processar arquivos, você pode usar a classe Params (veja acima). O Expert Advisor e o indicador são candidatos a receber parâmetros de programas ou scripts externos. Por exemplo, no manipulador OnTick, precisamos chamar a função CheckExternalCommand(), que verificará a presença de arquivos no diretório Files/ExtCmd. Se um arquivo for encontrado, lê, processa (aceita os parâmetros) e exclui o arquivo.
Assim, já ficam estudadas as formas de receber e passar parâmetros entre MQL e programas externos. Agora pensemos por que DLLs são necessárias para programas MQL?. Além disso, o Mercado MQL não aceita esse tipo de programas. Há apenas um motivo, e é a segurança, já que se pode obter acesso total a partir de uma DLL.
Transmissão de parâmetros para um smartphone
Preste atenção ao aplicativo Android WirePusher. Tiro o chapéu para os desenvolvedores por esse serviço (gratuito e sem anúncios). Não sei se existe algo semelhante no iPhone. Para fãs do iPhone, sigam a discussão neste artigo.
Para usar o serviço, primeiro devemos:
- Instalar o WirePusher no smartphone
- Iniciar o aplicativo. Veremos nossa ID na tela inicial
- Adicionar https://wirepusher.com a Terminal/Service/Settings/Expert Advisors/Allow WebRequest
Em seguida, executar o script, após substituir os asteriscos em id = "********" pelo ID
void OnStart() { string id = "**********"; // id do nosso smartphone no WirePusher WirePusher("Lucro $1000", "Negócio", "Fechado", id); } // ------------------------------------------------------------------------------------------------ // Enviar notificação para smartphone via serviço da web WirePusher // Adicionar https://wirepusher.com a Terminal/Service/Settings/Expert Advisors/Allow WebRequest // message - texto da notificação // title - título da notificação (por exemplo, Atenção / Sinal / Transação) // type - tipo de notificação (por exemplo, ordem pendente ativada / nível de lucro / fechado) // id - número exclusivo do smartphone no aplicativo Android WirePusher // ------------------------------------------------------------------------------------------------ bool WirePusher(string message, string title, string type, string id) { char data[]; // matriz de dados do corpo da mensagem HTTP char result[]; // matriz com dados de resposta do serviço da web string answer; // cabeçalhos de resposta do serviço da web string url = "https://wirepusher.com/send?id={id}&title={title}&message={message}&type={type}"; StringReplace(url, "{id}", id); StringReplace(url, "{type}", type); StringReplace(url, "{title}", title); StringReplace(url, "{message}", message); ResetLastError(); int rcode = WebRequest("GET", url, NULL, 3000, data, result, answer); if (rcode != 200) { PrintFormat("%s / error=%i / url=%s / answer=%s / %s", __FUNCTION__, GetLastError(), url, answer, CharArrayToString(result)); return false; } PrintFormat("%s / %s / %s", __FUNCTION__, title, message); return true; }
No EA do Cayman, a função WirePusher é chamada em AnalyzerTrade quando:
- Ativada uma ordem pendente
- Quebra do nível de negociação
- Fechado um trade
No aplicativo WirePusher, podemos anexar som a cada tipo de notificação. Eu costumava definir o som "vai nessa" ao fechar um trade com lucro e "explosão" quando fechava com prejuízo. Depois me cansei de explosões ;-)
Conclusão
O local de armazenamento mais confiável e versátil para parâmetros são os arquivos de texto. Além disso, as operações de arquivo em qualquer sistema operacional (aplicativo) estão bem desenvolvidas (em cache).
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/9327
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso