MQLArticles/Strategy/Core/Base.mqh
Nique_372 55dc7270d2
2026-02-14 17:00:45 -05:00

377 lines
31 KiB
MQL5

//+------------------------------------------------------------------+
//| 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 <typename TPadre>
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 <typename TPadre> 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 <typename TPadre> 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 <typename TPadre>
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 <typename TPadre>
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 <typename TPadre>
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 <typename TPadre>
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 <typename TPadre>
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 <typename TPadre>
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<CAccountGestor>
{
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<CAccountGestor>(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();
}
*/
//+------------------------------------------------------------------+