//+------------------------------------------------------------------+ //| MentorTrader.mq5 | //| Manda tus operaciones cerradas a tu diario de MentorTrader. | //| | //| Solo LEE tu historial. No abre, no cierra y no modifica ninguna | //| operacion. Nunca pide ni manda tu contrasena: lo unico que viaja | //| es el codigo de 8 letras que copiaste de la app. | //| | //| No hace falta tenerlo abierto todo el rato: cada vez que abres | //| MetaTrader se pone al dia con lo que paso mientras estaba | //| cerrado. No se pierde nada. | //+------------------------------------------------------------------+ #property copyright "MentorTrader" #property link "https://mentortrader.ai" #property version "1.00" #property description "Manda tus operaciones cerradas a tu diario de MentorTrader." #property description "Solo lee. No opera. No usa tu contrasena." #property strict //--- lo unico que hay que rellenar input string Codigo = ""; // Codigo de 8 letras (lo copias de la app) input int RevisarCadaSeg = 60; // Cada cuantos segundos mira si hay algo nuevo input int DiasLaPrimeraVez = 90; // Cuanto historial manda la primera vez #define DIR "https://jaumelopo.com/mt.php" #define HOST "https://jaumelopo.com" string g_codigo = ""; long g_login = 0; string g_servidor = ""; string g_clave = ""; // nombre de la variable global donde se recuerda lo enviado datetime g_desde = 0; double g_firma = -1; // para no reenviar lo mismo una y otra vez string g_estado = ""; bool g_avisado = false; //+------------------------------------------------------------------+ void Pinta(string txt) { g_estado = txt; Comment("MentorTrader\n" + txt); } //+------------------------------------------------------------------+ int OnInit() { g_codigo = Codigo; StringTrimLeft(g_codigo); StringTrimRight(g_codigo); StringToUpper(g_codigo); if(StringLen(g_codigo) != 8) { Pinta("Falta el codigo.\nAbre la app, copia el codigo de 8 letras\ny ponlo en los ajustes de este programa."); Print("MentorTrader: falta el codigo de 8 letras. Se copia de la app, en Ajustes > Conectar MetaTrader."); return(INIT_FAILED); } g_login = AccountInfoInteger(ACCOUNT_LOGIN); g_servidor = AccountInfoString(ACCOUNT_SERVER); g_clave = "MT_" + g_codigo + "_" + IntegerToString(g_login); // Se recuerda hasta donde se envio, para no repetir trabajo al reabrir el terminal. if(GlobalVariableCheck(g_clave)) g_desde = (datetime)GlobalVariableGet(g_clave); if(g_desde <= 0) g_desde = TimeCurrent() - (datetime)(DiasLaPrimeraVez * 86400); int cada = RevisarCadaSeg; if(cada < 15) cada = 15; if(cada > 900) cada = 900; EventSetTimer(cada); Pinta("Cuenta " + IntegerToString(g_login) + "\nBuscando operaciones..."); Print("MentorTrader: arrancado en la cuenta ", g_login, " (", g_servidor, ")."); Enviar(); // ponerse al dia nada mas arrancar return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ void OnDeinit(const int motivo) { EventKillTimer(); Comment(""); } //+------------------------------------------------------------------+ void OnTimer() { Enviar(); } //--- cuando se cierra algo, se manda enseguida en vez de esperar al siguiente repaso void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &req, const MqlTradeResult &res) { if(trans.type == TRADE_TRANSACTION_DEAL_ADD) Enviar(); } //+------------------------------------------------------------------+ //| Escapa lo que va dentro de una cadena JSON | //+------------------------------------------------------------------+ string Esc(string s) { string r = ""; int n = StringLen(s); for(int i = 0; i < n; i++) { ushort c = StringGetCharacter(s, i); if(c == '"') r += "\\\""; else if(c == '\\') r += "\\\\"; else if(c < 32) r += " "; else r += ShortToString(c); } return r; } //+------------------------------------------------------------------+ //| Junta las operaciones y las manda | //+------------------------------------------------------------------+ void Enviar() { datetime hasta = TimeCurrent() + 3600; // se repasan tres dias hacia atras por si algo llego tarde datetime desde = (g_desde > 3 * 86400) ? (datetime)(g_desde - 3 * 86400) : (datetime)0; if(!HistorySelect(desde, hasta)) { Pinta("Cuenta " + IntegerToString(g_login) + "\nNo he podido leer el historial."); return; } int total = HistoryDealsTotal(); if(total <= 0) { Pinta("Cuenta " + IntegerToString(g_login) + "\nSin operaciones que mandar."); return; } string deals = ""; int n = 0; ulong mayor = 0; for(int i = 0; i < total; i++) { ulong t = HistoryDealGetTicket(i); if(t == 0) continue; long tipo = HistoryDealGetInteger(t, DEAL_TYPE); if(tipo != DEAL_TYPE_BUY && tipo != DEAL_TYPE_SELL) continue; long pos = HistoryDealGetInteger(t, DEAL_POSITION_ID); if(pos == 0) continue; long ent = HistoryDealGetInteger(t, DEAL_ENTRY); string sEnt = "in"; if(ent == DEAL_ENTRY_OUT) sEnt = "out"; else if(ent == DEAL_ENTRY_OUT_BY) sEnt = "out_by"; else if(ent == DEAL_ENTRY_IN) sEnt = "in"; else continue; // inout y demas: no encajan en una posicion simple string sTipo = (tipo == DEAL_TYPE_BUY) ? "buy" : "sell"; if(n > 0) deals += ","; deals += "{\"ticket\":" + IntegerToString((long)t) + ",\"position\":\"" + IntegerToString(pos) + "\"" + ",\"time\":" + IntegerToString((long)HistoryDealGetInteger(t, DEAL_TIME)) + ",\"type\":\"" + sTipo + "\"" + ",\"entry\":\"" + sEnt + "\"" + ",\"symbol\":\"" + Esc(HistoryDealGetString(t, DEAL_SYMBOL)) + "\"" + ",\"volume\":" + DoubleToString(HistoryDealGetDouble(t, DEAL_VOLUME), 2) + ",\"price\":" + DoubleToString(HistoryDealGetDouble(t, DEAL_PRICE), 5) + ",\"profit\":" + DoubleToString(HistoryDealGetDouble(t, DEAL_PROFIT), 2) + ",\"commission\":" + DoubleToString(HistoryDealGetDouble(t, DEAL_COMMISSION), 2) + ",\"swap\":" + DoubleToString(HistoryDealGetDouble(t, DEAL_SWAP), 2) + "}"; n++; if(t > mayor) mayor = t; if(n >= 4000) break; // el servidor no admite mas de una tacada } if(n == 0) { Pinta("Cuenta " + IntegerToString(g_login) + "\nSin operaciones que mandar."); return; } // si es exactamente lo mismo que la ultima vez, no se molesta al servidor double firma = (double)n * 1000000.0 + (double)(mayor % 1000000); if(firma == g_firma) { Pinta("Cuenta " + IntegerToString(g_login) + "\nAl dia. " + IntegerToString(n) + " operaciones enviadas."); return; } string cuerpo = "{\"codigo\":\"" + g_codigo + "\"" + ",\"login\":\"" + IntegerToString(g_login) + "\"" + ",\"servidor\":\"" + Esc(g_servidor) + "\"" + ",\"plataforma\":\"mt5\"" + ",\"version\":\"1.00\"" + ",\"deals\":[" + deals + "]}"; char envio[], recibe[]; string cabeceras = "Content-Type: application/json\r\n"; string respuesta = ""; // Ojo: en UTF-8 los bytes no son los caracteres. Se copia entero y se quita el cero final, // que si no viaja dentro del cuerpo y el servidor rechaza el JSON. int bytes = StringToCharArray(cuerpo, envio, 0, WHOLE_ARRAY, CP_UTF8); if(bytes > 0) ArrayResize(envio, bytes - 1); ResetLastError(); int codigoHttp = WebRequest("POST", DIR, cabeceras, 30000, envio, recibe, respuesta); if(codigoHttp == -1) { int e = GetLastError(); if(e == 4014 || e == 5203 || e == 4060) { if(!g_avisado) { Print("MentorTrader: falta permitir la direccion. En MetaTrader: Herramientas > Opciones > Asesores Expertos,"); Print("MentorTrader: marca 'Permitir WebRequest para las siguientes URL' y anade: ", HOST); g_avisado = true; } Pinta("Falta un permiso.\n\nHerramientas > Opciones > Asesores Expertos\nMarca 'Permitir WebRequest' y anade:\n" + HOST); } else { Pinta("Cuenta " + IntegerToString(g_login) + "\nNo he podido conectar (error " + IntegerToString(e) + ").\nLo reintento solo."); Print("MentorTrader: fallo de conexion, error ", e, ". Se reintenta en el siguiente repaso."); } return; } string texto = CharArrayToString(recibe, 0, WHOLE_ARRAY, CP_UTF8); if(codigoHttp != 200) { Pinta("Cuenta " + IntegerToString(g_login) + "\nEl servidor ha dicho que no.\nMira el registro (pestana Expertos)."); Print("MentorTrader: el servidor respondio ", codigoHttp, ": ", StringSubstr(texto, 0, 300)); return; } g_firma = firma; g_desde = TimeCurrent(); GlobalVariableSet(g_clave, (double)g_desde); GlobalVariablesFlush(); Pinta("Cuenta " + IntegerToString(g_login) + "\nConectado. " + IntegerToString(n) + " operaciones enviadas.\n" + TimeToString(TimeLocal(), TIME_MINUTES)); Print("MentorTrader: enviadas ", n, " operaciones. Respuesta: ", StringSubstr(texto, 0, 200)); } //+------------------------------------------------------------------+