Motor de decisión Multi-IA para MQL5 (Parte 1): Integrar múltiples IA con votación por consenso
Introducción
Cuando empecé a trabajar con Expert Advisors, me topé con un límite. Un EA con reglas fijas hace siempre lo mismo: no puede leer el contexto del mercado ni razonar sobre muchos datos a la vez. Yo quería algo que sí pudiera hacerlo, analizar mucha información, con un contexto más complejo, y rápido. Por eso empecé a conectar el EA a modelos de inteligencia artificial.
La primera versión usaba una sola IA. Funcionaba, pero apareció un problema: un solo modelo a veces se contradice o se equivoca, y no hay nada que lo controle. Si esa única respuesta está mal, el EA opera mal.
De ahí salió la idea de este artículo. En vez de confiar en una sola IA, consulto a varias y tomo la decisión por votación. Cuando varios modelos coinciden, hay más certeza en la respuesta. No es seguro al 100%, ninguna IA lo es, pero baja el riesgo de depender de un solo modelo. Además cada modelo tiene lo suyo: algunos son más rápidos, otros más inteligentes, otros analizan distinto. Combinarlos aprovecha lo mejor de cada uno.
Eso sí, conectarse a varias IA no es tan simple como repetir la misma consulta. Cada proveedor tiene su propia API: su propia forma de recibir el pedido y de devolver la respuesta, con distinto formato y distinta manera de conectarse. Hay que adaptar la comunicación a cada uno y después unificar lo que devuelven para poder compararlos.
En esta primera parte vamos a construir, paso a paso, un motor de decisión multi-IA para MetaTrader 5: una capa que conecta el EA a varios proveedores de IA, les hace la misma consulta y combina sus respuestas en una sola decisión por votación, lista para usar en tus propios EAs.
Arquitectura: una capa entre el EA y las IA
La idea central es simple: el EA no le habla a ninguna IA en concreto. Le habla a una sola capa intermedia, que llamo el "AI Manager". El EA le pide una decisión, y el Manager se encarga de todo lo demás.
El Manager tiene configurados varios proveedores (OpenAI, Claude, Gemini, DeepSeek). Cuando el EA pide una decisión, el Manager le hace la misma consulta a cada uno, junta las respuestas y las pasa por el sistema de votación. Recién ahí devuelve una sola decisión final.
La clave está en que cada proveedor sabe hablar con su propia API, pero todos devuelven el resultado en el mismo formato. Así el EA nunca necesita saber con qué IA está hablando: siempre recibe lo mismo.

Para que esto funcione, primero definimos las estructuras básicas que todos los proveedores van a compartir. Empezamos por la dirección de la señal y la lista de proveedores:
//--- Possible decisions returned by an AI enum ENUM_AI_SIGNAL { AI_SIGNAL_BUY, AI_SIGNAL_SELL, AI_SIGNAL_HOLD }; //--- Supported providers enum ENUM_AI_PROVIDER { AI_OPENAI, AI_CLAUDE, AI_GEMINI, AI_DEEPSEEK };
Después definimos una estructura única para la respuesta de cualquier IA. No importa qué proveedor conteste: todos devuelven un AIResponse con la misma forma.
//--- Standardized response struct AIResponse { ENUM_AI_PROVIDER provider; ENUM_AI_SIGNAL signal; double confidence; bool valid; };
Esta estructura es la pieza que hace posible todo lo demás. Como las cuatro IA devuelven el mismo AIResponse, después las podemos comparar y votar sin importar de cuál vinieron.
Conectar a los proveedores vía WebRequest
MetaTrader 5 se comunica con el exterior usando la función WebRequest(). Con ella el EA puede mandarle una consulta a la API de una IA y recibir la respuesta. Hay un paso obligatorio antes: cada URL que vayamos a usar hay que permitirla en Herramientas > Opciones > Asesores Expertos > Permitir WebRequest. Si no, la llamada falla.
Acá aparece lo que mencioné en la introducción: cada proveedor tiene su propia API. No todos se conectan igual ni piden los datos en el mismo formato. Esta tabla resume las diferencias:
| Proveedor | Endpoint | Autenticación | Cuerpo (body) |
|---|---|---|---|
| OpenAI | api.openai.com/v1/chat/completions | Authorization: Bearer KEY | messages[] |
| DeepSeek | api.deepseek.com/v1/chat/completions | Authorization: Bearer KEY | messages[] (igual a OpenAI) |
| Claude | api.anthropic.com/v1/messages | x-api-key + anthropic-version | messages[] + max_tokens |
| Gemini | .../models/MODELO:generateContent?key=KEY | la key va en la URL | contents[] |
Para no repetir código, primero hacemos una función genérica que manda un POST y devuelve la respuesta como texto:
//+------------------------------------------------------------------+ //| Generic POST request | //+------------------------------------------------------------------+ bool SendPost(const string url, const string headers, const string body, string &response) { char post[]; char result[]; string result_headers; StringToCharArray(body, post, 0, WHOLE_ARRAY, CP_UTF8); ArrayResize(post, ArraySize(post) - 1); // remove the trailing zero ResetLastError(); int status = WebRequest("POST", url, headers, 5000, post, result, result_headers); if(status == -1) { PrintFormat("WebRequest error %d. Allow the URL in Tools>Options.", GetLastError()); return(false); } response = CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8); return(status == 200); }
Después, cada proveedor tiene su propia función que arma la URL, los headers y el body según la tabla. Este es el caso de OpenAI:
//+------------------------------------------------------------------+ //| Query OpenAI | //+------------------------------------------------------------------+ bool QueryOpenAI(const string apiKey, const string prompt, string &response) { string url = "https://api.openai.com/v1/chat/completions"; string headers = "Content-Type: application/json\r\nAuthorization: Bearer " + apiKey + "\r\n"; string body = "{\"model\":\"gpt-4o-mini-2024-07-18\",\"messages\":[{\"role\":\"user\",\"content\":\"" + JsonEscape(prompt) + "\"}],\"temperature\":0.2}"; return(SendPost(url, headers, body, response)); }
Los otros tres proveedores tienen su propia función (QueryClaude, QueryGemini, QueryDeepSeek) siguiendo la tabla de arriba. Todas hacen lo mismo desde afuera: reciben la key y el prompt, y devuelven la respuesta cruda como texto. Un detalle: si el prompt tiene comillas o saltos de línea, hay que escaparlos para que el JSON sea válido. Lo resolvemos al armar el prompt en la sección siguiente.
Ahora tenemos las respuestas crudas de cada IA, pero cada una viene en un formato distinto. El próximo paso es parsearlas y convertirlas todas en el mismo AIResponse.
Parsear las respuestas: convertir todo en el mismo formato
Ahora tenemos las respuestas crudas, pero hay dos problemas. El primero: cada proveedor envuelve la respuesta en su propio JSON. El segundo: la IA podría contestar en texto libre, y eso es imposible de comparar.
La solución al segundo problema es la más importante, y es simple: le pedimos a la IA, en el prompt, que conteste siempre en un formato fijo. Por ejemplo: SIGNAL=BUY;CONFIDENCE=80. Como todas contestan así, leer la decisión es igual para todas. Esta función extrae un valor de ese formato:
//+------------------------------------------------------------------+ //| Extract the value of a "KEY=value" tag | //+------------------------------------------------------------------+ string ExtractTag(const string text, const string tag) { int start = StringFind(text, tag + "="); if(start < 0) return(""); start += StringLen(tag) + 1; int end = StringFind(text, ";", start); if(end < 0) end = StringLen(text); return(StringSubstr(text, start, end - start)); }
Con eso convertimos el texto de la IA en nuestro AIResponse estandarizado:
//+------------------------------------------------------------------+ //| Convert the AI text into an AIResponse | //+------------------------------------------------------------------+ AIResponse ParseAIText(ENUM_AI_PROVIDER provider, const string aiText) { AIResponse r; r.provider = provider; r.valid = false; r.signal = AI_SIGNAL_HOLD; r.confidence = 0.0; string sig = ExtractTag(aiText, "SIGNAL"); string conf = ExtractTag(aiText, "CONFIDENCE"); if(sig == "") return(r); if(sig == "BUY") r.signal = AI_SIGNAL_BUY; else if(sig == "SELL") r.signal = AI_SIGNAL_SELL; else r.signal = AI_SIGNAL_HOLD; r.confidence = (double)StringToInteger(conf); r.valid = true; return(r); }
Falta el primer problema: sacar el texto de la IA del JSON de cada proveedor. Como el envoltorio cambia según quién responda, usamos un extractor por proveedor:
//+------------------------------------------------------------------+ //| Extract the AI text depending on the provider | //+------------------------------------------------------------------+ string ExtractContent(ENUM_AI_PROVIDER provider, const string raw) { string key = "\"content\""; if(provider == AI_CLAUDE || provider == AI_GEMINI) key = "\"text\""; int k = StringFind(raw, key); if(k < 0) return(""); int colon = StringFind(raw, ":", k + StringLen(key)); // tolerates spaces if(colon < 0) return(""); int q1 = StringFind(raw, "\"", colon); if(q1 < 0) return(""); int q2 = StringFind(raw, "\"", q1 + 1); if(q2 < 0) return(""); return(StringSubstr(raw, q1 + 1, q2 - q1 - 1)); }
Un detalle que parece menor pero rompe todo: algunos proveedores devuelven el JSON "compacto" ("content":"...") y otros con un espacio después de los dos puntos ("content": "..."). Por eso no buscamos el texto literal "content":": buscamos la clave, después el dos puntos, y recién ahí la primera comilla. Así funciona con cualquiera de los dos formatos. Sigue siendo un extractor liviano, no un parser JSON completo; para algo más robusto (comillas escapadas, anidamiento) conviene usar un parser de verdad, como el que se muestra en el artículo Building AI-Powered Trading Systems in MQL5 (Part 1): JSON Handling for AI APIs.
El resultado: pase lo que pase, las cuatro IA terminan devolviendo el mismo AIResponse. Y eso es justo lo que necesitamos para poder votar.
El prompt estandarizado: misma pregunta para todas
Para que la votación tenga sentido, todas las IA tienen que opinar sobre lo mismo. Por eso usamos un único prompt, que cumple dos funciones: darle a cada modelo el mismo contexto de mercado, y obligarlo a responder en el formato fijo que vimos en la sección anterior.
Esta función arma el prompt con datos del mercado y agrega la instrucción de formato:
//+------------------------------------------------------------------+ //| Build the prompt | //+------------------------------------------------------------------+ string BuildPrompt(const string symbol) { double bid = SymbolInfoDouble(symbol, SYMBOL_BID); double close1 = iClose(symbol, PERIOD_H1, 1); double close2 = iClose(symbol, PERIOD_H1, 2); string context = StringFormat("Symbol: %s. Price: %.5f. H1 closes: %.5f, %.5f.", symbol, bid, close1, close2); string instruction = "You are a trading analyst. Reply ONLY in this exact format, without explanations: SIGNAL=BUY|SELL|HOLD;CONFIDENCE=0-100"; return(context + " " + instruction); }
El contexto es simple a propósito (precio y dos cierres), para que se entienda la idea. En la práctica le podés sumar todo lo que quieras: RSI, medias móviles, ATR, la hora, etc. Cuanto mejor el contexto, mejor la respuesta. Lo importante es la instrucción final: le exige a la IA responder solo SIGNAL=...;CONFIDENCE=..., sin explicaciones.
Falta un detalle: el prompt tiene comillas y puede tener saltos de línea, así que hay que escaparlo antes de meterlo en el JSON. Esta función lo resuelve:
//+------------------------------------------------------------------+ //| Escape a text string for JSON | //+------------------------------------------------------------------+ string JsonEscape(string text) { StringReplace(text, "\\", "\\\\"); StringReplace(text, "\"", "\\\""); StringReplace(text, "\n", "\\n"); StringReplace(text, "\r", ""); return(text); }
Ahora tenemos todo lo de "ida y vuelta": armamos el prompt, lo mandamos a cada IA, y convertimos cada respuesta en un AIResponse. Llegó el momento del corazón del sistema: la votación.
El motor de votación: de varias opiniones a una decisión
Ya tenemos un AIResponse por cada IA. Ahora hay que combinarlos en una sola decisión. No lo hacemos contando cabezas a secas: usamos votación ponderada por confianza. Una IA que está 90% segura pesa más que otra que está 55%.
La lógica tiene tres reglas: solo cuentan las respuestas válidas; si respondieron menos IA que el mínimo (quórum), no operamos; y gana el lado con más confianza acumulada, siempre que supere un umbral. Si hay empate o nadie llega al umbral, HOLD.
//+------------------------------------------------------------------+ //| Vote and return the final decision | //+------------------------------------------------------------------+ AIResponse VoteDecision(const AIResponse &votes[], int minQuorum, double minConfidence) { AIResponse decision; decision.signal = AI_SIGNAL_HOLD; decision.confidence = 0.0; decision.valid = false; double buyScore = 0.0, sellScore = 0.0; int buyCount = 0, sellCount = 0, validCount = 0; for(int i = 0; i < ArraySize(votes); i++) { if(!votes[i].valid) continue; validCount++; if(votes[i].signal == AI_SIGNAL_BUY) { buyScore += votes[i].confidence; buyCount++; } if(votes[i].signal == AI_SIGNAL_SELL) { sellScore += votes[i].confidence; sellCount++; } } if(validCount < minQuorum) return(decision); decision.valid = true; if(buyScore > sellScore && buyScore >= minConfidence) { decision.signal = AI_SIGNAL_BUY; decision.confidence = buyScore / buyCount; } else if(sellScore > buyScore && sellScore >= minConfidence) { decision.signal = AI_SIGNAL_SELL; decision.confidence = sellScore / sellCount; } return(decision); }
Veámoslo con un ejemplo. Supongamos que las cuatro IA responden esto, con minQuorum=2 y minConfidence=100:
| IA | Señal | Confianza |
|---|---|---|
| OpenAI | BUY | 80 |
| Claude | BUY | 70 |
| Gemini | SELL | 60 |
| DeepSeek | HOLD | — |
Acá buyScore=150 (dos votos), sellScore=60 (un voto). Hay quórum (respondieron 4). El lado comprador supera al vendedor y pasa el umbral de 100, así que la decisión final es BUY con 75% de confianza (150 / 2 votos). Si las IA estuvieran repartidas o inseguras y ningún lado llegara a 100, el sistema devolvería HOLD: ante la duda, no opera.

Fiabilidad: qué pasa cuando una IA falla
Las APIs fallan. A veces tardan demasiado, otras devuelven un error o una respuesta rota. Un sistema serio tiene que aguantar eso sin trabarse ni operar a ciegas. La arquitectura que armamos ya resuelve gran parte con tres mecanismos: (1) un timeout en la llamada (en SendPost ya pusimos 5000 ms para que una IA colgada no congele el EA); (2) fallas aisladas (si una IA falla, devolvemos un voto no válido y seguimos con las demás; la votación ignora esos votos); (3) el quórum como red (votamos con las que sí contestaron, y si son menos que el mínimo, HOLD).
Esta función junta los votos manejando las fallas:
//+------------------------------------------------------------------+ //| Invalid vote | //+------------------------------------------------------------------+ AIResponse InvalidResponse(ENUM_AI_PROVIDER p) { AIResponse r; r.provider = p; r.signal = AI_SIGNAL_HOLD; r.confidence = 0.0; r.valid = false; return(r); } //+------------------------------------------------------------------+ //| Collect the votes from all the providers | //+------------------------------------------------------------------+ int CollectVotes(const string symbol, AIResponse &votes[]) { string prompt = BuildPrompt(symbol); string raw; int n = 0; ArrayResize(votes, 4); if(QueryOpenAI(g_keyOpenAI, prompt, raw)) votes[n++] = ParseAIText(AI_OPENAI, ExtractContent(AI_OPENAI, raw)); else votes[n++] = InvalidResponse(AI_OPENAI); if(QueryClaude(g_keyClaude, prompt, raw)) votes[n++] = ParseAIText(AI_CLAUDE, ExtractContent(AI_CLAUDE, raw)); else votes[n++] = InvalidResponse(AI_CLAUDE); if(QueryGemini(g_keyGemini, prompt, raw)) votes[n++] = ParseAIText(AI_GEMINI, ExtractContent(AI_GEMINI, raw)); else votes[n++] = InvalidResponse(AI_GEMINI); if(QueryDeepSeek(g_keyDeepSeek, prompt, raw)) votes[n++] = ParseAIText(AI_DEEPSEEK, ExtractContent(AI_DEEPSEEK, raw)); else votes[n++] = InvalidResponse(AI_DEEPSEEK); return(n); }
Un punto práctico importante: consultar cuatro APIs lleva tiempo (es red, no es instantáneo). No conviene hacerlo en cada OnTick, porque trabaría el gráfico. Lo correcto es hacerlo cada cierto intervalo con OnTimer. Así el EA sigue fluido mientras las IA piensan.
Integración en un EA de ejemplo
Con todas las piezas listas, armar el EA es corto. La idea: cada cierto tiempo consultamos a las IA, votamos, y si hay consenso claro, operamos. Si no, no hacemos nada. Usamos OnTimer (no OnTick) por lo que vimos en fiabilidad.
#include <Trade/Trade.mqh> CTrade trade; input int InpQuorum = 2; // Minimum number of AIs that must answer input double InpMinConfidence = 100; // Minimum accumulated confidence input double InpLots = 0.10; // Volume //+------------------------------------------------------------------+ //| Convert the signal into readable text (BUY / SELL / HOLD) | //+------------------------------------------------------------------+ string SignalToStr(ENUM_AI_SIGNAL s) { if(s == AI_SIGNAL_BUY) return("BUY"); if(s == AI_SIGNAL_SELL) return("SELL"); return("HOLD"); } //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { LoadKeys(); EventSetTimer(60); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { EventKillTimer(); } //+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { if(PositionSelect(_Symbol)) return; AIResponse votes[]; CollectVotes(_Symbol, votes); AIResponse decision = VoteDecision(votes, InpQuorum, InpMinConfidence); if(!decision.valid || decision.signal == AI_SIGNAL_HOLD) return; if(decision.signal == AI_SIGNAL_BUY) trade.Buy(InpLots, _Symbol); else if(decision.signal == AI_SIGNAL_SELL) trade.Sell(InpLots, _Symbol); PrintFormat("Decision: %s, confidence %.1f%%", SignalToStr(decision.signal), decision.confidence); }
El flujo completo queda así: OnTimer llama a CollectVotes (consulta a las cuatro IA), después a VoteDecision (vota), y si la decisión es válida y no es HOLD, abre la operación.
Este EA es a propósito mínimo. No tiene stop loss, take profit ni gestión de riesgo, para que el foco quede en el motor multi-IA. En un EA real le agregarías SL/TP, riesgo por operación como % del balance, filtros de horario, etc.

Errores comunes que me frenaron (y cómo los resolví)
Te cuento la parte que no aparece en los tutoriales: cuando armé esto por primera vez, no funcionó. El código compilaba sin un solo error, cero warnings, y aun así el EA no operaba. Así que me senté a debuggear en vivo, y los problemas que encontré son justo los que te vas a encontrar vos. Por eso los dejo acá, para ahorrarte las horas que perdí yo.
1. Las cuatro APIs me devolvían HTTP 400. Todas al mismo tiempo, y el código se veía bien. El problema estaba escondido en SendPost: yo armaba el array de bytes con StringLen(body) y después le restaba uno con ArrayResize. Eso me comía el último carácter del JSON, justo la llave de cierre, y el JSON quedaba inválido. La API lo rechazaba sin explicar mucho. La solución fue convertir el texto completo con WHOLE_ARRAY, y recién ahí sacar el cero final. Un solo detalle, y se cae todo.
// Wrong: StringLen + ArrayResize -1 eats the closing } at the end StringToCharArray(body, post, 0, StringLen(body), CP_UTF8); // Right: WHOLE_ARRAY and then you remove only the trailing zero StringToCharArray(body, post, 0, WHOLE_ARRAY, CP_UTF8); ArrayResize(post, ArraySize(post) - 1);
2. OpenAI me respondía 200, pero yo no parseaba nada. La respuesta llegaba bien, el estado era el correcto, y aun así mi extractor devolvía vacío. Resultó que algunos proveedores te mandan el JSON pegado ("content":"...") y otros con un espacio después de los dos puntos ("content": "..."). Yo buscaba el texto literal "content":" y, con el espacio en el medio, nunca lo encontraba. Por eso el extractor que viste más arriba no busca el texto pegado: busca la clave, después el dos puntos, y recién ahí la primera comilla. Así anda con los dos formatos, te manden lo que te manden.
3. Error 404, "modelo no encontrado". Me pasó con Gemini: tenía puesto el nombre de un modelo que ya no existía. Los proveedores cambian y deprecan modelos bastante seguido, así que si te tira 404, lo primero es ir a la documentación y verificar el nombre exacto del modelo que está vigente hoy. Con OpenAI me pasó algo parecido: según cómo esté configurada tu cuenta, a veces tenés que poner el ID exacto con fecha (por ejemplo, gpt-4o-mini-2024-07-18) en lugar del nombre corto.
Para que no te vuelvas loco como yo, te dejo una tabla con lo que significa cada código que te puede tirar la API, y dónde mirar:
| Código | Qué pasa | Dónde mirar |
|---|---|---|
| 400 | El JSON está mal armado | Casi siempre es el body (mirá el punto 1) |
| 401 / 403 | Problema con la key o el modelo | Key mal copiada, sin permisos, o un modelo que tu cuenta no tiene habilitado |
| 404 | El modelo no existe | Nombre mal escrito o modelo deprecado |
| 429 | Te pasaste del límite | Demasiadas consultas por minuto, o cuota agotada |
| 402 | Sin saldo | No te queda crédito en esa cuenta |
Fijate que los dos últimos (429 y 402) no son errores de código, son de la cuenta. Si te aparecen, el EA está bien, lo que falta es saldo o esperar a que se libere el límite.
Y la moraleja, que para mí es la lección más importante de todo esto: que compile sin errores no quiere decir que funcione. El compilador te avisa si escribiste mal el código, pero no tiene idea de si una API te va a contestar bien. La única forma de saber si esto anda es probarlo en vivo y leer el log. A mí me salvó poner un PrintFormat en cada paso para ver exactamente dónde se rompía.
Seguridad y consideraciones
La regla más importante: nunca pongas tus claves API dentro del código del EA. Si la clave queda codificada en el .mq5 y luego compilás y compartís, vendés o pasás el EA, cualquiera puede extraerla. La key tiene que vivir afuera del código.
La solución que uso es simple: guardo las keys en un archivo keys.txt dentro de MQL5\Files, y el EA las lee al arrancar. La primera vez que lo corrés, si el archivo no existe, el EA crea una plantilla vacía con una línea por proveedor; vos solo la abrís y pegás tus keys. Ese archivo queda en la máquina del usuario y nunca viaja con el EA. El formato es una línea por proveedor: openai:sk-..., claude:sk-ant-..., gemini:..., deepseek:sk-...
Y esta es la función que lo lee (la LoadKeys que llamamos en OnInit):
string g_keyOpenAI, g_keyClaude, g_keyGemini, g_keyDeepSeek; //+------------------------------------------------------------------+ //| Read the keys from MQL5\Files\keys.txt | //+------------------------------------------------------------------+ bool LoadKeys() { int h = FileOpen("keys.txt", FILE_READ | FILE_TXT | FILE_ANSI); if(h == INVALID_HANDLE) { //--- The file does not exist yet: create an empty template and ask the user to fill it int w = FileOpen("keys.txt", FILE_WRITE | FILE_TXT | FILE_ANSI); if(w != INVALID_HANDLE) { FileWrite(w, "openai:"); FileWrite(w, "claude:"); FileWrite(w, "gemini:"); FileWrite(w, "deepseek:"); FileClose(w); } Print("keys.txt created in MQL5\\Files. Open it and put your API key after each provider, e.g. openai:YOUR_KEY"); return(false); } while(!FileIsEnding(h)) { string line = FileReadString(h); int sep = StringFind(line, ":"); if(sep < 0) continue; string name = StringSubstr(line, 0, sep); string key = StringSubstr(line, sep + 1); if(name == "openai") g_keyOpenAI = key; if(name == "claude") g_keyClaude = key; if(name == "gemini") g_keyGemini = key; if(name == "deepseek") g_keyDeepSeek = key; } FileClose(h); return(true); }
Además de la seguridad, hay tres puntos a tener en cuenta: (1) costo: cada decisión implica cuatro llamadas a APIs pagas; si consultás muy seguido en varios gráficos, el gasto se acumula; (2) latencia y límites: las llamadas tardan segundos y cada proveedor limita la cantidad por minuto; (3) incertidumbre: el consenso aumenta la certeza, pero no garantiza aciertos; probá siempre en demo y usá gestión de riesgo.
Y no te olvides de permitir las URLs en Herramientas > Opciones > Asesores Expertos > Permitir WebRequest.
Conclusión
En esta primera parte construimos un motor de decisión multi-IA para MetaTrader 5: una estructura única (AIResponse) que estandariza la respuesta de cualquier IA, una capa que conecta a varios proveedores por WebRequest, un parseo que unifica formatos distintos, un prompt único que da el mismo contexto a todos, un motor de votación ponderada por confianza con quórum, y un EA de ejemplo que junta todo.
El resultado es lo que buscábamos al principio. En vez de depender de una sola IA y de sus errores, el EA consulta a varias y decide por consenso. Eso no garantiza acertar, ninguna IA lo hace, pero baja el riesgo de confiar en un solo modelo y aprovecha lo mejor de cada uno.
Todo el código está adjunto para que lo descargues, lo pruebes y lo adaptes a tus propios EAs.
En la Parte 2 vamos a llevar esto más lejos: ponderar a cada IA según qué tan acertada fue en el pasado (que el sistema aprenda en cuál confiar más), sumar más proveedores, y agregar gestión de riesgo real (SL/TP y tamaño de posición según la confianza).
Si lo probás o le hacés mejoras, contámelo en los comentarios. La idea es que esto crezca entre todos.
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.
Utilizando redes neuronales en MetaTrader
Formulación de un Asesor Experto Multipar Dinámico (Parte 4): Ajuste de volatilidad y riesgo
Particularidades del trabajo con números del tipo double en MQL4
De novato a experto: Noticias animadas utilizando MQL5 (VIII) Botones de operación rápida para trading de noticias
- 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