//+------------------------------------------------------------------+ //| 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 //+------------------------------------------------------------------+