Conselhos de um programador profissional (Parte II): armazenamento e troca de parâmetros entre um EA, scripts e programas externos
Malik Arykov | 19 julho, 2021
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).