forked from nique_372/MQLArticles
2387 lines
137 KiB
MQL5
2387 lines
137 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 MQLARTICLES_UTILS_FA_FUNCIONESBASES_MQH
| |
#define MQLARTICLES_UTILS_FA_FUNCIONESBASES_MQH
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
#include <Generic\\HashMap.mqh>
| |
#include "StringToArray.mqh"
| |
| |
//---
| |
// (https://forge.mql5.io/nique_372/TimeUtils) Fork of (https://forge.mql5.io/amrali/TimeUtils | autor = amrali)
| |
#include "..\\..\\..\\TimeUtils\\TimeUtils.mqh"
| |
| |
//+------------------------------------------------------------------+
| |
//| Definiciones |
| |
//+------------------------------------------------------------------+
| |
//--- Constantes
| |
const datetime wrong_datetime = 0;
| |
double DOUBLE_ARRAY_EMPTY[1];
| |
double EMPTY_DOUBLE = 0.00;
| |
string EMPTY_STRING = "";
| |
string EMPTY_STRING_NULL = NULL;
| |
vector EMPTY_VECTOR = {};
| |
const bool TESTER_FLAG = MQLInfoInteger(MQL_TESTER);
| |
| |
| |
| |
//--- Arrays globales
| |
string g_arr_str_timefrrames_short[22]
| |
{
| |
"Current",
| |
"M1",
| |
"M2",
| |
"M3",
| |
"M4",
| |
"M5",
| |
"M6",
| |
"M10",
| |
"M12",
| |
"M15",
| |
"M20",
| |
"M30",
| |
"H1",
| |
"H2",
| |
"H3",
| |
"H4",
| |
"H6",
| |
"H8",
| |
"H12",
| |
"D1",
| |
"W1",
| |
"MN1"
| |
};
| |
| |
ENUM_TIMEFRAMES g_arr_strtf_to_timeframes[22]
| |
{
| |
_Period,
| |
PERIOD_M1,
| |
PERIOD_M2,
| |
PERIOD_M3,
| |
PERIOD_M4,
| |
PERIOD_M5,
| |
PERIOD_M6,
| |
PERIOD_M10,
| |
PERIOD_M12,
| |
PERIOD_M15,
| |
PERIOD_M20,
| |
PERIOD_M30,
| |
PERIOD_H1,
| |
PERIOD_H2,
| |
PERIOD_H3,
| |
PERIOD_H4,
| |
PERIOD_H6,
| |
PERIOD_H8,
| |
PERIOD_H12,
| |
PERIOD_D1,
| |
PERIOD_W1,
| |
PERIOD_MN1
| |
};
| |
| |
//--- Defines
| |
#define NOT_DEFINED_STRING "Not Defined"
| |
#define UNDEFINED_REPLACE 1
| |
#define INVALID_INDEX -1
| |
| |
//--- Enums
| |
enum idiomas
| |
{
| |
English,
| |
Spanish
| |
};
| |
| |
//---
| |
string g_str_arr_dyn_arr[];
| |
| |
//---
| |
const string TERMINAL_MT5_DATA_PAH = ::TerminalInfoString(TERMINAL_DATA_PATH);
| |
const string TERMINAL_MT5_COMMON_PATH = ::TerminalInfoString(TERMINAL_COMMONDATA_PATH);
| |
const string TERMINAL_MT5_ROOT = TERMINAL_MT5_DATA_PAH + "\\MQL5\\";
| |
| |
//+------------------------------------------------------------------+
| |
//| Flags |
| |
//+------------------------------------------------------------------+
| |
//---
| |
#define MQLARTICLES_FUNC_REMOVE_FLAG(var, flag) var &= ~(flag)
| |
| |
//+------------------------------------------------------------------+
| |
//| 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;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Eliminar multiples indices (VirtualSize | Normal) |
| |
//+------------------------------------------------------------------+
| |
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;
| |
| |
//---
| |
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[], 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;
| |
| |
//---
| |
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[], 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;
| |
| |
//---
| |
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, 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
| |
}
| |
| |
| |
| |
//+------------------------------------------------------------------+
| |
//| Array copy |
| |
//+------------------------------------------------------------------+
| |
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);
| |
| |
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;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
int StringFindAtras(const string value, const string find, int aum = 0)
| |
{
| |
const int len = StringLen(value);
| |
const int len_find = StringLen(find);
| |
for(int i = len - len_find - aum; i >= 0; i--)
| |
{
| |
bool match = true;
| |
for(int k = 0; k < len_find; k++)
| |
{
| |
if(value[i + k] != find[k])
| |
{
| |
match = false;
| |
break;
| |
}
| |
}
| |
if(match)
| |
return i;
| |
}
| |
return -1;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
int StringFindAtras(const string value, const ushort caracter, int aum = 0)
| |
{
| |
const int len = StringLen(value);
| |
for(int i = len - 1 - aum; i >= 0; i--)
| |
{
| |
if(value[i] == caracter)
| |
return i;
| |
}
| |
return -1;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
int StringFindChar(const string value, const ushort caracter, int start = 0)
| |
{
| |
const int len = StringLen(value);
| |
for(int i = start; i < len; i++)
| |
{
| |
if(value[i] == caracter)
| |
return i;
| |
}
| |
return -1;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
inline int StringFindCharWRef(const string& value, const ushort& caracter, const int& len, const int& start)
| |
{
| |
for(int i = start; i < len; i++)
| |
if(value[i] == caracter)
| |
return i;
| |
return -1;
| |
}
| |
| |
| |
//+------------------------------------------------------------------+
| |
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
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
#define StringSubstrRange(s, ps, pe) s.Substr(ps,((pe)-(ps))+1)
| |
| |
//+------------------------------------------------------------------+
| |
int StringSplitStr(const string& str, const string& sep, string& arr[], int reserve)
| |
{
| |
//---
| |
if(str == NULL)
| |
return -1; // Vacio
| |
| |
//---
| |
int last_start = 0;
| |
const int len_str = int(str.Length());
| |
const int len_sep = int(sep.Length());
| |
int s = 0;
| |
int c = ArrayResize(arr, reserve);
| |
| |
// 1||N||0||1||sAdasdasdasd"
| |
//---
| |
while(true)
| |
{
| |
static int init;
| |
if((init = str.Find(sep, last_start)) >= 0)
| |
{
| |
//---
| |
if(s >= c)
| |
{
| |
c <<= 1;
| |
ArrayResize(arr, c);
| |
}
| |
| |
| |
//---
| |
arr[s++] = StringSubstrRange(str, last_start, init - 1);
| |
last_start = init + len_sep;
| |
}
| |
else
| |
{
| |
arr[ArrayResize(arr, (++s)) - 1] = str.Substr(last_start, len_str);
| |
return s;
| |
}
| |
}
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
int StringSplitFuncParams(const string& str, const ushort sep_params, string& out[], int reserve)
| |
{
| |
//---
| |
if(str == NULL)
| |
return -1; // Vacio
| |
| |
// 1:0, 2:'Hola'
| |
//---
| |
bool in = false;
| |
const int len = StringLen(str);
| |
string current = "";
| |
int reserve_size = ArrayResize(out, fmax(4, reserve));
| |
int current_size = 0;
| |
| |
//---
| |
for(int i = 0; i < len; i++)
| |
{
| |
if(str[i] == '\'') // ' = Inicio de string
| |
{
| |
in = !in;
| |
}
| |
if(!in && str[i] == sep_params)
| |
{
| |
out[current_size] = current;
| |
current_size++;
| |
if(current_size >= reserve_size)
| |
{
| |
reserve_size <<= 1;
| |
ArrayResize(out, reserve_size);
| |
}
| |
current = "";
| |
}
| |
else
| |
{
| |
current += ShortToString(str[i]);
| |
}
| |
}
| |
| |
//---
| |
if(current != "")
| |
{
| |
out[current_size++] = current;
| |
}
| |
else
| |
if(current_size == 0)
| |
{
| |
out[current_size++] = str; // Todo
| |
}
| |
| |
//---
| |
return ArrayResize(out, current_size);
| |
}
| |
| |
//---
| |
#define StringTrim(text) \
| |
StringTrimRight(text); \
| |
StringTrimLeft(text);
| |
| |
//+------------------------------------------------------------------+
| |
//| 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;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
datetime HoraYMinutoADatetime(int hora, int minuto, datetime time_objetivo)
| |
{
| |
static MqlDateTime tm;
| |
CTimeUtils::TimeToStructFast(time_objetivo, tm);
| |
tm.hour = hora;
| |
tm.min = minuto;
| |
tm.sec = 0;
| |
return CTimeUtils::CreateDateTimeByStruct(tm);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
__forceinline string HoraYMinutoAString(int Hora, int minuto)
| |
{
| |
return StringFormat("%02d:%02d", Hora, minuto);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
__forceinline int diffGMT_NY()
| |
{
| |
return TimeIsVerano(TimeTradeServer()) ? -4 : -5;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
__forceinline datetime GetFirstSundayOfMonth(int month, int year)
| |
{
| |
if(month < 1 || month > 12 || year < 1970)
| |
return 0;
| |
return CTimeUtils::GetNthWeekdayInYearMonth(year, month, 1, SUNDAY);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
__forceinline datetime GetSecondSundayOfMonth(int month, int year)
| |
{
| |
if(month < 1 || month > 12 || year < 1970)
| |
return 0;
| |
return CTimeUtils::GetNthWeekdayInYearMonth(year, month, 2, SUNDAY);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
__forceinline datetime GetInitMonth(int year, int month)
| |
{
| |
return CTimeUtils::CreateDateTime(year, month, 1);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
__forceinline int DaysInMonth(int year, int month)
| |
{
| |
return CTimeUtils::DaysInMonth(year, month);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
__forceinline int SecondsInMonth(int year, int month)
| |
{
| |
return CTimeUtils::DaysInMonth(year, month) * 86400;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
int SecondsInMonth(datetime time)
| |
{
| |
MqlDateTime tm;
| |
CTimeUtils::TimeToStructFast(time, tm);
| |
return SecondsInMonth(tm.year, tm.mon);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
__forceinline datetime GetInitDayTime(datetime time)
| |
{
| |
return CTimeUtils::StartOfDay(time);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
__forceinline datetime GetInitWeekTime(datetime time)
| |
{
| |
return CTimeUtils::StartOfWeek(time, true);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
datetime ModifiedTime(datetime original, int hour, int min, int sec)
| |
{
| |
MqlDateTime time_struct;
| |
CTimeUtils::TimeToStructFast(original, time_struct);
| |
return CTimeUtils::CreateDateTime(time_struct.year, time_struct.mon, time_struct.day, hour, min, sec);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
__forceinline int TimeDifferenceByHHMMSS(datetime event_time)
| |
{
| |
return (int)(event_time % 86400);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
__forceinline int ExtraerMinutoDatetime(const datetime time)
| |
{
| |
return CTimeUtils::GetMinute(time);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
#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;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
bool TimeIsInvierno(datetime time) //GMT - 5 (noviembre_pasado - marzo_actual)
| |
{
| |
static MqlDateTime t;
| |
CTimeUtils::TimeToStructFast(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)
| |
{
| |
static MqlDateTime t;
| |
CTimeUtils::TimeToStructFast(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)
| |
{
| |
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;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
vector string_to_vector(const string str, ushort separator = ',')
| |
{
| |
string valores_linea[];
| |
StringSplit(str, separator, valores_linea);
| |
int num_valores = ArraySize(valores_linea);
| |
| |
vector v(num_valores);
| |
| |
for(int i = 0; i < num_valores; i++)
| |
v[i] = StringToDouble(valores_linea[i]);
| |
| |
return v;
| |
}
| |
//+------------------------------------------------------------------+
| |
//| Funciones Extra |
| |
//+------------------------------------------------------------------+
| |
//++ Se quito Tester() dado que es lo mismo que MQLInfoInteger(MQL_TESTER)..
| |
//---
| |
#define MQLARTICLES_FUNCIONESBASES_FASTMIN(A, B) ((A)>(B)?(B):(A))
| |
#define MQLARTICLES_FUNCIONESBASES_FASTMAX(A, B) ((A)>(B)?(A):(B))
| |
//+------------------------------------------------------------------+
| |
void Remover(string short_name = " ", long chart_id = 0, int subwin = 0)
| |
{
| |
if(MQLInfoInteger(MQL_PROGRAM_TYPE) == PROGRAM_INDICATOR)
| |
{
| |
if(!MQLInfoInteger(MQL_TESTER))
| |
ChartIndicatorDelete(chart_id, subwin, short_name);
| |
else
| |
TesterStop();
| |
return;
| |
}
| |
| |
if(MQLInfoInteger(MQL_TESTER))
| |
TesterStop();
| |
else
| |
ExpertRemove();
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
string TimeframeToStrShort(const ENUM_TIMEFRAMES timeframe)
| |
{
| |
if(timeframe == PERIOD_CURRENT)
| |
return "Current";
| |
else
| |
{
| |
string str = EnumToString(timeframe);
| |
StringReplace(str, "PERIOD_", "");
| |
return str;
| |
}
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
ENUM_TIMEFRAMES StrShortTimeframeToEnum(const string& str)
| |
{
| |
//---
| |
const uint len = str.Length();
| |
const ushort first = str[0];
| |
| |
//---
| |
if(first == 'M')
| |
{
| |
if(len == 2)
| |
{
| |
const int n = (str[1] - 48);
| |
return (n < 1 || n > 6) ? _Period : ENUM_TIMEFRAMES(n);
| |
}
| |
else
| |
if(len == 3)
| |
{
| |
if(str[1] == 'N') // MN
| |
return PERIOD_MN1;
| |
| |
const int n = ((str[1] - '0') * 10 + (str[2] - '0'));
| |
return (n == 10 || n == 12 || n == 15 || n == 20 || n == 30) ? ENUM_TIMEFRAMES(n) : _Period;
| |
}
| |
}
| |
else
| |
if(first == 'H')
| |
{
| |
const int n = ((len == 2) ? str[1] - '0' : (str[1] - '0') * 10 + (str[2] - '0'));
| |
// Bitmask: 0001000101011110 = 0x115E
| |
// Bits: [12][8][6][4][3][2][1] = válidos
| |
// Aqui lo que hamos es desplazar a la derecha y el primer bit debe de ser 1.. si se da u numero que no es..
| |
// entonces el desplazamieot daria ene l bit 1 0 por lo que esto daria error
| |
return (n < 13 && bool((0x115E >> n) & 1)) ? ENUM_TIMEFRAMES(16384 + n) : _Period;
| |
}
| |
else
| |
if(first == 'D')
| |
return PERIOD_D1;
| |
else
| |
if(first == 'W')
| |
return PERIOD_W1;
| |
| |
//--- Cualquier otro caso Current
| |
return _Period;
| |
}
| |
| |
| |
//+------------------------------------------------------------------+
| |
ENUM_TIMEFRAMES StrShortTimeframeToEnumNoRef(const string str)
| |
{
| |
//---
| |
const uint len = str.Length();
| |
const ushort first = str[0];
| |
| |
//---
| |
if(first == 'M')
| |
{
| |
if(len == 2)
| |
{
| |
const int n = (str[1] - 48);
| |
return (n < 1 || n > 6) ? _Period : ENUM_TIMEFRAMES(n);
| |
}
| |
else
| |
if(len == 3)
| |
{
| |
if(str[1] == 'N') // MN
| |
return PERIOD_MN1;
| |
| |
const int n = ((str[1] - '0') * 10 + (str[2] - '0'));
| |
return (n == 10 || n == 12 || n == 15 || n == 20 || n == 30) ? ENUM_TIMEFRAMES(n) : _Period;
| |
}
| |
}
| |
else
| |
if(first == 'H')
| |
{
| |
const int n = ((len == 2) ? str[1] - '0' : (str[1] - '0') * 10 + (str[2] - '0'));
| |
// Bitmask: 0001000101011110 = 0x115E
| |
// Bits: [12][8][6][4][3][2][1] = válidos
| |
// Aqui lo que hamos es desplazar a la derecha y el primer bit debe de ser 1.. si se da u numero que no es..
| |
// entonces el desplazamieot daria ene l bit 1 0 por lo que esto daria error
| |
return (n < 13 && bool((0x115E >> n) & 1)) ? ENUM_TIMEFRAMES(16384 + n) : _Period;
| |
}
| |
else
| |
if(first == 'D')
| |
return PERIOD_D1;
| |
else
| |
if(first == 'W')
| |
return PERIOD_W1;
| |
| |
//--- Cualquier otro caso Current
| |
return _Period;
| |
}
| |
| |
| |
//+------------------------------------------------------------------+
| |
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 = TERMINAL_MT5_COMMON_PATH + "\\MQL5\\Files";
| |
else
| |
working_folder = TERMINAL_MT5_DATA_PAH + "\\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;
| |
}
| |
//+------------------------------------------------------------------+
| |
// Referencia: iCanvas_CB.mqh
| |
int PeriodSecondsFast(ENUM_TIMEFRAMES tf)
| |
{
| |
if(tf == PERIOD_CURRENT)
| |
tf = _Period;
| |
const int tff = tf >> 14;
| |
return (tff == 0) ? (tf & 0x0FFF) * 60 : (tff == 1) ? (tf & 0x0FFF) * 3600 : (tff == 2) ? 604800 : 2592000;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
__forceinline int PeriodSecondsFastSeg(ENUM_TIMEFRAMES tf)
| |
{
| |
const int tff = tf >> 14;
| |
return (tff == 0) ? (tf & 0x0FFF) * 60 : (tff == 1) ? (tf & 0x0FFF) * 3600 : (tff == 2) ? 604800 : 2592000;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
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 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
| |
//+------------------------------------------------------------------+
|