535 lines
36 KiB
MQL5
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
|
|
//+------------------------------------------------------------------+
|