English Deutsch 日本語 Português
preview
Creación de un modelo de restricción de tendencia de velas (Parte 5): Sistema de notificaciones (Parte III)

Creación de un modelo de restricción de tendencia de velas (Parte 5): Sistema de notificaciones (Parte III)

MetaTrader 5Sistemas comerciales | 6 noviembre 2024, 09:50
220 0
Clemence Benjamin
Clemence Benjamin

Contenido:


Introducción

Ahora hemos ampliado la accesibilidad de la señal en nuestro modelo, lo que lo hace beneficioso para todos los usuarios. Además, hemos motivado a numerosos desarrolladores futuros sobre cómo integrar sin problemas las redes sociales para obtener señales en nuestra conocida plataforma comercial MetaTrader 5. Concluyamos profundizando en los detalles de la integración de WhatsApp. Nuestro objetivo es enviar automáticamente señales generadas por nuestros indicadores MetaTrader 5 personalizados a un número o grupo de WhatsApp. Meta ha introducido una nueva función de canal en WhatsApp, que puede ayudar a que las señales lleguen a una audiencia más amplia. Como afirma Porter Gale en su libro, «Your Network Is Your Net Worth», es crucial formar parte de una comunidad para prosperar. Gracias a avances como estos, podemos influir en una gran comunidad, especialmente en plataformas populares como Telegram y WhatsApp.

He decidido incluir un diagrama de flujo que describe el proceso de integración de cualquier plataforma de medios sociales con MetaTrader 5. Este es un desarrollo resumido paso a paso basado en mi investigación. Las etapas críticas están claramente delineadas en el diagrama de flujo. Permítame explicar brevemente algunas de las etapas involucradas. El diagrama de flujo narra las experiencias encontradas durante el desarrollo. Es importante estar preparado para rehacer ciertos procesos ya que pueden ocurrir errores. En este artículo me centraré únicamente en lo que he verificado que es efectivo, aunque se realizó una cantidad considerable de depuración durante el proceso de escritura. Estas son las etapas por las que pasamos en la (Parte II) al integrar Telegram con el indicador Trend Constraint para las notificaciones.

Diagrama de flujo de integración de plataformas sociales con Meta Trader 5


Diagrama de flujo de integración de plataformas sociales con Meta Trader 5

El diagrama de flujo simplifica cómo entendemos todo el proceso de integración de la etapa final, la etapa de decisión, que tiene dos resultados posibles: Sí o No.

¿La prueba tuvo éxito?

En caso afirmativo:
  • Implementar integración
  • Monitorear y mantener (Todo está funcionando bien y podemos comenzar a usar nuestro nuevo sistema según el objetivo original)
  • Fin (El final de una integración exitosa)
Si no:
  • Depurar y solucionar problemas (Volver a configurar el entorno de desarrollo; la idea es principalmente volver a recorrer todos los pasos para verificar si hay errores, corregirlos y volver a realizar la prueba)
  • Explorar otras opciones (Volver a probar otras plataformas sociales, por lo tanto, las etapas anteriores si no hay una API disponible)

Configuración de una API de mensajería

Basándonos en el diagrama de flujo proporcionado, hemos decidido avanzar hacia la integración de WhatsApp, como hicimos para Telegram en el artículo anterior. Ambas plataformas ofrecen APIs que se alinean con nuestros objetivos. La decisión de optar por WhatsApp se debe principalmente a su amplia base de usuarios. Personalmente, uso WhatsApp con más frecuencia que cualquier otra plataforma social, y recibir señales a través de esta plataforma me ayudará a monitorear eficazmente mi negocio en MetaTrader 5.

Según, whatsthebigdata.com, en julio de 2023, WhatsApp tenía alrededor de 2.780 millones de usuarios activos únicos en todo el mundo. Esta popular aplicación de mensajería alcanzó los 2.000 millones de usuarios en 2020 y se espera que llegue a los 3.140 millones en 2025.

Los valores estadísticos deben tenerse en cuenta a la hora de seleccionar plataformas que nos ayuden a aumentar nuestro patrimonio neto a través de estas redes. En Internet se pueden encontrar numerosos proveedores de APIs de mensajería, con distintos niveles de facilidad de uso y facilidad de utilización. Muchos ofrecen la posibilidad de probarlos, pero requieren suscripciones para acceder a funciones avanzadas. Según mis investigaciones, algunas de las opciones disponibles en Internet son:

He utilizado Twilio para este proyecto porque ofrece acceso a la API de una manera más fácil de usar y es adecuado para la integración de WhatsApp. Las opciones ofrecidas son eficaces, aunque el proceso de configuración puede ser complejo y se requieren suscripciones para acceder a otras funciones clave. Sin embargo, vale la pena explorarlos para ampliar la comprensión.

Creé mi cuenta Twilio y configuré todo. Los elementos clave requeridos desde el sitio son account_sid, auth_token, el número de Whatsapp asignado en el sandbox de Twilio, y tu número receptor propio para iniciar conversaciones.Los elementos clave requeridos desde el sitio son account_sid, auth_token, el número de Whatsapp asignado en el sandbox de Twilio, y tu número receptor propio para iniciar conversaciones. Todas las credenciales necesarias para el script se encuentran en la página de tu cuenta Twilio. 

Twilio API

Navegando a la sección de consola de tu cuenta Twilio, podrás localizar dónde se envían los mensajes de WhatsApp. Allí, podrá ver su número de sandbox, que se guardará en el lado de la API para enviar las notificaciones entrantes de MetaTrader 5 a sus números de WhatsApp unidos. Es desafortunado que solo se envíe a los participantes unidos. Vea la captura de pantalla a continuación que muestra el mensaje de unión que debe enviar al número de sandbox para unirse. Comience guardando el número de sandbox en su lista de contactos, luego búsquelo y envíe "join so-cave". Automáticamente, una vez hecho esto, puede ejecutar el script de integración con mensajes de prueba en el símbolo del sistema y ver cómo funciona.

Únete al sandbox de Twilio

Aquí hay una captura de pantalla de mí unirse:

Unir número de whatsapp para Twilio

Llegados a este punto, hemos obtenido las credenciales de la API necesarias para el proceso de integración, tal y como se indica en el diagrama de flujo. Pasemos ahora a la siguiente etapa, donde profundizaremos en la aplicación de estas credenciales.

El script (send_whatsapp_message.py)

Podemos utilizar IDLE para el "scripting". Viene preinstalado con el intérprete de Python. Como alternativa, me inclino por opciones de código abierto como Notepad++ para crear scripts. Notepad++ admite varios idiomas con el formato adecuado. Comienza creando un nuevo archivo, llámalo send_whatsapp_message.py. Es aconsejable guardar el archivo de script en la carpeta de scripts junto con el intérprete de Python. No encontré ningún problema al probar el programa desde ese directorio. 

Empezaremos importando el módulo en nuestro script:

import sys
import requests

  • import sys: Este módulo proporciona acceso a algunas variables utilizadas o mantenidas por el intérprete de Python. Permite interactuar con el intérprete.
  • import requests: Este módulo se utiliza para enviar peticiones HTTP. Simplifica el proceso de solicitud de la API.

En la siguiente etapa se definen las funciones:

def send_whatsapp_message(account_sid, auth_token, from_whatsapp_number, to_whatsapp_number, message):
    url = f"https://api.twilio.com/2010-04-01/Accounts/{account_sid}/Messages.json"
    
    data = {
        "From": f"whatsapp:{from_whatsapp_number}",
        "To": f"whatsapp:{to_whatsapp_number}",
        "Body": message,
    }
    
    response = requests.post(url, data=data, auth=(account_sid, auth_token))
    
    if response.status_code == 201:
        print("Message sent successfully")
    else:
        print(f"Failed to send message: {response.status_code} - {response.text}")

send_whatsapp_message: Esta función envía un mensaje de WhatsApp utilizando la API de Twilio. 

En la definición de la función, tenemos varios parámetros necesarios para que se ejecute el script. Muchos de estos se adquirieron al configurar la cuenta Twilio. 

Parámetros:

  • account_sid: SID de la cuenta de su cuenta Twilio.
  • auth_token: Token de autenticación de su cuenta Twilio.
  • from_whatsapp_number: Número de Twilio habilitado para WhatsApp, formateado con el prefijo "whatsapp:".
  • to_whatsapp_number: Número de WhatsApp del destinatario, formateado con el prefijo "whatsapp:".
  • message: Texto del mensaje que se enviará.

url: Construye la URL para el punto final de la API de Twilio. data: Diccionario que contiene los datos del mensaje que se enviarán en la solicitud POST.

requests.post: Envía una solicitud POST a la API de Twilio con los datos del mensaje y las credenciales de autenticación.
response: Captura la respuesta de la API de Twilio.
status_code: Comprueba el código de estado de la respuesta para determinar si el mensaje se ha enviado correctamente (201 indica éxito).

El fragmento final del script forma el código principal y es donde tendrá lugar la ejecución. Cuando se ejecuta este script, hará lo siguiente:

  1. Lea el texto del mensaje desde los argumentos de la línea de comandos.
  2. Llame a la función send_whatsapp_message con las credenciales, los números de teléfono y el mensaje proporcionados.
  3. Imprima un mensaje de éxito o fracaso según la respuesta de la API de Twilio.
# Replace with your actual Twilio credentials and phone numbers
account_sid = "**********************" # Your SID
auth_token = "***************************" # Your Auth Token
from_whatsapp_number = "+14**********" # Your WhatsApp-enabled Twilio number
to_whatsapp_number = "+************" # Recipient's WhatsApp number
message = sys.argv[1]

send_whatsapp_message(account_sid, auth_token, from_whatsapp_number, to_whatsapp_number, message)

Juntando todo esto, nuestro archivo final send_whatsapp_message.py quedará así:

import sys
import requests

def send_whatsapp_message(account_sid, auth_token, from_whatsapp_number, to_whatsapp_number, message):
    url = f"https://api.twilio.com/2010-04-01/Accounts/{account_sid}/Messages.json"
    
    data = {
        "From": f"whatsapp:{from_whatsapp_number}",
        "To": f"whatsapp:{to_whatsapp_number}",
        "Body": message,
    }
    
    response = requests.post(url, data=data, auth=(account_sid, auth_token))
    
    if response.status_code == 201:
        print("Message sent successfully")
    else:
        print(f"Failed to send message: {response.status_code} - {response.text}")

# Replace with your actual Twilio credentials and phone numbers
account_sid = "****************"
auth_token = "**********************"
from_whatsapp_number = "+14***********" 
to_whatsapp_number = "+***************" 
message = sys.argv[1]

send_whatsapp_message(account_sid, auth_token, from_whatsapp_number, to_whatsapp_number, message)
 

 Podemos continuar probando en el símbolo del sistema para ver si el script está funcionando. Abra la ruta donde está almacenado el archivo send_whatsapp_message.py y escriba el siguiente comando:

python send_whatsapp_message.py "Test Message"

Si todo es correcto, el mensaje se enviará a tu número de WhatsApp y aparecerá el mensaje «Mensaje enviado correctamente» en la ventana del símbolo del sistema. Si encuentra errores, debe volver a comprobar su código y sus credenciales, especialmente porque el código proporcionado ha sido depurado y corregido para que funcione correctamente. Voy a compartir una captura de pantalla de mi integración de trabajo en algunas secciones a continuación.


Modificación del indicador de restricción de tendencia para las notificaciones de WhatsApp

Para completar la integración sin alterar significativamente el código del indicador, decidí concentrarme en ajustar el bloque myAlert para que admita mensajes de Whatsapp. Es crucial prestar mucha atención a la hora de introducir con precisión la ruta a tu intérprete de python y al script. Estos son los componentes clave de la integración al pasar de este fragmento de código. He mantenido sin cambios la mayor parte del código de nuestra anterior integración con Telegram. Además, incluí una función de inicialización de prueba que permite enviar un mensaje de bienvenida al arrancar para verificar la funcionalidad del sistema antes de recibir señales reales.

int OnInit()
{
// Send test message on launch
   myAlert("info", "Thank you for subscribing. You shall be receiving Trend Constraint signal alerts via Whatsapp.");
  
return(INIT_SUCCEEDED);
}

Incluido en el OnInit() asegurará que el mensaje será enviado a sus números cuando añada el indicador a cualquier chat.

A continuación se muestra la parte más crucial de nuestra lógica de integración para enviar mensajes a WhatsApp. He resaltado las secciones del código que he modificado en comparación con el código utilizado para la integración de Telegram.

//--- ShellExecuteW declaration ----------------------------------------------
#import "shell32.dll"
int ShellExecuteW(int hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd);
#import

//--- global variables ------------------------------------------------------
datetime last_alert_time;
input int alert_cooldown_seconds = 60; // Cooldown period in seconds


//--- myAlert function ------------------------------------------------------
void myAlert(string type, string message) {
    datetime current_time = TimeCurrent();
    if (current_time - last_alert_time < alert_cooldown_seconds) {
        // Skip alert if within cooldown period
        return;
    }

    last_alert_time = current_time;
    string full_message = type + " | Trend Constraint V1.06 @ " + Symbol() + "," + IntegerToString(Period()) + " | " + message;
    if (type == "print") {
        Print(message);
    } else if (type == "error") {
        Print(type + " | Trend Constraint V1.06 @ " + Symbol() + "," + IntegerToString(Period()) + " | " + message);
    } else if (type == "order") {
        // Add order alert handling if needed
    } else if (type == "modify") {
        // Add modify alert handling if needed
    } else if (type == "indicator" || type == "info") {
        if (Audible_Alerts) {
            Alert(full_message);
        }
        if (Push_Notifications) {
            SendNotification(full_message);
        }

        // Send to WhatsApp //Edit to match your actual path, these I gave as an example for my computer
        string python_path = "C:\\Users\\******\\AppData\\Local\\Programs\\Python\\Python312\\python.exe";
        string script_path = "C:\\Users\\******\\AppData\\Local\\Programs\\Python\\Python312\\Scripts\\send_whatsapp_message.py";
        string command = python_path + " \"" + script_path + "\" \"" + full_message + "\"";
        
        // Debugging: Print the command being executed
        Print("Executing command to send WhatsApp message: ", command);

        // Use cmd.exe to execute the command and then wait for 5 seconds
        string final_command = "/c " + command + " && timeout 5";
        int result = ShellExecuteW(0, "open", "cmd.exe", final_command, NULL, 1);
        if (result <= 32) {
            int error_code = GetLastError();
            Print("Failed to execute Python script. Error code: ", error_code);
        } else {
            Print("Successfully executed Python script. Result code: ", result);
        }
    }
}

Aquí está el programa principal después de los ajustes para darnos Trend Constraint V1.06:

//+------------------------------------------------------------------+
//|                                       Trend Constraint V1.06.mq5 |
//|                                Copyright 2024, Clemence Benjamin |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Clemence Benjamin"
#property link      "https://www.mql5.com"
#property version   "1.06"
#property description "A model that seeks to produce sell signals when D1 candle is Bearish only and buy signals when it is Bullish"


//--- indicator settings
#property indicator_chart_window
#property indicator_buffers 6
#property indicator_plots 6

#property indicator_type1 DRAW_ARROW
#property indicator_width1 5
#property indicator_color1 0xFF3C00
#property indicator_label1 "Buy"

#property indicator_type2 DRAW_ARROW
#property indicator_width2 5
#property indicator_color2 0x0000FF
#property indicator_label2 "Sell"

#property indicator_type3 DRAW_ARROW
#property indicator_width3 2
#property indicator_color3 0xE8351A
#property indicator_label3 "Buy Reversal"

#property indicator_type4 DRAW_ARROW
#property indicator_width4 2
#property indicator_color4 0x1A1AE8
#property indicator_label4 "Sell Reversal"

#property indicator_type5 DRAW_LINE
#property indicator_style5 STYLE_SOLID
#property indicator_width5 2
#property indicator_color5 0xFFAA00
#property indicator_label5 "Buy"

#property indicator_type6 DRAW_LINE
#property indicator_style6 STYLE_SOLID
#property indicator_width6 2
#property indicator_color6 0x0000FF
#property indicator_label6 "Sell"

#define PLOT_MAXIMUM_BARS_BACK 5000
#define OMIT_OLDEST_BARS 50

//--- indicator buffers
double Buffer1[];
double Buffer2[];
double Buffer3[];
double Buffer4[];
double Buffer5[];
double Buffer6[];

input double Oversold = 30;
input double Overbought = 70;
input int Slow_MA_period = 200;
input int Fast_MA_period = 100;
datetime time_alert; //used when sending alert
input bool Audible_Alerts = true;
input bool Push_Notifications = true;
double myPoint; //initialized in OnInit
int RSI_handle;
double RSI[];
double Open[];
double Close[];
int MA_handle;
double MA[];
int MA_handle2;
double MA2[];
int MA_handle3;
double MA3[];
int MA_handle4;
double MA4[];
double Low[];
double High[];
int MA_handle5;
double MA5[];
int MA_handle6;
double MA6[];
int MA_handle7;
double MA7[];

//--- ShellExecuteW declaration ----------------------------------------------
#import "shell32.dll"
int ShellExecuteW(int hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd);
#import

//--- global variables ------------------------------------------------------
datetime last_alert_time;
input int alert_cooldown_seconds = 60; // Cooldown period in seconds


//--- myAlert function ------------------------------------------------------
void myAlert(string type, string message) {
    datetime current_time = TimeCurrent();
    if (current_time - last_alert_time < alert_cooldown_seconds) {
        // Skip alert if within cooldown period
        return;
    }

    last_alert_time = current_time;
    string full_message = type + " | Trend Constraint V1.06 @ " + Symbol() + "," + IntegerToString(Period()) + " | " + message;
    if (type == "print") {
        Print(message);
    } else if (type == "error") {
        Print(type + " | Trend Constraint V1.06 @ " + Symbol() + "," + IntegerToString(Period()) + " | " + message);
    } else if (type == "order") {
        // Add order alert handling if needed
    } else if (type == "modify") {
        // Add modify alert handling if needed
    } else if (type == "indicator" || type == "info") {
        if (Audible_Alerts) {
            Alert(full_message);
        }
        if (Push_Notifications) {
            SendNotification(full_message);
        }

        // Send to WhatsApp
        string python_path = "C:\\Users\\****\\AppData\\Local\\Programs\\Python\\Python312\\python.exe";
        string script_path = "C:\\Users\\****\\AppData\\Local\\Programs\\Python\\Python312\\Scripts\\send_whatsapp_message.py";
        string command = python_path + " \"" + script_path + "\" \"" + full_message + "\"";
        
        // Debugging: Print the command being executed
        Print("Executing command to send WhatsApp message: ", command);

        // Use cmd.exe to execute the command and then wait for 5 seconds
        string final_command = "/c " + command + " && timeout 5";
        int result = ShellExecuteW(0, "open", "cmd.exe", final_command, NULL, 1);
        if (result <= 32) {
            int error_code = GetLastError();
            Print("Failed to execute Python script. Error code: ", error_code);
        } else {
            Print("Successfully executed Python script. Result code: ", result);
        }
    }
}


//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {   
   SetIndexBuffer(0, Buffer1);
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1));
   PlotIndexSetInteger(0, PLOT_ARROW, 241);
   SetIndexBuffer(1, Buffer2);
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1));
   PlotIndexSetInteger(1, PLOT_ARROW, 242);
   SetIndexBuffer(2, Buffer3);
   PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1));
   PlotIndexSetInteger(2, PLOT_ARROW, 236);
   SetIndexBuffer(3, Buffer4);
   PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetInteger(3, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1));
   PlotIndexSetInteger(3, PLOT_ARROW, 238);
   SetIndexBuffer(4, Buffer5);
   PlotIndexSetDouble(4, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetInteger(4, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1));
   SetIndexBuffer(5, Buffer6);
   PlotIndexSetDouble(5, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetInteger(5, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1));
   // Send test message on launch
   myAlert("info", "Thank you for subscribing. You shall be receiving Trend Constraint signal alerts via Whatsapp.");
   //initialize myPoint
   myPoint = Point();
   if(Digits() == 5 || Digits() == 3)
     {
      myPoint *= 10;
     }
   RSI_handle = iRSI(NULL, PERIOD_CURRENT, 14, PRICE_CLOSE);
   if(RSI_handle < 0)
     {
      Print("The creation of iRSI has failed: RSI_handle=", INVALID_HANDLE);
      Print("Runtime error = ", GetLastError());
      return(INIT_FAILED);
     }
   
   MA_handle = iMA(NULL, PERIOD_CURRENT, 7, 0, MODE_SMMA, PRICE_CLOSE);
   if(MA_handle < 0)
     {
      Print("The creation of iMA has failed: MA_handle=", INVALID_HANDLE);
      Print("Runtime error = ", GetLastError());
      return(INIT_FAILED);
     }
   
   MA_handle2 = iMA(NULL, PERIOD_CURRENT, 400, 0, MODE_SMA, PRICE_CLOSE);
   if(MA_handle2 < 0)
     {
      Print("The creation of iMA has failed: MA_handle2=", INVALID_HANDLE);
      Print("Runtime error = ", GetLastError());
      return(INIT_FAILED);
     }
   
   MA_handle3 = iMA(NULL, PERIOD_CURRENT, 100, 0, MODE_EMA, PRICE_CLOSE);
   if(MA_handle3 < 0)
     {
      Print("The creation of iMA has failed: MA_handle3=", INVALID_HANDLE);
      Print("Runtime error = ", GetLastError());
      return(INIT_FAILED);
     }
   
   MA_handle4 = iMA(NULL, PERIOD_CURRENT, 200, 0, MODE_SMA, PRICE_CLOSE);
   if(MA_handle4 < 0)
     {
      Print("The creation of iMA has failed: MA_handle4=", INVALID_HANDLE);
      Print("Runtime error = ", GetLastError());
      return(INIT_FAILED);
     }
   
   MA_handle5 = iMA(NULL, PERIOD_CURRENT, Fast_MA_period, 0, MODE_SMA, PRICE_CLOSE);
   if(MA_handle5 < 0)
     {
      Print("The creation of iMA has failed: MA_handle5=", INVALID_HANDLE);
      Print("Runtime error = ", GetLastError());
      return(INIT_FAILED);
     }
   
   MA_handle6 = iMA(NULL, PERIOD_CURRENT, Slow_MA_period, 0, MODE_SMA, PRICE_CLOSE);
   if(MA_handle6 < 0)
     {
      Print("The creation of iMA has failed: MA_handle6=", INVALID_HANDLE);
      Print("Runtime error = ", GetLastError());
      return(INIT_FAILED);
     }
   
   MA_handle7 = iMA(NULL, PERIOD_CURRENT, 200, 0, MODE_EMA, PRICE_CLOSE);
   if(MA_handle7 < 0)
     {
      Print("The creation of iMA has failed: MA_handle7=", INVALID_HANDLE);
      Print("Runtime error = ", GetLastError());
      return(INIT_FAILED);
     }
   
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime& time[],
                const double& open[],
                const double& high[],
                const double& low[],
                const double& close[],
                const long& tick_volume[],
                const long& volume[],
                const int& spread[])
  {
   int limit = rates_total - prev_calculated;
   //--- counting from 0 to rates_total
   ArraySetAsSeries(Buffer1, true);
   ArraySetAsSeries(Buffer2, true);
   ArraySetAsSeries(Buffer3, true);
   ArraySetAsSeries(Buffer4, true);
   ArraySetAsSeries(Buffer5, true);
   ArraySetAsSeries(Buffer6, true);
   //--- initial zero
   if(prev_calculated < 1)
     {
      ArrayInitialize(Buffer1, EMPTY_VALUE);
      ArrayInitialize(Buffer2, EMPTY_VALUE);
      ArrayInitialize(Buffer3, EMPTY_VALUE);
      ArrayInitialize(Buffer4, EMPTY_VALUE);
      ArrayInitialize(Buffer5, EMPTY_VALUE);
      ArrayInitialize(Buffer6, EMPTY_VALUE);
     }
   else
      limit++;
   datetime Time[];
   
   datetime TimeShift[];
   if(CopyTime(Symbol(), PERIOD_CURRENT, 0, rates_total, TimeShift) <= 0) return(rates_total);
   ArraySetAsSeries(TimeShift, true);
   int barshift_M1[];
   ArrayResize(barshift_M1, rates_total);
   int barshift_D1[];
   ArrayResize(barshift_D1, rates_total);
   for(int i = 0; i < rates_total; i++)
     {
      barshift_M1[i] = iBarShift(Symbol(), PERIOD_M1, TimeShift[i]);
      barshift_D1[i] = iBarShift(Symbol(), PERIOD_D1, TimeShift[i]);
   }
   if(BarsCalculated(RSI_handle) <= 0) 
      return(0);
   if(CopyBuffer(RSI_handle, 0, 0, rates_total, RSI) <= 0) return(rates_total);
   ArraySetAsSeries(RSI, true);
   if(CopyOpen(Symbol(), PERIOD_M1, 0, rates_total, Open) <= 0) return(rates_total);
   ArraySetAsSeries(Open, true);
   if(CopyClose(Symbol(), PERIOD_D1, 0, rates_total, Close) <= 0) return(rates_total);
   ArraySetAsSeries(Close, true);
   if(BarsCalculated(MA_handle) <= 0) 
      return(0);
   if(CopyBuffer(MA_handle, 0, 0, rates_total, MA) <= 0) return(rates_total);
   ArraySetAsSeries(MA, true);
   if(BarsCalculated(MA_handle2) <= 0) 
      return(0);
   if(CopyBuffer(MA_handle2, 0, 0, rates_total, MA2) <= 0) return(rates_total);
   ArraySetAsSeries(MA2, true);
   if(BarsCalculated(MA_handle3) <= 0) 
      return(0);
   if(CopyBuffer(MA_handle3, 0, 0, rates_total, MA3) <= 0) return(rates_total);
   ArraySetAsSeries(MA3, true);
   if(BarsCalculated(MA_handle4) <= 0) 
      return(0);
   if(CopyBuffer(MA_handle4, 0, 0, rates_total, MA4) <= 0) return(rates_total);
   ArraySetAsSeries(MA4, true);
   if(CopyLow(Symbol(), PERIOD_CURRENT, 0, rates_total, Low) <= 0) return(rates_total);
   ArraySetAsSeries(Low, true);
   if(CopyHigh(Symbol(), PERIOD_CURRENT, 0, rates_total, High) <= 0) return(rates_total);
   ArraySetAsSeries(High, true);
   if(BarsCalculated(MA_handle5) <= 0) 
      return(0);
   if(CopyBuffer(MA_handle5, 0, 0, rates_total, MA5) <= 0) return(rates_total);
   ArraySetAsSeries(MA5, true);
   if(BarsCalculated(MA_handle6) <= 0) 
      return(0);
   if(CopyBuffer(MA_handle6, 0, 0, rates_total, MA6) <= 0) return(rates_total);
   ArraySetAsSeries(MA6, true);
   if(BarsCalculated(MA_handle7) <= 0) 
      return(0);
   if(CopyBuffer(MA_handle7, 0, 0, rates_total, MA7) <= 0) return(rates_total);
   ArraySetAsSeries(MA7, true);
   if(CopyTime(Symbol(), Period(), 0, rates_total, Time) <= 0) return(rates_total);
   ArraySetAsSeries(Time, true);
   //--- main loop
   for(int i = limit-1; i >= 0; i--)
     {
      if (i >= MathMin(PLOT_MAXIMUM_BARS_BACK-1, rates_total-1-OMIT_OLDEST_BARS)) continue; //omit some old rates to prevent "Array out of range" or slow calculation   
      
      if(barshift_M1[i] < 0 || barshift_M1[i] >= rates_total) continue;
      if(barshift_D1[i] < 0 || barshift_D1[i] >= rates_total) continue;
      
      //Indicator Buffer 1
      if(RSI[i] < Oversold
      && RSI[i+1] > Oversold //Relative Strength Index crosses below fixed value
      && Open[barshift_M1[i]] >= Close[1+barshift_D1[i]] //Candlestick Open >= Candlestick Close
      && MA[i] > MA2[i] //Moving Average > Moving Average
      && MA3[i] > MA4[i] //Moving Average > Moving Average
      )
        {
         Buffer1[i] = Low[1+i]; //Set indicator value at Candlestick Low
         if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Buy"); //Alert on next bar open
         time_alert = Time[1];
        }
      else
        {
         Buffer1[i] = EMPTY_VALUE;
        }
      //Indicator Buffer 2
      if(RSI[i] > Overbought
      && RSI[i+1] < Overbought //Relative Strength Index crosses above fixed value
      && Open[barshift_M1[i]] <= Close[1+barshift_D1[i]] //Candlestick Open <= Candlestick Close
      && MA[i] < MA2[i] //Moving Average < Moving Average
      && MA3[i] < MA4[i] //Moving Average < Moving Average
      )
        {
         Buffer2[i] = High[1+i]; //Set indicator value at Candlestick High
         if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Sell"); //Alert on next bar open
         time_alert = Time[1];
        }
      else
        {
         Buffer2[i] = EMPTY_VALUE;
        }
      //Indicator Buffer 3
      if(MA5[i] > MA6[i]
      && MA5[i+1] < MA6[i+1] //Moving Average crosses above Moving Average
      )
        {
         Buffer3[i] = Low[i]; //Set indicator value at Candlestick Low
         if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Buy Reversal"); //Alert on next bar open
         time_alert = Time[1];
        }
      else
        {
         Buffer3[i] = EMPTY_VALUE;
        }
      //Indicator Buffer 4
      if(MA5[i] < MA6[i]
      && MA5[i+1] > MA6[i+1] //Moving Average crosses below Moving Average
      )
        {
         Buffer4[i] = High[i]; //Set indicator value at Candlestick High
         if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Sell Reversal"); //Alert on next bar open
         time_alert = Time[1];
        }
      else
        {
         Buffer4[i] = EMPTY_VALUE;
        }
      //Indicator Buffer 5, Alert muted by turning it into a comment
      if(MA3[i] > MA7[i] //Moving Average > Moving Average
      )
        {
         Buffer5[i] = MA3[i]; //Set indicator value at Moving Average
         //if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Buy"); //Alert on next bar open
         //time_alert = Time[1];
        }
      else
        {
         Buffer5[i] = EMPTY_VALUE;
        }
      //Indicator Buffer 6, Alert muted by turning it into a comment
      if(MA3[i] < MA7[i] //Moving Average < Moving Average
      )
        {
         Buffer6[i] = MA3[i]; //Set indicator value at Moving Average
         //if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Sell"); //Alert on next bar open
         //time_alert = Time[1];
        }
      else
        {
         Buffer6[i] = EMPTY_VALUE;
        }
     }
   return(rates_total);
  }
//+------------------------------------------------------------------+


Pruebas y resultados

Hemos realizado pruebas en dos de las subsecciones anteriores. Nuestro proyecto está funcionando correctamente desde mi punto de vista. He incluido algunas capturas de pantalla de las señales recibidas a continuación.

Una integración de WhatsApp que funciona

El indicador está funcionando bien, dibujando según el objetivo y enviando señales a mis canales sociales según lo diseñado. Se adjunta una imagen del gráfico del índice Boom 500 a continuación:

Índice Boom 500

Nuestro programa abre una ventana de símbolo del sistema al iniciarse para mostrar los procesos en curso y envía una nota de apertura. Sigue el mismo procedimiento al transmitir señales a lo largo del tiempo. Sin embargo, la ventana emergente puede resultar molesta cuando se trabaja en tareas, por lo que el programa puede codificarse para que funcione sin mostrar la ventana del Símbolo del sistema. En este proyecto optamos por no ocultar la ventana para observar el proceso en curso. Para evitar que aparezca la ventana del Símbolo del sistema, puede incluir la siguiente línea de código.

// execute the command without showing the window
string final_command = "/c " + command + " && timeout 5";
int result = ShellExecuteW(0, "open", "cmd.exe", final_command, NULL, 0); // Change the last parameter to 0 (SW_HIDE)
if (result <= 32) {
    int error_code = GetLastError();
    Print("Failed to execute Python script. Error code: ", error_code);
} else {
    Print("Successfully executed Python script. Result code: ", result);
}

 El último parámetro se llama nShowCmd, controla cómo se muestra la ventana de la aplicación. Cambiando los parámetros a 0 (SW_HIDE) en lugar de 1 (SW_SHOWNORMAL).


Seguridad

La técnica utilizada implica archivos DLL que se consideran una amenaza potencial. A través de mi investigación, he identificado a continuación los riesgos asociados con fines de concienciación. Asegúrate de salvaguardar tus credenciales para las API, ya que yo me he esforzado por ocultar las mías en algún punto de este artículo. Divúlguelos sólo a entidades de confianza.

Algunos peligros asociados a los archivos DLL:

  • Malware: La carga de una DLL maliciosa puede ejecutar código dañino en su sistema. Esto supone un riesgo importante si la DLL procede de una ubicación desconocida o que no sea de confianza.
  • DLL Hijacking: Si un atacante puede colocar una DLL maliciosa en una ubicación desde la que se carga la aplicación, puede ejecutar código arbitrario. Esto es especialmente peligroso si la aplicación se ejecuta con privilegios elevados.
  • Caídas e inestabilidad: El uso incorrecto de funciones DLL o la llamada a funciones con parámetros no válidos pueden provocar caídas de la aplicación o inestabilidad del sistema.
  • Fugas de recursos: El manejo inadecuado de recursos (por ejemplo, memoria, manejadores de archivos) dentro de funciones DLL puede conducir a fugas de recursos, afectando el rendimiento del sistema con el tiempo.
  • Conflictos de versión: Diferentes versiones de una DLL pueden tener diferentes firmas de función o comportamientos, lo que lleva a problemas de compatibilidad.
  • El infierno de las dependencias: Gestionar las dependencias de múltiples DLL y sus versiones puede ser complejo y propenso a errores, lo que conduce a errores en tiempo de ejecución.

Para combatir estas amenazas, es crucial incorporar elementos de seguridad a nuestro programa. La siguiente línea de código puede resolver los problemas de seguridad si se implementa en nuestro modelo:

// You can secure the program by adding SecureExecuteCommand(command); below these lines.
string python_path = "C:\\Users\\*****\\AppData\\Local\\Programs\\Python\\Python312\\python.exe";
string script_path = "C:\\Users\\*****\\AppData\\Local\\Programs\\Python\\Python312\\Scripts\\send_whatsapp_message.py"; //***** is your computer user name, the rest of the path txt is correct based on the python version being used.
string message = "Hello, this is a test message";
string command = python_path + " \"" + script_path + "\" \"" + message + "\"";

SecureExecuteCommand(command);

La función SecureExecuteCommand() comprueba si hay caracteres no válidos en la cadena de comandos, lo que puede ayudar a evitar ataques de inyección de comandos.

Algunas de las mejores prácticas para combatir los riesgos son:

  • Utilice fuentes de confianza: Utilice únicamente DLLs de fuentes fiables y verificadas.
  • Firma de código: Garantiza que las DLLs estén firmadas por una autoridad de confianza, lo que ayuda a verificar su integridad y origen.
  • Mínimo privilegio: Ejecute las aplicaciones con el mínimo privilegio necesario para reducir el impacto de cualquier posible explotación.
  • Validar entradas: Valide y sanee siempre los parámetros de entrada, especialmente cuando construya cadenas de comandos.
  • Manejo de errores: Implementa un manejo de errores robusto para gestionar y registrar errores con gracia.
  • Actualizaciones periódicas: Mantenga su software y bibliotecas al día con parches de seguridad y actualizaciones.
  • Aislamiento: Utiliza sandboxing u otras técnicas de aislamiento para ejecutar operaciones potencialmente arriesgadas en un entorno restringido.


Conclusión

La integración de WhatsApp se ha completado con éxito. Nuestro modelo ahora puede transmitir señales desde MetaTrader 5 a Telegram y WhatsApp. Es factible consolidar la integración en un solo programa y transmitir las señales en ambas plataformas simultáneamente. He observado ligeras discrepancias en la entrega de señales, ya que las notificaciones push llegan un poco antes que las de las plataformas sociales, aunque con una diferencia de tiempo muy pequeña, quizás alrededor de 7 segundos. Las señales de Telegram superaron a las de WhatsApp por unos segundos. Sin embargo, nuestro objetivo principal era asegurar la llegada de nuestras señales, y este objetivo se ha logrado.

Espero que este artículo te haya enriquecido con algo valioso. Como desarrolladores, no debemos darnos por vencidos, sino mantener el diagrama de flujo operativo hasta que logremos nuestros objetivos. Debemos aprovechar el poder de nuestra comunidad para mejorar nuestras habilidades en la programación de comercio algorítmico. Las discusiones están siempre abiertas a todos para que las ideas puedan compartirse abiertamente. En nuestro próximo proyecto, nos basaremos en nuestra base existente para explorar más a fondo. Encuentre los archivos adjuntos y su descripción a continuación.

Adjunto Descripción
send_whatsapp_message.py Aboga por la comunicación de MetaTrader 5 a Twilio y luego a WhatsApp.
Trend Constraint V1.06.mq5 WhatsApp está integrado pero necesita que actualices las credenciales y las rutas de directorios para que funcione en tu computadora.
Secure Command.mq5  Modificado para ocultar la ventana del símbolo del sistema y proteger el programa de ataques externos.


Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/14969

Desarrollamos un asesor experto multidivisa (Parte 10): Creación de objetos a partir de una cadena Desarrollamos un asesor experto multidivisa (Parte 10): Creación de objetos a partir de una cadena
El plan de desarrollo del EA comprende varias etapas con resultados intermedios almacenados en una base de datos. Solo se pueden recuperar desde allí como cadenas o números, no como objetos. Así que necesitaremos una forma de recrear en el EA los objetos deseados a partir de las cadenas leídas de la base de datos.
El criterio de homogeneidad de Smirnov como indicador de la no estacionariedad de las series temporales El criterio de homogeneidad de Smirnov como indicador de la no estacionariedad de las series temporales
El artículo analiza uno de los criterios de homogeneidad no paramétricos más famosos: el criterio de Smirnov. Asimismo, se consideran tanto datos modelo como cotizaciones reales, y se ofrece un ejemplo de construcción de un indicador de no estacionariedad (iSmirnovDistance).
Algoritmo de evolución del caparazón de tortuga (Turtle Shell Evolution Algorithm, TSEA) Algoritmo de evolución del caparazón de tortuga (Turtle Shell Evolution Algorithm, TSEA)
Hoy hablaremos sobre un algoritmo de optimización único inspirado en la evolución del caparazón de las tortugas. El algoritmo TSEA emula la formación gradual de los sectores de piel queratinizada que representan soluciones óptimas a un problema. Las mejores soluciones se vuelven más "duras" y se encuentran más cerca de la superficie exterior, mientras que las menos exitosas permanecen "blandas" y se hallan en el interior. El algoritmo utiliza la clusterización de soluciones según su calidad y distancia, lo cual permite conservar las opciones menos acertadas y aporta flexibilidad y adaptabilidad.
Reimaginando estrategias clásicas en Python: Cruce de medias móviles (MAs, Moving Averages) Reimaginando estrategias clásicas en Python: Cruce de medias móviles (MAs, Moving Averages)
En este artículo, revisamos la estrategia clásica de cruce de medias móviles para evaluar su eficacia actual. Dado el tiempo transcurrido desde su creación, exploramos las posibles mejoras que la IA puede aportar a esta estrategia de negociación tradicional. Mediante la incorporación de técnicas de IA, pretendemos aprovechar las capacidades predictivas avanzadas para optimizar potencialmente los puntos de entrada y salida de las operaciones, adaptarnos a las condiciones variables del mercado y mejorar el rendimiento global en comparación con los enfoques convencionales.