English Русский Deutsch 日本語
preview
Creación de sistemas de trading basados en IA en MQL5 (Parte 1): Implementación del manejo de JSON para APIs de IA

Creación de sistemas de trading basados en IA en MQL5 (Parte 1): Implementación del manejo de JSON para APIs de IA

MetaTrader 5 — Sistemas comerciales |
46 2
Allan Munene Mutiiria
Allan Munene Mutiiria

Introducción

En esta serie de artículos, presentamos la integración de la Inteligencia Artificial (IA) en sistemas de trading mediante MetaQuotes Language 5 (MQL5). Comenzamos con esta parte, en la que desarrollamos un framework robusto de análisis de JavaScript Object Notation (JSON) para gestionar el intercambio de datos en interacciones con interfaces de programación de aplicaciones (API) de IA, como ChatGPT. Así, vamos a centrarnos en crear una base para procesar estructuras JSON que permita una comunicación fluida con servicios de IA en futuras aplicaciones de trading. Hoy hablaremos de los siguientes temas:

  1. La comprensión de JSON y su papel en la integración de IA
  2. Implementación en MQL5
  3. Pruebas del analizador JSON
  4. Conclusión

Al terminar, usted contará con una base sólida para manejar datos JSON, lo que preparará el terreno para sistemas de trading basados en IA. ¡Empecemos!


La comprensión de JSON y su papel en la integración de IA

JSON (JavaScript Object Notation) es un formato ligero de intercambio de datos basado en texto, ampliamente usado para estructurar y transmitir datos entre sistemas, especialmente en API web, debido a su simplicidad, legibilidad y compatibilidad con distintos lenguajes de programación. En el contexto de los sistemas de trading basados en IA, como los que queremos desarrollar, JSON sirve como formato estándar para intercambiar datos con API de IA, como ChatGPT de OpenAI, lo que permite a las aplicaciones MQL5 enviar prompts relacionados con el trading y recibir respuestas estructuradas para la toma de decisiones. Nuestro enfoque en este artículo se centra en crear un framework de análisis de JSON para gestionar estas interacciones con las API, sentando así las bases para integrar información generada por IA en estrategias de trading automatizadas.

¿Qué es JSON y por qué es importante?

JSON representa los datos como pares clave-valor, arrays y objetos anidados, lo cual lo convierte en un formato ideal para codificar información compleja, como datos de mercado, señales de trading o respuestas de IA, de una forma legible para las personas y analizable por máquinas. Por ejemplo, un objeto JSON podría tener este aspecto:

{
  "model": "gpt-3.5-turbo",
  "messages": [
    {"role": "user", "content": "Analyze EURUSD trend"},
    {"role": "assistant", "content": "EURUSD shows a bullish trend"}
  ],
  "max_tokens": 500
}

Esta estructura incluye cadenas de caracteres, números, arrays y objetos anidados, que un Asesor Experto (EA) de MQL5 debe analizar para extraer la información relevante, como la respuesta de la IA a una consulta de trading. El papel de JSON en la integración de la IA resulta fundamental en este contexto, ya que las API devuelven respuestas en formato JSON, lo que obliga al programa a serializar las entradas (convertir los datos a JSON) y a deserializar las salidas (analizar el JSON para obtener datos utilizables) con el fin de permitir la toma de decisiones dinámicas de trading. Si todo esto le suena a jerga, aquí tiene una breve visualización de lo que significan la serialización y la deserialización de datos.

SERIALIZACIÓN Y DESERIALIZACIÓN

Hoja de ruta para la implementación

Nuestro plan de implementación consiste en crear una clase de manejo de JSON que admita las siguientes funcionalidades:

  • Representación de datos: una clase para almacenar valores JSON con atributos de tipo, clave y valor (por ejemplo, string, número o booleano), y un array para los elementos secundarios que permite gestionar estructuras anidadas.
  • Lógica de análisis sintáctico: métodos para deserializar cadenas JSON en objetos, procesando caracteres para identificar estructuras como objetos, arrays y tipos primitivos, al tiempo que se gestionan los espacios en blanco y las secuencias de escape.
  • Lógica de serialización: métodos para volver a convertir los datos internos en cadenas JSON, garantizando un formato adecuado para las solicitudes de la API, incluido el escapado de caracteres especiales.
  • Gestión de errores: comprobaciones estrictas para detectar JSON no válido, discrepancias de tipos y accesos fuera de límites, con el fin de evitar fallos durante la comunicación con la API.
  • Preparación de la interfaz de usuario: sentar las bases para una futura integración con una interfaz de usuario que permita introducir prompts y mostrar las respuestas de la IA, que se basarán en datos JSON analizados.

Probaremos este framework para asegurarnos de que pueda analizar respuestas típicas de API de IA, como las de OpenAI, y serializar con precisión prompts relacionados con el trading, tal y como mostramos a continuación.

RESPUESTAS DE IA EN MQL5

Dominando el análisis de JSON en este artículo, nos aseguramos de que los futuros programas basados en IA puedan procesar estructuras de datos complejas, allanando el camino para estrategias de trading sofisticadas que combinen acción del precio, patrones gráficos e información generada por la IA. ¡Pasemos ahora a la implementación!


Implementación en MQL5

Para realizar la integración, primero crearemos una clase de análisis sintáctico completa que utilizaremos para nuestros primeros prompts y futuras aplicaciones. Así es como lo conseguimos.

//+------------------------------------------------------------------+
//|                                            a. JSON Code File.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"
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){return(INIT_SUCCEEDED);}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){}
//+------------------------------------------------------------------+
//| JSON (JavaScript Object Notation)                                |
//+------------------------------------------------------------------+
#define DEBUG_PRINT false
//+------------------------------------------------------------------+
//| Enumeration of JSON value types                                  |
//+------------------------------------------------------------------+
enum JsonValueType {JsonUndefined,JsonNull,JsonBoolean,JsonInteger,JsonDouble,JsonString,JsonArray,JsonObject};
//+------------------------------------------------------------------+
//| Class representing a JSON value                                  |
//+------------------------------------------------------------------+
class JsonValue{
   public:
      // CONSTRUCTOR
      virtual void Reset(){
         m_parent=NULL;                                         //--- Set parent pointer to NULL
         m_key="";                                              //--- Clear the key string
         m_type=JsonUndefined;                                  //--- Set type to undefined
         m_booleanValue=false;                                  //--- Set boolean value to false
         m_integerValue=0;                                      //--- Set integer value to zero
         m_doubleValue=0;                                       //--- Set double value to zero
         m_stringValue="";                                      //--- Clear the string value
         ArrayResize(m_children,0);                             //--- Resize children array to zero
      }
      virtual bool CopyFrom(const JsonValue &source){
         m_key=source.m_key;                                    //--- Copy the key from source
         CopyDataFrom(source);                                  //--- Copy data from source
         return true;                                           //--- Return success
      }
      virtual void CopyDataFrom(const JsonValue &source){
         m_type=source.m_type;                                  //--- Copy the type from source
         m_booleanValue=source.m_booleanValue;                  //--- Copy the boolean value from source
         m_integerValue=source.m_integerValue;                  //--- Copy the integer value from source
         m_doubleValue=source.m_doubleValue;                    //--- Copy the double value from source
         m_stringValue=source.m_stringValue;                    //--- Copy the string value from source
         CopyChildrenFrom(source);                              //--- Copy children from source
      }
      virtual void CopyChildrenFrom(const JsonValue &source){
         int numChildren=ArrayResize(m_children,ArraySize(source.m_children)); //--- Resize children array to match source size
         for(int index=0; index<numChildren; index++){          //--- Loop through each child
            m_children[index]=source.m_children[index];         //--- Copy child from source
            m_children[index].m_parent=GetPointer(this);        //--- Set parent of child to current object
         }
      }
   public:
      JsonValue m_children[];                                   //--- Array to hold child JSON values
      string m_key;                                             //--- Key for this JSON value
      string m_temporaryKey;                                    //--- Temporary key used during parsing
      JsonValue *m_parent;                                      //--- Pointer to parent JSON value
      JsonValueType m_type;                                     //--- Type of this JSON value
      bool m_booleanValue;                                      //--- Boolean value storage
      long m_integerValue;                                      //--- Integer value storage
      double m_doubleValue;                                     //--- Double value storage
      string m_stringValue;                                     //--- String value storage
      static int encodingCodePage;                              //--- Static code page for encoding
   public:
      JsonValue(){
         Reset();                                               //--- Call reset to initialize
      }
      JsonValue(JsonValue *parent,JsonValueType type){
         Reset();                                               //--- Call reset to initialize
         m_type=type;                                           //--- Set the type
         m_parent=parent;                                       //--- Set the parent
      }
      JsonValue(JsonValueType type,string value){
         Reset();                                               //--- Call reset to initialize
         SetFromString(type,value);                             //--- Set value from string based on type
      }
      JsonValue(const int integerValue){
         Reset();                                               //--- Call reset to initialize
         m_type=JsonInteger;                                    //--- Set type to integer
         m_integerValue=integerValue;                           //--- Set integer value
         m_doubleValue=(double)m_integerValue;                  //--- Convert to double
         m_stringValue=IntegerToString(m_integerValue);         //--- Convert to string
         m_booleanValue=m_integerValue!=0;                      //--- Set boolean based on integer
      }
      JsonValue(const long longValue){
         Reset();                                               //--- Call reset to initialize
         m_type=JsonInteger;                                    //--- Set type to integer
         m_integerValue=longValue;                              //--- Set integer value
         m_doubleValue=(double)m_integerValue;                  //--- Convert to double
         m_stringValue=IntegerToString(m_integerValue);         //--- Convert to string
         m_booleanValue=m_integerValue!=0;                      //--- Set boolean based on integer
      }
      JsonValue(const double doubleValue){
         Reset();                                               //--- Call reset to initialize
         m_type=JsonDouble;                                     //--- Set type to double
         m_doubleValue=doubleValue;                             //--- Set double value
         m_integerValue=(long)m_doubleValue;                    //--- Convert to integer
         m_stringValue=DoubleToString(m_doubleValue);           //--- Convert to string
         m_booleanValue=m_integerValue!=0;                      //--- Set boolean based on integer
      }
      JsonValue(const bool booleanValue){
         Reset();                                               //--- Call reset to initialize
         m_type=JsonBoolean;                                    //--- Set type to boolean
         m_booleanValue=booleanValue;                           //--- Set boolean value
         m_integerValue=m_booleanValue;                         //--- Convert to integer
         m_doubleValue=m_booleanValue;                          //--- Convert to double
         m_stringValue=IntegerToString(m_integerValue);         //--- Convert to string
      }
      JsonValue(const JsonValue &other){
         Reset();                                               //--- Call reset to initialize
         CopyFrom(other);                                       //--- Copy from other object
      }
      // DECONSTRUCTOR
      ~JsonValue(){
         Reset();                                               //--- Call reset to clean up
      }
}

Comenzamos la implementación del framework de análisis de JSON, centrándonos en la clase fundamental «JsonValue» para gestionar datos JSON de cara a la integración con API de IA. En primer lugar, definimos una macro «DEBUG_PRINT» con el valor «false» para controlar la salida de depuración, lo que garantiza un registro mínimo durante la fase de producción. A continuación, definimos una enumeración «JsonValueType» con los valores (JsonUndefined, JsonNull, JsonBoolean, JsonInteger, JsonDouble, JsonString, JsonArray, JsonObject) para clasificar los tipos de datos JSON, lo que permite a la clase gestionar estructuras diversas como las que devuelven las API.

Luego implementamos la clase «JsonValue» con miembros públicos: un array «m_children» para los elementos JSON anidados, las cadenas «m_key» y «m_temporaryKey» para almacenar las claves durante el análisis, un puntero «m_parent» para las relaciones jerárquicas, una variable «m_type» de tipo «JsonValueType» para el tipo de datos, y las variables «m_booleanValue», «m_integerValue», «m_doubleValue» y «m_stringValue» para almacenar los datos correspondientes, además de una variable estática «encodingCodePage» para la codificación de caracteres. Asimismo, ofrecemos varios constructores para inicializar objetos «JsonValue»: un constructor por defecto que llama a «Reset», otro con el objeto padre y el tipo, otro con el tipo y un valor de cadena, y otros para la construcción a partir de enteros, long, double, booleanos y por copia, lo que garantiza flexibilidad a la hora de crear elementos JSON.

El método «Reset» restablece todos los miembros a sus valores por defecto (por ejemplo, padre nulo, cadenas vacías, tipo indefinido, valores a cero y array de hijos vacío), mientras que los métodos «CopyFrom», «CopyDataFrom» y «CopyChildrenFrom» facilitan la copia profunda de estructuras JSON, incluidos los hijos, con la reasignación adecuada de los padres. Dicha implementación básica establece la estructura necesaria para analizar y manipular datos JSON, algo fundamental para futuras interacciones con la API de IA. A continuación, podemos proceder a implementar alguna función virtual pura de la clase, todavía bajo un especificador de acceso «public» para completar la implementación. Utilizamos «virtual» para que el sistema sea flexible y polimórfico.

public:
   virtual bool IsNumericValue(){
      return (m_type==JsonDouble || m_type==JsonInteger);    //--- Check if type is double or integer
   }
   virtual JsonValue *FindChildByKey(string key){
      for(int index=ArraySize(m_children)-1; index>=0; --index){ //--- Loop backwards through children
         if(m_children[index].m_key==key){                   //--- Check if key matches
            return GetPointer(m_children[index]);            //--- Return pointer to matching child
         }
      }
      return NULL;                                           //--- Return NULL if no match
   }
   virtual JsonValue *HasChildWithKey(string key,JsonValueType type=JsonUndefined);
   virtual JsonValue *operator[](string key);
   virtual JsonValue *operator[](int index);
   void operator=(const JsonValue &value){
      CopyFrom(value);                                       //--- Copy from value
   }
   void operator=(const int integerValue){
      m_type=JsonInteger;                                    //--- Set type to integer
      m_integerValue=integerValue;                           //--- Set integer value
      m_doubleValue=(double)m_integerValue;                  //--- Convert to double
      m_booleanValue=m_integerValue!=0;                      //--- Set boolean based on integer
   }
   void operator=(const long longValue){
      m_type=JsonInteger;                                    //--- Set type to integer
      m_integerValue=longValue;                              //--- Set integer value
      m_doubleValue=(double)m_integerValue;                  //--- Convert to double
      m_booleanValue=m_integerValue!=0;                      //--- Set boolean based on integer
   }
   void operator=(const double doubleValue){
      m_type=JsonDouble;                                     //--- Set type to double
      m_doubleValue=doubleValue;                             //--- Set double value
      m_integerValue=(long)m_doubleValue;                    //--- Convert to integer
      m_booleanValue=m_integerValue!=0;                      //--- Set boolean based on integer
   }
   void operator=(const bool booleanValue){
      m_type=JsonBoolean;                                    //--- Set type to boolean
      m_booleanValue=booleanValue;                           //--- Set boolean value
      m_integerValue=(long)m_booleanValue;                   //--- Convert to integer
      m_doubleValue=(double)m_booleanValue;                  //--- Convert to double
   }
   void operator=(string stringValue){
      m_type=(stringValue!=NULL)?JsonString:JsonNull;        //--- Set type to string or null
      m_stringValue=stringValue;                             //--- Set string value
      m_integerValue=StringToInteger(m_stringValue);         //--- Convert to integer
      m_doubleValue=StringToDouble(m_stringValue);           //--- Convert to double
      m_booleanValue=stringValue!=NULL;                      //--- Set boolean based on string presence
   }

   bool operator==(const int integerValue){return m_integerValue==integerValue;}  //--- Compare integer value
   bool operator==(const long longValue){return m_integerValue==longValue;}       //--- Compare long value
   bool operator==(const double doubleValue){return m_doubleValue==doubleValue;}  //--- Compare double value
   bool operator==(const bool booleanValue){return m_booleanValue==booleanValue;} //--- Compare boolean value
   bool operator==(string stringValue){return m_stringValue==stringValue;}        //--- Compare string value
  
   bool operator!=(const int integerValue){return m_integerValue!=integerValue;}  //--- Check inequality for integer
   bool operator!=(const long longValue){return m_integerValue!=longValue;}       //--- Check inequality for long
   bool operator!=(const double doubleValue){return m_doubleValue!=doubleValue;}  //--- Check inequality for double
   bool operator!=(const bool booleanValue){return m_booleanValue!=booleanValue;} //--- Check inequality for boolean
   bool operator!=(string stringValue){return m_stringValue!=stringValue;}        //--- Check inequality for string

   long ToInteger() const{return m_integerValue;}            //--- Return integer value
   double ToDouble() const{return m_doubleValue;}            //--- Return double value
   bool ToBoolean() const{return m_booleanValue;}            //--- Return boolean value
   string ToString(){return m_stringValue;}                  //--- Return string value

   virtual void SetFromString(JsonValueType type,string stringValue){
      m_type=type;                                           //--- Set the type
      switch(m_type){                                        //--- Handle based on type
      case JsonBoolean:
         m_booleanValue=(StringToInteger(stringValue)!=0);   //--- Convert string to boolean
         m_integerValue=(long)m_booleanValue;                //--- Set integer from boolean
         m_doubleValue=(double)m_booleanValue;               //--- Set double from boolean
         m_stringValue=stringValue;                          //--- Set string value
         break;                                              //--- Exit case
      case JsonInteger:
         m_integerValue=StringToInteger(stringValue);        //--- Convert string to integer
         m_doubleValue=(double)m_integerValue;               //--- Set double from integer
         m_stringValue=stringValue;                          //--- Set string value
         m_booleanValue=m_integerValue!=0;                   //--- Set boolean from integer
         break;                                              //--- Exit case
      case JsonDouble:
         m_doubleValue=StringToDouble(stringValue);          //--- Convert string to double
         m_integerValue=(long)m_doubleValue;                 //--- Set integer from double
         m_stringValue=stringValue;                          //--- Set string value
         m_booleanValue=m_integerValue!=0;                   //--- Set boolean from integer
         break;                                              //--- Exit case
      case JsonString:
         m_stringValue=UnescapeString(stringValue);          //--- Unescape the string
         m_type=(m_stringValue!=NULL)?JsonString:JsonNull;   //--- Set type based on string presence
         m_integerValue=StringToInteger(m_stringValue);      //--- Convert to integer
         m_doubleValue=StringToDouble(m_stringValue);        //--- Convert to double
         m_booleanValue=m_stringValue!=NULL;                 //--- Set boolean based on string
         break;                                              //--- Exit case
      }
   }
   virtual string GetSubstringFromArray(char &jsonCharacterArray[],int startPosition,int substringLength){
      #ifdef __MQL4__
            if(substringLength<=0) return "";                //--- Return empty if length invalid in MQL4
      #endif
      char temporaryArray[];                                 //--- Declare temporary array
      ArrayCopy(temporaryArray,jsonCharacterArray,0,startPosition,substringLength); //--- Copy substring to temporary array
      return CharArrayToString(temporaryArray, 0, WHOLE_ARRAY, JsonValue::encodingCodePage); //--- Convert to string using code page
   }
   virtual void SetValue(const JsonValue &value){
      if(m_type==JsonUndefined) {m_type=JsonObject;}         //--- Set type to object if undefined
      CopyDataFrom(value);                                   //--- Copy data from value
   }
   virtual void SetArrayValues(const JsonValue &list[]);
   virtual JsonValue *AddChild(const JsonValue &item){
      if(m_type==JsonUndefined){m_type=JsonArray;}           //--- Set type to array if undefined
      return AddChildInternal(item);                         //--- Call internal add child
   }
   virtual JsonValue *AddChild(const int integerValue){
      JsonValue item(integerValue);                          //--- Create item from integer
      return AddChild(item);                                 //--- Add the item
   }
   virtual JsonValue *AddChild(const long longValue){
      JsonValue item(longValue);                             //--- Create item from long
      return AddChild(item);                                 //--- Add the item
   }
   virtual JsonValue *AddChild(const double doubleValue){
      JsonValue item(doubleValue);                           //--- Create item from double
      return AddChild(item);                                 //--- Add the item
   }
   virtual JsonValue *AddChild(const bool booleanValue){
      JsonValue item(booleanValue);                          //--- Create item from boolean
      return AddChild(item);                                 //--- Add the item
   }
   virtual JsonValue *AddChild(string stringValue){
      JsonValue item(JsonString,stringValue);                //--- Create item from string
      return AddChild(item);                                 //--- Add the item
   }
   virtual JsonValue *AddChildInternal(const JsonValue &item){
      int currentSize=ArraySize(m_children);                 //--- Get current children size
      ArrayResize(m_children,currentSize+1);                 //--- Resize array to add one more
      m_children[currentSize]=item;                          //--- Add the item
      m_children[currentSize].m_parent=GetPointer(this);     //--- Set parent to current object
      return GetPointer(m_children[currentSize]);            //--- Return pointer to added child
   }
   virtual JsonValue *CreateNewChild(){
      if(m_type==JsonUndefined) {m_type=JsonArray;}          //--- Set type to array if undefined
      return CreateNewChildInternal();                       //--- Call internal create new child
   }
   virtual JsonValue *CreateNewChildInternal(){
      int currentSize=ArraySize(m_children);                 //--- Get current children size
      ArrayResize(m_children,currentSize+1);                 //--- Resize array to add one more
      return GetPointer(m_children[currentSize]);            //--- Return pointer to new child
   }

   virtual string EscapeString(string value);
   virtual string UnescapeString(string value);

Sin salir del especificador de acceso público, implementamos el método «IsNumericValue», que comprueba si el valor JSON es un número devolviendo «true» si «m_type» es «JsonDouble» o «JsonInteger», lo cual permite realizar un control específico por tipo para los datos numéricos. A continuación, desarrollamos el método «FindChildByKey», que recorre hacia atrás el array «m_children» para localizar un elemento hijo con una clave coincidente, devolviendo un puntero a dicho elemento o NULL si no lo encuentra, lo que facilita el acceso a objetos JSON anidados.

Después definimos operadores de asignación sobrecargados («operator=») para los tipos «JsonValue», entero, long, double, booleano y cadena, actualizando «m_type» y los campos de valor correspondientes («m_integerValue», «m_doubleValue», «m_stringValue» y «m_booleanValue»), al tiempo que garantizamos la coherencia de tipos, por ejemplo, convirtiendo enteros a «double» y a cadenas para un almacenamiento unificado. También implementamos operadores de comparación («operator==» y «operator!=») para los tipos entero, long, double, booleano y cadena, lo que permite comparar directamente los valores con los campos correspondientes.

Además, ofrecemos los métodos de conversión «ToInteger», «ToDouble», «ToBoolean» y «ToString» para obtener los valores en sus respectivos formatos. El método «SetFromString» establece el valor JSON según el «JsonValueType» especificado, gestionando los tipos booleano, entero, double y de cadena mediante la conversión de la cadena de entrada y la actualización de los campos relacionados, con los valores de cadena desescapados mediante «UnescapeString». El método «GetSubstringFromArray» extrae una subcadena de un array de caracteres, copiando una parte especificada y convirtiéndola en una cadena mediante «CharArrayToString» con la «encodingCodePage» definida.

Por último, implementamos los métodos necesarios para gestionar los elementos hijos: «AddChild» (para «JsonValue», entero, long, double, booleano y cadena) añade un nuevo elemento hijo a «m_children», estableciendo el padre y el tipo según sea necesario; «CreateNewChild» y «CreateNewChildInternal» crean un hijo vacío; y «SetValue» copia datos desde otro «JsonValue», lo que garantiza que la clase pueda construir, manipular y acceder a estructuras JSON. Por último, podemos concluir la clase incluyendo los métodos para serializar, deserializar y codificar caracteres de respuestas y prompts.

public:
   virtual void SerializeToString(string &jsonString,bool includeKey=false,bool includeComma=false);
   virtual string SerializeToString(){
      string jsonString;                                     //--- Declare json string
      SerializeToString(jsonString);                         //--- Call serialize with default params
      return jsonString;                                     //--- Return the serialized string
   }
   virtual bool DeserializeFromArray(char &jsonCharacterArray[],int arrayLength,int &currentIndex);
   virtual bool ExtractStringFromArray(char &jsonCharacterArray[],int arrayLength,int &currentIndex);
   virtual bool DeserializeFromString(string jsonString,int encoding=CP_ACP){
      int currentIndex=0;                                    //--- Initialize current index
      Reset();                                               //--- Reset the object
      JsonValue::encodingCodePage=encoding;                  //--- Set encoding code page
      char characterArray[];                                 //--- Declare character array
      int arrayLength=StringToCharArray(jsonString,characterArray,0,WHOLE_ARRAY,JsonValue::encodingCodePage); //--- Convert string to char array
      return DeserializeFromArray(characterArray,arrayLength,currentIndex); //--- Call deserialize from array
   }
   virtual bool DeserializeFromArray(char &jsonCharacterArray[],int encoding=CP_ACP){
      int currentIndex=0;                                    //--- Initialize current index
      Reset();                                               //--- Reset the object
      JsonValue::encodingCodePage=encoding;                  //--- Set encoding code page
      return DeserializeFromArray(jsonCharacterArray,ArraySize(jsonCharacterArray),currentIndex); //--- Call deserialize with size
   }

Aquí implementamos el método «SerializeToString» con parámetros para una referencia a cadena, «includeKey» e «includeComma», que convierte la estructura JSON en una cadena; añade una coma si «includeComma» es «true», incluye la clave si «includeKey» es «true» y gestiona los distintos tipos («JsonNull» como «null», «JsonBoolean» como «true» o «false», «JsonInteger» y «JsonDouble» mediante conversión a cadena, «JsonString» con valores escapados, «JsonArray» con elementos hijos entre corchetes y «JsonObject» con elementos hijos con clave), serializando recursivamente los hijos en estructuras anidadas. Una sobrecarga de conveniencia de «SerializeToString» crea una cadena, llama a la versión parametrizada y devuelve el resultado.

A continuación, implementamos «DeserializeFromString», que toma una cadena JSON y una codificación (por defecto, CP_ACP), restablece el objeto, establece «encodingCodePage», convierte la cadena en un array de caracteres mediante StringToCharArray y delega en «DeserializeFromArray». La sobrecarga «DeserializeFromArray» con codificación inicializa el índice, restablece el objeto, establece «encodingCodePage» y llama al método principal «DeserializeFromArray», que analiza un array de caracteres iterando por los caracteres, gestionando los espacios en blanco, los arrays («[» a «]»), los objetos («{» a «}»), los booleanos («true» o «false»), el valor nulo («null»), los números (detectando enteros o valores double mediante caracteres válidos) y las cadenas (con gestión del escapado), creando elementos hijos según sea necesario y gestionando la jerarquía con «m_parent» y «m_temporaryKey».

Por último, «ExtractStringFromArray» analiza las cadenas contenidas en el array, gestionando los caracteres escapados (por ejemplo, «\n», «\t», Unicode «\uXXXX») hasta llegar a una comilla de cierre, por si decidimos utilizar caracteres Unicode como emojis, lo que garantiza una extracción precisa de las cadenas. Este sería su aspecto.

EJEMPLO DE UNICODE

Ahora podemos definir estos métodos para que realicen su función prevista con precisión.

int JsonValue::encodingCodePage=CP_ACP;                      //--- Initialize static code page
//+------------------------------------------------------------------+
//| Checks if child with specific key and optional type exists       |
//+------------------------------------------------------------------+
JsonValue *JsonValue::HasChildWithKey(string key,JsonValueType type){
   for(int index=0; index<ArraySize(m_children); index++) if(m_children[index].m_key==key){ //--- Loop through children
      if(type==JsonUndefined || type==m_children[index].m_type){ //--- Check type condition
         return GetPointer(m_children[index]);               //--- Return matching child
      }
      break;                                                 //--- Exit loop
   }
   return NULL;                                              //--- Return NULL if no match
}
//+------------------------------------------------------------------+
//| Accessor for object key, creates if not exists                   |
//+------------------------------------------------------------------+
JsonValue *JsonValue::operator[](string key){
   if(m_type==JsonUndefined){m_type=JsonObject;}             //--- Set type to object if undefined
   JsonValue *value=FindChildByKey(key);                     //--- Find child by key
   if(value){return value;}                                  //--- Return if found
   JsonValue newValue(GetPointer(this),JsonUndefined);       //--- Create new undefined value
   newValue.m_key=key;                                       //--- Set key for new value
   value=AddChild(newValue);                                 //--- Add new value as child
   return value;                                             //--- Return the new value
}
//+------------------------------------------------------------------+
//| Accessor for array index, expands if necessary                   |
//+------------------------------------------------------------------+
JsonValue *JsonValue::operator[](int index){
   if(m_type==JsonUndefined) m_type=JsonArray;               //--- Set type to array if undefined
   while(index>=ArraySize(m_children)){                      //--- Loop to expand array if needed
      JsonValue newElement(GetPointer(this),JsonUndefined);  //--- Create new undefined element
      if(CheckPointer(AddChild(newElement))==POINTER_INVALID){return NULL;} //--- Add and check pointer
   }
   return GetPointer(m_children[index]);                     //--- Return pointer to element at index
}
//+------------------------------------------------------------------+
//| Sets array values from list                                      |
//+------------------------------------------------------------------+
void JsonValue::SetArrayValues(const JsonValue &list[]){
   if(m_type==JsonUndefined){m_type=JsonArray;}              //--- Set type to array if undefined
   int numChildren=ArrayResize(m_children,ArraySize(list));  //--- Resize children to list size
   for(int index=0; index<numChildren; ++index){             //--- Loop through list
      m_children[index]=list[index];                         //--- Copy from list
      m_children[index].m_parent=GetPointer(this);           //--- Set parent to current
   }
}

Para definir los métodos de clase fuera del cuerpo, usamos el operador de resolución de ámbito. Podríamos definirlos dentro, pero de esta forma el código resulta más modular. Luego inicializamos el miembro estático «encodingCodePage» con el valor CP_ACP (página de códigos activa) para definir la codificación de caracteres predeterminada en las conversiones de cadenas, lo que garantiza la compatibilidad con los datos JSON procedentes de las API de IA. A continuación, implementamos el método «HasChildWithKey», que itera hacia delante el array «m_children» para encontrar un elemento hijo con una clave coincidente y, opcionalmente, un «JsonValueType» específico (cuyo valor por defecto es «JsonUndefined» para ignorar el tipo), devolviendo un puntero al elemento hijo coincidente o NULL si no se encuentra, lo que mejora la eficiencia respecto a «FindChildByKey» al interrumpir la búsqueda antes de tiempo y permitir comprobaciones específicas de tipo.

Entonces, desarrollamos el método «operator» sobrecargado, que establece «m_type» en «JsonObject» si no está definido, busca un hijo con la clave dada mediante «FindChildByKey» y, si no lo encuentra, crea un nuevo «JsonValue» con esa clave y lo añade a «m_children» mediante «AddChild», devolviendo un puntero al hijo existente o al nuevo para permitir un acceso fluido al objeto. Del mismo modo, el método «operator» establece «m_type» en «JsonArray» si no está definido, amplía el array «m_children» con elementos no definidos si el índice supera su tamaño mediante «AddChild» y devuelve un puntero al elemento situado en el índice especificado, lo que garantiza un acceso seguro al array.

Por último, implementamos «SetArrayValues», que establece «m_type» en «JsonArray» si no está definido, ajusta el tamaño de «m_children» para que coincida con el de la lista de entrada, copia cada «JsonValue» de la lista a «m_children» y establece el padre de cada hijo como el objeto actual, lo que permite la asignación masiva de valores del array. Para los demás métodos, utilizamos un formato similar. Vamos a empezar por los métodos para serializar y deserializar cadenas de caracteres.

//+------------------------------------------------------------------+
//| Serializes the JSON value to string                              |
//+------------------------------------------------------------------+
void JsonValue::SerializeToString(string &jsonString,bool includeKey,bool includeComma){
   if(m_type==JsonUndefined){return;}                        //--- Return if undefined
   if(includeComma){jsonString+=",";}                        //--- Add comma if needed
   if(includeKey){jsonString+=StringFormat("\"%s\":", m_key);} //--- Add key if needed
   int numChildren=ArraySize(m_children);                    //--- Get number of children
   switch(m_type){                                           //--- Handle based on type
   case JsonNull:
      jsonString+="null";                                    //--- Append null
      break;                                                 //--- Exit case
   case JsonBoolean:
      jsonString+=(m_booleanValue?"true":"false");           //--- Append true or false
      break;                                                 //--- Exit case
   case JsonInteger:
      jsonString+=IntegerToString(m_integerValue);           //--- Append integer as string
      break;                                                 //--- Exit case
   case JsonDouble:
      jsonString+=DoubleToString(m_doubleValue);             //--- Append double as string
      break;                                                 //--- Exit case
   case JsonString:
   {
      string value=EscapeString(m_stringValue);              //--- Escape the string
      if(StringLen(value)>0){jsonString+=StringFormat("\"%s\"",value);} //--- Append escaped string if not empty
      else{jsonString+="null";}                              //--- Append null if empty
   }
   break;                                                    //--- Exit case
   case JsonArray:
      jsonString+="[";                                       //--- Start array
      for(int index=0; index<numChildren; index++){m_children[index].SerializeToString(jsonString,false,index>0);} //--- Serialize each child
      jsonString+="]";                                       //--- End array
      break;                                                 //--- Exit case
   case JsonObject:
      jsonString+="{";                                       //--- Start object
      for(int index=0; index<numChildren; index++){m_children[index].SerializeToString(jsonString,true,index>0);} //--- Serialize each child with key
      jsonString+="}";                                       //--- End object
      break;                                                 //--- Exit case
   }
}
//+------------------------------------------------------------------+
//| Deserializes from character array                                |
//+------------------------------------------------------------------+
bool JsonValue::DeserializeFromArray(char &jsonCharacterArray[],int arrayLength,int &currentIndex){
   string validNumericCharacters="0123456789+-.eE";          //--- Define valid number characters
   int startPosition=currentIndex;                           //--- Set start position
   for(; currentIndex<arrayLength; currentIndex++){          //--- Loop through array
      char currentCharacter=jsonCharacterArray[currentIndex]; //--- Get current character
      if(currentCharacter==0){break;}                        //--- Break if null character
      switch(currentCharacter){                              //--- Handle based on character
      case '\t':
      case '\r':
      case '\n':
      case ' ':                                              //--- Skip whitespace
         startPosition=currentIndex+1;                       //--- Update start position
         break;                                              //--- Exit case
      case '[':                                              //--- Start of array
      {
         startPosition=currentIndex+1;                       //--- Update start position
         if(m_type!=JsonUndefined){                          //--- Check if type is undefined
            if(DEBUG_PRINT){Print(m_key+" "+string(__LINE__));} //--- Print debug if error
            return false;                                    //--- Return false on error
         }
         m_type=JsonArray;                                   //--- Set type to array
         currentIndex++;                                     //--- Increment index
         JsonValue childValue(GetPointer(this),JsonUndefined); //--- Create child value
         while(childValue.DeserializeFromArray(jsonCharacterArray,arrayLength,currentIndex)){ //--- Deserialize children
            if(childValue.m_type!=JsonUndefined){AddChild(childValue);} //--- Add if not undefined
            if(childValue.m_type==JsonInteger || childValue.m_type==JsonDouble || childValue.m_type==JsonArray){currentIndex++;} //--- Increment if certain types
            childValue.Reset();                              //--- Reset child
            childValue.m_parent=GetPointer(this);            //--- Set parent
            if(jsonCharacterArray[currentIndex]==']'){break;} //--- Break if end of array
            currentIndex++;                                  //--- Increment index
            if(currentIndex>=arrayLength){                   //--- Check bounds
               if(DEBUG_PRINT){Print(m_key+" "+string(__LINE__));} //--- Print debug
               return false;                                 //--- Return false
            }
         }
         return (jsonCharacterArray[currentIndex]==']' || jsonCharacterArray[currentIndex]==0); //--- Return true if properly ended
      }
      break;                                                 //--- Exit case
      case ']':                                              //--- End of array
         if(!m_parent){return false;}                        //--- Return false if no parent
         return (m_parent.m_type==JsonArray);                //--- Check parent is array
      case ':':                                              //--- Key-value separator
      {
         if(m_temporaryKey==""){                             //--- Check temporary key
            if(DEBUG_PRINT){Print(m_key+" "+string(__LINE__));} //--- Print debug
            return false;                                    //--- Return false
         }
         JsonValue childValue(GetPointer(this),JsonUndefined); //--- Create child
         JsonValue *addedChild=AddChild(childValue);           //--- Add child
         addedChild.m_key=m_temporaryKey;                      //--- Set key
         m_temporaryKey="";                                    //--- Clear temporary key
         currentIndex++;                                       //--- Increment index
         if(!addedChild.DeserializeFromArray(jsonCharacterArray,arrayLength,currentIndex)){ //--- Deserialize child
            if(DEBUG_PRINT){Print(m_key+" "+string(__LINE__));} //--- Print debug
            return false;                                    //--- Return false
         }
         break;                                              //--- Exit case
      }
      case ',':                                              //--- Value separator
         startPosition=currentIndex+1;                       //--- Update start
         if(!m_parent && m_type!=JsonObject){                //--- Check conditions
            if(DEBUG_PRINT){Print(m_key+" "+string(__LINE__));} //--- Print debug
            return false;                                    //--- Return false
         }
         else if(m_parent){                                  //--- If has parent
            if(m_parent.m_type!=JsonArray && m_parent.m_type!=JsonObject){ //--- Check parent type
               if(DEBUG_PRINT){Print(m_key+" "+string(__LINE__));} //--- Print debug
               return false;                                //--- Return false
            }
            if(m_parent.m_type==JsonArray && m_type==JsonUndefined){return true;} //--- Return true for undefined in array
         }
         break;                                              //--- Exit case
      case '{':                                              //--- Start of object
         startPosition=currentIndex+1;                       //--- Update start
         if(m_type!=JsonUndefined){                          //--- Check type
            if(DEBUG_PRINT){Print(m_key+" "+string(__LINE__));} //--- Print debug
            return false;                                    //--- Return false
         }
         m_type=JsonObject;                                  //--- Set type to object
         currentIndex++;                                     //--- Increment index
         if(!DeserializeFromArray(jsonCharacterArray,arrayLength,currentIndex)){ //--- Recurse deserialize
            if(DEBUG_PRINT){Print(m_key+" "+string(__LINE__));} //--- Print debug
            return false;                                    //--- Return false
         }
         return (jsonCharacterArray[currentIndex]=='}' || jsonCharacterArray[currentIndex]==0); //--- Check end
         break;                                              //--- Exit case
      case '}':                                              //--- End of object
         return (m_type==JsonObject);                        //--- Check type is object
      case 't':
      case 'T':                                              //--- Start of true
      case 'f':
      case 'F':                                              //--- Start of false
         if(m_type!=JsonUndefined){                          //--- Check type
            if(DEBUG_PRINT){Print(m_key+" "+string(__LINE__));} //--- Print debug
            return false;                                    //--- Return false
         }
         m_type=JsonBoolean;                                 //--- Set type to boolean
         if(currentIndex+3<arrayLength){                     //--- Check for true
            if(StringCompare(GetSubstringFromArray(jsonCharacterArray, currentIndex, 4), "true", false)==0){ //--- Compare substring
               m_booleanValue=true;                          //--- Set to true
               currentIndex+=3;                              //--- Advance index
               return true;                                  //--- Return true
            }
         }
         if(currentIndex+4<arrayLength){                     //--- Check for false
            if(StringCompare(GetSubstringFromArray(jsonCharacterArray, currentIndex, 5), "false", false)==0){ //--- Compare substring
               m_booleanValue=false;                         //--- Set to false
               currentIndex+=4;                              //--- Advance index
               return true;                                  //--- Return true
            }
         }
         if(DEBUG_PRINT){Print(m_key+" "+string(__LINE__));} //--- Print debug
         return false;                                       //--- Return false
         break;                                              //--- Exit case
      case 'n':
      case 'N':                                              //--- Start of null
         if(m_type!=JsonUndefined){                          //--- Check type
            if(DEBUG_PRINT){Print(m_key+" "+string(__LINE__));} //--- Print debug
            return false;                                    //--- Return false
         }
         m_type=JsonNull;                                    //--- Set type to null
         if(currentIndex+3<arrayLength){                     //--- Check bounds
            if(StringCompare(GetSubstringFromArray(jsonCharacterArray,currentIndex,4),"null",false)==0){ //--- Compare substring
               currentIndex+=3;                              //--- Advance index
               return true;                                  //--- Return true
            }
         }
         if(DEBUG_PRINT){Print(m_key+" "+string(__LINE__));} //--- Print debug
         return false;                                       //--- Return false
         break;                                              //--- Exit case
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
      case '-':
      case '+':
      case '.':                                              //--- Start of number
      {
         if(m_type!=JsonUndefined){                          //--- Check type
            if(DEBUG_PRINT){Print(m_key+" "+string(__LINE__));} //--- Print debug
            return false;                                    //--- Return false
         }
         bool isDouble=false;                                //--- Initialize double flag
         int startOfNumber=currentIndex;                     //--- Set start of number
         while(jsonCharacterArray[currentIndex]!=0 && currentIndex<arrayLength){ //--- Loop to parse number
            currentIndex++;                                  //--- Increment index
            if(StringFind(validNumericCharacters,GetSubstringFromArray(jsonCharacterArray,currentIndex,1))<0){break;} //--- Break if invalid char
            if(!isDouble){isDouble=(jsonCharacterArray[currentIndex]=='.' || jsonCharacterArray[currentIndex]=='e' || jsonCharacterArray[currentIndex]=='E');} //--- Set double flag
         }
         m_stringValue=GetSubstringFromArray(jsonCharacterArray,startOfNumber,currentIndex-startOfNumber); //--- Get number string
         if(isDouble){                                       //--- If double
            m_type=JsonDouble;                               //--- Set type to double
            m_doubleValue=StringToDouble(m_stringValue);     //--- Convert to double
            m_integerValue=(long)m_doubleValue;              //--- Convert to integer
            m_booleanValue=m_integerValue!=0;                //--- Set boolean
         }
         else{                                               //--- Else integer
            m_type=JsonInteger;                              //--- Set type to integer
            m_integerValue=StringToInteger(m_stringValue);   //--- Convert to integer
            m_doubleValue=(double)m_integerValue;            //--- Convert to double
            m_booleanValue=m_integerValue!=0;                //--- Set boolean
         }
         currentIndex--;                                     //--- Decrement index
         return true;                                        //--- Return true
         break;                                              //--- Exit case
      }
      case '\"':                                             //--- Start of string or key
         if(m_type==JsonObject){                             //--- If object type
            currentIndex++;                                  //--- Increment index
            int startOfString=currentIndex;                  //--- Set start of string
            if(!ExtractStringFromArray(jsonCharacterArray,arrayLength,currentIndex)){ //--- Extract string
               if(DEBUG_PRINT){Print(m_key+" "+string(__LINE__));} //--- Print debug
               return false;                                 //--- Return false
            }
            m_temporaryKey=GetSubstringFromArray(jsonCharacterArray,startOfString,currentIndex-startOfString); //--- Set temporary key
         }
         else{                                               //--- Else value string
            if(m_type!=JsonUndefined){                       //--- Check type
               if(DEBUG_PRINT){Print(m_key+" "+string(__LINE__));} //--- Print debug
               return false;                                 //--- Return false
            }
            m_type=JsonString;                               //--- Set type to string
            currentIndex++;                                  //--- Increment index
            int startOfString=currentIndex;                  //--- Set start of string
            if(!ExtractStringFromArray(jsonCharacterArray,arrayLength,currentIndex)){ //--- Extract string
               if(DEBUG_PRINT){Print(m_key+" "+string(__LINE__));} //--- Print debug
               return false;                                 //--- Return false
            }
            SetFromString(JsonString,GetSubstringFromArray(jsonCharacterArray,startOfString,currentIndex-startOfString)); //--- Set from extracted string
            return true;                                     //--- Return true
         }
         break;                                              //--- Exit case
      }
   }
   return true;                                           
      //--- Return true at end
}

Continuaremos con la implementación del framework de análisis sintáctico, centrándonos en los métodos clave de serialización y deserialización.

En primer lugar, definimos el método «SerializeToString», que convierte una estructura JSON en una cadena; omite los tipos no definidos, añade una coma si «includeComma» es verdadero, añade la clave si «includeKey» es verdadero utilizando StringFormat, y gestiona cada caso de «m_type»: «JsonNull» añade «null», «JsonBoolean» añade «true» o «false» en función de «m_booleanValue», «JsonInteger» utiliza IntegerToString para «m_integerValue», «JsonDouble» utiliza DoubleToString para «m_doubleValue», «JsonString» escapa «m_stringValue» con «EscapeString» y lo envuelve entre comillas (o «null» si está vacío), «JsonArray» envuelve los hijos entre corchetes con llamadas recursivas a «SerializeToString» (sin claves, añadiendo comas para los elementos que no sean el primero), y «JsonObject» envuelve los hijos entre llaves, incluyendo las claves. Una sobrecarga de conveniencia de «SerializeToString» crea una cadena, llama a la versión parametrizada con los argumentos predeterminados y devuelve el resultado.

A continuación, implementamos «DeserializeFromArray», que analiza un array de caracteres para construir una estructura JSON, utilizando una cadena de caracteres numéricos válidos («0123456789+-.eE») e iterando por el array; omite los espacios en blanco, gestiona los arrays («[») estableciendo «m_type» en «JsonArray» y deserializando recursivamente los elementos secundarios hasta llegar a un corchete de cierre, valida el cierre del array, procesa los separadores clave-valor («:») asignando «m_temporaryKey» a un nuevo elemento secundario, gestiona los separadores de valores («,»), procesa los objetos («{») estableciendo «m_type» en «JsonObject» y deserializando los elementos secundarios hasta encontrar una llave de cierre, valida el cierre del objeto, analiza los valores booleanos («true» o «false») estableciendo «m_booleanValue», analiza el valor nulo («null») estableciendo «m_type» en «JsonNull», analiza los números detectando la notación decimal o exponencial para establecer «m_type» en «JsonDouble» o «JsonInteger», y analiza las cadenas (entre comillas) utilizando «ExtractStringFromArray» para gestionar los caracteres escapados.

El método «DeserializeFromArray» garantiza unas relaciones padre-hijo adecuadas y la gestión de errores mediante registros de «DEBUG_PRINT» para estructuras no válidas, lo que lo hace robusto para analizar respuestas. Por último, podemos definir los métodos de escapado.

//+------------------------------------------------------------------+
//| Extracts string from array handling escapes                      |
//+------------------------------------------------------------------+
bool JsonValue::ExtractStringFromArray(char &jsonCharacterArray[],int arrayLength,int &currentIndex){
   for(; jsonCharacterArray[currentIndex]!=0 && currentIndex<arrayLength; currentIndex++){ //--- Loop through string
      char currentCharacter=jsonCharacterArray[currentIndex]; //--- Get current char
      if(currentCharacter=='\"') break;                      //--- Break on closing quote
      if(currentCharacter=='\\' && currentIndex+1<arrayLength){ //--- Handle escape
         currentIndex++;                                     //--- Increment for escaped char
         currentCharacter=jsonCharacterArray[currentIndex];  //--- Get escaped char
         switch(currentCharacter){                           //--- Handle escaped type
         case '/':
         case '\\':
         case '\"':
         case 'b':
         case 'f':
         case 'r':
         case 'n':
         case 't':
            break;                                           //--- Allowed escapes
         case 'u':                                           //--- Unicode escape
         {
            currentIndex++;                                  //--- Increment
            for(int hexDigitIndex=0; hexDigitIndex<4 && currentIndex<arrayLength && jsonCharacterArray[currentIndex]!=0; hexDigitIndex++,currentIndex++){ //--- Loop hex digits
               if(!((jsonCharacterArray[currentIndex]>='0' && jsonCharacterArray[currentIndex]<='9') || (jsonCharacterArray[currentIndex]>='A' && jsonCharacterArray[currentIndex]<='F') || (jsonCharacterArray[currentIndex]>='a' && jsonCharacterArray[currentIndex]<='f'))){ //--- Check hex
                  if(DEBUG_PRINT){Print(m_key+" "+CharToString(jsonCharacterArray[currentIndex])+" "+string(__LINE__));} //--- Print debug
                  return false;                                 //--- Return false on invalid hex
               }
            }
            currentIndex--;                                  //--- Decrement after loop
            break;                                           //--- Exit case
         }
         default:
            break;                                           //--- Handle other (commented return false)
         }
      }
   }
   return true;                                              //--- Return true
}
//+------------------------------------------------------------------+
//| Escapes special characters in string                             |
//+------------------------------------------------------------------+
string JsonValue::EscapeString(string stringValue){
   ushort inputCharacters[], escapedCharacters[];               //--- Declare arrays
   int inputLength=StringToShortArray(stringValue, inputCharacters); //--- Convert string to short array
   if(ArrayResize(escapedCharacters, 2*inputLength)!=2*inputLength){return NULL;} //--- Resize escaped array, return NULL on fail
   int escapedIndex=0;                                          //--- Initialize escaped index
   for(int inputIndex=0; inputIndex<inputLength; inputIndex++){ //--- Loop through input
      switch(inputCharacters[inputIndex]){                      //--- Handle special chars
      case '\\':
         escapedCharacters[escapedIndex]='\\';                  //--- Add escape
         escapedIndex++;                                        //--- Increment
         escapedCharacters[escapedIndex]='\\';                  //--- Add backslash
         escapedIndex++;                                        //--- Increment
         break;                                                 //--- Exit case
      case '"':
         escapedCharacters[escapedIndex]='\\';                  //--- Add escape
         escapedIndex++;                                        //--- Increment
         escapedCharacters[escapedIndex]='"';                   //--- Add quote
         escapedIndex++;                                        //--- Increment
         break;                                                 //--- Exit case
      case '/':
         escapedCharacters[escapedIndex]='\\';                  //--- Add escape
         escapedIndex++;                                        //--- Increment
         escapedCharacters[escapedIndex]='/';                   //--- Add slash
         escapedIndex++;                                        //--- Increment
         break;                                                 //--- Exit case
      case 8:
         escapedCharacters[escapedIndex]='\\';                  //--- Add escape
         escapedIndex++;                                        //--- Increment
         escapedCharacters[escapedIndex]='b';                   //--- Add backspace
         escapedIndex++;                                        //--- Increment
         break;                                                 //--- Exit case
      case 12:
         escapedCharacters[escapedIndex]='\\';                  //--- Add escape
         escapedIndex++;                                        //--- Increment
         escapedCharacters[escapedIndex]='f';                   //--- Add form feed
         escapedIndex++;                                        //--- Increment
         break;                                                 //--- Exit case
      case '\n':
         escapedCharacters[escapedIndex]='\\';                  //--- Add escape
         escapedIndex++;                                        //--- Increment
         escapedCharacters[escapedIndex]='n';                   //--- Add newline
         escapedIndex++;                                        //--- Increment
         break;                                                 //--- Exit case
      case '\r':
         escapedCharacters[escapedIndex]='\\';                  //--- Add escape
         escapedIndex++;                                        //--- Increment
         escapedCharacters[escapedIndex]='r';                   //--- Add carriage return
         escapedIndex++;                                        //--- Increment
         break;                                                 //--- Exit case
      case '\t':
         escapedCharacters[escapedIndex]='\\';                  //--- Add escape
         escapedIndex++;                                        //--- Increment
         escapedCharacters[escapedIndex]='t';                   //--- Add tab
         escapedIndex++;                                        //--- Increment
         break;                                                 //--- Exit case
      default:
         escapedCharacters[escapedIndex]=inputCharacters[inputIndex]; //--- Copy normal char
         escapedIndex++;                                        //--- Increment
         break;                                                 //--- Exit case
      }
   }
   stringValue=ShortArrayToString(escapedCharacters,0,escapedIndex); //--- Convert back to string
   return stringValue;                                          //--- Return escaped string
}
//+------------------------------------------------------------------+
//| Unescapes special characters in string                           |
//+------------------------------------------------------------------+
string JsonValue::UnescapeString(string stringValue){
   ushort inputCharacters[], unescapedCharacters[];             //--- Declare arrays
   int inputLength=StringToShortArray(stringValue, inputCharacters); //--- Convert to short array
   if(ArrayResize(unescapedCharacters, inputLength)!=inputLength){return NULL;} //--- Resize, return NULL on fail
   int outputIndex=0,inputIndex=0;                              //--- Initialize indices
   while(inputIndex<inputLength){                               //--- Loop through input
      ushort currentCharacter=inputCharacters[inputIndex];      //--- Get current char
      if(currentCharacter=='\\' && inputIndex<inputLength-1){   //--- Handle escape
         switch(inputCharacters[inputIndex+1]){                 //--- Handle escaped type
         case '\\':
            currentCharacter='\\';                              //--- Set to backslash
            inputIndex++;                                       //--- Increment
            break;                                              //--- Exit case
         case '"':
            currentCharacter='"';                               //--- Set to quote
            inputIndex++;                                       //--- Increment
            break;                                              //--- Exit case
         case '/':
            currentCharacter='/';                               //--- Set to slash
            inputIndex++;                                       //--- Increment
            break;                                              //--- Exit case
         case 'b':
            currentCharacter=8;                                 //--- Set to backspace
            inputIndex++;                                       //--- Increment
            break;                                              //--- Exit case
         case 'f':
            currentCharacter=12;                                //--- Set to form feed
            inputIndex++;                                       //--- Increment
            break;                                              //--- Exit case
         case 'n':
            currentCharacter='\n';                              //--- Set to newline
            inputIndex++;                                       //--- Increment
            break;                                              //--- Exit case
         case 'r':
            currentCharacter='\r';                              //--- Set to carriage return
            inputIndex++;                                       //--- Increment
            break;                                              //--- Exit case
         case 't':
            currentCharacter='\t';                              //--- Set to tab
            inputIndex++;                                       //--- Increment
            break;                                              //--- Exit case
         }
      }
      unescapedCharacters[outputIndex]=currentCharacter;        //--- Copy to output
      outputIndex++;                                            //--- Increment output
      inputIndex++;                                             //--- Increment input
   }
   stringValue=ShortArrayToString(unescapedCharacters,0,outputIndex); //--- Convert back to string
   return stringValue;                                          //--- Return unescaped string
}

Finalizamos la implementación centrándonos en los métodos de manejo de cadenas. En primer lugar, implementamos el método «ExtractStringFromArray», que analiza una cadena a partir de un array de caracteres iterando hasta encontrar una comilla de cierre o el final del array, gestionando las secuencias de escape (por ejemplo, «\\», «\"», «/», «\b», «\f», «\n», «\r», «\t») y los escapes Unicode («\uXXXX») mediante la validación de cuatro dígitos hexadecimales, devolviendo «false» en caso de hexadecimal no válido o errores de límites, y utilizando «DEBUG_PRINT» para depurar los casos no válidos, lo que garantiza una extracción precisa de cadenas para el análisis de JSON.

A continuación, desarrollamos el método «EscapeString», que convierte una cadena a un formato compatible con JSON transformándola en un array short con StringToShortArray, redimensionando un array de salida al doble de la longitud de la entrada para dar cabida a los escapes, e iterando por cada carácter para gestionar los casos especiales: la barra invertida, las comillas, la barra, el retroceso (8), el salto de página (12), el salto de línea, el retorno de carro y la tabulación se escapan anteponiéndoles una barra invertida (por ejemplo, «\n» para el salto de línea), mientras que los demás caracteres se copian directamente, devolviendo la cadena escapada mediante la función ShortArrayToString.

Por último, implementamos el método «UnescapeString», que invierte el proceso de escapado convirtiendo la cadena de entrada en un array short, redimensionando un array de salida a la longitud de la entrada e iterando para procesar las secuencias de escape: cuando se encuentra una barra invertida, se interpreta el siguiente carácter (por ejemplo, «\n» como salto de línea, «\t» como tabulación), se copia el carácter desescapado en la salida, mientras que los caracteres no escapados se copian directamente, y se devuelve la cadena desescapada mediante «ShortArrayToString» o NULL si falla el redimensionamiento. Estos métodos garantizan que la clase «JsonValue» pueda gestionar caracteres especiales en las cadenas JSON, lo cual resulta fundamental para dar formato correctamente a las solicitudes de la API y analizar las respuestas de la IA de forma robusta y fiable. Más abajo se muestra un ejemplo del uso de caracteres especiales que se interpretarán correctamente para facilitar la comunicación y garantizar una comprensión fluida.

EJEMPLO DE USO DE CARACTERES ESPECIALES

Ahora que ya hemos implementado la clase, estamos listos para empezar a utilizar la lógica de análisis sintáctico. Pero primero le propongo probar el analizador sintáctico y comprobar que todo va bien. Lo haremos en la siguiente sección.


Prueba del analizador JSON

Para garantizar la fiabilidad de nuestro framework de análisis de JSON, debemos someter a rigurosas pruebas la clase «JsonValue» en MQL5 con el fin de verificar su capacidad para gestionar diversas estructuras JSON fundamentales para la integración con la API de IA. A continuación, describimos el enfoque de las pruebas, incluidos los casos de prueba, los resultados esperados y los métodos para validar la funcionalidad del analizador sintáctico en un entorno de trading. Vamos a definir una función de código y a llamarla en el controlador de eventos OnInit, mostrando su contenido. Este es el primer código. Utilizaremos el mismo archivo. Empezamos por mostrar «Hello world», como se suele hacer al iniciar un programa.

//+------------------------------------------------------------------+
//| Test Functions                                                   |
//+------------------------------------------------------------------+
void TestBasicSerialization(){
   Print("\n--- Testing Basic Serialization ---");
   
   JsonValue root;
   root["string"] = "hello world";
   root["number"] = 42;
   root["double"] = 3.14159;
   root["boolean"] = true;
   root["empty"] = "";
   
   string json = root.SerializeToString();
   Print("Serialized JSON: ", json);
   
   JsonValue parsed;
   if(parsed.DeserializeFromString(json)){
      Print("Deserialization successful");
      Print("String: ", parsed["string"].ToString());
      Print("Number: ", (int)parsed["number"].ToInteger());
      Print("Double: ", parsed["double"].ToDouble());
      Print("Boolean: ", parsed["boolean"].ToBoolean());
      Print("Empty: ", parsed["empty"].ToString());
   } else {
      Print("Deserialization failed!");
   }
}

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){
   TestBasicSerialization();
return(INIT_SUCCEEDED);
}

Luego creamos una función «TestBasicSerialization» y un objeto «JsonValue» llamado «root», al que asignamos valores utilizando «operator[]»: a «string» el valor «hello world», a «number» el valor 42, a «double» el valor 3.14159, a «boolean» el valor true y a «empty» una cadena vacía, cubriendo así los principales tipos de datos JSON (JsonString, JsonInteger, JsonDouble, JsonBoolean). A continuación, llamamos a «SerializeToString» sobre «root» para generar una cadena JSON, la almacenamos en «json» y la mostramos en el terminal de MetaTrader 5 con Print para su verificación.

Después creamos un nuevo objeto «JsonValue» llamado «parsed» y llamamos a «DeserializeFromString» con la cadena «json» para reconstruir la estructura JSON, comprobando si devuelve true para confirmar que se ha realizado correctamente; si es así, mostramos el mensaje «Deserialization successful» y utilizamos «ToString», «ToInteger» (convertido a int), «ToDouble» y «ToBoolean» para recuperar e imprimir los valores de «string», «number», «double», «boolean» y «empty» mediante «operator[]»; si falla, mostramos el mensaje «Deserialization failed!». Por último, en OnInit, llamamos a «TestBasicSerialization» para ejecutar la prueba al iniciar el programa y devolvemos «INIT_SUCCEEDED» para indicar que la configuración se ha realizado correctamente. Este es el resultado que obtenemos.

SERIALIZACIÓN BÁSICA INICIAL

En la imagen podemos ver que la serialización básica se ha realizado correctamente. Probemos ahora la deserialización básica y veamos el resultado.

void TestBasicDeserialization(){
   Print("\n--- Testing Basic Deserialization ---");
   
   string testJson = "{\"name\":\"John\",\"age\":30,\"isStudent\":false,\"salary\":1500.75}";
   
   JsonValue parsed;
   if(parsed.DeserializeFromString(testJson)){
      Print("Parsed successfully:");
      Print("Name: ", parsed["name"].ToString());
      Print("Age: ", (int)parsed["age"].ToInteger());
      Print("Is Student: ", parsed["isStudent"].ToBoolean());
      Print("Salary: ", parsed["salary"].ToDouble());
   } else {
      Print("Failed to parse JSON");
   }
}

Al realizar la prueba, obtenemos el siguiente resultado.

DESERIALIZACIÓN BÁSICA INICIAL

Eso también ha sido un éxito. Aumentemos ahora el nivel de complejidad y veamos qué pasa.

void TestComplexObject(){
   Print("\n--- Testing Complex Object ---");
   
   JsonValue root;
   root["person"]["name"] = "Alice";
   root["person"]["age"] = 25;
   root["person"]["isActive"] = true;
   root["person"]["score"] = 95.5;
   root["person"]["address"]["street"] = "123 Main St";
   root["person"]["address"]["city"] = "New York";
   root["person"]["address"]["zipcode"] = "10001";
   
   root["person"]["hobbies"].AddChild("reading");
   root["person"]["hobbies"].AddChild("gaming");
   root["person"]["hobbies"].AddChild("coding");
   
   root["person"]["preferences"]["theme"] = "dark";
   root["person"]["preferences"]["notifications"] = true;
   
   string json = root.SerializeToString();
   Print("Complex JSON: ", json);
   
   JsonValue parsed;
   if(parsed.DeserializeFromString(json)){
      Print("Round-trip successful");
      Print("Name: ", parsed["person"]["name"].ToString());
      Print("Age: ", (int)parsed["person"]["age"].ToInteger());
      Print("City: ", parsed["person"]["address"]["city"].ToString());
      Print("Zipcode: ", parsed["person"]["address"]["zipcode"].ToString());
      Print("Theme: ", parsed["person"]["preferences"]["theme"].ToString());
      Print("Hobby count: ", ArraySize(parsed["person"]["hobbies"].m_children));
   }
}

void TestArrayHandling(){
   Print("\n--- Testing Array Handling ---");
   
   JsonValue root;
   
   // Array of numbers
   for(int i = 0; i < 5; i++){
      root["numbers"].AddChild(i * 10);
   }
   
   // Array of mixed types
   root["mixed"].AddChild("string");
   root["mixed"].AddChild(123);
   root["mixed"].AddChild(45.67);
   root["mixed"].AddChild(true);
   
   // Array of objects
   JsonValue item;
   item["id"] = 1;
   item["name"] = "Item 1";
   item["price"] = 19.99;
   root["items"].AddChild(item);
   
   item["id"] = 2;
   item["name"] = "Item 2";
   item["price"] = 29.99;
   root["items"].AddChild(item);
   
   // Nested arrays
   JsonValue nestedArray;
   nestedArray.AddChild("nested1");
   nestedArray.AddChild("nested2");
   root["nested"].AddChild(nestedArray);
   
   string json = root.SerializeToString();
   Print("Array JSON: ", json);
   
   JsonValue parsed;
   if(parsed.DeserializeFromString(json)){
      Print("Numbers array length: ", ArraySize(parsed["numbers"].m_children));
      Print("Mixed array length: ", ArraySize(parsed["mixed"].m_children));
      Print("Items array length: ", ArraySize(parsed["items"].m_children));
      Print("First item name: ", parsed["items"][0]["name"].ToString());
      Print("Second item price: ", parsed["items"][1]["price"].ToDouble());
   }
}

void TestErrorHandling(){
   Print("\n--- Testing Error Handling ---");
   
   JsonValue parsed;
   
   // Test invalid JSON
   string invalidJson = "{\"name\": \"test\", \"age\": }";
   if(!parsed.DeserializeFromString(invalidJson)){
      Print("✓ Correctly rejected invalid JSON");
   }
   
   // Test malformed JSON
   string malformedJson = "{\"name\": \"test\" \"age\": 30}"; // Missing comma
   if(!parsed.DeserializeFromString(malformedJson)){
      Print("✓ Correctly rejected malformed JSON");
   }
   
   // Test empty string
   if(!parsed.DeserializeFromString("")){
      Print("✓ Correctly handled empty string");
   }
   
   // Test incomplete object
   string incompleteJson = "{\"name\": \"test\"";
   if(!parsed.DeserializeFromString(incompleteJson)){
      Print("✓ Correctly rejected incomplete JSON");
   }
}

Hacemos la prueba y obtenemos el siguiente resultado.

PRUEBAS COMPLEJAS

Probemos ahora el rendimiento, las estructuras anidadas y los tipos de datos.

void TestPerformance(){
   Print("\n--- Testing Performance ---");
   
   int startTime = GetTickCount();
   int iterations = 100;
   int successCount = 0;
   
   for(int i = 0; i < iterations; i++){
      JsonValue root;
      root["test_id"] = i;
      root["name"] = "Test Item " + IntegerToString(i);
      root["value"] = i * 1.5;
      root["active"] = (i % 2 == 0);
      
      // Add array
      for(int j = 0; j < 5; j++){
         root["tags"].AddChild("tag" + IntegerToString(j));
      }
      
      string json = root.SerializeToString();
      
      JsonValue parsed;
      if(parsed.DeserializeFromString(json)){
         successCount++;
      }
   }
   
   int endTime = GetTickCount();
   Print("Performance: ", iterations, " iterations in ", endTime - startTime, "ms");
   Print("Success rate: ", successCount, "/", iterations, " (", DoubleToString(successCount*100.0/iterations, 1), "%)");
}

void TestNestedStructures(){
   Print("\n--- Testing Nested Structures ---");
   
   JsonValue root;
   
   // Deep nesting
   root["level1"]["level2"]["level3"]["level4"]["value"] = "deep_nested";
   root["level1"]["level2"]["level3"]["level4"]["number"] = 999;
   
   // Array of objects with nesting
   JsonValue user;
   user["name"] = "John";
   user["profile"]["age"] = 30;
   user["profile"]["settings"]["theme"] = "dark";
   user["profile"]["settings"]["notifications"] = true;
   root["users"].AddChild(user);
   
   user["name"] = "Jane";
   user["profile"]["age"] = 25;
   user["profile"]["settings"]["theme"] = "light";
   user["profile"]["settings"]["notifications"] = false;
   root["users"].AddChild(user);
   
   string json = root.SerializeToString();
   Print("Nested JSON: ", json);
   
   JsonValue parsed;
   if(parsed.DeserializeFromString(json)){
      Print("✓ Nested structures parsed successfully");
      Print("Deep value: ", parsed["level1"]["level2"]["level3"]["level4"]["value"].ToString());
      Print("User count: ", ArraySize(parsed["users"].m_children));
      Print("First user theme: ", parsed["users"][0]["profile"]["settings"]["theme"].ToString());
      Print("Second user age: ", (int)parsed["users"][1]["profile"]["age"].ToInteger());
   }
}

void TestDataTypes(){
   Print("\n--- Testing Data Types ---");
   
   JsonValue root;
   
   // All supported data types
   root["string_type"] = "hello world";
   root["int_type"] = 42;
   root["long_type"] = 1234567890123;
   root["double_type"] = 3.14159265358979;
   root["bool_true"] = true;
   root["bool_false"] = false;
   root["empty_string"] = "";
   
   // Scientific notation
   root["scientific_positive"] = 1.23e+10;
   root["scientific_negative"] = 1.23e-10;
   
   string json = root.SerializeToString();
   Print("Data Types JSON: ", json);
   
   JsonValue parsed;
   if(parsed.DeserializeFromString(json)){
      Print("✓ All data types parsed successfully");
      Print("String: ", parsed["string_type"].ToString());
      Print("Integer: ", (int)parsed["int_type"].ToInteger());
      Print("Long: ", parsed["long_type"].ToInteger());
      Print("Double: ", parsed["double_type"].ToDouble());
      Print("Bool True: ", parsed["bool_true"].ToBoolean());
      Print("Bool False: ", parsed["bool_false"].ToBoolean());
      Print("Scientific Positive: ", parsed["scientific_positive"].ToDouble());
      Print("Scientific Negative: ", parsed["scientific_negative"].ToDouble());
   }
}

Aquí está el resultado.

PRUEBA DE RENDIMIENTO, ESTRUCTURAS ANIDADAS Y TIPOS DE DATOS

Ha sido un éxito total. Probemos ahora los caracteres de escape.

void TestEscapeCharacters(){
   Print("\n--- Testing Escape Characters ---");
   
   JsonValue root;
   
   // Various escape sequences - using only MQL5 supported escapes
   root["backslash"] = "\\\\"; // Double backslash for single backslash
   root["quote"] = "\\\"";     // Escaped quote
   root["newline"] = "\\n";    // Escaped newline
   root["tab"] = "\\t";        // Escaped tab
   root["carriage_return"] = "\\r"; // Escaped carriage return
   
   // For form feed and backspace, we need to use their actual ASCII codes
   // since MQL5 doesn't support \f and \b escape sequences
   root["form_feed"] = "\\u000C"; // Unicode escape for form feed
   root["backspace"] = "\\u0008"; // Unicode escape for backspace
   
   root["mixed_escapes"] = "Line1\\nLine2\\tTabbed\\\"Quoted\\\"\\\\Backslash";
   
   string json = root.SerializeToString();
   Print("Escape Chars JSON: ", json);
   
   JsonValue parsed;
   if(parsed.DeserializeFromString(json)){
      Print("✓ Escape characters parsed successfully");
      Print("Backslash: '", parsed["backslash"].ToString(), "'");
      Print("Quote: '", parsed["quote"].ToString(), "'");
      Print("Newline: '", parsed["newline"].ToString(), "'");
      Print("Tab: '", parsed["tab"].ToString(), "'");
      Print("Carriage Return: '", parsed["carriage_return"].ToString(), "'");
      Print("Form Feed: '", parsed["form_feed"].ToString(), "'");
      Print("Backspace: '", parsed["backspace"].ToString(), "'");
      Print("Mixed: '", parsed["mixed_escapes"].ToString(), "'");
   } else {
      Print("✗ Escape characters parsing failed");
   }
}

Obtenemos el siguiente resultado.

CARACTERES DE ESCAPE

Ha sido un éxito total. Como hemos comprobado que nuestra implementación de JSON es utilizable, ahora podemos usarla para crear integraciones de IA en los futuros programas que vayamos desarrollando.


Conclusión

En conclusión, hemos desarrollado un framework de análisis sintáctico de JSON (JavaScript Object Notation) en MQL5, implementando una clase «JsonValue» para gestionar la serialización y deserialización de datos JSON, algo fundamental para las interacciones con API (Application Programming Interface). Mediante métodos como «SerializeToString», «DeserializeFromArray» y «EscapeString», y realizando pruebas con funciones como «TestBasicSerialization», garantizamos un procesamiento fiable de diversas estructuras JSON, sentando así unas bases sólidas para futuros sistemas de trading basados en IA. En las siguientes partes, integraremos los sistemas de IA en nuestras aplicaciones de trading e interactuaremos con ellos. Permanezca atento.

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

Archivos adjuntos |
Peter Wlliams
Peter Wlliams | 23 sept 2025 en 23:44

Tiene muy buena pinta. ¿Por dónde empiezo?

¿El archivo de descarga es a._JSON_Code_File.mq5 y un EA?

He cambiado el nombre de A_JSON_Code_File.mq5 (me resultaba más familiar) y luego lo he compilado. No ha habido errores.

Sin embargo, no he visto ninguna referencia como EXPERT en mi plataforma; es decir, no he podido cargarlo ni ejecutarlo.

Solo tengo que «jugar» un poco e intentar entender qué pasa y por qué.


Muchas gracias por compartir lo que parece ser una oportunidad fantástica para utilizar la IA.

Allan Munene Mutiiria
Allan Munene Mutiiria | 24 sept 2025 en 11:44
Peter Wlliams #:

Tiene una pinta fantástica. ¿Por dónde empiezo?

¿El archivo de descarga es a._JSON_Code_File.mq5 y EA?

He cambiado el nombre de A_JSON_Code_File.mq5 (me resultaba más familiar) y luego lo he compilado. No ha habido errores.

Sin embargo, no he visto ninguna referencia a EXPERT en mi plataforma; es decir, no he podido cargarlo ni ejecutarlo.

Solo tengo que «jugar» un poco, probar y tratar de entender qué pasa y por qué.


Muchas gracias por compartir lo que parece ser una oportunidad fantástica para utilizar la IA.

Gracias por tu comentario. Parece que realmente deberías leer el artículo. Trata sobre el uso de JSON para permitir la integración de la IA en futuras versiones.
Creación de un sistema de trading profesional con Heikin Ashi (Parte 2): Desarrollo de un EA Creación de un sistema de trading profesional con Heikin Ashi (Parte 2): Desarrollo de un EA
En este artículo se explica cómo desarrollar un Asesor Experto (EA) profesional basado en Heikin Ashi en MQL5. Así, aprenderá a configurar parámetros de entrada, enumeraciones, indicadores y variables globales, además de a implementar la lógica principal de trading. También podrá ejecutar un backtest en oro para validar su trabajo.
Simplificación de bases de datos en MQL5 (Parte 1): Introducción a las bases de datos y SQL Simplificación de bases de datos en MQL5 (Parte 1): Introducción a las bases de datos y SQL
Analizamos cómo manipular bases de datos en MQL5 utilizando las funciones nativas del lenguaje. Cubrimos todo, desde la creación de tablas, la inserción, la actualización y la eliminación de datos hasta la importación y exportación de datos, todo ello con código de ejemplo. El contenido sirve como base sólida para comprender el funcionamiento interno del acceso a los datos, allanando el camino para el análisis de los ORM, donde crearemos uno en MQL5.
Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 41): Creación de un EA estadístico de niveles de precio en MQL5 Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 41): Creación de un EA estadístico de niveles de precio en MQL5
La estadística siempre ha sido un elemento fundamental del análisis financiero. Por definición, la estadística es la disciplina que recopila, analiza, interpreta y presenta datos de forma significativa. Ahora imagine aplicar ese mismo marco a las velas japonesas: condensar la acción del precio bruta en información cuantificable. ¿En qué medida resultaría útil conocer, para un periodo de tiempo concreto, la tendencia central, la dispersión y la distribución del comportamiento del mercado? En este artículo presentamos precisamente ese enfoque y mostramos cómo los métodos estadísticos pueden transformar los datos de las velas japonesas en señales claras y aplicables.
El oscilador Parafrac V2: integración de Parabolic SAR con Average True Range El oscilador Parafrac V2: integración de Parabolic SAR con Average True Range
El oscilador Parafrac V2 es una herramienta avanzada de análisis técnico que integra Parabolic SAR con Average True Range (ATR) para superar las limitaciones de su predecesor, que se basaba en fractales y era propenso a que los picos de señal eclipsaran las señales anteriores y actuales. Aprovechando la medida de volatilidad que ofrece ATR, la versión 2 proporciona un método más fluido y fiable para detectar tendencias, cambios de tendencia y divergencias, lo que ayuda a los traders a reducir la saturación de los gráficos y la parálisis del análisis.