forked from nique_372/MQLArticles
		
	
		
			
				
	
	
		
			1952 lines
		
	
	
	
		
			115 KiB
		
	
	
	
		
			MQL5
		
	
	
	
	
	
			
		
		
	
	
			1952 lines
		
	
	
	
		
			115 KiB
		
	
	
	
		
			MQL5
		
	
	
	
	
	
//+------------------------------------------------------------------+
 | 
						|
//|                                               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 <Generic\HashMap.mqh>
 | 
						|
#include "StringToArray.mqh"
 | 
						|
 | 
						|
#define INVALID_INDEX -1
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| Definiciones                                                     |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
const datetime wrong_datetime = 0;
 | 
						|
double DOUBLE_ARRAY_EMPTY[];
 | 
						|
string EMPTY_STRING = "";
 | 
						|
string EMPTY_STRING_NULL = NULL;
 | 
						|
vector EMPTY_VECTOR = {};
 | 
						|
 | 
						|
#define NOT_DEFINED_STRING "Not Defined"
 | 
						|
#define UNDEFINED_REPLACE 1
 | 
						|
 | 
						|
enum idiomas
 | 
						|
 {
 | 
						|
  English,
 | 
						|
  Spanish
 | 
						|
 };
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|                    Trabajo Con Arrays                            |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
template <typename T>
 | 
						|
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 <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;
 | 
						|
 }
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
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;
 | 
						|
 }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
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)
 | 
						|
     {
 | 
						|
      // 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 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 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>
 | 
						|
int 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 old_size;
 | 
						|
 | 
						|
  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];
 | 
						|
     }
 | 
						|
   }
 | 
						|
 | 
						|
  return ArrayResize(arr, write_index, capacity);
 | 
						|
 }
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
template<typename T>
 | 
						|
int 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 old_size; 
 | 
						|
 | 
						|
  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];
 | 
						|
     }
 | 
						|
   }
 | 
						|
 | 
						|
  return ArrayResize(arr, write_index, capacity);
 | 
						|
 }
 | 
						|
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
template<typename T>
 | 
						|
int 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 old_size;
 | 
						|
 | 
						|
  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];
 | 
						|
     }
 | 
						|
   }
 | 
						|
 | 
						|
  return ArrayResize(arr, write_index, capacity);
 | 
						|
 }
 | 
						|
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
template <typename T>
 | 
						|
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 <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) 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 <typename X>
 | 
						|
inline void AddArrayNoVerification(X &array[], const X &value, int capacity)
 | 
						|
 {
 | 
						|
  const int size = ArraySize(array);
 | 
						|
  ArrayResize(array, size + 1, capacity);
 | 
						|
  array[size] = value;
 | 
						|
 }
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
template <typename X>
 | 
						|
inline void AddArrayNoVerification(X* &array[], X* value, int capacity)
 | 
						|
 {
 | 
						|
  const int size = ArraySize(array);
 | 
						|
  ArrayResize(array, size + 1, capacity);
 | 
						|
  array[size] = value;
 | 
						|
 }
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
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;
 | 
						|
 }
 | 
						|
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
template<typename T>
 | 
						|
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 <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;
 | 
						|
 }
 | 
						|
/*
 | 
						|
template <typename ST>
 | 
						|
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 <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 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 <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);
 | 
						|
 | 
						|
  for(int i = start; i < count && !IsStopped(); i++)
 | 
						|
    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);
 | 
						|
 | 
						|
  for(int i = start; i < count && !IsStopped(); i++)
 | 
						|
    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--;
 | 
						|
   }
 | 
						|
 }
 | 
						|
 | 
						|
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|                        Strings                                   |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
template <typename S>
 | 
						|
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 <typename S>
 | 
						|
inline double IntegerToDouble(S valor)
 | 
						|
 {
 | 
						|
  return (double)valor;
 | 
						|
 }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
template <typename S>
 | 
						|
inline double dividir(S valor1, S valor2)
 | 
						|
 {
 | 
						|
  return (valor2 == 0) ? 0.0 : IntegerToDouble(valor1) / IntegerToDouble(valor2);
 | 
						|
 }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
template <typename S>
 | 
						|
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 <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] =  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 <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;
 | 
						|
   }
 | 
						|
 }
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|                       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
 | 
						|
//+------------------------------------------------------------------+
 |