2026-08-27 18:09:07 -05:00 | | | //+------------------------------------------------------------------+
|
| | | //| 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 MQLARTICLES_UTILS_FA_FUNCIONESBASES_MQH
|
| | | #define MQLARTICLES_UTILS_FA_FUNCIONESBASES_MQH
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | //---
|
2026-09-01 20:00:53 -05:00 | | | // (https://forge.mql5.io/nique_372/TimeUtils)
|
| | | // Fork of (https://forge.mql5.io/amrali/TimeUtils | autor = amrali)
|
2026-08-27 18:09:07 -05:00 | | | #include "..\\..\\..\\TimeUtils\\TimeUtils.mqh"
|
| | |
|
| | | //---
|
2026-09-12 19:41:52 -05:00 | | | #include "String\\All.mqh"
|
2026-09-01 20:00:53 -05:00 | | | #include "Int\\Constant.mqh"
|
2026-09-12 19:41:52 -05:00 | | | #include "..\\Tfi\\Base.mqh"
|
2026-08-27 18:09:07 -05:00 | | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Arrays |
|
| | | //+------------------------------------------------------------------+
|
| | | template <typename S>
|
| | | 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;
|
| | | }
|
| | |
|
| | |
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Eliminar por campo name (Virtual size + normal) |
|
| | | //+------------------------------------------------------------------+
|
| | | template<typename T>
|
| | | 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)
|
| | | {
|
| | | for(int j = i; j < size - 1; j++)
|
| | | {
|
| | | array[j] = array[j + 1];
|
| | | }
|
| | | ArrayResize(array, size - 1, capacity);
|
| | | return true;
|
| | | }
|
| | | }
|
| | | return false;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | template<typename T>
|
| | | bool DeleteArrayBiNameWithVSize(T &array[], const string &targetName, int& virtual_size)
|
| | | {
|
| | | for(int i = 0; i < virtual_size; i++)
|
| | | {
|
| | | if(array[i].name == targetName)
|
| | | {
|
| | | for(int j = i; j < virtual_size - 1; j++)
|
| | | {
|
| | | array[j] = array[j + 1];
|
| | | }
|
| | | virtual_size--;
|
| | | return true;
|
| | | }
|
| | | }
|
| | | return false;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | template<typename T>
|
| | | 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<typename T>
|
| | | bool DeleteArrayBiNameWithVSize(T &array[], const string &targetName, T &out, int& virtual_size)
|
| | | {
|
| | | for(int i = 0; i < virtual_size; i++)
|
| | | {
|
| | | if(array[i].name == targetName)
|
| | | {
|
| | | out = array[i];
|
| | | // Desplazar elementos hacia atrás
|
| | | for(int j = i; j < virtual_size - 1; j++)
|
| | | {
|
| | | array[j] = array[j + 1];
|
| | | }
|
| | | virtual_size--;
|
| | | return true;
|
| | | }
|
| | | }
|
| | | return false;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Eliminar por indice |
|
| | | //+------------------------------------------------------------------+
|
| | | template <typename T>
|
| | | 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 <typename T>
|
| | | void DeleteArrayByIndexWithVSizeRango(T &array[], int& arr_size, const int index, const int num)
|
| | | {
|
| | | if(arr_size < 1 || num < 1)
|
| | | return;
|
| | | for(int j = index; j < arr_size - num; j++)
|
| | | array[j] = array[j + num];
|
| | | arr_size -= num;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | template <typename T>
|
| | | void DeleteArrayByIndexWithRango(T &array[], const int arr_size, const int index, const int num)
|
| | | {
|
| | | if(arr_size < 1 || num < 1)
|
| | | return;
|
| | | for(int j = index; j < arr_size - num; j++)
|
| | | array[j] = array[j + num];
|
| | | ArrayResize(array, (arr_size - num));
|
| | | }
|
| | |
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Eliminar multiples indices (VirtualSize | Normal) |
|
| | | //+------------------------------------------------------------------+
|
| | | template<typename T>
|
| | | int RemoveMultipleIndexes(T &arr[], const int &indexes_to_remove[], const int capacity)
|
| | | {
|
| | | const int old_size = ArraySize(arr);
|
| | | const int remove_size = ArraySize(indexes_to_remove);
|
| | | if(old_size == 0 || remove_size == 0)
|
| | | return old_size;
|
| | |
|
| | | //---
|
| | | bool to_remove[];
|
| | | ArrayResize(to_remove, old_size);
|
| | | ArrayInitialize(to_remove, false);
|
| | |
|
| | | //---
|
| | | for(int i = 0; i < remove_size; i++)
|
| | | {
|
| | | to_remove[indexes_to_remove[i]] = true;
|
| | | }
|
| | |
|
| | | //---
|
| | | int write_index = 0;
|
| | | for(int read_index = 0; read_index < old_size; read_index++)
|
| | | {
|
| | | if(!to_remove[read_index])
|
| | | {
|
| | | if(write_index != read_index)
|
| | | arr[write_index] = arr[read_index];
|
| | | write_index++;
|
| | | }
|
| | | }
|
| | |
|
| | | //---
|
| | | return ArrayResize(arr, write_index, capacity);
|
| | | }
|
| | |
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | template<typename T>
|
| | | int RemoveMultipleIndexes(T* &arr[], const int &indexes_to_remove[], const int capacity)
|
| | | {
|
| | | const int old_size = ArraySize(arr);
|
| | | const int remove_size = ArraySize(indexes_to_remove);
|
| | | if(old_size == 0 || remove_size == 0)
|
| | | return old_size;
|
| | |
|
| | | //---
|
| | | bool to_remove[];
|
| | | ArrayResize(to_remove, old_size);
|
| | | ArrayInitialize(to_remove, false);
|
| | |
|
| | | //---
|
| | | for(int i = 0; i < remove_size; i++)
|
| | | to_remove[indexes_to_remove[i]] = true;
|
| | |
|
| | | //---
|
| | | int write_index = 0;
|
| | | for(int read_index = 0; read_index < old_size; read_index++)
|
| | | {
|
| | | if(!to_remove[read_index])
|
| | | {
|
| | | if(write_index != read_index)
|
| | | arr[write_index] = arr[read_index];
|
| | | write_index++;
|
| | | }
|
| | | }
|
| | |
|
| | | //---
|
| | | return ArrayResize(arr, write_index, capacity);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | template<typename T>
|
| | | void RemoveMultipleIndexesNR(T &arr[], const int &indexes_to_remove[], const int capacity)
|
| | | {
|
| | | const int old_size = ArraySize(arr);
|
| | | const int remove_size = ArraySize(indexes_to_remove);
|
| | | if(old_size == 0 || remove_size == 0)
|
| | | return;
|
| | |
|
| | | //---
|
| | | bool to_remove[];
|
| | | ArrayResize(to_remove, old_size);
|
| | | ArrayInitialize(to_remove, false);
|
| | |
|
| | | //---
|
| | | for(int i = 0; i < remove_size; i++)
|
| | | to_remove[indexes_to_remove[i]] = true;
|
| | |
|
| | | //---
|
| | | int write_index = 0;
|
| | | for(int read_index = 0; read_index < old_size; read_index++)
|
| | | {
|
| | | if(!to_remove[read_index])
|
| | | {
|
| | | if(write_index != read_index)
|
| | | arr[write_index] = arr[read_index];
|
| | | write_index++;
|
| | | }
|
| | | }
|
| | |
|
| | | //---
|
| | | ArrayResize(arr, write_index, capacity);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | template<typename T>
|
| | | void RemoveMultipleIndexesWithVSize(T &arr[], int &old_size, const int &indexes_to_remove[]) // oldize = virtual size
|
| | | {
|
| | | const int remove_size = ArraySize(indexes_to_remove);
|
| | | if(old_size == 0 || remove_size == 0)
|
| | | return;
|
| | |
|
| | | //---
|
| | | bool to_remove[];
|
| | | ArrayResize(to_remove, old_size);
|
| | | ArrayInitialize(to_remove, false);
|
| | |
|
| | | //---
|
| | | for(int i = 0; i < remove_size; i++)
|
| | | {
|
| | | to_remove[indexes_to_remove[i]] = true;
|
| | | }
|
| | |
|
| | | //---
|
| | | int write_index = 0;
|
| | | for(int read_index = 0; read_index < old_size; read_index++)
|
| | | {
|
| | | if(!to_remove[read_index])
|
| | | {
|
| | | if(write_index != read_index)
|
| | | arr[write_index] = arr[read_index];
|
| | | write_index++;
|
| | | }
|
| | | }
|
| | |
|
| | | //---
|
| | | old_size = write_index;
|
| | | // Quite el resize no se por que lo puse XD
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | template<typename T>
|
| | | void RemoveMultipleIndexesWithVSizeRemoveCts(T &arr[], int &old_size, const int &indexes_to_remove[],
|
| | | const int rstart, const int remove_size) // oldize = virtual size
|
| | | {
|
| | | //---
|
| | | if(old_size == 0 || remove_size == 0)
|
| | | return;
|
| | |
|
| | | //---
|
| | | bool to_remove[];
|
| | | ArrayResize(to_remove, old_size);
|
| | | ArrayInitialize(to_remove, false);
|
| | |
|
| | | //---
|
| | | for(int i = 0; i < remove_size; i++)
|
| | | {
|
| | | to_remove[indexes_to_remove[rstart + i]] = true;
|
| | | }
|
| | |
|
| | | //---
|
| | | int write_index = 0;
|
| | | for(int read_index = 0; read_index < old_size; read_index++)
|
| | | {
|
| | | if(!to_remove[read_index])
|
| | | {
|
| | | if(write_index != read_index)
|
| | | arr[write_index] = arr[read_index];
|
| | | write_index++;
|
| | | }
|
| | | }
|
| | |
|
| | | //---
|
| | | old_size = write_index;
|
| | | // Quite el resize no se por que lo puse XD
|
| | | }
|
| | |
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Array copy |
|
| | | //+------------------------------------------------------------------+
|
| | | template <typename T>
|
| | | int ArrayCopyAllPtr(T* &dst_array[], T* &source_array[])
|
| | | {
|
| | | //---
|
| | | const int total = ArraySize(source_array);
|
| | | if(total < 1)
|
| | | return 0;
|
| | |
|
| | | //---
|
| | | ArrayResize(dst_array, total);
|
| | | for(int i = 0; i < total; i++)
|
| | | dst_array[i] = source_array[i];
|
| | |
|
| | | //---
|
| | | return total; // Numero de elementos copiados
|
| | | }
|
| | |
|
| | | //---
|
| | | template <typename T>
|
| | | int ArrayCopyAll(T &dst_array[], const T &source_array[])
|
| | | {
|
| | | //---
|
| | | const int total = ArraySize(source_array);
|
| | | if(total < 1)
|
| | | return 0;
|
| | |
|
| | | //---
|
| | | ArrayResize(dst_array, total);
|
| | | for(int i = 0; i < total; i++)
|
| | | dst_array[i] = source_array[i];
|
| | |
|
| | | //---
|
| | | return total; // Numero de elementos copiados
|
| | | }
|
| | |
|
| | | //--- Array copy con rangos
|
| | | template <typename T>
|
| | | int ArrayCopyCts(T& dst_array[], const T& src_array[], int dst_start = 0, int src_start = 0, int count = WHOLE_ARRAY)
|
| | | {
|
| | | 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;
|
| | | }
|
| | |
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Add to array (al final) (Con verificacion|Defines|Etc) |
|
| | | //+------------------------------------------------------------------+
|
| | | template <typename K>
|
| | | 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 <typename S>
|
| | | 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 <typename T>
|
| | | 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) array[::ArrayResize(array, size + 1, capacity) - 1] = value;
|
| | | #define AddArrayNoVerification2(array, value, capacity) const int size = ::ArraySize(array); \
|
| | | array[::ArrayResize(array, size + 1, capacity) - 1] = value;
|
| | |
|
| | | #define AddArrayNoVerification3(array, value, capacity) array[::ArrayResize(array, ArraySize(array) + 1, capacity) - 1] = value;
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | template <typename X>
|
| | | __forceinline void AddArrayNoVerification(X &array[], const X &value, int capacity)
|
| | | {
|
| | | array[ArrayResize(array, ArraySize(array) + 1, capacity) - 1] = value;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | template <typename X>
|
| | | __forceinline void AddArrayNoVerification(X* &array[], X* value, int capacity)
|
| | | {
|
| | | array[ArrayResize(array, ArraySize(array) + 1, capacity) - 1] = value;
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | template <typename S>
|
| | | 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;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Find |
|
| | | //+------------------------------------------------------------------+
|
| | | template <typename H>
|
| | | 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 <typename S>
|
| | | 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;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Conversiones |
|
| | | //+------------------------------------------------------------------+
|
| | | template<typename T>
|
| | | string ArrayToString(const T &array[], string separator)
|
| | | {
|
| | | //---
|
| | | string val = ""; // Inicializamos la cadena sin espacios extra
|
| | | const int s = ArraySize(array);
|
| | | for(int i = 0; i < s; i++)
|
| | | {
|
| | | val += string(array[i]) + separator; // Añadimos el separador
|
| | | }
|
| | | //---
|
| | | if(val != "")
|
| | | StringSetLength(val, val.Length() - 1);
|
| | |
|
| | | //---
|
| | | return val; // Devolvemos la cadena construida
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | string ArrayStrToString(const string &array[], string separator)
|
| | | {
|
| | | //---
|
| | | string val = ""; // Inicializamos la cadena sin espacios extra
|
| | | const int s = ArraySize(array);
|
| | | for(int i = 0; i < s; i++)
|
| | | {
|
| | | val += array[i] + separator; // Añadimos el separador
|
| | | }
|
| | | //---
|
| | | if(val != "")
|
| | | StringSetLength(val, val.Length() - 1);
|
| | |
|
| | | //---
|
| | | return val; // Devolvemos la cadena construida
|
| | | }
|
| | |
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | template <typename ST>
|
| | | 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;
|
| | | }
|
| | |
|
| | | //---
|
| | | enum MODE_CLOSET_INDEX
|
| | | {
|
| | | MAYOR_A_TARGET_TIME,
|
| | | MENOR_A_TARGET_TIME
|
| | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | template <typename S>
|
| | | 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<typename T>
|
| | | 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 <typename S>
|
| | | 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 <typename S>
|
| | | 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 <typename S>
|
| | | 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 <typename S>
|
| | | 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 <typename S>
|
| | | 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<typename S>
|
| | | 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<typename T>
|
| | | 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<typename T>
|
| | | int SortByPrice2(const T &arr_src[], T &arr_dest[],
|
| | | const double target_price,
|
| | | const ENUM_MODE_SORT_PRICE mode, int virtual_size = -1) // -1 = no hay virutal size usar el mismo del array
|
| | | {
|
| | | //---
|
| | | int count = 0;
|
| | | const int size = virtual_size == -1 ? ArraySize(arr_src) : virtual_size;
|
| | | ArrayResize(arr_dest, size);
|
| | |
|
| | | //---
|
| | | for(int i = 0, j = 0; i < size * size; i++)
|
| | | {
|
| | | const int row = i / size;
|
| | | const int col = i % size;
|
| | |
|
| | | // Paso 1: Filtrar y copiar una sola vez
|
| | | if(i < size)
|
| | | {
|
| | | 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 <typename S>
|
| | | 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 <typename S>
|
| | | 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<typename S>
|
| | | 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);
|
| | |
|
2026-09-12 19:41:52 -05:00 | | | for(int i = start; i < count; i++)
|
2026-08-27 18:09:07 -05:00 | | | if(array[i] < 0)
|
| | | return true;
|
| | | return (false);
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | template<typename S>
|
| | | 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);
|
| | |
|
2026-09-12 19:41:52 -05:00 | | | for(int i = start; i < count; i++)
|
2026-08-27 18:09:07 -05:00 | | | if(array[i] > 0)
|
| | | return true;
|
| | | return (false);
|
| | | }
|
| | |
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | template <typename T>
|
| | | 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 <typename S>
|
| | | 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--;
|
| | | }
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Time |
|
| | | //+------------------------------------------------------------------+
|
| | | 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)
|
| | | {
|
| | | static MqlDateTime tm;
|
| | | CTimeUtils::TimeToStructFast(time, tm);
|
| | |
|
| | | if(tm.day_of_year == 0)
|
| | | return false; //si es 1 de enero retornamos
|
| | |
|
| | | if(!symbol_is_cripto)
|
| | | {
|
| | | if(tm.day_of_week == 0 || tm.day_of_week == 6)
|
| | | return false;
|
| | | }
|
| | |
|
| | | if(tm.day == 25 && tm.mon == 12)
|
| | | return false;
|
| | |
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //--- Modificacion de tiempo
|
| | | #define MQLARTICLES_TIME_MOD_MIN(m, d) d += ((m) - int((d / MINSECS) % MINSECS)) * MINSECS
|
| | | #define MQLARTICLES_TIME_MOD_HOUR(h, d) d += ((h) - int((time_objetivo / HOURSECS) % 24U)) * HOURSECS
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | datetime HoraYMinutoADatetime(int hora, int minuto, datetime time_objetivo)
|
| | | {
|
| | | time_objetivo -= int(time_objetivo % MINSECS); // Le quitamos los segundos
|
| | | //Print(time_objetivo);
|
| | | MQLARTICLES_TIME_MOD_MIN(minuto, time_objetivo);
|
| | | MQLARTICLES_TIME_MOD_HOUR(hora, time_objetivo);
|
| | | return time_objetivo;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | __forceinline string HoraYMinutoAString(int Hora, int minuto)
|
| | | {
|
| | | return StringFormat("%02d:%02d", Hora, minuto);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | __forceinline int diffGMT_NY()
|
| | | {
|
| | | return TimeIsVerano(TimeTradeServer()) ? -4 : -5;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | __forceinline int SecondsInMonth(int year, int month)
|
| | | {
|
| | | return CTimeUtils::DaysInMonth(year, month) * 86400;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | __forceinline int TimeDifferenceByHHMMSS(datetime event_time)
|
| | | {
|
| | | return (int)(event_time % 86400);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | #define GetTiempoCoeficiente(tiempo_inicio, tiempo_final, coeficiente) ((tiempo_inicio) + int(((tiempo_final) - (tiempo_inicio)) * (coeficiente)))
|
| | |
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | __forceinline 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);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | 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;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | __forceinline datetime MaxTime(datetime time1, datetime time2)
|
| | | {
|
| | | return (time1 > time2) ? time1 : time2;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | __forceinline datetime MinTime(datetime time1, datetime time2)
|
| | | {
|
| | | return (time1 > time2) ? time2 : time1;
|
| | | }
|
| | |
|
2026-08-31 20:49:50 -05:00 | | |
|
2026-08-27 18:09:07 -05:00 | | | //+------------------------------------------------------------------+
|
2026-08-31 20:49:50 -05:00 | | | bool TimeIsInvierno(const datetime time) //GMT - 5 (noviembre_pasado - marzo_actual)
|
2026-08-27 18:09:07 -05:00 | | | {
|
| | | const int year = CTimeUtils::Year(time);
|
| | | const int mon = CTimeUtils::Month(time);
|
| | | if(mon >= 11)
|
| | | {
|
| | | // datetime tiempo = ;//GetFirstSundayOfMonth(11, year);
|
| | | return (time > CTimeUtils::NthWeekdayOfYearMonth(year, 11, 1, SUNDAY));
|
| | | }
|
| | | else
|
| | | if(mon < 11)
|
| | | {
|
| | | // datetime tiempo = GetSecondSundayOfMonth(3, year);
|
| | | return time < CTimeUtils::NthWeekdayOfYearMonth(year, 3, 2, SUNDAY);
|
| | | }
|
| | | return false;
|
| | | }
|
2026-08-31 20:49:50 -05:00 | | |
|
| | | //+------------------------------------------------------------------+
|
| | | __forceinline int GMTOffsetTimeInvierno(const datetime time) { return TimeIsInvierno(time) * -3600; }
|
2026-08-27 18:09:07 -05:00 | | |
|
| | | //+------------------------------------------------------------------+
|
2026-08-31 20:49:50 -05:00 | | | bool TimeIsVerano(const datetime time) //GMT -4 (marzo - noviembre)
|
2026-08-27 18:09:07 -05:00 | | | {
|
| | | const int year = CTimeUtils::Year(time);
|
| | | const int mon = CTimeUtils::Month(time);
|
| | | if(mon >= 11)
|
| | | {
|
| | | // datetime tiempo = GetFirstSundayOfMonth(11, t.year);
|
| | | // solo aseguras que ya pasó el primer domingo de noviembre
|
| | | return (time < CTimeUtils::NthWeekdayOfYearMonth(year, 11, 1, SUNDAY));
|
| | | }
|
| | | else
|
| | | if(mon < 11)
|
| | | {
|
| | | // datetime tiempo = GetSecondSundayOfMonth(3, t.year);
|
| | | return (time >= CTimeUtils::NthWeekdayOfYearMonth(year, 3, 2, SUNDAY));
|
| | | }
|
| | | return false;
|
| | | }
|
2026-08-31 20:49:50 -05:00 | | |
|
| | | //+------------------------------------------------------------------+
|
| | | __forceinline int GMTOffsetTimeVerano(const datetime time) { return TimeIsVerano(time) * 3600; }
|
2026-08-27 18:09:07 -05:00 | | |
|
| | | //+------------------------------------------------------------------+
|
| | | 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)
|
| | | {
|
| | | static MqlDateTime tm;
|
| | | CTimeUtils::TimeToStructFast(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));
|
| | | }
|
| | |
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Converions \ Math |
|
| | | //+------------------------------------------------------------------+
|
| | | template <typename S>
|
| | | inline double dividir(S valor1, S valor2)
|
| | | {
|
| | | return (valor2 == 0) ? 0.0 : double(valor1) / double(valor2);
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | template <typename S>
|
| | | inline double dividir(S valor1, int valor2)
|
| | | {
|
| | | return (valor2 == 0) ? 0.0 : double(valor1) / double(valor2);
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | 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, double(GetIntegerDigits(minimo)));
|
| | |
|
| | | return variacion / double(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
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | __forceinline double GetDist(const double& price1, const double& price2)
|
| | | {
|
| | | return MathMax(price1, price2) - MathMin(price1, price2);
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | __forceinline double GetPriceCoeficiente(const double& price1, const double& price2, double coeficiente)
|
| | | {
|
| | | return (fmax((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 <typename T>
|
| | | void VectorPrint(const vector<T> &v, int digitos, string separator = " | ")
|
| | | {
|
| | | string p = "";
|
| | | for(ulong i = 0; i < v.Size() ; i++)
|
| | | {
|
| | | p += DoubleToString(v[i], digitos) + separator;
|
| | | }
|
| | | Print(p);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | template <typename S>
|
| | | vector ArrayToVector(const S &array[])
|
| | | {
|
| | | vector v(ArraySize(array));
|
| | |
|
| | | for(int i = 0; i < ArraySize(array) ; i++)
|
| | | v[i] = 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 <typename S>
|
| | | void SortVector(vector<S>& data)
|
| | | {
|
| | | if(data.Size() <= 1)
|
| | | return;
|
| | | QuickSortRecursive(data, 0, (int)data.Size() - 1);
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | template <typename S>
|
| | | void QuickSortRecursive(vector<S>& 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 <typename S>
|
| | | int Partition(vector<S>& 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 <typename S>
|
| | | void SwapVectorValue(vector<S>& data, int i, int j)
|
| | | {
|
| | | if(i != j)
|
| | | {
|
| | | S temp = data[i];
|
| | | data[i] = data[j];
|
| | | data[j] = temp;
|
| | | }
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | void UnirVectores(vector &res, vector &arr[])
|
| | | {
|
| | | int total = 0;
|
| | | int size_vector = (int)arr.Size();
|
| | |
|
| | | for(int h = 0; h < size_vector; h++)
|
| | | total += (int)arr[h].Size();
|
| | |
|
| | | res.Resize(total);
|
| | |
|
| | | int k = 0;
|
| | | int i = 0;
|
| | | int x = 0;
|
| | |
|
| | | for(int i = 0; i < size_vector; i++)
|
| | | {
|
| | | for(ulong z = 0; z < arr[i].Size(); z++)
|
| | | {
|
| | | res[x] = arr[i][z];
|
| | | x++;
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | string vector_to_string(const vector &v, string separator = ",")
|
| | | {
|
| | | string vector_str = "";
|
| | | ulong length = v.Size();
|
| | |
|
| | | for(ulong i = 0; i < length; i++)
|
| | | vector_str += DoubleToString(v[i], 14) + separator;
|
| | |
|
| | | //---
|
| | | StringSetLength(vector_str, StringLen(vector_str) - StringLen(separator));
|
| | |
|
| | | //---
|
| | | return vector_str;
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | //| Funciones Extra |
|
| | | //+------------------------------------------------------------------+
|
| | | //++ Se quito Tester() dado que es lo mismo que MQLInfoInteger(MQL_TESTER)..
|
| | | //---
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | long OpenChartAndDevoler(long base_chart_id, string symbol, ENUM_TIMEFRAMES tf, int milisecond_espera)
|
| | | {
|
| | | //---
|
| | | long chart_id = ChartOpen(symbol, tf);
|
| | | if(chart_id == 0) // Fallo
|
| | | {
|
| | | chart_id = -1;
|
| | | }
|
| | | else
|
| | | {
|
| | | Sleep(milisecond_espera); // Para sincronizacion
|
| | | SwitchToChart(base_chart_id); // Cambiamos al grafico actual
|
| | | }
|
| | | return chart_id;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | #define ConvertPriceToPoints(symbol, priceDistance) (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);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | __forceinline void EMPTY_FUNCTION()
|
| | | {
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| 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;
|
| | | }
|
| | |
|
| | | #endif // MQLARTICLES_UTILS_FA_FUNCIONESBASES_MQH
|
| | | //+------------------------------------------------------------------+
|