MQLArticles/Utils/Fibonacci.mqh
2025-09-22 09:08:13 -05:00

535 lines
36 KiB
MQL5

//+------------------------------------------------------------------+
//| Fibonacci.mqh |
//| Copyright 2025, Niquel Mendoza. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Niquel Mendoza."
#property link "https://www.mql5.com"
#property strict
#ifndef MQH_FIBBO
#define MQH_FIBBO
#include "..\\Utils\\Objectos 2D.mqh"
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
struct FibboLevel
{
double level_price;
double level_value;
color level_color;
FibboLevel()
: level_color(clrBlack), level_price(0.00), level_value(0.00)
{
}
FibboLevel(const FibboLevel& other)
{
this = other;
}
};
enum ENUM_MODE_NEAREST_LEVEL_FIBBO
{
FIBBO_LEVEL_MAYOR_A_TARGE_PRICE,
FIBBO_LEVEL_MENOR_A_TARGE_PRICE,
};
//+------------------------------------------------------------------+
#define GEN_SORT(TYPE, FIELD_ACCESS) \
void SortArrayBy##FIELD_ACCESS(TYPE &array[], int left, int right) \
{ \
if(left >= right) \
return; \
\
int pivotIndex = (left + right) >> 1; \
double pivotValue = array[pivotIndex].FIELD_ACCESS; \
int i = left, j = right; \
\
while(i <= j) \
{ \
while(array[i].FIELD_ACCESS < pivotValue) \
i++; \
\
while(array[j].FIELD_ACCESS > pivotValue) \
j--; \
\
if(i <= j) \
{ \
TYPE temp = array[i]; \
array[i] = array[j]; \
array[j] = temp; \
i++; \
j--; \
} \
} \
\
SortArrayBy##FIELD_ACCESS(array, left, j); \
SortArrayBy##FIELD_ACCESS(array, i, right); \
}
#define GEN_SORT_HELPER(TYPE, FIELD_ACCESS) \
void SortArrayBy##FIELD_ACCESS(TYPE &array[]) \
{ \
int size = ArraySize(array); \
if(size > 1) \
SortArrayBy##FIELD_ACCESS(array, 0, size - 1); \
}
//+------------------------------------------------------------------+
GEN_SORT(FibboLevel, level_value)
GEN_SORT_HELPER(FibboLevel, level_value)
//+------------------------------------------------------------------+
GEN_SORT(FibboLevel, level_price)
GEN_SORT_HELPER(FibboLevel, level_price)
#undef GEN_SORT
#undef GEN_SORT_HELPER
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CFibbo : public CLoggerBase
{
private:
//--- Caracteristicas principales
long chart_id;
int subwin;
bool create; //Indica si el fibbo ha sido creado
//--- Caracteristicas del fibbo
string obj_name;
double price_2;
double price_1;
datetime init_fibbo;
//---
double max_level;
double min_level;
//---
int total_fibbo;
FibboLevel fibbo_levels[];
//---
int8_t digits;
//--- Find
bool CheckIndex(const int index) const;
//--- Add
bool AddLevel(const FibboLevel &new_level);
//---
void CalculatePriceLevelsFibbo();
public:
CFibbo();
~CFibbo(void);
//--- Creacion
bool Create(const long chart_ID, // ID del gráfico
const string name, // nombre del objeto
const int sub_window, // número de subventana
const color clr, // color del objeto
const ENUM_LINE_STYLE style, // estilo de las líneas del objeto
const int width,
const string tooltip, // grosor de las líneas del objeto
const bool back = false, // al fondo
const bool selection = false, // seleccionar para mover
const long z_order = 0);
//--- Objetos Graficos\Niveles
bool SetLevels(string vals, // valores de las líneas del nivel
string clrs, // color de las líneas del nivel
ENUM_LINE_STYLE styles, // estilo de las líneas del nivel
int widths,
ushort separator_str
); // grosor de las líneas del nivel
void ModifyLevel(double level, color new_color = clrNONE, ENUM_LINE_STYLE new_style = WRONG_VALUE, int new_widt = -1); //Cambiar un nivel
void ModifyLevel(int level_index, color new_color = clrNONE, ENUM_LINE_STYLE new_style = WRONG_VALUE, int new_widt = -1); //Cambiar un nivel
//--- Modificar el donde se ubica el fibbo
void Move(datetime new_init, datetime new_end, double price1, double price2); //Momer el fibbo completamente
void UpdateTime2(datetime new_end); //Solo actulizar el fibbo a un nuevo time2
//--- Funciones para trabajar con niveles
inline double GetLevelPrice(const double level_val) const; //Obtener el precio actual de un nivel(double)
int GetLevelIndex(const double level_val) const; //Obtener el indice en el array de un nivel(double)
void GetArrayLevel(double &levels[], bool contains_lebel_0_100 = false) const;
double GetNearestLevel(double target_price, ENUM_MODE_NEAREST_LEVEL_FIBBO mode) const;
//--- Getter - Max\Min Level
inline double MaxLevel() const { return this.max_level; }
inline double MinLevel() const { return this.min_level; }
//---
void PrintInfo();
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CFibbo::~CFibbo()
{
ArrayFree(fibbo_levels);
ObjectDelete(this.chart_id, this.obj_name);
}
//+------------------------------------------------------------------+
CFibbo::CFibbo()
: init_fibbo(wrong_datetime), price_1(0), price_2(0), subwin(0), chart_id(0), max_level(0), min_level(0)
{
ArrayResize(fibbo_levels, 0);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CFibbo::PrintInfo(void)
{
int d = fmax(3, digits);
ArrayPrint(this.fibbo_levels, d, " | ");
}
//+------------------------------------------------------------------+
bool CFibbo::AddLevel(const FibboLevel &new_level)
{
const static double fibbo_min_value = 0.00040; //Considerar esto, de pormedio un fibbo level como maximo tiene 3 decimales
for(int i = 0; i < total_fibbo; i++)
{
if(fabs(fibbo_levels[i].level_value - new_level.level_value) <= fibbo_min_value)
{
LogWarning(StringFormat("No se agregara el fibbo level %f dado que ya existe", new_level.level_value), FUNCION_ACTUAL);
return false;
}
}
total_fibbo++;
AddArrayNoVerification(fibbo_levels, new_level, 0);
return true;
}
//+------------------------------------------------------------------+
double CFibbo::GetNearestLevel(double target_price, ENUM_MODE_NEAREST_LEVEL_FIBBO mode) const
{
if(total_fibbo == 0)
return 0.00;
//---
FibboLevel temp_levels[];
ArrayResize(temp_levels, total_fibbo);
ArrayCopyCts(temp_levels, fibbo_levels);
//---
SortArrayBylevel_price(temp_levels);
if(mode == FIBBO_LEVEL_MAYOR_A_TARGE_PRICE)
{
for(int i = 0; i < total_fibbo; i++)
{
if(temp_levels[i].level_price > target_price)
{
return temp_levels[i].level_price;
}
}
}
else
if(mode == FIBBO_LEVEL_MENOR_A_TARGE_PRICE)
{
double nivel_menor = 0.00;
bool encontrado = false;
for(int i = 0; i < total_fibbo; i++)
{
if(temp_levels[i].level_price < target_price)
{
nivel_menor = temp_levels[i].level_price;
encontrado = true;
}
}
if(encontrado)
return nivel_menor;
}
LogWarning(StringFormat("No se ha encontrado un nivel cercano al target price = %.*f >> modo = %s", digits, target_price, EnumToString(mode)), FUNCION_ACTUAL);
return 0.00;
}
//+------------------------------------------------------------------+
bool CFibbo::Create(const long chart_ID, // ID del gráfico
const string name, // nombre del objeto
const int sub_window, // número de subventana
const color clr, // color del objeto
const ENUM_LINE_STYLE style, // estilo de las líneas del objeto
const int width,
const string tooltip, // grosor de las líneas del objeto
const bool back = false, // al fondo
const bool selection = false, // seleccionar para mover
const long z_order = 0)
{
if(create)
{
LogWarning("Se esta volviendo a redibujar el objeto fibbo..", FUNCION_ACTUAL);
}
this.chart_id = chart_ID;
this.subwin = sub_window;
string s = ChartSymbol(chart_ID);
this.digits = (int8_t)SymbolInfoInteger(s, SYMBOL_DIGITS);
bool res = FiboLevelsCreate(chart_ID, name, sub_window, 0, 0, 0, 0, clr, style, width, back, selection, false, false, z_order);
this.obj_name = name;
if(!res)
{
LogError(StringFormat("No se pudo crear el fibbo[ %s ], ultimo error = %d", name, GetLastError()), FUNCION_ACTUAL);
}
else
{
ObjectSetString(chart_ID, name, OBJPROP_TOOLTIP, tooltip);
create = true;
}
return res;
}
//+------------------------------------------------------------------+
void CFibbo::GetArrayLevel(double &levels[], bool contains_lebel_0_100 = false) const
{
for(int i = 0; i < ArraySize(fibbo_levels) ; i++)
{
if((fibbo_levels[i].level_value == 0.00 || fibbo_levels[i].level_value == 1.00) && contains_lebel_0_100 == false)
continue;
AddArrayNoVerification(levels, fibbo_levels[i].level_value, 0);
}
}
//+------------------------------------------------------------------+
bool CFibbo::CheckIndex(const int index) const
{
if(index < 0 || index >= total_fibbo)
{
LogError(StringFormat("El indice %d esta fuera de rango, rango diponible: [ %d - %d ]", 0, total_fibbo), FUNCION_ACTUAL);
Remover();
return false;
}
return true;
}
//+------------------------------------------------------------------+
int CFibbo::GetLevelIndex(const double level_val) const
{
int left = 0;
int right = total_fibbo - 1;
const static double fibbo_min_value = 0.000040;
while(left <= right)
{
int mid = left + ((right - left) >> 1);
if(fabs(fibbo_levels[mid].level_value - level_val) <= fibbo_min_value) //no es seguro comparar double
return mid; // Encontrado
else
if(fibbo_levels[mid].level_value < level_val)
left = mid + 1;
else
right = mid - 1;
}
LogWarning(StringFormat("No se ha encontrado el indice del level_value = %.3f", level_val), FUNCION_ACTUAL);
return -1;
}
//+------------------------------------------------------------------+
void CFibbo::ModifyLevel(double level, color new_color = clrNONE, ENUM_LINE_STYLE new_style = -1, int new_widt = -1)
{
int idx;
if((idx = GetLevelIndex(level)) == -1)
return;
//---
if(new_color != clrNONE)
ObjectSetInteger(chart_id, obj_name, OBJPROP_LEVELCOLOR, idx, new_color);
//---
if(new_style != WRONG_VALUE)
ObjectSetInteger(chart_id, obj_name, OBJPROP_LEVELSTYLE, idx, new_style);
//---
if(new_widt >= 1)
ObjectSetInteger(chart_id, obj_name, OBJPROP_LEVELWIDTH, idx, new_widt);
}
//+------------------------------------------------------------------+
void CFibbo::ModifyLevel(int level_index, color new_color = clrNONE, ENUM_LINE_STYLE new_style = -1, int new_widt = -1)
{
if(!CheckIndex(level_index))
return;
//---
if(new_color != clrNONE)
ObjectSetInteger(chart_id, obj_name, OBJPROP_LEVELCOLOR, level_index, new_color);
//---
if(new_style != WRONG_VALUE)
ObjectSetInteger(chart_id, obj_name, OBJPROP_LEVELSTYLE, level_index, new_style);
//---
if(new_widt >= 1)
ObjectSetInteger(chart_id, obj_name, OBJPROP_LEVELWIDTH, level_index, new_widt);
}
//+------------------------------------------------------------------+
void CFibbo::UpdateTime2(datetime new_end)
{
ObjectSetInteger(this.chart_id, this.obj_name, OBJPROP_TIME, 1, new_end);
}
//+------------------------------------------------------------------+
inline double CFibbo::GetLevelPrice(const double level_val) const
{
int idx;
if((idx = GetLevelIndex(level_val)) == -1)
return 0.00;
return fibbo_levels[idx].level_price;
}
//+------------------------------------------------------------------+
void CFibbo::CalculatePriceLevelsFibbo(void)
{
for(int i = 0; i < total_fibbo; i++)
{
double val = ObjectGetDouble(this.chart_id, obj_name, OBJPROP_LEVELVALUE, i);
double diff = MathAbs(price_1 - price_2);
if(price_1 > price_2)
this.fibbo_levels[i].level_price = NormalizeDouble((price_2 + (val * diff)), digits);
else
if(price_2 > price_1)
this.fibbo_levels[i].level_price = NormalizeDouble((price_2 - (val * diff)), digits);
else //Si son iguales error
{
LogError(StringFormat("No se pudo obtener un valor para el level fibbo = %.3f", fibbo_levels[i].level_value), FUNCION_ACTUAL);
}
}
}
//+------------------------------------------------------------------+
void CFibbo::Move(datetime new_init, datetime new_end, double price1, double price2)
{
ResetLastError();
if(!ObjectMove(this.chart_id, obj_name, 0, new_init, price1))
{
LogError(StringFormat("No se pudo movel el anclaje 0, new_init = %s >> price1 = %.*f, ultimo error = %d", TimeToString(new_init), digits, price1, GetLastError()), FUNCION_ACTUAL);
}
ResetLastError();
if(!ObjectMove(this.chart_id, obj_name, 1, new_end, price2))
{
LogError(StringFormat("No se pudo movel el anclaje 1, time2 = %s >> price2 = %.*f, ultimo error = %d", TimeToString(new_end), digits, price2, GetLastError()), FUNCION_ACTUAL);
}
this.price_1 = price1;
this.price_2 = price2;
this.init_fibbo = new_init;
CalculatePriceLevelsFibbo();
}
//+------------------------------------------------------------------+
bool CFibbo::SetLevels(string vals, string clrs, ENUM_LINE_STYLE styles, int widths, ushort separator_str)
{
//---
string res_vals[];
int num_vals;
string res_clrs[];
int num_clrs;
//---
if((num_vals = StringSplit(vals, separator_str, res_vals)) == -1)
{
LogError(StringFormat("La cadena de valores %s no contiene el separador %s o esta vacia", vals, ShortToString(separator_str)), FUNCION_ACTUAL);
return false;
}
if((num_clrs = StringSplit(clrs, separator_str, res_clrs)) == -1)
{
LogError(StringFormat("La cadena de colores %s no contiene el separador %s o esta vacia", vals, ShortToString(separator_str)), FUNCION_ACTUAL);
return false;
}
//---
if(num_vals != num_clrs)
{
LogError(StringFormat("El tamaño del array de niveles %d y colores %d no son iguales", num_vals, num_clrs), FUNCION_ACTUAL);
return(false);
}
//---
ArrayFree(this.fibbo_levels);
this.total_fibbo = 0;
//---
for(int i = 0; i < num_clrs; i++)
{
FibboLevel new_level;
//--- para color trim, mql5 confunde con espacion asuem black
string res_color = StringTrimChar(res_clrs[i]);
new_level.level_color = StringToColor(res_color);
//--- para oduble si, si hay eopacions no importa
new_level.level_value = StringToDouble(res_vals[i]);
new_level.level_price = 0.00;
AddLevel(new_level);
}
SortArrayBylevel_value(this.fibbo_levels);
//---
if(IsInfoLogEnabled())
{
FastLog(FUNCION_ACTUAL, INFO_TEXT, "Niveles de fibbos listos: ");
ArrayPrint(this.fibbo_levels, 3, " | ");
}
//--- establecemos el número de los niveles
ObjectSetInteger(chart_id, obj_name, OBJPROP_LEVELS, total_fibbo);
//--- establecemos las propiedades de los niveles en el ciclo
for(int i = 0; i < total_fibbo; i++)
{
//--- valor del nivel
ObjectSetDouble(chart_id, obj_name, OBJPROP_LEVELVALUE, i, fibbo_levels[i].level_value);
//--- color del nivel
ObjectSetInteger(chart_id, obj_name, OBJPROP_LEVELCOLOR, i, fibbo_levels[i].level_color);
//--- estilo del nivel
ObjectSetInteger(chart_id, obj_name, OBJPROP_LEVELSTYLE, i, styles);
//--- grosor del nivel
ObjectSetInteger(chart_id, obj_name, OBJPROP_LEVELWIDTH, i, widths);
//--- descripción del nivel
ObjectSetString(chart_id, obj_name, OBJPROP_LEVELTEXT, i, DoubleToString(100 * fibbo_levels[i].level_value, 1));
}
this.max_level = this.fibbo_levels[total_fibbo - 1].level_value;
this.min_level = this.fibbo_levels[0].level_value;
//--- ejecución con éxito
return(true);
}
//+------------------------------------------------------------------+
#endif
//+------------------------------------------------------------------+