
Consejos de un programador profesional (parte II): Organizando el almacenamiento y el intercambio de parámetros entre el experto, los scripts y los programas externos
Contenido
- Introducción
- Lugar de almacenamiento de los parámetros
- Variables globales del terminal
- Objetos gráficos
- Comentarios de las órdenes
- Archivos de texto
- Ajustes de la aplicación
- Parámetros de los analizadores
- Transmisión de los parámetros del script al asesor experto
- /es/articles/9327#topic10
- Recepción de los parámetros desde programas externos
- Transmisión de los parámetros a un smartphone
- Conclusión
Introducción
El artículo se centrará en los parámetros que podemos restaurar tras reiniciar (cerrar) el terminal. Todos los ejemplos son en realidad trozos del código operativo del proyecto Cayman del propio autor.
Lugar de almacenamiento de los parámetros
Ejemplos de parámetros
- Hora de la barra cero. Por ejemplo, para detectar un patrón de velas, resulta lógico evaluarlo una vez después de que surja una nueva barra en un periodo determinado.
- Parámetros de nivel comercial. Por ejemplo, resaltamos un nivel comercial y establecemos con un script su tipo y el tamaño de la transacción en caso de ruptura. El script transmite los parámetros al asesor experto. El asesor crea un analizador de nivel. El analizador «se activa» solo después de que aparezca una nueva barra en el marco temporal indicado.
- Preferencias del usuario. Por ejemplo, el color, las reglas comerciales, los métodos de dibujado, etcétera. Es lógico ajustarlos una vez, por ejemplo, en el archivo de configuración.
- Variables globales del terminal
- Objetos gráficos
- Comentarios de las órdenes
- Archivos de texto
Lugar de almacenamiento | Tipo | Ámbito | Duración |
---|---|---|---|
Variables globales del terminal | double | Todos los gráficos | 4 semanas después de la última llamada |
Objetos gráficos | Cualquiera. Líneas <= 63 caracteres | Gráfico actual | Vida útil del gráfico |
Comentarios de las órdenes | Longitud de línea <= 23 caracteres | Todos los gráficos | Vida útil del terminal |
Archivos de texto | Cualquiera. Sin límites | Todos los gráficos | Vida útil del archivo |
Variables globales del terminal
Las variables globales del terminal están disponibles en cualquier gráfico. El ámbito se puede limitar incluyendo componentes como ChartId, Symbol, Period en el nombre de la variable. Sin embargo, lo que no podemos hacer es introducir cambios en el tipo de variable. No se puede guardar texto.
No obstante, hay un truco: empaquetar/desempaquetar valores enteros. Como ya sabemos, el tipo double ocupa 8 bytes (64 bits). Vamos a ver con un ejemplo cómo almacenar varios valores enteros en una variable. Lo más importante es determinar el tamaño de bits de sus valores máximos.
// ----------------------------------------------------------------------------- // Ejemplo de desempaquetado/empaquetado de valores enteros/de la variable global // con ayuda de operaciones de bits | // ----------------------------------------------------------------------------- void OnStart() { int value10 = 10; // max = 255 (8 bits) int value20 = 300; // max = 65535 (16 bits) bool value30 = true; // max = 1 (1 bit) // empaquetamos los valores en 25 bits (8+16+1) // quedan 39 bits libres (64-25) ulong packedValue = (value10 << 17) + // reservamos espacio (16+1) para value20, value30 (value20 << 1) + // reservamos espacio (1) para value30 value30; // guardamos la variable global string nameGVar = "temp"; GlobalVariableSet(nameGVar, packedValue); // leemos la variable global packedValue = (ulong)GlobalVariableGet(nameGVar); // desempaquetamos los valores // 0xFF, 0xFFFF, 0x1 - máscaras de bits de los valores máximos int value11 = (int)((packedValue >> 17) & 0xFF); int value21 = (int)((packedValue >> 1) & 0xFFFF); bool value31 = (bool)(packedValue & 0x1); // comparamos los 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
¿Es posible almacenar los parámetros de los scripts en los objetos gráficos? ¿Y por qué no? Establecemos la propiedad del objeto OBJPROP_PRICE = 0, luego el objeto se volverá visualmente "invisible", pero accesible de forma programática. Para mayor fiabilidad, podemos guardar dicho objeto en una plantilla de gráfico. La lógica de acceso a los parámetros funciona así: si hay un objeto, extraemos los parámetros; si no hay ninguno, establecemos los valores predeterminados.
Comentarios de las órdenes
La longitud máxima de los comentarios de una orden es de 23 símbolos. ¿Qué podemos almacenar allí? Por ejemplo, SOP/H1/SS/C2/Br/Br/Br. Donde (de izquierda a derecha)
- SOP — emisor de la orden (SOP - script de comandos SendOrderByPlan)
- H1 — periodo de formación de la orden (H1)
- SS — tipo de orden (SS - Sell Stop)
- C2 — algoritmo de cierre de la orden
- Br — tendencia en D1 (Br - Bear)
- Br — tendencia en H4 (Br - Bear)
- Br — tendencia durante el periodo de formación de la orden (Br - Bear)
¿Para qué necesitamos esto? Por ejemplo, para analizar la historia de transacciones o al activar una orden pendiente, extraemos el valor del algoritmo de cierre y creamos el analizador de stops virtuales AnalyserVirtSL, que cerrará por sí mismo la transacción en ciertas condiciones.
Archivos de texto
Esta es quizás la forma más fiable y versátil de almacenar los parámetros de recuperación. Una vez que hemos depurado las clases de acceso, las usaremos siempre y en todas partes.
Ajustes de la aplicación
Parte del archivo de ajustes de la aplicación AppSettings.txt
# ------------------------------------------------------------------- # Ajustes del experto y los scripts # Codificación del archivo = UCS-2 LE con BOM (imprescindible!!!) // es unicode # ------------------------------------------------------------------- TimeEurWinter = 10:00 # horario invernal de comienzo de la sesión europea (hora del servidor) TimeEurSummer = 09:00 # horario estival de comienzo de la sesión europea (hora del servidor) ColorSessionEur = 224,255,255 # color de la sesión europea ColorSessionUsd = 255,240,245 # color de la sesión americana NumberColorDays = 10 # número de días (sesiones) resaltados
Clase AppSettings.mqh
#property copyright "Copyright 2020, Malik Arykov" #property link "malik.arykov@gmail.com" #property strict #include <Cayman/Params.mqh> // nombres de los parámetros de la aplicación #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" // ----------------------------------------------------------------------------- // Ajustes generales del Asesor y los Scripts | // ----------------------------------------------------------------------------- class AppSettings { private: Params *m_params; public: // se establecen en el archivo AppSettings.txt string TimeEurSummer; // horario estival de inicio de la sesión europea string TimeEurWinter; // horario invernal de inicio de la sesión europea string TimeTradeAsia; // hora de finalización del comercio del corredor asiático color ColorSessionEur; // color de la sesión europea color ColorSessionUsd; // color de la sesión americana int NumberColorDays; // número de días resaltados // se establecen de forma programática string PeriodTrends; // Periodos de cálculo de las tendencias (D1,H4) string TradePlan; // Dirección del comercio (plan breve) bool IsValid; // validez de los parámetros // métodos AppSettings(); ~AppSettings() { delete m_params; }; void Dump(string sender); }; // ----------------------------------------------------------------------------- // Constructor | // ----------------------------------------------------------------------------- AppSettings::AppSettings() { IsValid = true; m_params = new Params(); m_params.Load(PATH_APP_SETTINGS); if (m_params.Total() == 0) { PrintFormat("%s / ERROR: Archivo no vá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 los parámetros de los ajustes | // ----------------------------------------------------------------------------- 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
Hemos colocado la declaración de la clase AppSettings en el archivo Uterminal.mqh, que se conecta a través de #include al asesor experto y a cualquier script.
extern AppSettings *gAppSettings; // ajustes de la aplicación
Esta solución permite:
- Inicializar gAppSettings una vez en cualquier lugar
- Usar gAppSettings en una instancia de cualquier clase (en lugar de transmitirla como parámtro)
Parámetros de los analizadores
El asesor Cayman gestiona diferentes analizadores, por ejemplo AnalyserTrend, AnalyserLevel, AnalyserVirtSL. Cada analizador está vinculado a un marco temporal determinado, es decir, el análisis de inicia solo cuando aparece una nueva barra en el periodo indicado. Los parámetros del analizador se almacenan en un archivo de texto con las línea Key = Value. Por ejemplo, el analizador de nivel comercial en H4 almacena sus parámetros en el archivo Files\Cayman\Params\128968168864101576\exp_05_Lev607A160E_H4.txt
- Cayman — nombre del proyecto
- Params — subdirectorio con los parámetros del analizador
- 128968168864101576 — ID del gráfico // IntergerToString(ChartID())
- exp_05_Lev607A160E_H4.txt — nombre del archivo con los parámetros del analizador —
- exp — prefijo
- 05 — tipo de analizador
- Lev607A160E — nombre del analizador (nivel comercial)
- H4 — periodo de seguimiento.
Vamos a mostrar el contenido del archivo con comentarios (el archivo real no tiene comentarios)
// parámetros del nivel comercial nameObj=Lev607A160E // nombre del nivel comercial kindLevel=1 // tipo de nivel (1 - resistencia) riskValue=1.00 // volumen de la transacción al romper el nivel (1) riskUnit=1 // unidad de medida del volumen de la transacción (1 - % fondos para el margen) algClose=2 // algoritmo de cierre de la transacción (2 – dos barras de corrección) ticketNew=0 // ticket de la transacción al romper el nivel ticketOld=0 // ticket para cerrar una transacción al romper el nivel profits=0 // beneficio planeado en puntos losses=0 // pérdidas planeadas en puntos // parámetros del analizador symbol=EURUSD // nombre del símbolo period=16388 // periodo del analizador (H4) time0Bar=1618603200 // hora de la barra cero (seg) typeAnalyser=5 // tipo de analizador colorAnalyser=16711935 // color de los resultados del analizador resultAnalyser=Lev607A160E, H4, 20:00, RS // resultado del analizador
Tenemos la clase básica Analyser que puede guardar y restaurar los parámetros de cualquier analizador. Al reiniciar el asesor (por ejemplo, al cambiar el marco temporal), los analizadores restauran sus parámetros desde los archivos de texto correspondientes. Al mismo tiempo, el análisis no se reiniciará si no ha llegado el momento de una nueva barra. Los resultados del analizador (reseultAnalyser, colorAnalyser) calculados en la barra anterior se muestran en los comentarios del experto.
Transmisión de los parámetros del script al asesor experto
El script SetTradeLevel nos permite establecer los parámetros del nivel comercial. En el gráfico se destaca un objeto (línea recta, línea de tendencia o rectángulo). El script SetTradeLevel encuentra el objeto (nivel comercial) destacado y establece sus parámetros.
A continuación, el script guarda los parámetros en el archivo Files Files\Cayman\Params\128968168864101576\exp_05_Lev607A160E_H4.txt y envía el comando y la ruta al archivo a través de la función SendCommand.
// ----------------------------------------------------------------------------- // Enviar los parámetros del nivel del experto | // ----------------------------------------------------------------------------- NCommand SendCommand() { // cargando los parámetros del nivel (si hay) Params *params = new Params(); string speriod = UConvert::PeriodToStr(_Period); params.Load(PREFIX_EXPERT, anaLevel, gNameLev, speriod); // determinamos el comando NCommand cmd = (gKindLevel == levUnknown) ? cmdDelete : (params.Total() > 0) ? cmdUpdate : cmdCreate; // guardamos los 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 el comando al experto params.SendCommand(cmd); delete params; return cmd; }
La función params.SendCommand(cmd) tiene el aspecto siguiente
// ----------------------------------------------------------------------------- // Enviar el comando al experto | // ----------------------------------------------------------------------------- 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); }
Cada tick (OnTick), el asesor experto comprueba la existencia del objeto llamado NAME_OBJECT_CMD a través de la función CheckExpernalCommand(). Si existe, se leerán el comando y la ruta al archivo con los parámetros del analizador, y el objeto se eliminará de inmediato. A continuación, el experto buscará un analizador que funcione según el nombre del archivo. Si cmd == cmdDelete, se eliminará el analizador. Si cmd == cmdUpdate, se actualizarán los parámetros del analizador del archivo. Si cmd == cmdNew, se creará un nuevo analizador con los parámetros del archivo.
Aquí tenemos el texto completo de la clase Params, que encapsula la lógica necesaria para trabajar con los archivos de los parámetros (líneas 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> // ----------------------------------------------------------------------------- // Clase de los parámetros (líneas key=value con comentarios #) | // ----------------------------------------------------------------------------- class Params { private: string m_path; // ruta al archivo de parámetros NCommand m_cmd; // comando para el experto CArrayString *m_items; // matriz de parejas {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); }; // ----------------------------------------------------------------------------- // Constructor por defecto | // ----------------------------------------------------------------------------- Params::Params() { m_items = new CArrayString(); } // ----------------------------------------------------------------------------- // Añadir pareja clave=valor | // ----------------------------------------------------------------------------- bool Params::Add(string key, string value) { int j = Find(key); string line = key + "=" + value; if (j >= 0) { // actualizamos m_items.Update(j, line); return false; } else { // añadimos m_items.Add(line); return true; } } // ----------------------------------------------------------------------------- // Obtener valor del parámetro según la clave | // ----------------------------------------------------------------------------- string Params::GetValue(string key) { // buscamos la clave int j = Find(key); if (j < 0) return NULL; // no hay clave // comprobamos el separador string line = m_items.At(j); j = StringFind(line, "="); if (j < 0) { // no = PrintFormat("%s / ERROR: línea no válida %s", __FUNCTION__, line); return NULL; } // retornamos el valor return UConvert::Trim(StringSubstr(line, j + 1)); } // ----------------------------------------------------------------------------- // Encontrar el valor del parámetro según la clave | // ----------------------------------------------------------------------------- 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; } // ----------------------------------------------------------------------------- // Cargar 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); } // ----------------------------------------------------------------------------- // Cargar 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 el texto en líneas string line, lines[]; int numLines = StringSplit(text, DLM_LINE, lines); for (int j = 0; j < numLines; j++) { line = lines[j]; // eliminamos los comentarios int k = StringFind(line, "#"); if (k == 0) continue; // la línea completa es un comentario if (k > 0) line = StringSubstr(line, 0, k); // añadimos una línea no vacía if (line != "") m_items.Add(line); } } // ----------------------------------------------------------------------------- // Guardar parámetros | // ----------------------------------------------------------------------------- void Params::Save() { string text = ""; for (int j = 0; j < m_items.Total(); j++) { text += m_items.At(j) + "\n"; } // reescribir archivo existente UFile::SaveText(text, m_path, true); } // ----------------------------------------------------------------------------- // Enviar el comando al experto | // ----------------------------------------------------------------------------- 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); } // ----------------------------------------------------------------------------- // Recibir un comando del 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; } // ----------------------------------------------------------------------------- // |Volcar 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 los fanáticos de MQL5: al cambiar el tipo m_items a CHashMap, el código de las funciones Add, GetValue, Find se reducirá significativamente, pero la clase Params también se utiliza en MQL4. Además, la velocidad de acceso a los parámetros no resulta importante en este caso, ya que los parámetros se leen una vez para inicializar las variables locales. ¿Por qué no hemos rehecho la clase según CHashMap para MQL5? Probablemente porque el propio autor trabajó durante mucho tiempo en un banco. Los desarrolladores de software financiero tienen un principio muy importante: ¡Si funciona, no lo toques! ;-)
/es/articles/9327#topic10
La unidad de intercambio entre diferentes sistemas es de hecho un archivo json. Antes era un archivo xml. Principales ventajas de los archivos json:
- Son fáciles de crear (dar forma/formatear)
- Ofrecen un excelente soporte en todos los lenguajes de alto nivel
- Su legibilidad
Por ejemplo, tenemos la clase Bar con los campos m_time, m_open, m_high, m_low, m_close, m_body. Donde m_body es el color de la vela: blanco, negro o doji. La clase Bar tiene un método ToJson(), que genera una línea 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}"; }
Podemos utilizar StringFormat, pero tendremos dificultades para reorganizar o eliminar los valores. Es posible quitar el formateo “\n\t”, ya que existen bastantes servicios de formato json en línea. Uno de ellos es JSON Parser. Basta con depurar una vez la obtención de un json válido y usar la función bar.ToJson() sin dudarlo.
Un programa externo, por ejemplo en C#, puede convertir un archivo json de cualquier complejidad en un objeto. ¿Y cómo transferimos un archivo json desde MQL? Es muy simple. Pasamos (guardamos) el archivo json, por ejemplo, al directorio del terminal Files/Json. El programa externo supervisa este directorio en busca de nuevos archivos. Tras encontrar el archivo, lo lee, lo convierte en un objeto e inmediatamente lo borra (para que no ocupe lugar) o lo mueve al archivo (para las estadísticas).
Recepción de los parámetros desde programas externos
Conectar una biblioteca json (Dios nos libre de inventar de nuevo esa rueda) a nuestros programas MQL es una molestia adicional. Resulta más simple transferir archivos de texto con líneas Key=Value. Para procesar los archivos, podemos usar la clase Params (mire más arriba). El experto y el indicador son los candidatos para recibir los parámetros de los programas o scripts externos. Por ejemplo, en el manejador OnTick, debemos llamar a la función CheckExternalCommand(), que comprobará la presencia de archivos en el directorio Files/ExtCmd. Si se encuentra un archivo, este será leído, procesado (se aceptarán los parámetros) y eliminado.
Por consiguiente, hemos analizado las formas de obtención y transmisión de parámetros entre MQL y los programas externos. Ahora, toca pensar sobre lo siguiente: ¿Por qué necesitamos DLL para los programas MQL?. Además, el Mercado MQL no acepta este tipo de programas. Solo hay una razón: la seguridad, ya que desde una DLL se puede entrar en cualquier parte.
Transmisión de los parámetros a un smartphone
Echemos un vistazo a la aplicación de Android WirePusher. El servicio de estos desarrolladores (gratis y sin anuncios) es para quitarse el sombrero. Dudamos que haya algo similar en iPhone. Los fanáticos de iPhone pueden escribir en los comentarios al artículo.
Para trabajar con el servicio, primero debemos:
- Instalar WirePusher en el smartphone
- Iniciar la aplicacion. En la pantalla de inicio veremos nuestra id.
- Añadir https://wirepusher.com a Terminal/Servicio/Ajustes/Asesores Expertos/Permitir WebRequest
A continuación, ejecutamos el script, reemplazando previamente los asteriscos en id = "********" con nuestra id.
void OnStart() { string id = "**********"; // id de su smartphone en WirePusher WirePusher("Beneficio $1000", "Transacción", "Cerrada", id); } // ------------------------------------------------------------------------------------------------ // Enviar notificación al smartphone a través del servicio web WirePusher // Añadir https://wirepusher.com en Terminal/Servicio/Ajustes/Asesor/Permitir WebRequest // message - texto de la notificación // title - encabezado de la notificación (Ejemplo, Atención / Señal / Transacción) // type - tipo de notificación (ejemplo, Activación de orden pendiente / Nivel roto / Cerrada) // id - número único de smartphone en la aplicación de android WirePusher // ------------------------------------------------------------------------------------------------ bool WirePusher(string message, string title, string type, string id) { char data[]; // matriz de datos del cuerpo del mensaje HTTP char result[]; // matriz con los datos de la respuesta del servicio web string answer; // encabezado de la respuesta del servicio 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; }
En Cayman Expert Advisor, la función WirePusher se llama en AnalyzerTrade cuando:
- Se activa una orden pendiente
- El precio rompe a través de un nivel comercial
- Se cierra una transacción
En la aplicación WirePusher, podemos adjuntar a cada tipo de notificación nuestro propio tipo de sonido. Antes teníamos el sonido "ta-da" para el cierre de transacciones con beneficios, y una "explosión" para las transacciones con pérdidas. Pero uno terminaba cansado de tanta explosión ;-)
Conclusión
Los archivos de texto son el lugar más fiable y adecuado para almacenar los parámetros. Tanto más que las operaciones de archivos en cualquier SO (aplicación) se procesan/almacenan en la caché bastante bien.
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/9327





- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso