//+------------------------------------------------------------------+ //| 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 MQLARTICLES_UTILS_FIBONACCI_MQH #define MQLARTICLES_UTILS_FIBONACCI_MQH #include "..\\Utils\\GraphicObjects.mqh" #include "FA\\Sort.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ struct pack(8) 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; } //--- static __forceinline bool MayorLevel(const FibboLevel& f1, const FibboLevel& f2) { return f1.level_value > f2.level_value; } }; enum ENUM_MODE_NEAREST_LEVEL_FIBBO { FIBBO_LEVEL_MAYOR_A_TARGE_PRICE = 0, FIBBO_LEVEL_MENOR_A_TARGE_PRICE, }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CFibbo : public CLoggerBase { private: //--- Caracteristicas principales long m_chart_id; int m_subwin; bool m_create; //Indica si el fibbo ha sido creado //--- Caracteristicas del fibbo string m_obj_name; double m_price_2; double m_price_1; datetime m_init_fibbo; //--- CHashMap m_hash_level_to_index; //--- int m_total_fibbo; FibboLevel m_fibbo_levels[]; //--- Add bool AddLevel(const FibboLevel &new_level); //--- void CalculatePriceLevelsFibbo(); public: CFibbo(); ~CFibbo(void); //--- Creacion bool Create(const long chart_ID, const string name, const int sub_window, const color clr, const ENUM_LINE_STYLE style, const int width, const string tooltip, const bool back = false, const bool selection = false, const long z_order = 0); //--- Seteo - Modifiacion de nivel-niveeles bool SetLevels(string vals, string clrs, ENUM_LINE_STYLE styles, int widths, ushort separator_str); __forceinline uint8_t ModifyLevel(double level, color new_color = clrNONE, ENUM_LINE_STYLE new_style = WRONG_VALUE, int new_widt = -1); uint8_t ModifyLevel(int level_index, color new_color = clrNONE, ENUM_LINE_STYLE new_style = WRONG_VALUE, int new_widt = -1); //--- Funciones para trabajar con el objeto grafico void Move(datetime new_init, datetime new_end, double price1, double price2); // Momer el fibbo completamente __forceinline void UpdateTime2(datetime new_end); //Solo actulizar el fibbo a un nuevo time2 bool Hide() const { return HIDE_OBJECT(m_obj_name, m_chart_id); } bool Show() const { return SHOW_OBJECT(m_obj_name, m_chart_id); } //--- Funciones para trabajar con niveles // Obtiene el precio de un nivel por su el valor del nivel __forceinline double GetLevelPrice(const double level_val); // Obtiene el precio de un nivel por su indice real __forceinline double GetLevelPrice(const int level_index) const { return m_fibbo_levels[level_index].level_price; } // Obtiene el indice en el array de un nivel __forceinline int GetLevelIndex(const double level_val); // Obtiene los niveles del fibbo void GetArrayLevel(double &levels[], bool contains_lebel_0_100 = false) const; double GetNearestLevel(double target_price, ENUM_MODE_NEAREST_LEVEL_FIBBO mode) const; //--- Getters y Setters generales // Generales inline int TotalFibboLevels() const { return m_total_fibbo; } inline double Price1() const { return m_price_1; } inline double Price2() const { return m_price_2; } inline datetime InitTime() const { return m_init_fibbo; } inline long ChartId() const { return m_chart_id; } inline int Subwin() const { return m_subwin; } __forceinline int FibboLevelLastIndex() const { return m_total_fibbo - 1; } // Obtencion de un nivel // Por indice __forceinline FibboLevel GetLevelByIndex(const int idx) const { return m_fibbo_levels[idx]; } // Por valor __forceinline FibboLevel GetLevelByValue(const double val) { return m_fibbo_levels[GetLevelIndex(val)]; } //--- Info void Summary(); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CFibbo::~CFibbo() { ObjectDelete(m_chart_id, m_obj_name); } //+------------------------------------------------------------------+ CFibbo::CFibbo() : m_init_fibbo(wrong_datetime), m_price_2(0.00), m_price_1(0.00), m_subwin(0), m_chart_id(0), m_total_fibbo(0) { ArrayResize(m_fibbo_levels, 0); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFibbo::Summary(void) { PrintFormat("------| Fibbo %s |------", m_obj_name); Print("Levels: "); ArrayPrint(m_fibbo_levels, 3, " | "); } //+------------------------------------------------------------------+ bool CFibbo::AddLevel(const FibboLevel &new_level) { //--- Verificar si esta dentro //Considerar esto, de pormedio un fibbo level como maximo tiene 3 decimales #define CFIBBO_MIN_VALUE 0.00040 for(int i = 0; i < m_total_fibbo; i++) { if(fabs(m_fibbo_levels[i].level_value - new_level.level_value) <= CFIBBO_MIN_VALUE) { LogWarning(StringFormat("No se agregara el fibbo level %f dado que ya existe", new_level.level_value), FUNCION_ACTUAL); return false; } } //--- Añadir ArrayResize(m_fibbo_levels, m_total_fibbo + 1); m_fibbo_levels[m_total_fibbo] = new_level; m_total_fibbo++; return true; } //+------------------------------------------------------------------+ double CFibbo::GetNearestLevel(double target_price, ENUM_MODE_NEAREST_LEVEL_FIBBO mode) const { //--- if(m_total_fibbo == 0) return 0.00; //--- const bool precio_ascendente = (m_fibbo_levels[0].level_price < m_fibbo_levels[m_total_fibbo - 1].level_price); //--- if(mode == FIBBO_LEVEL_MAYOR_A_TARGE_PRICE) { if(precio_ascendente) { // Buscar de menor a mayor for(int i = 0; i < m_total_fibbo; i++) if(m_fibbo_levels[i].level_price > target_price) return m_fibbo_levels[i].level_price; } else { // Buscar de mayor a menor (invertido) for(int i = m_total_fibbo - 1; i >= 0; i--) if(m_fibbo_levels[i].level_price > target_price) return m_fibbo_levels[i].level_price; } } else { if(precio_ascendente) { // Buscar el último menor for(int i = m_total_fibbo - 1; i >= 0; i--) if(m_fibbo_levels[i].level_price < target_price) return m_fibbo_levels[i].level_price; } else { // Buscar el primero menor (en orden descendente) for(int i = 0; i < m_total_fibbo; i++) if(m_fibbo_levels[i].level_price < target_price) return m_fibbo_levels[i].level_price; } } //--- LogWarning(StringFormat("No se ha encontrado nivel cercano al target price = %.*f", 5, target_price), FUNCION_ACTUAL); return 0.00; } //+------------------------------------------------------------------+ bool CFibbo::Create(const long chart_ID, const string name, const int sub_window, const color clr, const ENUM_LINE_STYLE style, const int width, const string tooltip, const bool back = false, const bool selection = false, const long z_order = 0) { //--- if(m_create) { LogWarning("Se esta volviendo a redibujar el objeto fibbo..", FUNCION_ACTUAL); } //--- m_chart_id = chart_ID; m_subwin = sub_window; const bool res = FiboLevelsCreate(chart_ID, name, sub_window, 0, 0, 0, 0, clr, style, width, back, selection, false, false, z_order); m_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); m_create = true; } return res; } //+------------------------------------------------------------------+ void CFibbo::GetArrayLevel(double &levels[], bool contains_lebel_0_100 = false) const { for(int i = 0; i < ArraySize(m_fibbo_levels) ; i++) { if(!contains_lebel_0_100 && (m_fibbo_levels[i].level_value == 0.00 || m_fibbo_levels[i].level_value == 1.00)) continue; AddArrayNoVerification(levels, m_fibbo_levels[i].level_value, 0); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #define FIBBO_FLAG_LEVEL_MODIFY_COLOR_SUCCESS 1 #define FIBBO_FLAG_LEVEL_MODIFY_STYLE_SUCCESS 2 #define FIBBO_FLAG_LEVEL_MODIFY_WIDTH_SUCCESS 4 //+------------------------------------------------------------------+ __forceinline uint8_t CFibbo::ModifyLevel(double level, color new_color = clrNONE, ENUM_LINE_STYLE new_style = -1, int new_widt = -1) { int level_index; return m_hash_level_to_index.TryGetValue(level, level_index) ? ModifyLevel(level_index, new_color, new_style, new_widt) : 0; } //+------------------------------------------------------------------+ uint8_t CFibbo::ModifyLevel(int level_index, color new_color = clrNONE, ENUM_LINE_STYLE new_style = -1, int new_widt = -1) { //--- uint8_t f = 0; //--- if(new_color != clrNONE && ObjectSetInteger(m_chart_id, m_obj_name, OBJPROP_LEVELCOLOR, level_index, new_color)) f |= FIBBO_FLAG_LEVEL_MODIFY_COLOR_SUCCESS; //--- if(new_style != WRONG_VALUE && ObjectSetInteger(m_chart_id, m_obj_name, OBJPROP_LEVELSTYLE, level_index, new_style)) f |= FIBBO_FLAG_LEVEL_MODIFY_STYLE_SUCCESS; //--- if(new_widt >= 1 && ObjectSetInteger(m_chart_id, m_obj_name, OBJPROP_LEVELWIDTH, level_index, new_widt)) f |= FIBBO_FLAG_LEVEL_MODIFY_WIDTH_SUCCESS; return f; } //+------------------------------------------------------------------+ __forceinline void CFibbo::UpdateTime2(datetime new_end) { ObjectSetInteger(m_chart_id, m_obj_name, OBJPROP_TIME, 1, new_end); } //+------------------------------------------------------------------+ __forceinline int CFibbo::GetLevelIndex(const double level_val) { int idx; m_hash_level_to_index.TryGetValue(level_val, idx); return idx; } //+------------------------------------------------------------------+ __forceinline double CFibbo::GetLevelPrice(const double level_val) { int idx; m_hash_level_to_index.TryGetValue(level_val, idx); return m_fibbo_levels[idx].level_price; } //+------------------------------------------------------------------+ void CFibbo::CalculatePriceLevelsFibbo(void) { if(m_price_1 > m_price_2) { /* p1 100 p2 p2 0 */ const double diff = (m_price_1 - m_price_2); for(int i = 0; i < m_total_fibbo; i++) m_fibbo_levels[i].level_price = ((m_price_2 + ((m_fibbo_levels[i].level_value) * diff))); } else if(m_price_2 > m_price_1) { /* p2 p2 0 p1 100 */ const double diff = (m_price_2 - m_price_1); for(int i = 0; i < m_total_fibbo; i++) m_fibbo_levels[i].level_price = ((m_price_2 - ((m_fibbo_levels[i].level_value) * diff))); } else { LogError("Ambos precios del fibbo, 1 y 2 son iguales", FUNCION_ACTUAL); } } //+------------------------------------------------------------------+ typedef bool (*CFibboFuncCompareLevelPrice)(const FibboLevel&, const FibboLevel&); void CFibbo::Move(datetime new_init, datetime new_end, double price1, double price2) { //--- ResetLastError(); if(!ObjectMove(m_chart_id, m_obj_name, 0, new_init, price1)) { LogError(StringFormat("No se pudo movel el anclaje 0, new_init = %s >> price1 = %.5f, ultimo error = %d", TimeToString(new_init), price1, GetLastError()), FUNCION_ACTUAL); } //--- ResetLastError(); if(!ObjectMove(m_chart_id, m_obj_name, 1, new_end, price2)) { LogError(StringFormat("No se pudo movel el anclaje 1, time2 = %s >> price2 = %.5f, ultimo error = %d", TimeToString(new_end), price2, GetLastError()), FUNCION_ACTUAL); } //--- m_price_1 = price1; m_price_2 = price2; m_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); Remover(); 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); Remover(); return false; } //--- if(num_vals != num_clrs) { LogCriticalError(StringFormat("El tamaño del array de niveles %d y colores %d no son iguales", num_vals, num_clrs), FUNCION_ACTUAL); Remover(); return(false); } //--- m_total_fibbo = ArrayResize(m_fibbo_levels, 0); m_hash_level_to_index.Clear(); //--- for(int i = 0; i < num_clrs; i++) { FibboLevel new_level; //--- para color trim, mql5 confunde con espacion asuem black const 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); } //--- Ordenamos de menor a mayor CSimpleSort::SortAscendente(m_fibbo_levels, m_total_fibbo, (CFibboFuncCompareLevelPrice)FibboLevel::MayorLevel); //--- Agregamos al hashmap for(int i = 0; i < m_total_fibbo; i++) m_hash_level_to_index.Add(m_fibbo_levels[i].level_value, i); //--- Info if(IsInfoLogEnabled()) { FastLog(FUNCION_ACTUAL, INFO_TEXT, "Niveles de fibbos listos: "); ArrayPrint(m_fibbo_levels, 3, " | "); } //--- Establecemos el número de los niveles ObjectSetInteger(m_chart_id, m_obj_name, OBJPROP_LEVELS, m_total_fibbo); //--- Establecemos las propiedades de los niveles en el ciclo for(int i = 0; i < m_total_fibbo; i++) { //--- valor del nivel ObjectSetDouble(m_chart_id, m_obj_name, OBJPROP_LEVELVALUE, i, m_fibbo_levels[i].level_value); //--- color del nivel ObjectSetInteger(m_chart_id, m_obj_name, OBJPROP_LEVELCOLOR, i, m_fibbo_levels[i].level_color); //--- estilo del nivel ObjectSetInteger(m_chart_id, m_obj_name, OBJPROP_LEVELSTYLE, i, styles); //--- grosor del nivel ObjectSetInteger(m_chart_id, m_obj_name, OBJPROP_LEVELWIDTH, i, widths); //--- descripción del nivel ObjectSetString(m_chart_id, m_obj_name, OBJPROP_LEVELTEXT, i, DoubleToString(100 * m_fibbo_levels[i].level_value, 1)); } //--- ejecución con éxito return(true); } //+------------------------------------------------------------------+ #endif // MQLARTICLES_UTILS_FIBONACCI_MQH //+------------------------------------------------------------------+