//+------------------------------------------------------------------+ //| StrategyBase.mqh | //| Copyright 2025, Leo. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Leo." #property link "https://www.mql5.com" #property strict #ifndef MQLARTICLES_STRATEGY_CORE_BASE_MQH #define MQLARTICLES_STRATEGY_CORE_BASE_MQH //+------------------------------------------------------------------+ //| Include | //+------------------------------------------------------------------+ #include "Defines.mqh" #include "..\\Utils\\YearFilter.mqh" #include "..\\Utils\\DailyFilter.mqh" //+------------------------------------------------------------------+ //| Clase base para las estrategias de trading | //+------------------------------------------------------------------+ // TPadre = [Cualquier clase de eventos CAllEventsBasic, CAccountGestor, etc] template class CStrategyBaseTemplate : public TPadre { protected: //--- Variables // Generales ENUM_TIMEFRAMES m_timeframe; //Timeframe de la estrategia string m_symbol; int m_subwindow; long m_chart_id; int m_period_in_seconds; string m_strategy_name; string m_obj_code; bool m_enable_strategy; // Habilitar la logica de la estrategia (como un boton de off on para ejecutar la estrategia) int8_t m_new_bar_idx; // Indice para consultar nueva barra (otros TFs - CNewBarManager) // Parametros generales bool m_is_enable_buy; bool m_is_enable_sell; ulong m_max_deviation; ulong m_magic_number; // Info del simbolo MqlTick m_tick; double m_point_value; int8_t m_digits; double m_vol_min; double m_vol_max; double m_vol_step; double m_stops_leevel; // Apertura de operaciones double m_TP_POINTS; double m_SL_POINTS; double m_LOT_SIZE; CTrade* m_trade; ENUM_TYPE_TP_SL_FIXED m_tp_sl_mode; // Atr optimizado para el calculo del tp, sl e uso interno CAtrUltraOptimized* m_atr_pointer; double m_atr_multiplier_tp; double m_atr_multiplier_sl; //--- Funciones // TP\SL basico para posiciones double GetSL(double entry_price, ENUM_POSITION_TYPE position_type, const datetime curr_time); double GetTP(double entry_price, ENUM_POSITION_TYPE position_type, const datetime curr_time); // Utilidades extra inline int DistanceToPoints(double dist) { return (m_point_value == 0.00) ? 0 : (int)::MathRound(dist / m_point_value); } inline double GetDiff(ENUM_MODE_DIFF mode, double val, datetime curr_time) const { return mode == MODE_DIFF_BY_ATR ? (m_atr_pointer.base.GetAtrValue(curr_time) * val) : (val * m_point_value); } public: CStrategyBaseTemplate(const ulong magic_number_, const string& symbol_, const ENUM_TIMEFRAMES timeframe_, const long chart_id_, const int subwindow_, const ulong max_deviation_, const string& name); ~CStrategyBaseTemplate(); //--- Funciones para establecer valores de la clase void SetTP_SL(long new_sl_value, long new_tp_value); void SetAtrTP_SL(CAtrUltraOptimized* atr_ptr, double atr_mult_tp_, double atr_mult_sl_); void SetOperateMode(ENUM_TYPE_TRADE trade_type, ENUM_TYPE_TP_SL_FIXED tp_sl_mode_); //--- Funciones getter y setter // General getters inline ENUM_TIMEFRAMES Timeframe() const { return m_timeframe; } inline long ChartId() const { return m_chart_id; } inline int Subwin() const { return m_subwindow; } inline string Symbol() const { return m_symbol; } inline int TimeframeInSeconds() const { return m_period_in_seconds; } inline ulong MagicNumber() const { return m_magic_number; } inline string Name() const { return m_strategy_name; } // Codigo de objeto inline string ObjCode() const { return m_obj_code; } void ObjCode(const string& new_value) { m_obj_code = new_value; } __forceinline string ObjCodeConc(string str) const { return (m_obj_code + str); } // Puntero de CTrade utilizado const CTrade* const TradePointer() const { return m_trade; } // Evitamos la modificacion de dirreicon y del obejto y de las variabes miembro de la clase // Tamaño de lote __forceinline double FixedLotSize() const { return m_LOT_SIZE; } double FixedLotSize(double new_lot); // Ventas y commpras habilitadas __forceinline bool EnableBuyTrades() const { return m_is_enable_buy; } __forceinline bool EnableSellTrades() const { return m_is_enable_sell; } // __forceinline bool EnableStrategy() const { return m_enable_strategy; } __forceinline bool EnableStrategy(const bool enable) { return (m_enable_strategy = enable); } //--- Funciones de ejecucion virtual void OnNewBar(const datetime curr_time) = 0; // Funcion que se ejecutara cada nueva vela virtual void OnTick(const datetime curr_time) = 0; // Funcion que se ejecutara cada nuevo tick // Funcion que se llama anted de detener el llamado a OnTick y OnNewBar // La estrategia tomara sus acciones como que en la siguiente llamada a OnNewBar o OnTick hacer un recalculo completo // Esto depende de la estrategia habra unas que no lo requieren otras que si etc virtual void OnInterupcion() {} }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template CStrategyBaseTemplate::CStrategyBaseTemplate(const ulong magic_number_, const string& symbol_, const ENUM_TIMEFRAMES timeframe_, const long chart_id_, const int subwindow_, const ulong max_deviation_, const string& name) : m_TP_POINTS(0), m_SL_POINTS(0), m_LOT_SIZE(0.00), m_atr_multiplier_sl(2.0), m_atr_multiplier_tp(4.0), m_is_enable_buy(true), m_is_enable_sell(true), m_atr_pointer(NULL), m_enable_strategy(true) { //--- //AddLogFlags(LOG_LEVEL_WARNING); m_trade = new CTrade(); //--- m_magic_number = magic_number_; m_symbol = symbol_; m_chart_id = chart_id_; m_subwindow = subwindow_; m_timeframe = (timeframe_ == PERIOD_CURRENT ? _Period : timeframe_); //--- m_trade.LogLevel(LOG_LEVEL_NO); m_trade.SetExpertMagicNumber(m_magic_number); m_period_in_seconds = PeriodSecondsFastSeg(m_timeframe); //--- m_digits = (int8_t)::SymbolInfoInteger(symbol_, SYMBOL_DIGITS); m_vol_max = ::SymbolInfoDouble(symbol_, SYMBOL_VOLUME_MAX); m_vol_step = ::SymbolInfoDouble(symbol_, SYMBOL_VOLUME_STEP); m_vol_min = ::SymbolInfoDouble(symbol_, SYMBOL_VOLUME_MIN); m_point_value = ::SymbolInfoDouble(m_symbol, SYMBOL_POINT); m_stops_leevel = (::SymbolInfoInteger(symbol_, SYMBOL_TRADE_STOPS_LEVEL) * m_point_value) + (m_point_value * 10); //--- if(max_deviation_ > NO_MAX_DEVIATION_DEFINED) m_trade.SetDeviationInPoints(max_deviation_); m_max_deviation = max_deviation_; //--- ::MathSrand(::GetTickCount()); //--- m_strategy_name = name; m_obj_code = ::StringFormat("%s_%s_%d_", name, AbreviarPeriodo(m_timeframe), ::rand()); } //+------------------------------------------------------------------+ template CStrategyBaseTemplate::~CStrategyBaseTemplate() /* ADVERTENCIA SI SE AÑADEN PUNTEROS EN ITEMS ENTONCES ESTOS SE LIMPIARAN NO HACE FALTA HACERLO EN EL DESTRUCTOE DEL HIJO*/ { // Para que no se elimina y copratri recomeindion instancia global //Se Elimina el atr ultra optimizado CleanItems("StrategyBase"); //Limpiamos todos los punteros de CLogger, en este caso solo se limpiaria el "base", el atr_pointer lo hacemos manual: if(::CheckPointer(m_atr_pointer) == POINTER_DYNAMIC) delete m_atr_pointer; if(::CheckPointer(m_trade) == POINTER_DYNAMIC) delete m_trade; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // Notas: // - Tick debe de ser obtenido antes // - Esta funcion solo debe invocarse para posiciones, mejor para ordenes a mercado // - Esta funcion solo es compatible con el tipo de tp y sl Atr, Points, si tiene otro modo redefina la funcion.. dado que m_tp_sl_mdode para un valor (0) llamara a atr // de lo contrario para cualquier valor a puntos... asi que ya esta avisado. //+------------------------------------------------------------------+ template double CStrategyBaseTemplate::GetTP(double entry_price, ENUM_POSITION_TYPE position_type, const datetime curr_time) { const double distance = (m_tp_sl_mode) ? (m_point_value * m_TP_POINTS) : (m_atr_multiplier_tp * m_atr_pointer.base.GetAtrValue(curr_time)); if(distance < DBL_EPSILON) return 0.00; const double to_sum = ::fmax((m_stops_leevel + (m_tick.ask - m_tick.bid)), distance); return position_type == POSITION_TYPE_BUY ? entry_price + to_sum : entry_price - to_sum; } //+------------------------------------------------------------------+ template double CStrategyBaseTemplate::GetSL(double entry_price, ENUM_POSITION_TYPE position_type, const datetime curr_time) { const double distance = (m_tp_sl_mode) ? (m_point_value * m_SL_POINTS) : (m_atr_multiplier_sl * m_atr_pointer.base.GetAtrValue(curr_time)); if(distance < DBL_EPSILON) return 0.00; const double to_sum = ::fmax((m_stops_leevel + (m_tick.ask - m_tick.bid)), distance); return position_type == POSITION_TYPE_BUY ? entry_price - to_sum : entry_price + to_sum; } //+------------------------------------------------------------------+ //| Setters | //+------------------------------------------------------------------+ template double CStrategyBaseTemplate::FixedLotSize(double new_lot) { if(new_lot <= STRATEGY_BASE_EMPTY_VALUE) return 0.00; if(new_lot < m_vol_min) { LogWarning(::StringFormat("El nuevo lote %f es menor al minimo volumen permitido, se ajustara al valor permitido mas cercano", new_lot), FUNCION_ACTUAL); new_lot = m_vol_min; } if(new_lot > m_vol_max) { LogWarning(::StringFormat("El nuevo lote %f es mayor al maximo volumen permitido, se ajustara al valor permitido mas cercano", new_lot), FUNCION_ACTUAL); new_lot = m_vol_max; } return (m_LOT_SIZE = new_lot); } //+------------------------------------------------------------------+ template void CStrategyBaseTemplate::SetAtrTP_SL(CAtrUltraOptimized* atr_ptr, double atr_mult_tp_, double atr_mult_sl_) { if(atr_ptr != NULL) { if(m_atr_pointer != NULL) //Si no es invalido eliminamos el anteior solo si es dinamico { RemoveLogger(m_atr_pointer.base); if(::CheckPointer(m_atr_pointer) == POINTER_DYNAMIC) { delete m_atr_pointer; m_atr_pointer = NULL; LogWarning("Se ha eliminado el puntero anteior a CAtrUltraOptimized*", FUNCION_ACTUAL); } } m_atr_pointer = atr_ptr; AddLogger(m_atr_pointer.base); } else { if(::CheckPointer(m_atr_pointer) == POINTER_INVALID || ::CheckPointer(m_atr_pointer.base) == POINTER_INVALID) { LogError("El puntero a CAtrUltraOptimized es invalido, corregir ahora", FUNCION_ACTUAL); Remover(); } } if(atr_mult_sl_ > STRATEGY_BASE_EMPTY_VALUE) m_atr_multiplier_sl = atr_mult_sl_; if(atr_mult_tp_ > STRATEGY_BASE_EMPTY_VALUE) m_atr_multiplier_tp = atr_mult_tp_; } //+------------------------------------------------------------------+ template void CStrategyBaseTemplate::SetTP_SL(long new_sl_value, long new_tp_value) { if(new_sl_value > STRATEGY_BASE_EMPTY_VALUE) { if(new_sl_value < 1) { LogWarning(::StringFormat("Los puntos del stoploss %d son menores a 1 los proximos trades no tendran stoploss", new_sl_value), FUNCION_ACTUAL); new_sl_value = 0; } m_SL_POINTS = (double)new_sl_value; } if(new_tp_value > STRATEGY_BASE_EMPTY_VALUE) { if(new_tp_value < 1) { LogWarning(::StringFormat("Los puntos del takeprofit %d son menores a 1, los proximos trades no tendran takeprofit", new_tp_value), FUNCION_ACTUAL); new_tp_value = 0; } m_TP_POINTS = (double)new_tp_value; } } //+------------------------------------------------------------------+ template void CStrategyBaseTemplate::SetOperateMode(ENUM_TYPE_TRADE trade_type, ENUM_TYPE_TP_SL_FIXED tp_sl_mode_) { if(trade_type != WRONG_VALUE) { if(trade_type == TR_BUY_SELL) { m_is_enable_sell = true; m_is_enable_buy = true; } else if(trade_type == TR_BUY) { m_is_enable_sell = false; m_is_enable_buy = true; } else { m_is_enable_sell = true; m_is_enable_buy = false; } } if(tp_sl_mode_ != WRONG_VALUE) m_tp_sl_mode = tp_sl_mode_; } //+------------------------------------------------------------------+ //| Clase para compatibilidad | //+------------------------------------------------------------------+ class CStrategyBase : public CStrategyBaseTemplate { public: CStrategyBase(const ulong magic_number_, const string& symbol_, const ENUM_TIMEFRAMES timeframe_, const long chart_id_, const int subwindow_, const ulong max_deviation_, const string& name) : CStrategyBaseTemplate(magic_number_, symbol_, timeframe_, chart_id_, subwindow_, max_deviation_, name) {} ~CStrategyBase(void) {} }; #endif // MQLARTICLES_STRATEGY_CORE_BASE_MQH //+------------------------------------------------------------------+` /* //+------------------------------------------------------------------+ //| Open Order Default | //+------------------------------------------------------------------+ void CEstrategia::OpenOrder(ENUM_POSITION_TYPE type) { ::SymbolInfoTick(m_symbol, m_tick); if(type == POSITION_TYPE_BUY) { double entry = m_tick.ask; double sl = GetSL(entry, type, m_tick.time); double tp = GetTP(entry, type, m_tick.time); risk.SetStopLoss(entry - sl); double l = m_LOT_SIZE > 0.00 ? m_LOT_SIZE : risk.GetLote(ORDER_TYPE_BUY, entry, m_max_deviation, 0); m_trade.Buy(l, m_symbol, entry, sl, tp, "Ea buy"); } else if(type == POSITION_TYPE_SELL) { double entry = m_tick.bid; double sl = GetSL(entry, type, m_tick.time); double tp = GetTP(entry, type, m_tick.time); risk.SetStopLoss(sl - entry); double l = m_LOT_SIZE > 0.00 ? m_LOT_SIZE : risk.GetLote(ORDER_TYPE_SELL, entry, m_max_deviation, 0); m_trade.Sell(l, m_symbol, entry, sl, tp, "Ea sell"); } Clean(); } */ //+------------------------------------------------------------------+