Creación de sistemas de trading basados en IA en MQL5 (Parte 2): Desarrollo de un programa integrado con ChatGPT y con interfaz de usuario
Introducción
En nuestro artículo anterior (Parte 1), desarrollamos un framework para el procesamiento de JSON (JavaScript Object Notation) en MetaQuotes Language 5 (MQL5) mediante una clase que permite la serialización y deserialización de datos JSON para interactuar con las API (interfaces de programación de aplicaciones) de IA (inteligencia artificial). En la parte 2, crearemos un programa integrado con ChatGPT que cuenta con una interfaz de usuario, aprovechando el framework JSON para enviar prompts a la API de OpenAI y mostrar las respuestas en un gráfico de MetaTrader 5, lo que facilita la información y las observaciones interactivas para trading basadas en IA. Hoy abordaremos los siguientes temas:
- Comprensión del framework del programa de IA con ChatGPT
- Configuración del acceso a la API de OpenAI y de MetaTrader 5
- Implementación en MQL5
- Pruebas del programa ChatGPT
- Conclusión
Al final, tendrá un programa MQL5 funcional que integra ChatGPT para realizar consultas interactivas con IA, listo para mejorar sus estrategias de trading. ¡Manos a la obra!
Comprensión del framework del programa de IA con ChatGPT
El programa de IA con ChatGPT que pretendemos desarrollar integra las capacidades de un modelo de IA en MQL5, lo que nos permite interactuar con la IA enviando prompts y recibiendo análisis directamente en el gráfico de MetaTrader 5, sobre la base del framework JSON desarrollado en la parte anterior. Este programa ofrecerá una interfaz intuitiva para introducir consultas y visualizar las respuestas de la IA, y constituye un paso práctico hacia sistemas de trading basados en IA capaces de analizar los mercados o sugerir estrategias.
Nuestro plan consiste en crear un panel de control con un campo de entrada, un botón de envío y un área de visualización de respuestas, lo cual nos permitirá formular preguntas a la IA y ver las respuestas formateadas, al tiempo que nos aseguramos de que se registren todas las interacciones con fines de depuración. Además, configuraremos el programa para que se comunique con una API de IA, procese las respuestas y las muestre de forma clara, sentando así las bases para futuros programas que mejorarán el sistema con operaciones automatizadas basadas en los análisis de la IA. Aquí tiene una representación de lo que queremos conseguir.

¡Vamos a configurar la API y MetaTrader 5!
Configuración del acceso a la API de OpenAI y de MetaTrader 5
Para que el programa de IA ChatGPT pueda comunicarse con la API de OpenAI, debemos obtener una clave de API válida y configurar MetaTrader 5 (MT5) para que permita las solicitudes HTTP, garantizando así una integración sin problemas. Le guiaremos en el proceso de obtención de una clave de API de OpenAI, en la verificación de su funcionamiento mediante pruebas con curl y en la configuración del terminal de trading para permitir el acceso a la URL (localizador uniforme de recursos) del punto de conexión de la API y garantizar así una comunicación fiable.
Cómo obtener una clave de API de OpenAI
Cree una cuenta de OpenAI: visite «platform.openai.com» y regístrese o inicie sesión en su cuenta. Vaya a la sección de la API y genere una nueva clave de API en el panel «API Keys». Copie la clave (por ejemplo, una que empiece por «sk-») de forma segura, ya que se utilizará en la configuración del programa.
Carga inicial:

Generación de la clave secreta:

Proteja la clave de API: guárdela en un lugar seguro, ya que permite acceder a los servicios de OpenAI. Tenga en cuenta que pueden revocar la clave en caso de que se filtre.
Verificación de la clave API mediante pruebas con Curl
Para asegurarnos de que la clave de la API funciona correctamente, realizaremos una prueba con curl desde la línea de comandos para simular una solicitud a la API:
- Instale Curl: asegúrese de que curl esté instalado en su sistema (está disponible de forma predeterminada en la mayoría de los sistemas Linux/macOS o se puede descargar para Windows).
- Ejecute una solicitud de prueba: abra un terminal (Símbolo del sistema en Windows) y ejecute el siguiente comando curl, sustituyendo <YOUR_API_KEY> por su clave real de la API de OpenAI:
curl -X POST https://api.openai.com/v1/chat/completions -H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json" -d "{\"model\":\"gpt-3.5-turbo\",\"messages\":[{\"role\":\"user\",\"content\":\"Test prompt\"}],\"max_tokens\":50}" - Verifique la respuesta: una respuesta correcta (HTTP 200) devuelve un objeto JSON con un array «choices» que contiene la respuesta de la IA (por ejemplo, «content»: «Esta es una respuesta de prueba»). Si la respuesta incluye un campo «error» (por ejemplo, clave de API no válida o cuota superada), solucione el problema comprobando la validez de la clave o el estado de facturación de su cuenta de OpenAI. Registre la respuesta para comprobar que todo funciona correctamente. Si la ejecución se ha realizado correctamente, debería obtener un resultado similar a este.

Si este proceso le resulta complicado, puede crear un archivo de prueba sencillo y ejecutarlo en su navegador de la siguiente manera.
<!DOCTYPE html> <html> <head> <title>OpenAI API Test</title> <style> body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; } .container { border: 1px solid #ddd; padding: 20px; border-radius: 5px; } input, textarea, button { width: 100%; padding: 10px; margin: 5px 0; } textarea { height: 100px; } .response { background-color: #f5f5f5; padding: 15px; margin-top: 10px; } </style> </head> <body> <div class="container"> <h2>OpenAI API Test</h2> <div> <label for="apiKey">API Key:</label> <input type="text" id="apiKey" placeholder="Enter your API key"> </div> <div> <label for="prompt">Prompt:</label> <textarea id="prompt" placeholder="Enter your prompt">Test prompt</textarea> </div> <div> <label for="maxTokens">Max Tokens:</label> <input type="number" id="maxTokens" value="50"> </div> <button onclick="testAPI()">Test API</button> <div id="response" class="response" style="display: none;"> <h3>Response:</h3> <pre id="responseContent"></pre> </div> </div> <script> async function testAPI() { const apiKey = document.getElementById('apiKey').value; const prompt = document.getElementById('prompt').value; const maxTokens = document.getElementById('maxTokens').value; if (!apiKey) { alert('Please enter your API key'); return; } try { const response = await fetch('https://api.openai.com/v1/chat/completions', { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'gpt-3.5-turbo', messages: [{role: 'user', content: prompt}], max_tokens: parseInt(maxTokens) }) }); const data = await response.json(); // Display response document.getElementById('responseContent').textContent = JSON.stringify(data, null, 2); document.getElementById('response').style.display = 'block'; // Check for errors if (data.error) { console.error('API Error:', data.error); } } catch (error) { console.error('Error:', error); document.getElementById('responseContent').textContent = `Error: ${error.message}`; document.getElementById('response').style.display = 'block'; } } // Pre-fill with your API key (optional - remove if you don't want this) document.getElementById('apiKey').value = 'sk-proj-fb... <YOUR_API_KEY>... X75...'; </script> </body> </html>
Guárdelo como un archivo HTML (por ejemplo, api_test.html), ábralo en su navegador web y añada su clave. Debería ver la siguiente interfaz.

Al hacer clic en «Test API», debería obtener una respuesta como la siguiente.

Configuración de MetaTrader 5 para la comunicación mediante API
Para que el programa pueda enviar solicitudes HTTP al punto de conexión de la API de OpenAI, configuraremos el terminal de trading de la siguiente manera:
- Habilite las solicitudes web: Abra MT5, vaya a Herramientas > Opciones > Asesores Expertos y marque «Permitir WebRequest para las URL listadas».
- Añada el punto de conexión de la API: En el campo «URL de WebRequest», añada «https://api.openai.com» para permitir las solicitudes a la API de OpenAI. Esto garantiza que la función WebRequest del programa pueda comunicarse sin que la configuración de seguridad de MT5 la bloquee.
Aquí tiene la visualización completa.

Esta configuración permitirá que el programa envíe prompts y reciba respuestas de la IA, allanando el camino para su implementación. ¡Manos a la obra con la programación!
Implementación en MQL5
Para crear el programa en MQL5, abra MetaEditor, vaya al Navegador, busque la carpeta «Indicadores», haga clic en la pestaña «Nuevo» y siga las instrucciones para crear el archivo. Una vez creado el archivo, tendremos que declarar en el entorno de programación algunas variables globales y parámetros de entrada que utilizaremos a lo largo de todo el programa.
//+------------------------------------------------------------------+ //| a. ChatGPT AI EA.mq5 | //| Copyright 2025, Allan Munene Mutiiria. | //| https://t.me/Forex_Algo_Trader | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Allan Munene Mutiiria." #property link "https://t.me/Forex_Algo_Trader" #property version "1.00" #property strict //--- Input parameters input string OpenAI_Model = "gpt-3.5-turbo"; // OpenAI Model input string OpenAI_Endpoint = "https://api.openai.com/v1/chat/completions"; // OpenAI API Endpoint input int MaxResponseLength = 500; // Max length of ChatGPT response to display input string LogFileName = "ChatGPT_EA_Log.txt"; // Log file name //--- Hardcoded API key (confirmed valid via curl test) //--- Keep your API key private, for us, we added some parts so you can get the actual thing you should be having string OpenAI_API_Key = "sk-proj-vjKCf ... <YOUR FULL ACTUAL API KEY HERE> ... jY..79n...";
Comenzaremos la implementación del programa, centrándonos en la configuración inicial de los parámetros de entrada para configurar la conexión del programa con la API de OpenAI. En primer lugar, debemos definir el parámetro de entrada «OpenAI_Model» como una cadena de caracteres con el valor «gpt-3.5-turbo», lo que especifica el modelo de ChatGPT que se utilizará para las solicitudes a la API y permite cambiar de modelo con flexibilidad si fuera necesario. Aquí puede seleccionar su modelo. A continuación, configuramos «OpenAI_Endpoint» como «https://api.openai.com/v1/chat/completions», definiendo así la URL para enviar solicitudes HTTP POST a la API de OpenAI.
A continuación, configuramos «MaxResponseLength» como un número entero con el valor 500, lo que limita la longitud de visualización de la respuesta de ChatGPT para garantizar que el resultado sea manejable en el gráfico de MetaTrader 5. Por último, definimos «LogFileName» como «ChatGPT_EA_Log.txt» para especificar el archivo en el que se registrarán las interacciones con la API y los errores, y asignamos directamente en el código a «OpenAI_API_Key» nuestra clave de API, cuya validez hemos confirmado (que comienza por «sk-proj-»), para autenticar las solicitudes, sentando así las bases de la funcionalidad del programa. Para el registro y la visualización de botones, añadimos las siguientes variables.
//--- Global variables string conversationHistory = ""; //--- Stores conversation history int logFileHandle = INVALID_HANDLE; //--- Handle for log file bool button_hover = false; //--- Flag for button hover state color button_original_bg = clrRoyalBlue; //--- Original button background color color button_darker_bg; //--- Darkened button background for hover
Además, hemos añadido comentarios a las variables para que se entiendan por sí mismas. Lo siguiente que debemos hacer es definir las funciones auxiliares que utilizaremos para crear la interfaz, pero antes, asegúrese de copiar y pegar la lógica de procesamiento de JSON de la parte anterior, ya que no queremos crear varios archivos. También podría crear el archivo e incluirlo en el programa, pero lo haremos a medida que el programa se vuelva más complejo. Por ahora, vamos a mantenerlo todo sencillo y claro.
//+------------------------------------------------------------------+ //| Creates a rectangle label object | //+------------------------------------------------------------------+ bool createRecLabel(string objName, int xDistance, int yDistance, int xSize, int ySize, color bgColor, int borderWidth, color borderColor = clrNONE, ENUM_BORDER_TYPE borderType = BORDER_FLAT, ENUM_LINE_STYLE borderStyle = STYLE_SOLID, ENUM_BASE_CORNER corner = CORNER_LEFT_UPPER) { //--- Create rectangle label ResetLastError(); //--- Reset previous errors if (!ObjectCreate(0, objName, OBJ_RECTANGLE_LABEL, 0, 0, 0)) { //--- Attempt creation Print(__FUNCTION__, ": failed to create rec label! Error code = ", _LastError); //--- Print error return (false); //--- Return failure } ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xDistance); //--- Set x distance ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yDistance); //--- Set y distance ObjectSetInteger(0, objName, OBJPROP_XSIZE, xSize); //--- Set width ObjectSetInteger(0, objName, OBJPROP_YSIZE, ySize); //--- Set height ObjectSetInteger(0, objName, OBJPROP_CORNER, corner); //--- Set corner ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, bgColor); //--- Set background color ObjectSetInteger(0, objName, OBJPROP_BORDER_TYPE, borderType); //--- Set border type ObjectSetInteger(0, objName, OBJPROP_STYLE, borderStyle); //--- Set border style ObjectSetInteger(0, objName, OBJPROP_WIDTH, borderWidth); //--- Set border width ObjectSetInteger(0, objName, OBJPROP_COLOR, borderColor); //--- Set border color ObjectSetInteger(0, objName, OBJPROP_BACK, false); //--- Not background ObjectSetInteger(0, objName, OBJPROP_STATE, false); //--- Not pressed ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); //--- Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); //--- Not selected ChartRedraw(0); //--- Redraw chart return (true); //--- Success } //+------------------------------------------------------------------+ //| Creates a button object | //+------------------------------------------------------------------+ bool createButton(string objName, int xDistance, int yDistance, int xSize, int ySize, string text = "", color textColor = clrBlack, int fontSize = 12, color bgColor = clrNONE, color borderColor = clrNONE, string font = "Arial Rounded MT Bold", ENUM_BASE_CORNER corner = CORNER_LEFT_UPPER, bool isBack = false) { //--- Create button ResetLastError(); //--- Reset errors if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) { //--- Attempt creation Print(__FUNCTION__, ": failed to create the button! Error code = ", _LastError); //--- Print error return (false); //--- Failure } ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xDistance); //--- Set x distance ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yDistance); //--- Set y distance ObjectSetInteger(0, objName, OBJPROP_XSIZE, xSize); //--- Set width ObjectSetInteger(0, objName, OBJPROP_YSIZE, ySize); //--- Set height ObjectSetInteger(0, objName, OBJPROP_CORNER, corner); //--- Set corner ObjectSetString(0, objName, OBJPROP_TEXT, text); //--- Set text ObjectSetInteger(0, objName, OBJPROP_COLOR, textColor); //--- Set text color ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); //--- Set font size ObjectSetString(0, objName, OBJPROP_FONT, font); //--- Set font ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, bgColor); //--- Set background ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, borderColor); //--- Set border color ObjectSetInteger(0, objName, OBJPROP_BACK, isBack); //--- Set back ObjectSetInteger(0, objName, OBJPROP_STATE, false); //--- Not pressed ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); //--- Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); //--- Not selected ChartRedraw(0); //--- Redraw return (true); //--- Success } //+------------------------------------------------------------------+ //| Creates an edit field object | //+------------------------------------------------------------------+ bool createEdit(string objName, int xDistance, int yDistance, int xSize, int ySize, string text = "", color textColor = clrBlack, int fontSize = 12, color bgColor = clrNONE, color borderColor = clrNONE, string font = "Arial Rounded MT Bold", ENUM_BASE_CORNER corner = CORNER_LEFT_UPPER, int align = ALIGN_LEFT, bool readOnly = false) { //--- Create edit ResetLastError(); //--- Reset errors if (!ObjectCreate(0, objName, OBJ_EDIT, 0, 0, 0)) { //--- Attempt creation Print(__FUNCTION__, ": failed to create the edit! Error code = ", _LastError); //--- Print error return (false); //--- Failure } ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xDistance); //--- Set x distance ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yDistance); //--- Set y distance ObjectSetInteger(0, objName, OBJPROP_XSIZE, xSize); //--- Set width ObjectSetInteger(0, objName, OBJPROP_YSIZE, ySize); //--- Set height ObjectSetInteger(0, objName, OBJPROP_CORNER, corner); //--- Set corner ObjectSetString(0, objName, OBJPROP_TEXT, text); //--- Set text ObjectSetInteger(0, objName, OBJPROP_COLOR, textColor); //--- Set text color ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); //--- Set font size ObjectSetString(0, objName, OBJPROP_FONT, font); //--- Set font ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, bgColor); //--- Set background ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, borderColor); //--- Set border color ObjectSetInteger(0, objName, OBJPROP_ALIGN, align); //--- Set alignment ObjectSetInteger(0, objName, OBJPROP_READONLY, readOnly); //--- Set read-only ObjectSetInteger(0, objName, OBJPROP_BACK, false); //--- Not back ObjectSetInteger(0, objName, OBJPROP_STATE, false); //--- Not active ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); //--- Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); //--- Not selected ChartRedraw(0); //--- Redraw return (true); //--- Success } //+------------------------------------------------------------------+ //| Creates a text label object | //+------------------------------------------------------------------+ bool createLabel(string objName, int xDistance, int yDistance, string text, color textColor = clrBlack, int fontSize = 12, string font = "Arial Rounded MT Bold", ENUM_BASE_CORNER corner = CORNER_LEFT_UPPER, ENUM_ANCHOR_POINT anchor = ANCHOR_LEFT_UPPER) { //--- Create label ResetLastError(); //--- Reset errors if (!ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0)) { //--- Attempt creation Print(__FUNCTION__, ": failed to create the label! Error code = ", _LastError); //--- Print error return (false); //--- Failure } ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xDistance); //--- Set x distance ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yDistance); //--- Set y distance ObjectSetInteger(0, objName, OBJPROP_CORNER, corner); //--- Set corner ObjectSetString(0, objName, OBJPROP_TEXT, text); //--- Set text ObjectSetInteger(0, objName, OBJPROP_COLOR, textColor); //--- Set color ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); //--- Set font size ObjectSetString(0, objName, OBJPROP_FONT, font); //--- Set font ObjectSetInteger(0, objName, OBJPROP_BACK, false); //--- Not back ObjectSetInteger(0, objName, OBJPROP_STATE, false); //--- Not active ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); //--- Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); //--- Not selected ObjectSetInteger(0, objName, OBJPROP_ANCHOR, anchor); //--- Set anchor ChartRedraw(0); //--- Redraw return (true); //--- Success }
Aquí creamos los elementos gráficos esenciales para que los operadores interactúen con el gráfico. En primer lugar, implementamos la función «createRecLabel», que utiliza ObjectCreate para dibujar una etiqueta rectangular (OBJ_RECTANGLE_LABEL) en las coordenadas especificadas («xDistance», «yDistance») con las dimensiones («xSize», «ySize»). Mediante OBJPROP_BGCOLOR se establece el color de fondo; también se configuran la anchura del borde, el color («OBJPROP_COLOR»), el tipo de borde («OBJPROP_BORDER_TYPE» como «BORDER_FLAT»), el estilo («OBJPROP_STYLE» como «STYLE_SOLID») y la esquina («OBJPROP_CORNER» como «CORNER_LEFT_UPPER»), utilizando ObjectSetInteger. La función garantiza que la etiqueta no se pueda seleccionar ni se muestre en segundo plano y utiliza ChartRedraw para actualizar el gráfico. Si la creación falla, devuelve «false» y registra el error mediante la función Print.
A continuación, desarrollamos la función «createButton», que crea un botón (OBJ_BUTTON) con parámetros de coordenadas y tamaño similares. Configura el texto (OBJPROP_TEXT), el color del texto («OBJPROP_COLOR»), el tamaño de la fuente, la fuente (Arial Rounded MT Bold), el color de fondo, el color del borde y la esquina mediante ObjectSetString y «ObjectSetInteger», con un indicador opcional para mostrarlo en segundo plano («isBack»). Garantiza que el botón no se pueda seleccionar ni esté pulsado y actualiza el gráfico. En caso de fallo, devuelve «false» y registra el error.
Luego implementamos la función «createEdit», que crea un campo de texto editable (OBJ_EDIT) con parámetros para el texto, la alineación («OBJPROP_ALIGN» como «ALIGN_LEFT»), el estado de solo lectura y otras propiedades de estilo similares, utilizando «ObjectSetString» y «ObjectSetInteger» para configurar la apariencia y el comportamiento, actualizando el gráfico y gestionando los errores de forma similar. Por último, añadimos la función «createLabel», que crea una etiqueta de texto (OBJ_LABEL) con el texto, el color, el tamaño de fuente, la fuente, la esquina y el punto de anclaje especificados («OBJPROP_ANCHOR» como «ANCHOR_LEFT_UPPER»), garantizando que no se pueda seleccionar y que el gráfico se actualice. Estas funciones constituyen la base gráfica del panel interactivo de nuestro programa, lo cual permite introducir datos y visualizar las interacciones con la IA. Queremos que haya un botón que se oscurezca al pasar el cursor por encima. Así, vamos a crear una función para eso.
//+------------------------------------------------------------------+ //| Darkens a given color by a factor | //+------------------------------------------------------------------+ color DarkenColor(color colorValue, double factor = 0.8) { //--- Darken color function int red = int((colorValue & 0xFF) * factor); //--- Calculate darkened red component int green = int(((colorValue >> 8) & 0xFF) * factor); //--- Calculate darkened green component int blue = int(((colorValue >> 16) & 0xFF) * factor); //--- Calculate darkened blue component return (color)(red | (green << 8) | (blue << 16)); //--- Combine and return darkened color }
Aquí implementamos la función «DarkenColor», que recibe un valor de color y un factor opcional (por defecto, 0,8) para reducir el brillo. Para calcular los componentes rojo, verde y azul oscurecidos, extrae cada uno mediante operaciones bit a bit («colorValue & 0xFF» para el rojo, «(colorValue >> 8) & 0xFF» para el verde y «(colorValue >> 16) & 0xFF» para el azul), los multiplica por el factor y los combina mediante desplazamientos y una operación OR bit a bit («red | (green << 8) | (blue << 16)») para devolver el nuevo color. Ahora creamos el panel interactivo para dejarlo todo listo.
//+------------------------------------------------------------------+ //| Creates the dashboard UI | //+------------------------------------------------------------------+ void CreateDashboard() { //--- Create UI createEdit("ChatGPT_InputEdit", 20, 20, 400, 40, "", clrBlack, 12, clrWhiteSmoke, clrDarkGray, "Arial", CORNER_LEFT_UPPER, ALIGN_LEFT, false); //--- Input edit createButton("ChatGPT_SubmitButton", 430, 20, 100, 40, "Send", clrWhite, 12, button_original_bg, clrDarkBlue, "Arial", CORNER_LEFT_UPPER, false); //--- Submit button createRecLabel("ChatGPT_ResponseBg", 20, 70, 510, 300, clrWhite, 2, clrLightGray, BORDER_FLAT, STYLE_SOLID, CORNER_LEFT_UPPER); //--- Response background ChartRedraw(); //--- Redraw }
Luego implementamos la función «CreateDashboard» para crear la interfaz de usuario en el gráfico, lo que nos permite interactuar con la IA. En primer lugar, llamamos a «createEdit» para crear un campo de texto editable llamado «ChatGPT_InputEdit» en las coordenadas (20, 20), con unas dimensiones de 400 x 40 píxeles, texto negro, fuente Arial de 12 puntos, fondo blanco humo, borde gris oscuro y alineación a la izquierda. El modo de solo lectura está desactivado, lo que nos permite introducir prompts. Después utilizamos «createButton» para añadir un botón llamado «ChatGPT_SubmitButton» en (430, 20), con unas dimensiones de 100 x 40 píxeles y la etiqueta «Send» en texto blanco con fuente Arial de 12 puntos. Tiene un fondo azul real («button_original_bg») y un borde azul oscuro, y se sitúa tomando como referencia la esquina superior izquierda para enviar solicitudes a la API.
A continuación, llamamos a «createRecLabel» para crear un fondo de respuesta denominado «ChatGPT_ResponseBg» en la posición (20, 70) con unas dimensiones de 510 x 300 píxeles, con fondo blanco, un borde gris claro de 2 píxeles, de tipo plano y estilo sólido, lo que proporciona un área despejada para mostrar las respuestas de la IA. Por último, llamamos a ChartRedraw para actualizar el gráfico, asegurándonos de que todos los elementos de la interfaz de usuario estén visibles. Ahora podemos llamar a la función en el controlador de eventos OnInit para ver qué hemos conseguido.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Initialization button_darker_bg = DarkenColor(button_original_bg); //--- Set darker button color logFileHandle = FileOpen(LogFileName, FILE_READ | FILE_WRITE | FILE_TXT); //--- Open log if(logFileHandle == INVALID_HANDLE) { //--- Check handle Print("Failed to open log file: ", GetLastError()); //--- Print error return(INIT_FAILED); //--- Fail init } FileSeek(logFileHandle, 0, SEEK_END); //--- Seek end FileWriteString(logFileHandle, "EA Initialized at " + TimeToString(TimeCurrent()) + "\n"); //--- Log init CreateDashboard(); //--- Create UI return(INIT_SUCCEEDED); //--- Success } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Deinit ObjectsDeleteAll(0, "ChatGPT_"); //--- Delete objects if(logFileHandle != INVALID_HANDLE) { //--- Check handle FileClose(logFileHandle); //--- Close log } }
En la función OnInit, llamamos a «DarkenColor» con «button_original_bg» para calcular un tono más oscuro para «button_darker_bg», lo que permite un efecto al pasar el cursor por encima del botón de envío. A continuación, abrimos el archivo de registro especificado por «LogFileName» mediante FileOpen con los indicadores de lectura, escritura y texto, y almacenamos el identificador en «logFileHandle»; si el identificador no es válido, registramos un error y devolvemos INIT_FAILED. Esto nos permitirá llevar un control de lo que hacemos en segundo plano.
Entonces, utilizamos «FileSeek» para desplazarnos hasta el final del archivo de registro y escribimos un mensaje de inicialización con FileWriteString, incluyendo la hora actual mediante la función TimeToString. Luego llamamos a «CreateDashboard» para configurar la interfaz de usuario y devolvemos INIT_SUCCEEDED para confirmar que la inicialización se ha realizado correctamente. En la función OnDeinit, realizamos una limpieza llamando a ObjectsDeleteAll para eliminar todos los objetos de gráfico cuyo nombre comience por «ChatGPT_» y, si «logFileHandle» es válido, cerramos el archivo de registro con la función FileClose. Durante todo este tiempo, mientras el programa está vinculado al gráfico, mantenemos el archivo abierto. Eso significa que no podemos abrirlo mientras el programa esté en ejecución. Podría optar por cerrar el archivo tras enviar un prompt y recibir una respuesta, pero no resulta práctico si estamos interactuando de forma activa. Al ejecutar el programa, obtenemos el siguiente resultado.

Ahora que hemos creado la interfaz, tenemos que encontrar una forma de mostrar la conversación como un chat normal, pero el problema es que MQL5 no ofrece una forma directa de hacerlo. La función «label» por sí sola puede mostrar un texto de hasta 63 caracteres. Así pues, lo que tenemos que hacer es implementar un mecanismo de ajuste de texto que divida la conversación en varias etiquetas en lugar de una sola y limite el número de caracteres de cada etiqueta al máximo permitido. Esta es la lógica que seguimos para ello.
//+------------------------------------------------------------------+ //| Wraps text respecting newlines and max width | //+------------------------------------------------------------------+ void WrapText(const string inputText, const string font, const int fontSize, const int maxWidth, string &wrappedLines[], int offset = 0) { //--- Wrap text function const int maxChars = 60; //--- Max chars per line ArrayResize(wrappedLines, 0); //--- Clear output array TextSetFont(font, -fontSize * 10, 0); //--- Set font for measurement string paragraphs[]; //--- Array for paragraphs int numParagraphs = StringSplit(inputText, '\n', paragraphs); //--- Split by newline for (int p = 0; p < numParagraphs; p++) { //--- Loop paragraphs string para = paragraphs[p]; //--- Get paragraph if (StringLen(para) == 0) continue; //--- Skip empty string words[]; //--- Array for words int numWords = StringSplit(para, ' ', words); //--- Split by space string currentLine = ""; //--- Current line builder for (int w = 0; w < numWords; w++) { //--- Loop words string testLine = currentLine + (StringLen(currentLine) > 0 ? " " : "") + words[w]; //--- Test add word uint wid, hei; //--- Width, height TextGetSize(testLine, wid, hei); //--- Get size int textWidth = (int)wid; //--- Cast width if (textWidth + offset <= maxWidth && StringLen(testLine) <= maxChars) { //--- Fits currentLine = testLine; //--- Update line } else { //--- Doesn't fit if (StringLen(currentLine) > 0) { //--- Add current if not empty int size = ArraySize(wrappedLines); //--- Get size ArrayResize(wrappedLines, size + 1); //--- Resize wrappedLines[size] = currentLine; //--- Add line } currentLine = words[w]; //--- Start new with word TextGetSize(currentLine, wid, hei); //--- Get size textWidth = (int)wid; //--- Cast if (textWidth + offset > maxWidth || StringLen(currentLine) > maxChars) { //--- Word too long string wrappedWord = ""; //--- Word builder for (int c = 0; c < StringLen(words[w]); c++) { //--- Char loop string testWord = wrappedWord + StringSubstr(words[w], c, 1); //--- Test add char TextGetSize(testWord, wid, hei); //--- Get size int wordWidth = (int)wid; //--- Cast if (wordWidth + offset > maxWidth || StringLen(testWord) > maxChars) { //--- Char exceeds if (StringLen(wrappedWord) > 0) { //--- Add if not empty int size = ArraySize(wrappedLines); //--- Get size ArrayResize(wrappedLines, size + 1); //--- Resize wrappedLines[size] = wrappedWord; //--- Add } wrappedWord = StringSubstr(words[w], c, 1); //--- New with char } else { //--- Fits wrappedWord = testWord; //--- Update } } currentLine = wrappedWord; //--- Set current } } } if (StringLen(currentLine) > 0) { //--- Add remaining line int size = ArraySize(wrappedLines); //--- Get size ArrayResize(wrappedLines, size + 1); //--- Resize wrappedLines[size] = currentLine; //--- Add } } }
Vamos a implementar la función «WrapText» para dar formato al texto que se muestra en el gráfico, con el fin de garantizar que las respuestas de la IA sean legibles. En primer lugar, definimos una constante «maxChars» con un valor de 60 para limitar la longitud de las líneas, aunque usted podría fijar el límite en 63; vaciamos el array de salida «wrappedLines» con ArrayResize y configuramos la fuente mediante TextSetFont con la fuente y el tamaño especificados (con el tamaño escalado mediante -fontSize * 10). A continuación, dividimos el texto de entrada en párrafos utilizando StringSplit con el salto de línea como delimitador y recorremos cada párrafo que no esté vacío. Dividimos cada párrafo en palabras utilizando de nuevo «StringSplit», pero esta vez con un espacio como delimitador, e inicializamos una variable «currentLine» vacía.
Luego iteramos las palabras y construimos «testLine» añadiendo cada palabra a «currentLine», con un espacio separador si «currentLine» no está vacía, y utilizamos TextGetSize para medir su anchura; si la anchura más el desplazamiento no supera «maxWidth» y la longitud es inferior a «maxChars», actualizamos «currentLine»; de lo contrario, añadimos «currentLine» a «wrappedLines» si no está vacía y comenzamos una nueva línea con la palabra actual. En el caso de las palabras demasiado largas, recorremos sus caracteres, construimos «wrappedWord» y comprobamos su anchura; añadimos «wrappedWord» a «wrappedLines» cuando su anchura supera «maxWidth» o su longitud supera «maxChars», y actualizamos «currentLine» con la parte restante de la palabra. Por último, añadimos cualquier «currentLine» restante a «wrappedLines», asegurándonos de que todo el texto esté formateado para que se muestre con claridad en el área de respuesta del programa. Ahora podemos aplicar esta lógica y actualizar nuestra pantalla con un texto de ejemplo en el que se explique al usuario cómo empezar.
//+------------------------------------------------------------------+ //| Updates the response display | //+------------------------------------------------------------------+ void UpdateResponseDisplay() { //--- Update display int total = ObjectsTotal(0, 0, -1); //--- Get total objects for (int j = total - 1; j >= 0; j--) { //--- Loop backwards string name = ObjectName(0, j, 0, -1); //--- Get name if (StringFind(name, "ChatGPT_ResponseLine_") == 0 || StringFind(name, "ChatGPT_MessageBg_") == 0) { //--- Check prefix ObjectDelete(0, name); //--- Delete } } string displayText = conversationHistory; //--- Get history if (displayText == "") { //--- If empty string objName = "ChatGPT_ResponseLine_0"; //--- Name createLabel(objName, 30, 80, "Type your message above and click Send to chat with the AI.", clrGray, 10, "Arial", CORNER_LEFT_UPPER, ANCHOR_LEFT_UPPER); //--- Placeholder ChartRedraw(); //--- Redraw return; //--- Exit } string font = "Arial"; //--- Font int fontSize = 10; //--- Size int padding = 10; //--- Padding int maxWidth = 510 - 2 * padding; //--- Max width string wrappedLines[]; //--- Wrapped lines WrapText(displayText, font, fontSize, maxWidth, wrappedLines, 0); //--- Wrap text TextSetFont(font, -fontSize * 10, 0); //--- Set font uint wid, hei; //--- Size vars TextGetSize("A", wid, hei); //--- Get height int lineHeight = (int)hei; //--- Line height int responseHeight = 300; //--- Response height int maxVisibleLines = (responseHeight - 2 * padding) / lineHeight; //--- Max lines int numLines = ArraySize(wrappedLines); //--- Num lines int startLine = MathMax(0, numLines - maxVisibleLines); //--- Start line int textX = 20 + padding; //--- Text x int textY = 70 + padding; //--- Text y color currentColor = clrWhite; //--- Current color for (int i = startLine; i < numLines; i++) { //--- Loop lines string line = wrappedLines[i]; //--- Get line if (StringFind(line, "You: ") == 0) { //--- User color currentColor = clrGray; //--- Set gray } else if (StringFind(line, "AI: ") == 0) { //--- AI color currentColor = clrBlue; //--- Set blue } string objName = "ChatGPT_ResponseLine_" + IntegerToString(i - startLine); //--- Name createLabel(objName, textX, textY, line, currentColor, fontSize, font, CORNER_LEFT_UPPER, ANCHOR_LEFT_UPPER); //--- Create label textY += lineHeight; //--- Next y } ChartRedraw(); //--- Redraw }
Aquí implementamos la función «UpdateResponseDisplay» para mostrar dinámicamente el historial de la conversación. En primer lugar, utilizamos ObjectsTotal para contar todos los objetos del gráfico y realizamos una iteración hacia atrás, llamando a ObjectName para recuperar el nombre de cada objeto y a ObjectDelete para eliminar los objetos con los prefijos «ChatGPT_ResponseLine_» o «ChatGPT_MessageBg_», borrando así los elementos de visualización anteriores. A continuación, comprobamos si «conversationHistory» está vacío; si es así, llamamos a «createLabel» para mostrar un mensaje provisional en la posición (30, 80) en color gris, con fuente Arial de 10 puntos, en el que se pide a los usuarios que introduzcan un mensaje, y salimos tras volver a dibujar la pantalla.
Si «conversationHistory» contiene texto, establecemos la fuente en Arial, el tamaño de fuente en 10 y el relleno en 10, y calculamos «maxWidth» como 510 menos el doble del relleno; a continuación, llamamos a «WrapText» para dividir el texto en «wrappedLines». Luego utilizamos TextSetFont y TextGetSize para determinar la altura de línea, calculamos «maxVisibleLines» basándonos en una altura de respuesta de 300 píxeles y establecemos la línea inicial con MathMax para mostrar las líneas más recientes. Después iteramos «wrappedLines» a partir de «startLine», estableciendo «currentColor» en gris para las líneas que empiezan por «You: » o en azul para las que empiezan por «AI: », y llamamos a «createLabel» para mostrar cada línea en las coordenadas («textX», «textY»), incrementando «textY» en «lineHeight» para cada etiqueta, con nombres únicos como «ChatGPT_ResponseLine_0». Por último, llamamos a ChartRedraw para actualizar el gráfico, asegurándonos de que la conversación se muestre de forma clara y fácil de distinguir a simple vista. Cuando llamamos a esta función durante la inicialización, obtenemos el siguiente resultado.

En la imagen podemos ver que hemos actualizado la pantalla con un mensaje inicial y que ya estamos listos para mostrar las conversaciones. Ahora creamos una función para obtener la respuesta a los prompts que enviemos, pero antes definamos las funciones auxiliares que vamos a necesitar.
//+------------------------------------------------------------------+ //| Escapes string for JSON | //+------------------------------------------------------------------+ string JsonEscape(string value) { //--- JSON escape StringReplace(value, "\\", "\\\\"); //--- Escape backslash StringReplace(value, "\"", "\\\""); //--- Escape quote StringReplace(value, "\n", "\\n"); //--- Escape newline StringReplace(value, "\r", "\\r"); //--- Escape carriage StringReplace(value, "\t", "\\t"); //--- Escape tab StringReplace(value, "\f", "\\f"); //--- Escape form feed for(int i = 0; i < StringLen(value); i++) { //--- Loop chars ushort charCode = StringGetCharacter(value, i); //--- Get char if(charCode < 32 || charCode == 127) { //--- Control chars string hex = StringFormat("\\u%04x", charCode); //--- Hex escape string before = StringSubstr(value, 0, i); //--- Before part string after = StringSubstr(value, i + 1); //--- After part value = before + hex + after; //--- Replace i += 5; //--- Skip added } } return value; //--- Return escaped } //+------------------------------------------------------------------+ //| Logs char array as hex for debugging | //+------------------------------------------------------------------+ string LogCharArray(char &data[]) { //--- Log char array string result = ""; //--- Result string for(int i = 0; i < ArraySize(data); i++) { //--- Loop array result += StringFormat("%02X ", data[i]); //--- Append hex } return result; //--- Return }
Antes de crear la lógica propiamente dicha para gestionar las respuestas, implementamos funciones de utilidad para gestionar el escape de cadenas JSON y la depuración, con el fin de garantizar una comunicación robusta a través de la API. En primer lugar, implementamos la función «JsonEscape», que toma una cadena como entrada y escapa los caracteres especiales para que cumpla con el formato JSON utilizando StringReplace para convertir la barra invertida en «\\», las comillas en «\"», el salto de línea en «\n», el retorno de carro en «\r», la tabulación en «\t» y el salto de página en «\f»; a continuación, recorre cada carácter con StringGetCharacter, comprueba si es un carácter de control (código ASCII inferior a 32 o igual a 127) y lo sustituye por una secuencia de escape Unicode («\uXXXX») mediante StringFormat, actualizando la cadena con StringSubstr para concatenar las partes anteriores y posteriores al escape y ajustando el índice para omitir los caracteres añadidos; finalmente, devuelve la cadena escapada.
Entonces, desarrollamos la función «LogCharArray», que convierte un array de caracteres en una cadena hexadecimal para la depuración; para ello, inicializamos una cadena de resultado vacía, recorremos el array con ArraySize, añadimos el valor hexadecimal de cada carácter utilizando «StringFormat» con el formato «%02X» y devolvemos la cadena formateada. Estas funciones garantizarán el formato adecuado de los prompts para las solicitudes a la API de OpenAI y ofrecerán ayuda para la depuración a la hora de inspeccionar los datos de la API. Ahora podemos utilizarlas para crear la función básica que nos permita obtener las respuestas.
//+------------------------------------------------------------------+ //| Gets ChatGPT response via API | //+------------------------------------------------------------------+ string GetChatGPTResponse(string prompt) { //--- Get AI response string escapedPrompt = JsonEscape(prompt); //--- Escape prompt string requestData = "{\"model\":\"" + OpenAI_Model + "\",\"messages\":[{\"role\":\"user\",\"content\":\"" + escapedPrompt + "\"}],\"max_tokens\":500}"; //--- Build JSON FileWriteString(logFileHandle, "Request Data: " + requestData + "\n"); //--- Log data char postData[]; //--- Post array int dataLen = StringToCharArray(requestData, postData, 0, WHOLE_ARRAY, CP_UTF8); //--- To char array ArrayResize(postData, dataLen - 1); //--- Remove terminator FileWriteString(logFileHandle, "Raw Post Data (Hex): " + LogCharArray(postData) + "\n"); //--- Log hex string headers = "Authorization: Bearer " + OpenAI_API_Key + "\r\n" + //--- Build headers "Content-Type: application/json; charset=UTF-8\r\n" + "Content-Length: " + IntegerToString(dataLen - 1) + "\r\n\r\n"; FileWriteString(logFileHandle, "Request Headers: " + headers + "\n"); //--- Log headers char result[]; //--- Result array string resultHeaders; //--- Result headers int res = WebRequest("POST", OpenAI_Endpoint, headers, 10000, postData, result, resultHeaders); //--- Send request if(res != 200) { //--- Check status string response = CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8); //--- To string string errMsg = "API request failed: HTTP Code " + IntegerToString(res) + ", Error: " + IntegerToString(GetLastError()) + ", Response: " + response; //--- Error msg Print(errMsg); //--- Print FileWriteString(logFileHandle, errMsg + "\n"); //--- Log FileWriteString(logFileHandle, "Raw Response Data (Hex): " + LogCharArray(result) + "\n"); //--- Log hex return errMsg; //--- Return error } string response = CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8); //--- To string FileWriteString(logFileHandle, "API Response: " + response + "\n"); //--- Log response JsonValue jsonObject; //--- JSON object int index = 0; //--- Index char charArray[]; //--- Char array int arrayLength = StringToCharArray(response, charArray, 0, WHOLE_ARRAY, CP_UTF8); //--- To char if(!jsonObject.DeserializeFromArray(charArray, arrayLength, index)) { //--- Deserialize string errMsg = "Error: Failed to parse API response JSON: " + response; //--- Error Print(errMsg); //--- Print FileWriteString(logFileHandle, errMsg + "\n"); //--- Log return errMsg; //--- Return } JsonValue *error = jsonObject.FindChildByKey("error"); //--- Find error if(error != NULL) { //--- If error string errMsg = "API Error: " + error["message"].ToString(); //--- Get message Print(errMsg); //--- Print FileWriteString(logFileHandle, errMsg + "\n"); //--- Log return errMsg; //--- Return } string content = jsonObject["choices"][0]["message"]["content"].ToString(); //--- Get content if(StringLen(content) > 0) { //--- If content StringReplace(content, "\\n", "\n"); //--- Replace escapes StringTrimLeft(content); //--- Trim left StringTrimRight(content); //--- Trim right return content; //--- Return } string errMsg = "Error: No content in API response: " + response; //--- Error Print(errMsg); //--- Print FileWriteString(logFileHandle, errMsg + "\n"); //--- Log return errMsg; //--- Return }
Finalizamos la funcionalidad principal de nuestro programa implementando la función «GetChatGPTResponse» para gestionar la comunicación mediante la API con ChatGPT de OpenAI. En primer lugar, llamamos a «JsonEscape» para escapar el prompt de entrada, garantizando así la compatibilidad con JSON, y construimos una cadena JSON «requestData» con «OpenAI_Model», un array de mensajes de usuario que contiene el prompt escapado y «max_tokens» establecido en 500; a continuación, la registramos con FileWriteString en «logFileHandle». A continuación, convertimos «requestData» en un array de caracteres «postData» utilizando StringToCharArray con codificación UTF-8, ajustamos su tamaño con ArrayResize para eliminar el carácter de terminación nulo y registramos la representación hexadecimal mediante la función «LogCharArray».
Después creamos los encabezados HTTP con la «OpenAI_API_Key», el tipo de contenido y la longitud del contenido, los registramos y enviamos una solicitud POST a «OpenAI_Endpoint» utilizando WebRequest con un tiempo de espera de 10 segundos, almacenando la respuesta en «result». Si el código de respuesta no es 200, convertimos «result» en una cadena mediante CharArrayToString, registramos un error con «Print» y «FileWriteString», y devolvemos el mensaje de error. De lo contrario, analizamos la respuesta y la convertimos en un objeto «JsonValue» mediante «DeserializeFromArray» tras convertirla en un array de caracteres; si el análisis falla, registramos el error y devolvemos un mensaje de error. Comprobamos si existe una clave «error» mediante «FindChildByKey», registrando y devolviendo cualquier mensaje de error que haya.
Por último, extraemos la respuesta de la IA de «jsonObject['choices'][0]['message']['content']» utilizando «ToString», sustituimos los saltos de línea escapados con StringReplace, eliminamos los espacios en blanco al principio y al final con StringTrimLeft y StringTrimRight, y devolvemos el contenido si no está vacío; de lo contrario, registramos y devolvemos un mensaje de error. Esta función permitirá ahora al programa realizar consultas a ChatGPT y procesar sus respuestas para mostrarlas en pantalla. Lo que tenemos que hacer ahora es enviar nuestro primer mensaje. Tendremos que implementar la función de gestión de eventos «OnChartEvent» para detectar los cambios que realicemos y actuar en consecuencia.
//+------------------------------------------------------------------+ //| Chart event handler | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- Event handler if(id == CHARTEVENT_OBJECT_CLICK && sparam == "ChatGPT_SubmitButton") { //--- Button click string prompt = (string)ObjectGetString(0, "ChatGPT_InputEdit", OBJPROP_TEXT); //--- Get input if(StringLen(prompt) > 0) { //--- If not empty string response = GetChatGPTResponse(prompt); //--- Get AI response Print("User: " + prompt); //--- Print user Print("AI: " + response); //--- Print AI conversationHistory += "You: " + prompt + "\nAI: " + response + "\n\n"; //--- Append history ObjectSetString(0, "ChatGPT_InputEdit", OBJPROP_TEXT, ""); //--- Clear input UpdateResponseDisplay(); //--- Update display FileWriteString(logFileHandle, "Prompt: " + prompt + " | Response: " + response + " | Time: " + TimeToString(TimeCurrent()) + "\n"); //--- Log ChartRedraw(); //--- Redraw } } }
En este caso, mejoramos la interactividad del programa mediante la implementación de la función OnChartEvent para gestionar las interacciones del usuario en el gráfico. En primer lugar, comprobamos si el «id» del evento es CHARTEVENT_OBJECT_CLICK y si el «sparam» es «ChatGPT_SubmitButton», lo que indica que se ha pulsado el botón de enviar. Si es cierto, recuperamos la entrada del usuario del campo de texto «ChatGPT_InputEdit» mediante ObjectGetString con OBJPROP_TEXT y la almacenamos en la variable «prompt».
A continuación, si «prompt» no está vacío (lo comprobamos con StringLen), llamamos a «GetChatGPTResponse» para obtener la respuesta de la IA. Registramos el prompt del usuario y la respuesta de la IA, y los añadimos a «conversationHistory» con las etiquetas «You: » y «AI: », separados por saltos de línea. Vaciamos el campo de entrada utilizando ObjectSetString con una cadena vacía, actualizamos la visualización del gráfico llamando a «UpdateResponseDisplay», registramos la interacción con una marca de tiempo utilizando FileWriteString para escribir en «logFileHandle» y TimeToString para convertir la hora en una cadena, y actualizamos el gráfico. Esta implementación permite que el programa procese nuestros prompts y muestre las respuestas de la IA de forma interactiva, completando así la funcionalidad principal del panel interactivo. Tras la compilación, este es el resultado que obtenemos.

En la imagen podemos ver que hemos integrado con éxito el modelo de IA en nuestro programa. Intentemos abrir el archivo de registro y veamos si la interacción ha quedado registrada. Puede encontrar el archivo en la carpeta de archivos comunes, tal y como se muestra a continuación. Haga clic con el botón derecho del ratón y siga las instrucciones para abrirlo. Mire más abajo.

Al abrir el archivo, este es el resultado que obtenemos.

En la imagen podemos ver que es posible registrar los datos de actividad, lo que nos permitirá hacer un seguimiento de lo que hacemos, de nuestros prompts y de las respuestas que obtenemos. Lo que tenemos que hacer ahora es asegurarnos de añadir un efecto al pasar el cursor por encima del botón «Enviar». Para ello, tendremos que realizar un seguimiento de las coordenadas del ratón mientras se mueve, lo que requerirá habilitar los eventos de movimiento del ratón en la capa superior del gráfico.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Initialization button_darker_bg = DarkenColor(button_original_bg); //--- Set darker button color logFileHandle = FileOpen(LogFileName, FILE_READ | FILE_WRITE | FILE_TXT); //--- Open log if(logFileHandle == INVALID_HANDLE) { //--- Check handle Print("Failed to open log file: ", GetLastError()); //--- Print error return(INIT_FAILED); //--- Fail init } FileSeek(logFileHandle, 0, SEEK_END); //--- Seek end FileWriteString(logFileHandle, "EA Initialized at " + TimeToString(TimeCurrent()) + "\n"); //--- Log init CreateDashboard(); //--- Create UI UpdateResponseDisplay(); //--- Update display ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true); //--- Enable mouse events return(INIT_SUCCEEDED); //--- Success }
Simplemente utilizamos la función ChartSetInteger para activar los eventos del ratón en el gráfico. Hemos resaltado la función para que quede más claro. A continuación, tenemos que pasar a la función OnChartEvent del gráfico y añadir la lógica que se ejecuta cuando se mueve el ratón.
//+------------------------------------------------------------------+ //| Chart event handler | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- Event handler if(id == CHARTEVENT_OBJECT_CLICK && sparam == "ChatGPT_SubmitButton") { //--- Button click string prompt = (string)ObjectGetString(0, "ChatGPT_InputEdit", OBJPROP_TEXT); //--- Get input if(StringLen(prompt) > 0) { //--- If not empty string response = GetChatGPTResponse(prompt); //--- Get AI response Print("User: " + prompt); //--- Print user Print("AI: " + response); //--- Print AI conversationHistory += "You: " + prompt + "\nAI: " + response + "\n\n"; //--- Append history ObjectSetString(0, "ChatGPT_InputEdit", OBJPROP_TEXT, ""); //--- Clear input UpdateResponseDisplay(); //--- Update display FileWriteString(logFileHandle, "Prompt: " + prompt + " | Response: " + response + " | Time: " + TimeToString(TimeCurrent()) + "\n"); //--- Log ChartRedraw(); //--- Redraw } } else if(id == CHARTEVENT_MOUSE_MOVE) { //--- Mouse move int mouseX = (int)lparam; //--- X coord int mouseY = (int)dparam; //--- Y coord bool isOver = (mouseX >= 430 && mouseX <= 530 && mouseY >= 20 && mouseY <= 60); //--- Check hover if(isOver && !button_hover) { //--- Enter hover ObjectSetInteger(0, "ChatGPT_SubmitButton", OBJPROP_BGCOLOR, button_darker_bg); //--- Darken button_hover = true; //--- Set flag ChartRedraw(); //--- Redraw } else if(!isOver && button_hover) { //--- Exit hover ObjectSetInteger(0, "ChatGPT_SubmitButton", OBJPROP_BGCOLOR, button_original_bg); //--- Restore button_hover = false; //--- Clear flag ChartRedraw(); //--- Redraw } } }
En la función OnChartEvent, completamos la implementación añadiendo efectos al pasar el cursor por encima de los elementos de la interfaz de usuario. Dentro de un bloque «else-if», comprobamos si el «id» del evento es CHARTEVENT_MOUSE_MOVE, lo que indica un movimiento del ratón, y convertimos «lparam» en «mouseX» y «dparam» en «mouseY» para obtener las coordenadas. A continuación, comprobamos si el cursor del ratón se encuentra sobre el botón de envío, verificando si «mouseX» está entre 430 y 530 y «mouseY» entre 20 y 60, y almacenamos el resultado en la variable «isOver».
Si «isOver» es verdadero y «button_hover» es falso (lo que indica que el ratón ha entrado en el área del botón), llamamos a ObjectSetInteger para establecer el color de fondo de «ChatGPT_SubmitButton» en «button_darker_bg» y crear así un efecto al pasar el ratón por encima, establecemos «button_hover» en verdadero y actualizamos el gráfico. Por el contrario, si «isOver» es falso y «button_hover» es verdadero (lo que indica que el ratón ha salido del área del botón), restablecemos el fondo del botón a «button_original_bg» mediante «ObjectSetInteger», establecemos «button_hover» en falso y llamamos a ChartRedraw para actualizar el gráfico. Ahora, al pasar el cursor por encima del botón, obtenemos el siguiente resultado.

La imagen muestra que podemos añadir un efecto al pasar el cursor por encima del botón creado, con lo que logramos nuestro objetivo de crear un panel de control de IA interactivo. Lo que queda por hacer es probar el programa, y eso se trata en la siguiente sección.
Pruebas del programa ChatGPT
Hemos realizado las pruebas y, a continuación, se muestra la visualización completa en una única imagen de mapa de bits en formato Graphics Interchange Format (GIF).

Conclusión
En conclusión, hemos desarrollado en MQL5 un programa con ChatGPT integrado que aprovecha el framework de análisis de JSON de la parte anterior para enviar prompts a la API de OpenAI y visualizar las respuestas a través de un panel de control interactivo. Mediante una interfaz fácil de usar con un campo de entrada, un botón de envío y un área de visualización de respuestas, junto con la comunicación con la API y el registro de actividad, el programa facilita la obtención de información en tiempo real mediante inteligencia artificial. En las próximas secciones, actualizaremos la visualización para que admita más respuestas y permita el desplazamiento. No se lo pierda.
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/19567
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.
Automatización del indicador del sentimiento del mercado
Modificación del algoritmo de optimización de Dingo — Dingo Optimization Algorithm M (DOAm)
Redes neuronales en el trading: Señales de trading robustas en cualquier régimen de mercado (Conclusión)
Desarrollo de estrategias de trading con los osciladores Parafrac y Parafrac V2: análisis del rendimiento de una única entrada
- 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