//+------------------------------------------------------------------+ //| FuncionesBases.mqh | //| Copyright 2025, Niquel Mendoza. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Niquel Mendoza." #property link "https://www.mql5.com" #property strict #ifndef FUNCTIONS_MQH #define FUNCTIONS_MQH #include #include "StringToArray.mqh" #define INVALID_INDEX -1 //+------------------------------------------------------------------+ //| Definiciones | //+------------------------------------------------------------------+ const datetime wrong_datetime = 0; double DOUBLE_ARRAY_EMPTY[]; string EMPTY_STRING = ""; vector EMPTY_VECTOR = {}; #define NOT_DEFINED_STRING "Not Defined" #define UNDEFINED_REPLACE 1 enum idiomas { English, Spanish }; //+------------------------------------------------------------------+ //| Trabajo Con Arrays | //+------------------------------------------------------------------+ template int ArrayCopyCts(T& dst_array[], // array de destino const T& src_array[], // array de origen int dst_start = 0, // índice inicial para copiar desde el array de origen int src_start = 0, // primer índice del array de destino int count = WHOLE_ARRAY) // número de elementos ) { int src_size = ArraySize(src_array); int dst_size = ArraySize(dst_array); //--- if(src_start < 0 || src_start >= src_size) return 0; if(dst_start < 0) return 0; //--- int elements_to_copy = count; if(count == WHOLE_ARRAY || count < 0) elements_to_copy = src_size - src_start; //--- if(src_start + elements_to_copy > src_size) elements_to_copy = src_size - src_start; //--- if(elements_to_copy <= 0) return 0; //--- int required_size = dst_start + elements_to_copy; if(dst_size < required_size) ArrayResize(dst_array, required_size); //--- for(int i = 0; i < elements_to_copy; i++) dst_array[dst_start + i] = src_array[src_start + i]; return elements_to_copy; } template int ExpandArray(S &arr[], int spaces, S fillValue, ENUM_ALIGN_MODE align) { if(align == ALIGN_CENTER) return -1; int oldSize = ArraySize(arr); int newSize = oldSize + spaces; //--- if(spaces <= 0) return -1; //--- S temp[]; ArrayResize(temp, newSize); //--- for(int i = 0; i < newSize; i++) { temp[i] = fillValue; } //--- if(align == ALIGN_RIGHT) { for(int i = 0; i < oldSize; i++) { temp[i] = arr[i]; } } else // ALIGN_LEFT { for(int i = 0; i < oldSize; i++) { temp[i + spaces] = arr[i]; } } //--- ArrayResize(arr, newSize); for(int i = 0; i < newSize; i++) arr[i] = temp[i]; return newSize; } //+------------------------------------------------------------------+ template void AddArraySeries(S &Array[], const S &value) { ArrayResize(Array, ArraySize(Array) + 1); if(ArraySize(Array) > 1) for(int x = ArraySize(Array) - 1 ; x > 0 ; x--) Array[x] = Array[x - 1]; Array[0] = value; } //+------------------------------------------------------------------+ template bool DeleteArrayBiName(T &array[], const string &targetName, int capacity) { int size = ArraySize(array); for(int i = 0; i < size; i++) { if(array[i].name == targetName) { // Desplazar elementos hacia atrás for(int j = i; j < size - 1; j++) { array[j] = array[j + 1]; } ArrayResize(array, size - 1, capacity); return true; } } return false; } //+------------------------------------------------------------------+ template bool DeleteArrayBiName(T &array[], const string &targetName, T &out, int capacity) { int size = ArraySize(array); for(int i = 0; i < size; i++) { if(array[i].name == targetName) { out = array[i]; // Desplazar elementos hacia atrás for(int j = i; j < size - 1; j++) { array[j] = array[j + 1]; } ArrayResize(array, size - 1, capacity); return true; } } return false; } //+------------------------------------------------------------------+ template bool DeleteArrayByIndex(T &array[], const int index) { const int size = ArraySize(array); if(size == 0) return false; for(int j = index; j < size - 1; j++) array[j] = array[j + 1]; ArrayResize(array, size - 1); return true; } //+------------------------------------------------------------------+ template void RemoveMultipleIndexes(T &arr[], int &indexes_to_remove[], int capacity) { const int old_size = ArraySize(arr); const int remove_size = ArraySize(indexes_to_remove); if(old_size == 0 || remove_size == 0) return; int write_index = 0; for(int read_index = 0; read_index < old_size; read_index++) { bool is_removed = false; for(int k = 0; k < remove_size; k++) { if(indexes_to_remove[k] == read_index) { is_removed = true; break; } } if(!is_removed) { arr[write_index++] = arr[read_index]; } } ArrayResize(arr, write_index, capacity); } //+------------------------------------------------------------------+ template void RemoveMultipleIndexes(T &arr[], int16_t &indexes_to_remove[], int capacity) { int16_t old_size = (int16_t)ArraySize(arr); int16_t remove_size = (int16_t)ArraySize(indexes_to_remove); if(old_size == 0 || remove_size == 0) return; int16_t write_index = 0; for(int16_t read_index = 0; read_index < old_size; read_index++) { bool is_removed = false; for(int16_t k = 0; k < remove_size; k++) { if(indexes_to_remove[k] == read_index) { is_removed = true; break; } } if(!is_removed) { arr[write_index++] = arr[read_index]; } } ArrayResize(arr, write_index, capacity); } //+------------------------------------------------------------------+ template bool ArrayCopyAll(T &dst_array[], const T &source_array[]) { if(source_array.Size() < 1) return false; int total = (int)source_array.Size(); ArrayResize(dst_array, total); if(total == 1) { dst_array[0] = source_array[0]; return true; } else { for(int i = 0; i < total; i++) { dst_array[i] = source_array[i]; } return true; } } //+------------------------------------------------------------------+ template bool AddArrayVefificationByIndex(K &Array[], const K &Value) { for(int i = 0 ; i < ArraySize(Array) ; i++) { if(Array[i].index == Value.index) return false; } ArrayResize(Array, Array.Size() + 1); Array[Array.Size() - 1] = Value; return true; } //+------------------------------------------------------------------+ template bool AddArrayName(S &Array[], const S &Value) { for(int i = 0 ; i < ArraySize(Array) ; i++) if(Array[i].name == Value.name) return false; ArrayResize(Array, Array.Size() + 1); Array[Array.Size() - 1] = Value; return true; } //+------------------------------------------------------------------+ template bool AddArrayVal(T &Array[], const T Value) { for(int i = 0 ; i < ArraySize(Array) ; i++) if(Array[i] == Value) return false; ArrayResize(Array, Array.Size() + 1); Array[Array.Size() - 1] = Value; return true; } //+------------------------------------------------------------------+ #define AddArrayNoVerification1(array, value, size, capacity) ArrayResize(array, size + 1, capacity); \ array[size] = value; \ #define AddArrayNoVerification2(array, value, capacity) const int size = ArraySize(array); \ ArrayResize(array, size + 1, capacity); \ array[size] = value; \ template inline void AddArrayNoVerification(X &array[], const X &value, int capacity) { const int size = ArraySize(array); ArrayResize(array, size + 1, capacity); array[size] = value; } //+------------------------------------------------------------------+ template inline void AddArrayNoVerification(X* &array[], X* value, int capacity) { const int size = ArraySize(array); ArrayResize(array, size + 1, capacity); array[size] = value; } //+------------------------------------------------------------------+ template inline int ArrayFindValue(const H &array[], const H value) { for(int i = 0 ; i < ArraySize(array) ; i++) if(array[i] == value) return i; return -1; } //+------------------------------------------------------------------+ template bool ExistNameInArray(S &Array[], const S &value) { for(int i = 0; i < ArraySize(Array) ; i++) if(value.name == Array[i].name) return true; return false; } //+------------------------------------------------------------------+ template string ArrayToString(const T &array[], string separator) { string val = ""; // Inicializamos la cadena sin espacios extra for(int i = 0; i < ArraySize(array); i++) { if(i == ArraySize(array) - 1) // Si es el último elemento val += (string)array[i]; // No añadimos el separador else val += (string)array[i] + separator; // Añadimos el separador } return val; // Devolvemos la cadena construida } //+------------------------------------------------------------------+ template ST ArraYSumar(ST &array[], int start = 0, int count = WHOLE_ARRAY) { //--- if(count >= ArraySize(array) && count != WHOLE_ARRAY) { Print("Invalid count: ", count); return 0; } if(start < 0 || start >= ArraySize(array)) { Print("Invalid start: ", start, " | ArraySize: ", ArraySize(array)); return 0; } //--- if(count == WHOLE_ARRAY) count = ArraySize(array); ST suma = 0; //--- for(int i = start; i < count; i++) { suma += array[i]; } return suma; } /* template ST sum(ST &array[], int start = 0, int count = WHOLE_ARRAY) { //--- if(count >= ArraySize(array) && count != WHOLE_ARRAY) { Print("Invalid count: ", count); return 0; } if(start < 0 || start >= ArraySize(array)) { Print("Invalid start: ", start, " | ArraySize: ", ArraySize(array)); return 0; } //--- if(count == WHOLE_ARRAY) count = ArraySize(array); ST suma = 0; //--- for(int i = start; i < count; i++) { suma += array[i]; } return suma; } */ //--- enum MODE_CLOSET_INDEX { MAYOR_A_TARGET_TIME, MENOR_A_TARGET_TIME }; //+------------------------------------------------------------------+ template int GetClosestArrayIndexByTime(datetime target_time, S &array[], MODE_CLOSET_INDEX mode) { if(ArraySize(array) == 0) return -1; int closest_index = -1; int min_diff = INT_MAX; for(int i = 0; i < ArraySize(array); i++) { // Solo comparamos si el tiempo de bos[i] es mayor que target_time if((array[i].time > target_time && mode == MAYOR_A_TARGET_TIME) || (array[i].time < target_time && mode == MENOR_A_TARGET_TIME)) { int diff = (int)MathAbs(array[i].time - target_time); // Calculamos la diferencia actual if(diff < min_diff) { min_diff = diff; // Actualizamos la diferencia mínima closest_index = i; // Actualizamos el índice más cercano } } } return closest_index; // Retornamos el índice del Bos con el tiempo más cercano, o -1 si no se encontró } //+------------------------------------------------------------------+ template void RemoveDuplicates(T &array[]) { int size = ArraySize(array); if(size <= 1) return; int write_index = 0; for(int read_index = 0; read_index < size; read_index++) { bool is_duplicate = false; for(int j = 0; j < write_index; j++) { if(array[read_index] == array[j]) { is_duplicate = true; break; } } if(!is_duplicate) { array[write_index] = array[read_index]; write_index++; } } ArrayResize(array, write_index); } //+------------------------------------------------------------------+ template void SortArrayTimeByInitDayTime(S &array[], int left, int right) { if(left >= right) return; // Elegimos un pivote (el del medio) int pivotIndex = (left + right) >> 1; int pivotValue = TimeDifferenceByHHMMSS(array[pivotIndex].time); int i = left, j = right; while(i <= j) { // Encuentra un elemento mayor que el pivote while(TimeDifferenceByHHMMSS(array[i].time) > pivotValue) i++; // Encuentra un elemento menor que el pivote while(TimeDifferenceByHHMMSS(array[j].time) < pivotValue) j--; if(i <= j) { // Intercambia eventos si están fuera de lugar S temp = array[i]; array[i] = array[j]; array[j] = temp; i++; j--; } } // Orden recursivo de las dos mitades SortArrayTimeByInitDayTime(array, left, j); SortArrayTimeByInitDayTime(array, i, right); } //+------------------------------------------------------------------+ template void SortArrayTimeDescendente(S &array[], int left, int right) { if(left >= right) return; int pivotIndex = (left + right) >> 1; datetime pivotValue = array[pivotIndex].time; // Usar directamente el valor datetime int i = left, j = right; while(i <= j) { while(array[i].time > pivotValue) // Compara directamente los datetime i++; while(array[j].time < pivotValue) j--; if(i <= j) { S temp = array[i]; array[i] = array[j]; array[j] = temp; i++; j--; } } SortArrayTimeDescendente(array, left, j); SortArrayTimeDescendente(array, i, right); } //+------------------------------------------------------------------+ template void SortArrayTimeAscendente(S &array[], int left, int right) { if(left >= right) return; int pivotIndex = (left + right) >> 1; datetime pivotValue = array[pivotIndex].time; // Usar directamente el valor datetime int i = left, j = right; while(i <= j) { while(array[i].time < pivotValue) // Compara directamente los datetime i++; while(array[j].time > pivotValue) j--; if(i <= j) { S temp = array[i]; array[i] = array[j]; array[j] = temp; i++; j--; } } SortArrayTimeAscendente(array, left, j); SortArrayTimeAscendente(array, i, right); } //+------------------------------------------------------------------+ template void SortArrayPrice2Asendente(S &array[], int left, int right) { if(left >= right) return; int pivotIndex = (left + right) >> 1; double pivotValue = array[pivotIndex].price2; int i = left, j = right; while(i <= j) { while(array[i].price2 < pivotValue) i++; while(array[j].price2 > pivotValue) j--; if(i <= j) { S temp = array[i]; array[i] = array[j]; array[j] = temp; i++; j--; } } SortArrayPrice2Asendente(array, left, j); SortArrayPrice2Asendente(array, i, right); } //+------------------------------------------------------------------+ template void SortArrayPrice2Desendente(S &array[], int left, int right) { if(left >= right) return; int pivotIndex = (left + right) >> 1; double pivotValue = array[pivotIndex].price2; int i = left, j = right; while(i <= j) { while(array[i].price2 > pivotValue) i++; while(array[j].price2 < pivotValue) j--; if(i <= j) { S temp = array[i]; array[i] = array[j]; array[j] = temp; i++; j--; } } SortArrayPrice2Desendente(array, left, j); SortArrayPrice2Desendente(array, i, right); } //--- template inline bool IdxIsValid(const S &array[], int idx) { return idx >= 0 && idx < array.Size(); } //+------------------------------------------------------------------+ enum ENUM_MODE_SORT_PRICE { SORT_PRICE_MINOR_A_TARGET_PRICE = 0, // solo menores al target SORT_PRICE_MAYOR_A_TARGET_PRICE = 1 // solo mayores al target }; //--- template int SortByPrice(const T &arr_src[], T &arr_dest[], const double target_price, const ENUM_MODE_SORT_PRICE mode) { int count = 0; ArrayResize(arr_dest, ArraySize(arr_src)); //--- for(int i = 0, j = 0; i < ArraySize(arr_src) * ArraySize(arr_src); i++) { int row = i / ArraySize(arr_src); int col = i % ArraySize(arr_src); // Paso 1: Filtrar y copiar una sola vez if(i < ArraySize(arr_src)) { double p = arr_src[i].price; if((mode == SORT_PRICE_MINOR_A_TARGET_PRICE && p < target_price) || (mode == SORT_PRICE_MAYOR_A_TARGET_PRICE && p > target_price)) { arr_dest[count++] = arr_src[i]; } } // Paso 2: Selection sort plano dentro del mismo bucle if(count > 1 && row < count - 1 && col > row) { double diff_row = MathAbs(arr_dest[row].price - target_price); double diff_col = MathAbs(arr_dest[col].price - target_price); if(diff_col < diff_row) { T tmp = arr_dest[row]; arr_dest[row] = arr_dest[col]; arr_dest[col] = tmp; } } } ArrayResize(arr_dest, count); return count; } //--- template int SortByPrice2(const T &arr_src[], T &arr_dest[], const double target_price, const ENUM_MODE_SORT_PRICE mode) { int count = 0; ArrayResize(arr_dest, ArraySize(arr_src)); //--- for(int i = 0, j = 0; i < ArraySize(arr_src) * ArraySize(arr_src); i++) { int row = i / ArraySize(arr_src); int col = i % ArraySize(arr_src); // Paso 1: Filtrar y copiar una sola vez if(i < ArraySize(arr_src)) { double p = arr_src[i].price2; if((mode == SORT_PRICE_MINOR_A_TARGET_PRICE && p < target_price) || (mode == SORT_PRICE_MAYOR_A_TARGET_PRICE && p > target_price)) { arr_dest[count++] = arr_src[i]; } } // Paso 2: Selection sort plano dentro del mismo bucle if(count > 1 && row < count - 1 && col > row) { double diff_row = MathAbs(arr_dest[row].price2 - target_price); double diff_col = MathAbs(arr_dest[col].price2 - target_price); if(diff_col < diff_row) { T tmp = arr_dest[row]; arr_dest[row] = arr_dest[col]; arr_dest[col] = tmp; } } } ArrayResize(arr_dest, count); return count; } //+------------------------------------------------------------------+ template void SortArrayTime1(S &array[], int left, int right) { if(left >= right) return; int pivotIndex = (left + right) / 2; datetime pivotValue = array[pivotIndex].time1; // Usar directamente el valor datetime int i = left, j = right; while(i <= j) { Print("I: ", i); while(array[i].time1 > pivotValue) // Compara directamente los datetime i++; while(array[j].time1 < pivotValue) j--; if(i <= j) { S temp = array[i]; array[i] = array[j]; array[j] = temp; i++; j--; } } SortArrayTime1(array, left, j); SortArrayTime1(array, i, right); } //+------------------------------------------------------------------+ template void SortArrayTimeDiffAscendingTime2(S &array[], int left, int right, datetime currentTime) { if(left >= right) return; // Elegir un pivote usando el elemento central y calcular la diferencia absoluta int pivotIndex = (left + right) / 2; long pivotDiff = MathAbs((long)(currentTime - array[pivotIndex].time2)); int i = left, j = right; while(i <= j) { while(MathAbs((long)(currentTime - array[i].time2)) < pivotDiff) i++; while(MathAbs((long)(currentTime - array[j].time2)) > pivotDiff) j--; if(i <= j) { // Intercambiar elementos S temp = array[i]; array[i] = array[j]; array[j] = temp; i++; j--; } } //--- SortArrayTimeDiffAscendingTime2(array, left, j, currentTime); SortArrayTimeDiffAscendingTime2(array, i, right, currentTime); } //+------------------------------------------------------------------+ template bool HayNegativosArray(S &array[], int start = 0, int count = WHOLE_ARRAY) { if(start < 0 || start >= ArraySize(array)) return false; if(count >= ArraySize(array)) return false; if(count == WHOLE_ARRAY) count = ArraySize(array); for(int i = start; i < count && !IsStopped(); i++) if(array[i] < 0) return true; return (false); } //+------------------------------------------------------------------+ template bool HayPositivosArray(S &array[], int start = 0, int count = WHOLE_ARRAY) { if(start < 0 || start >= ArraySize(array)) return false; if(count >= ArraySize(array)) return false; if(count == WHOLE_ARRAY) count = ArraySize(array); for(int i = start; i < count && !IsStopped(); i++) if(array[i] > 0) return true; return (false); } //+------------------------------------------------------------------+ template void FindMaxMinArray(const T &arr[], T &max_val, T &min_val) { int size = ArraySize(arr); if(size == 0) { max_val = 0.0; min_val = 0.0; return; } max_val = arr[0]; min_val = arr[0]; for(int i = 1; i < size; i++) { if(arr[i] > max_val) max_val = arr[i]; if(arr[i] < min_val) min_val = arr[i]; } } //+------------------------------------------------------------------+ template void ObtenerLosUltimosValoresArr(const int max_valores, const int start, const S &data_array[], S & desitno_array[]) { int count = max_valores; for(int i = start; i < ArraySize(data_array) && count > 0 ; i++) { ArrayResize(desitno_array, desitno_array.Size() + 1); desitno_array[desitno_array.Size() - 1] = data_array[i]; count--; } } //+------------------------------------------------------------------+ //| Strings | //+------------------------------------------------------------------+ template void StringToType(string token, S &value, ENUM_DATATYPE type) { if(StringLen(token) == 0) { Print("Error: String is empty."); return; } switch(type) { case TYPE_BOOL: value = (S)(StringToInteger(token) != 0); // Convertir a bool break; case TYPE_CHAR: value = (S)((char)StringToInteger(token)); // Convertir a char break; case TYPE_UCHAR: value = (S)((uchar)StringToInteger(token)); // Convertir a uchar break; case TYPE_SHORT: value = (S)((short)StringToInteger(token)); // Convertir a short break; case TYPE_USHORT: value = (S)((ushort)StringToInteger(token)); // Convertir a ushort break; case TYPE_COLOR: value = (S)((color)StringToInteger(token)); // Convertir a color break; case TYPE_INT: value = (S)(StringToColor(token)); // Convertir a int break; case TYPE_UINT: value = (S)((uint)StringToInteger(token)); // Convertir a uint break; case TYPE_DATETIME: value = (S)(StringToTime(token)); // Convertir a datetime break; case TYPE_LONG: value = (S)((long)StringToInteger(token)); // Convertir a long break; case TYPE_ULONG: value = (S)((ulong)StringToInteger(token)); // Convertir a ulong break; case TYPE_FLOAT: value = (S)((float)StringToDouble(token)); // Convertir a float break; case TYPE_DOUBLE: value = (S)(StringToDouble(token)); // Convertir a double break; case TYPE_STRING: value = (S)(token); // Mantener como string break; default: Print("Error: Unsupported data type in ConvertToType."); break; } } //+------------------------------------------------------------------+ string AbreviarPeriodo(ENUM_TIMEFRAMES timeframe) { string periodo_string = EnumToString(timeframe); StringReplace(periodo_string, "PERIOD_", ""); StringToLower(periodo_string); return periodo_string; } //+------------------------------------------------------------------+ string StringRepeat(string cadena_a_repeitr, int numero_repeticiones) { string result = ""; for(int i = 0; i < numero_repeticiones; i++) { result += cadena_a_repeitr; } return result; } //+------------------------------------------------------------------+ string RemoveMultipleSubStrings(const string original, const string &subStringsToRemove[]) { string modified = original; // Copia de la cadena original para modificar int position; // Posición de la subcadena a remover // Itera sobre el array de subcadenas a remover for(int i = 0; i < ArraySize(subStringsToRemove); i++) { // Bucle para remover todas las ocurrencias de cada subcadena while((position = StringFind(modified, subStringsToRemove[i])) != -1) { // Remueve la subcadena encontrada modified = StringSubstr(modified, 0, position) + StringSubstr(modified, position + StringLen(subStringsToRemove[i])); } } return modified; // Devuelve la cadena modificada } //+------------------------------------------------------------------+ string RemoveSubString(const string original, const string toRemove) { string modified = original; // Copia de la cadena original para modificar int position; // Posición de la subcadena a remover // Bucle para remover todas las ocurrencias de la subcadena while((position = StringFind(modified, toRemove)) != -1) { // Remueve la subcadena encontrada modified = StringSubstr(modified, 0, position) + StringSubstr(modified, position + StringLen(toRemove)); } return modified; // Devuelve la cadena modificada } //+------------------------------------------------------------------+ //| Trabajo Con Datetime | //+------------------------------------------------------------------+ datetime MinTime(datetime time1, datetime time2, datetime time3) { datetime minTime = time1; if(time2 < minTime) minTime = time2; if(time3 < minTime) minTime = time3; return minTime; } //+------------------------------------------------------------------+ bool IsValidDay(datetime time, bool symbol_is_cripto) { MqlDateTime tm; TimeToStruct(time, tm); if(tm.day_of_year == 0) return false; //si es 1 de enero retornamos if(symbol_is_cripto == false) { if(tm.day_of_week == 0 || tm.day_of_week == 6) return false; } if(tm.day == 25 && tm.mon == 12) return false; return true; } //+------------------------------------------------------------------+ datetime HoraYMinutoADatetime(int hora, int minuto, datetime time_objetivo) { MqlDateTime tm; TimeToStruct(time_objetivo, tm); tm.hour = hora; tm.min = minuto; tm.sec = 0; // Puedes ajustar los segundos si es necesario return StructToTime(tm);; } //+------------------------------------------------------------------+ inline string HoraYMinutoAString(int Hora, int minuto) { return IntegerToString(Hora) + ":" + IntegerToString(minuto); } //+------------------------------------------------------------------+ inline int diffGMT_NY() { datetime currentTime = TimeTradeServer(); return TimeIsVerano(currentTime) ? -4 : -5; } //+------------------------------------------------------------------+ datetime GetSecondSundayOfMonth(int month, int year) { datetime firstSunday = GetFirstSundayOfMonth(month, year); return firstSunday + 7 * 86400; } //+------------------------------------------------------------------+ datetime GetFirstSundayOfMonth(int month, int year) { // Validar parámetros de entrada if(month < 0 || month > 12 || year < 1970) return 0; // Crear fecha para el primer día del mes MqlDateTime dt; dt.year = year; dt.mon = month; dt.day = 1; dt.hour = 0; dt.min = 0; dt.sec = 0; // Convertir a datetime datetime firstDay = StructToTime(dt); // Obtener el día de la semana del primer día (0=domingo, 1=lunes, ..., 6=sábado) MqlDateTime firstDayInfo; TimeToStruct(firstDay, firstDayInfo); int dayOfWeek = firstDayInfo.day_of_week; // Calcular cuántos días hay que agregar para llegar al primer domingo // Si el primer día es domingo (dayOfWeek=0), no sumamos nada // En caso contrario, sumamos días hasta llegar al domingo (7-dayOfWeek) int daysToAdd = (dayOfWeek == 0) ? 0 : (7 - dayOfWeek); // Retornar la fecha del primer domingo return firstDay + daysToAdd * 86400; // 86400 segundos = 1 día } //+------------------------------------------------------------------+ #define GetTiempoCoeficiente(tiempo_inicio, tiempo_final, coeficiente) tiempo_inicio + int((tiempo_final - tiempo_inicio) * coeficiente) //+------------------------------------------------------------------+ datetime GetInitMonth(int year, int month) { MqlDateTime tm; tm.year = year; tm.mon = month; tm.day = 1; tm.hour = 0; tm.min = 0; tm.sec = 0; return StructToTime(tm); } //+------------------------------------------------------------------+ int DaysInMonth(int year, int month) { datetime start = GetInitMonth(year, month); int nextYear = (month == 12) ? year + 1 : year; int nextMonth = (month == 12) ? 1 : month + 1; datetime nextStart = GetInitMonth(nextYear, nextMonth); int days = int((nextStart - start) / 86400); return days; } //+------------------------------------------------------------------+ int SecondsInMonth(int year, int month) { datetime start = GetInitMonth(year, month); int nextYear = (month == 12) ? year + 1 : year; int nextMonth = (month == 12) ? 1 : month + 1; datetime nextStart = GetInitMonth(nextYear, nextMonth); return int(nextStart - start); } //+------------------------------------------------------------------+ int SecondsInMonth(datetime time) { MqlDateTime tm; TimeToStruct(time, tm); datetime start = GetInitMonth(tm.year, tm.mon); int nextYear = (tm.mon == 12) ? tm.year + 1 : tm.year; int nextMonth = (tm.mon == 12) ? 1 : tm.mon + 1; datetime nextStart = GetInitMonth(nextYear, nextMonth); return int(nextStart - start); } //+------------------------------------------------------------------+ datetime GetInitDayTime(datetime time) { MqlDateTime tm; TimeToStruct(time, tm); tm.hour = 0; tm.min = 0; tm.sec = 0; return StructToTime(tm); } //+------------------------------------------------------------------+ datetime GetInitWeekTime(datetime time) { MqlDateTime tm; TimeToStruct(time, tm); tm.hour = 0; tm.min = 0; tm.sec = 0; datetime midnight = StructToTime(tm); int dias_a_restar = (tm.day_of_week + 6) % 7; return midnight - dias_a_restar * 86400; } //+------------------------------------------------------------------+ datetime ModifiedTime(datetime original, int hour, int min, int sec) { MqlDateTime time_struct; TimeToStruct(original, time_struct); time_struct.hour = hour; time_struct.min = min; time_struct.sec = sec; return StructToTime(time_struct); } //+------------------------------------------------------------------+ inline int TimeDifferenceByHHMMSS(datetime event_time) { return (int)(event_time % 86400); // 86400 segundos en un día } //+------------------------------------------------------------------+ inline bool ContainsRangeTime(datetime time, datetime _init_time, datetime _end_time, bool considerar_igual) { return !considerar_igual ? (time > _init_time && time < _end_time) : (time >= _init_time && time <= _end_time); } //+------------------------------------------------------------------+ int ExtraerMinutoDatetime(const datetime time) { MqlDateTime t; TimeToStruct(time, t); return t.min; } //+------------------------------------------------------------------+ datetime MinTime(datetime time1, datetime time2, datetime target_time, MODE_CLOSET_INDEX mode) { if(mode == MAYOR_A_TARGET_TIME) { if(time1 <= target_time) return time2 <= target_time ? 0 : time2; else if(time2 <= target_time) return time1 <= target_time ? 0 : time1; int diff_time1 = (int)MathAbs(time1 - target_time); int diff_time2 = (int)MathAbs(time2 - target_time); if(diff_time1 > diff_time2) return time2; else if(diff_time2 >= diff_time1) return time1; } else { if(time1 >= target_time) return time2 >= target_time ? 0 : time2; else if(time2 >= target_time) return time1 >= target_time ? 0 : time1; int diff_time1 = (int)MathAbs(target_time - time1); int diff_time2 = (int)MathAbs(target_time - time2); if(diff_time1 > diff_time2) return time2; else if(diff_time2 >= diff_time1) return time1; } return 0; } //+------------------------------------------------------------------+ inline datetime MaxTime(datetime time1, datetime time2) { return (time1 > time2) ? time1 : time2; } //+------------------------------------------------------------------+ inline datetime MinTime(datetime time1, datetime time2) { return (time1 > time2) ? time2 : time1; } //+------------------------------------------------------------------+ bool TimeIsInvierno(datetime time) //GMT - 5 (noviembre_pasado - marzo_actual) { MqlDateTime t; TimeToStruct(time, t); if(t.mon >= 11) { datetime tiempo = GetFirstSundayOfMonth(11, t.year); return (time > tiempo); } else if(t.mon < 11) { datetime tiempo = GetSecondSundayOfMonth(3, t.year); return time < tiempo; } return false; } //+------------------------------------------------------------------+ bool TimeIsVerano(datetime time) //GMT -4 (marzo - noviembre) { MqlDateTime t; TimeToStruct(time, t); if(t.mon >= 11) { datetime tiempo = GetFirstSundayOfMonth(11, t.year); return (time < tiempo); // solo aseguras que ya pasó el primer domingo de noviembre } else if(t.mon < 11) { datetime tiempo = GetSecondSundayOfMonth(3, t.year); return (time >= tiempo); } return false; } //+------------------------------------------------------------------+ datetime MaxTime(datetime time1, datetime time2, datetime target_time, MODE_CLOSET_INDEX mode) { if(mode == MAYOR_A_TARGET_TIME) { if(time1 <= target_time || time2 <= target_time) return 0; int diff_time1 = (int)MathAbs(time1 - target_time); int diff_time2 = (int)MathAbs(time2 - target_time); if(diff_time1 > diff_time2) return time1; else if(diff_time2 > diff_time1) return time2; else return time2; } else { if(time1 >= target_time || time2 >= target_time) return 0; int diff_time1 = (int)MathAbs(target_time - time1); int diff_time2 = (int)MathAbs(target_time - time2); if(diff_time1 > diff_time2) return time1; else if(diff_time2 > diff_time1) return time2; else return time2; } return 0; } //+------------------------------------------------------------------+ inline bool EstaEnHorario(int _hora_inicio, int _min_inicio, int _hora_fin, int _min_fin, datetime curr_time) { MqlDateTime tm; TimeToStruct(curr_time, tm); return (tm.hour < _hora_inicio || tm.hour > _hora_fin || (tm.hour == _hora_inicio && tm.min < _min_inicio) || (tm.hour == _hora_fin && tm.min >= _min_fin)) == true ? false : true; } //+------------------------------------------------------------------+ //| Matematicas | //+------------------------------------------------------------------+ template inline double IntegerToDouble(S valor) { return (double)valor; } //+------------------------------------------------------------------+ template inline double dividir(S valor1, S valor2) { return (valor2 == 0) ? 0.0 : IntegerToDouble(valor1) / IntegerToDouble(valor2); } //+------------------------------------------------------------------+ template inline double dividir(S valor1, int valor2) { return (valor2 == 0) ? 0.0 : IntegerToDouble(valor1) / IntegerToDouble(valor2); } //+------------------------------------------------------------------+ inline long DoubleToInteger(double value) { double round_value = MathRound(value); return (long)round_value; } //+------------------------------------------------------------------+ inline double sigmoid(double x) { return 1.0 / (1.0 + MathExp(-x)); // función de activación sigmoide } //+------------------------------------------------------------------+ inline double CalcularPorcentaje(double x, double numero_a_comparar) { if(numero_a_comparar == 0) return 0; // Evitar división entre 0 return (x / numero_a_comparar) * 100.0; } //+------------------------------------------------------------------+ typedef double (*funcion_variacion)(const double valor_actual, const double valor_anterior); enum ENUM_TYPE_CALCULATION_VARIACION { CALCULATION_VARIACION_ABSOLUTA, CALCULATION_VARIACION_PORCENTUAL, CALCULATION_VARIACION_LOGARITMICA, CALCULATION_VARIACION_ESCALA_DINAMICA, CALCULATION_VARIACION_NORMALIZADA, CALCULATION_VARIACION_RANGOS, CALCULATION_VARIACION_TANH, CALCULATION_VARIACION_RAIZ, CALCULATION_VARIACION_MEDIA }; // Funciones de variación double VariacionAbsoluta(const double valor_actual, const double valor_anterior) { return valor_actual - valor_anterior; } //+------------------------------------------------------------------+ double VariacionPorcentual(const double valor_actual, const double valor_anterior) { if(valor_anterior == 0.0) return valor_actual; // Evitar división por cero return (valor_actual - valor_anterior) / (MathAbs(valor_anterior)); } //+------------------------------------------------------------------+ double VariacionLogaritmica(const double valor_actual, const double valor_anterior) { if(valor_anterior <= 0.0 || valor_actual <= 0.0) return 0.0; // Evitar log(0) o log(negativo) return MathLog(valor_actual / valor_anterior); } //+------------------------------------------------------------------+ double VariacionEscalaDinamica(const double valor_actual, const double valor_anterior) { double escala = MathMax(MathAbs(valor_actual), MathAbs(valor_anterior)); if(escala == 0.0) return VariacionNormalizada(valor_actual, valor_anterior); return (valor_actual - valor_anterior) / escala; } //+------------------------------------------------------------------+ double VariacionNormalizada(const double valor_actual, const double valor_anterior) { const double variacion = valor_actual - valor_anterior; if(MathAbs(variacion) < 1.0) return variacion; double minimo = MathMin(MathAbs(valor_actual), MathAbs(valor_anterior)); int factor_normalizacion = (minimo < 1) ? 1 : (int)MathPow(10, IntegerToDouble(GetIntegerDigits(minimo))); return variacion / IntegerToDouble(factor_normalizacion); } //+------------------------------------------------------------------+ double VariacionNormalizadaRangos(const double valor_actual, const double valor_anterior) { if(valor_anterior == 0.0) return MathMin(5.0, MathMax(-5.0, valor_actual)); // Evitar división por 0 double variacion = (valor_actual - valor_anterior) / (1.0 + MathAbs(valor_anterior)); return MathMin(5.0, MathMax(-5.0, variacion * 5.0)); // Escalar entre -5 y 5 } //+------------------------------------------------------------------+ double VariacionTanh(const double valor_actual, const double valor_anterior) { return MathTanh(valor_actual - valor_anterior); } //+------------------------------------------------------------------+ double VariacionRaiz(const double valor_actual, const double valor_anterior) { return (valor_actual - valor_anterior) / MathSqrt(MathAbs(valor_anterior) + 1.0); } //+------------------------------------------------------------------+ double VariacionMedia(const double valor_actual, const double valor_anterior) { return 2.0 * (valor_actual - valor_anterior) / (valor_actual + valor_anterior + 1.0); } //+------------------------------------------------------------------+ funcion_variacion GetVariacionFunction(ENUM_TYPE_CALCULATION_VARIACION tipo) { switch(tipo) { case CALCULATION_VARIACION_ABSOLUTA: return VariacionAbsoluta; case CALCULATION_VARIACION_PORCENTUAL: return VariacionPorcentual; case CALCULATION_VARIACION_LOGARITMICA: return VariacionLogaritmica; case CALCULATION_VARIACION_ESCALA_DINAMICA: return VariacionEscalaDinamica; case CALCULATION_VARIACION_NORMALIZADA: return VariacionNormalizada; case CALCULATION_VARIACION_RANGOS: return VariacionNormalizadaRangos; case CALCULATION_VARIACION_TANH: return VariacionTanh; case CALCULATION_VARIACION_RAIZ: return VariacionRaiz; case CALCULATION_VARIACION_MEDIA: return VariacionMedia; default: return NULL; } } //+------------------------------------------------------------------+ double TrendWeightedDifference(const double &values[], int size) { if(size < 2) return 0.0; double num = 0.0, denom = 0.0; for(int i = 0; i < size - 1; i++) { double weight = i + 1; num += weight * (values[i + 1] - values[i]); denom += weight; } return tanh(num / (denom + 1e-9)); // Escalado con tanh } //+------------------------------------------------------------------+ double TrendSumNormalized(const double &values[], int size) { if(size < 2) return 0.0; double sumDiff = 0.0, sumAbsDiff = 0.0; for(int i = 0; i < size - 1; i++) { double diff = values[i + 1] - values[i]; sumDiff += diff; sumAbsDiff += fabs(diff); } return (sumAbsDiff == 0) ? 0.0 : sumDiff / sumAbsDiff; } //+------------------------------------------------------------------+ inline double GetVariacion(const double valor_actual, const double valor_anterior, ENUM_TYPE_CALCULATION_VARIACION type_variacion) { funcion_variacion funcion = GetVariacionFunction(type_variacion); return funcion == NULL ? 0.0 : funcion(valor_actual, valor_anterior); } //+------------------------------------------------------------------+ inline int GetIntegerDigits(double number) { number = MathAbs(number); // Asegurarse de que es positivo return (number < 1.0) ? 1 : int(MathFloor(MathLog10(number))) + 1 ; // Tomamos el piso del logaritmo y sumamos 1 } //+------------------------------------------------------------------+ inline double GetDist(double price1, double price2) { return MathMax(price1, price2) - MathMin(price1, price2); } //+------------------------------------------------------------------+ inline double GetPriceCoeficiente(double price1, double price2, double coeficiente) { return MathMax(price1, price2) - (GetDist(price1, price2) * coeficiente); } //+------------------------------------------------------------------+ inline bool CumpleMinDistancia(const double min_dist, double price1, double price2) { return GetDist(price1, price2) < min_dist ? false : true; } //+------------------------------------------------------------------+ inline double RoundToStep(double number, double step) { if(step <= DBL_EPSILON) return number; // Evitar división por cero return MathRound((number / step)) * step; } //+------------------------------------------------------------------+ //| Vectores | //+------------------------------------------------------------------+ template void VectorPrint(const vector &v, int digitos, string separator = " | ") { string p = ""; for(ulong i = 0; i < v.Size() ; i++) { p += DoubleToString(v[i], digitos) + separator; } Print(p); } template vector ArrayToVector(const S &array[]) { vector v(ArraySize(array)); for(int i = 0; i < ArraySize(array) ; i++) v[i] = IntegerToDouble(array[i]); return v; } //+------------------------------------------------------------------+ void VectorToDoubleArray(const vector &v, double &array[]) { int size = (int)v.Size(); ArrayResize(array, size); for(ulong i = 0; i < v.Size() ; i++) array[i] = v[i]; } //+------------------------------------------------------------------+ template void SortVector(vector& data) { if(data.Size() <= 1) return; QuickSortRecursive(data, 0, (int)data.Size() - 1); } //+------------------------------------------------------------------+ template void QuickSortRecursive(vector& data, int low, int high) { if(low < high) { int pivot_index = Partition(data, low, high); QuickSortRecursive(data, low, pivot_index - 1); QuickSortRecursive(data, pivot_index + 1, high); } } //+------------------------------------------------------------------+ template int Partition(vector& data, int low, int high) { S pivot = data[high]; int i = low - 1; for(int j = low; j < high; j++) { if(data[j] <= pivot) { i++; SwapVectorValue(data, i, j); } } SwapVectorValue(data, i + 1, high); return i + 1; } //+------------------------------------------------------------------+ template void SwapVectorValue(vector& data, int i, int j) { if(i != j) { S temp = data[i]; data[i] = data[j]; data[j] = temp; } } //+------------------------------------------------------------------+ //| Funciones Extra | //+------------------------------------------------------------------+ inline bool Tester() { return (MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_OPTIMIZATION) || MQLInfoInteger(MQL_DEBUG)); } //+------------------------------------------------------------------+ void Remover(string short_name = " ", long chart_id = 0, int subwin = 0) { if(MQLInfoInteger(MQL_PROGRAM_TYPE) == PROGRAM_INDICATOR) { if(Tester() == false) ChartIndicatorDelete(chart_id, subwin, short_name); else TesterStop(); return; } if(Tester()) TesterStop(); else ExpertRemove(); } //+------------------------------------------------------------------+ bool CreateFolder(string folder_path, bool common_flag) { int flag = common_flag ? FILE_COMMON : 0; string working_folder; //--- aclaramos la ruta completa dependiendo del parámetro common_flag if(common_flag) working_folder = TerminalInfoString(TERMINAL_COMMONDATA_PATH) + "\\MQL5\\Files"; else working_folder = TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL5\\Files"; //--- mensaje de depuración PrintFormat("folder_path=%s", folder_path); //---intentando crear una carpeta relativa a la ruta MQL5\Files if(FolderCreate(folder_path, flag)) { //--- mostramos la ruta completa para la carpeta creada PrintFormat("Hemos creado la carpeta %s", working_folder + "\\" + folder_path); //--- reseteamos el código de error ResetLastError(); //--- ejecutado con éxito return true; } else PrintFormat("No se ha logrado crear la carpeta %s. Código de error %d", working_folder + folder_path, GetLastError()); //--- ejecutado sin éxito return false; } //+------------------------------------------------------------------+ inline bool PeriodIsBig(ENUM_TIMEFRAMES Period) { return Period == PERIOD_MN1 || Period == PERIOD_W1 || Period == PERIOD_D1 || Period == PERIOD_H4 || Period == PERIOD_H2 || Period == PERIOD_H3 || Period == PERIOD_H8 || Period == PERIOD_H12 || Period == PERIOD_H6 || Period == PERIOD_H1 ? true : false ; } //+------------------------------------------------------------------+ long OpenChartAndGetChartID(string symbol, ENUM_TIMEFRAMES timeframe) { long chart_id = ChartOpen(symbol, timeframe); return chart_id > 0 ? chart_id : 0; } //+------------------------------------------------------------------+ inline double ConvertPriceToPoints(const string symbol, double priceDistance) { return (MathRound(priceDistance / SymbolInfoDouble(symbol, SYMBOL_POINT))); } // Checks if the symbol is categorized under the "Crypto" sector bool SymbolIsCrypto(const string sym) { string sector = SymbolInfoString(sym, SYMBOL_SECTOR_NAME); if(StringLen(sector) > 0) // Ensuring the string is not empty { StringToLower(sector); // Convert the sector name to lower case for case-insensitive comparison return StringFind(sector, "rypto") >= 0; // Search for "crypto" in the sector name } return false; // Return false if the sector name is empty } //+------------------------------------------------------------------+ inline ulong GetTickCount64Seconds() { ulong cof = 1000; return (ulong)dividir(GetTickCount64(), cof); } //+------------------------------------------------------------------+ #define GetTickCount64Minutes (uint)(GetTickCount64() * 1.6666666666666666666666666666667e-5) //+------------------------------------------------------------------+ void SwitchToChart(long target_chart_id) { //--- if(ChartGetInteger(target_chart_id, CHART_WINDOWS_TOTAL) < 0) { Print("Error: El gráfico no existe o fue cerrado"); return; } ChartSetInteger(target_chart_id, CHART_BRING_TO_TOP, true); ChartSetInteger(target_chart_id, CHART_FOREGROUND, true); } //+------------------------------------------------------------------+ inline bool CandleIsBull(const int index, string symbol, ENUM_TIMEFRAMES timeframe) { return iClose(symbol, timeframe, index) > iOpen(symbol, timeframe, index); } //+------------------------------------------------------------------+ inline bool CandleIsBear(const int index, string symbol, ENUM_TIMEFRAMES timeframe) { return iClose(symbol, timeframe, index) < iOpen(symbol, timeframe, index); } //+------------------------------------------------------------------+ inline void EMPTY_FUNCTION() { return; } //+------------------------------------------------------------------+ inline void EMPTY_FUNCTION_TIME(datetime curr_time, bool new_day) { return; } //+------------------------------------------------------------------+ //| Funiones Comerciales | //+------------------------------------------------------------------+ double AjustarNivelDeStopArriba(double pPrecioActual, double pPrecioParaAjustar, int pPuntosAdicionales = 10) { double PrecioAjustado = pPrecioParaAjustar; long nivelesStop = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL); if(nivelesStop > 0) { double nivelesStopPrecio = nivelesStop * _Point; nivelesStopPrecio = pPrecioActual + nivelesStopPrecio; double PuntosAdicionales = pPuntosAdicionales * _Point; if(PrecioAjustado <= nivelesStopPrecio + PuntosAdicionales) { PrecioAjustado = nivelesStopPrecio + PuntosAdicionales; // Print("precio ajustado por encima de niuvel de stops a ", string(PrecioAjustado)); } } return NormalizeDouble(PrecioAjustado, _Digits); } //+------------------------------------------------------------------+ double AjustarNivelDeStopAbajo(double pPrecioActual, double pPrecioParaAjustar, int pPuntosAdicionales = 10) { double PrecioAjustado = pPrecioParaAjustar; long nivelesStop = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL); if(nivelesStop > 0) { double nivelesStopPrecio = nivelesStop * _Point; nivelesStopPrecio = pPrecioActual - nivelesStopPrecio; double PuntosAdicionales = pPuntosAdicionales * _Point; if(PrecioAjustado >= nivelesStopPrecio + PuntosAdicionales) { PrecioAjustado = nivelesStopPrecio + PuntosAdicionales; // Print("precio ajustado por debajo de niuvel de stops a ", string(PrecioAjustado)); } } return NormalizeDouble(PrecioAjustado, _Digits); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ struct TickMaxMinResult { double high_price; // Precio máximo double low_price; // Precio mínimo datetime max_time; // Momento exacto del máximo datetime min_time; // Momento exacto del mínimo int max_seconds; // Segundos desde inicio hasta máximo (0-59) int min_seconds; // Segundos desde inicio hasta mínimo (0-59) int total_ticks; // Total de ticks analizados bool success; // ¿Operación exitosa? TickMaxMinResult() : high_price(0.0), low_price(0.0), max_time(0), min_time(0), max_seconds(0), min_seconds(0), total_ticks(0), success(false) {} TickMaxMinResult(const TickMaxMinResult& other) { this = other; } }; //+------------------------------------------------------------------+ TickMaxMinResult GetCandleMaxMinByTicks(datetime candle_start, datetime end_time, string symbol = NULL) { TickMaxMinResult result; // Usar símbolo actual si no se especifica if(symbol == NULL) symbol = Symbol(); // Calcular rango de tiempo (1 minuto) ulong from_msc = (ulong)candle_start * 1000; // Inicio en milisegundos ulong to_msc = (ulong)(end_time) * 1000; // Fin en milisegundos (+60 segundos) // Array para recibir ticks MqlTick ticks[]; ArrayResize(ticks, 0); // Obtener ticks del rango ResetLastError(); int tick_count = CopyTicksRange(symbol, ticks, COPY_TICKS_ALL, from_msc, to_msc); if(tick_count < 1) { PrintFormat("Error obteniendo ticks %d, start = %s >> end = %s ", GetLastError(), TimeToString(candle_start), TimeToString(end_time)); result.success = false; return result; } // Inicializar valores result.high_price = ticks[0].bid; result.low_price = ticks[0].bid; result.max_time = (datetime)(ticks[0].time_msc / 1000); result.min_time = (datetime)(ticks[0].time_msc / 1000); result.total_ticks = tick_count; // Buscar máximo y mínimo for(int i = 0; i < tick_count; i++) { double price = (ticks[i].bid + ticks[i].ask) / 2.0; // Precio promedio bid/ask datetime tick_time = (datetime)(ticks[i].time_msc / 1000); // Verificar si es nuevo máximo if(price > result.high_price) { result.high_price = price; result.max_time = tick_time; result.max_seconds = (int)(tick_time - candle_start); } // Verificar si es nuevo mínimo if(price < result.low_price) { result.low_price = price; result.min_time = tick_time; result.min_seconds = (int)(tick_time - candle_start); } } result.success = true; return result; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ const bool TESTER_FLAG = Tester(); #endif //+------------------------------------------------------------------+