MQLArticles/Strategy/Core/Base.mqh

381 lines
31 KiB
MQL5
Raw Permalink Normal View History

2025-12-05 16:33:47 -05:00
<EFBFBD><EFBFBD>//+------------------------------------------------------------------+
//| 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
//+------------------------------------------------------------------+
//| Notas |
//+------------------------------------------------------------------+
/*
ADVERTENCIA:
Dado que StrategyBase hereda de CAccountGestor (la clase base que permite crear clases que necesiten conocer el estado de las posiciones, por ejemplo saber cu<EFBFBD>ndo una posici<EFBFBD>n se abri<EFBFBD> o se cerr<EFBFBD>),
Es necesario que, si se crea una instancia o un puntero de CStrategyBase,
Este se agregue al array de <EFBFBD>tems que luego se recorre en un bucle para ejecutar las funciones virtuales de CAccountGestor en todos los elementos registrados.
Para agregar una clase, simplemente se debe llamar a:
account_status.AddItem()
Con esto se vincula la instancia o puntero al array de <EFBFBD>tems de account_status, y como consecuencia, en esa clase quedar<EFBFBD>n activas las funciones virtuales heredadas de CAccountGestor.
Un punto importante a tener en cuenta es que, cuando finalice la ejecuci<EFBFBD>n del programa (bot/EA), se llamar<EFBFBD> al destructor de account_status, y dentro de este destructor se eliminar<EFBFBD>n los <EFBFBD>tems registrados en el array.
Esto significa que cualquier clase agregada ser<EFBFBD> eliminada mediante delete, pero <EFBFBD>nicamente si es un puntero din<EFBFBD>mico creado con new.
Por lo tanto, es fundamental tener precauci<EFBFBD>n: si se vincula un puntero que no fue creado din<EFBFBD>micamente, o si m<EFBFBD>s adelante se intenta eliminar por separado, se producir<EFBFBD>a un doble delete,
lo que generar<EFBFBD>a un error fatal.
*/
//+------------------------------------------------------------------+
//| Include |
//+------------------------------------------------------------------+
#include "Defines.mqh"
#include "..\\Utils\\YearFilter.mqh"
#include "..\\Utils\\DailyFilter.mqh"
//+------------------------------------------------------------------+
//| Clase base para las estrategias de trading |
//+------------------------------------------------------------------+
class CStrategyBase : public CAccountGestor
{
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;
// 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
double GetSL(double entry_price, ENUM_POSITION_TYPE position_type, datetime curr_time);
double GetTP(double entry_price, ENUM_POSITION_TYPE position_type, 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:
CStrategyBase(ulong magic_number_, string symbol_, ENUM_TIMEFRAMES timeframe_ = PERIOD_CURRENT, long chart_id_ = 0,
int subwindow_ = 0, ulong max_deviation_ = NO_MAX_DEVIATION_DEFINED, string name = "Strategy");
~CStrategyBase();
//--- 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
2025-12-07 12:29:58 -05:00
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); }
2025-12-05 16:33:47 -05:00
// 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<EFBFBD>o de lote
inline double FixedLotSize() const { return m_LOT_SIZE; }
void FixedLotSize(double new_lot);
// Ventas y commpras habilitadas
bool EnableBuyTrades() const { return m_is_enable_buy; }
bool EnableSellTrades() const { return m_is_enable_sell; }
//--- Funciones de ejecucion
virtual void OnNewBar(const datetime &curr_time) = 0; // Funcion que se ejecutara cada nueva vela
virtual void OnTick() = 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() {}
//--- Funciones para el maneje de posiciones, solo si se requiere
void OnOpenClosePosition(const ROnOpenClosePosition &pos) override { } // Funcion heredada de CAccountGestor
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CStrategyBase::CStrategyBase(ulong magic_number_, string symbol_, ENUM_TIMEFRAMES timeframe_ = PERIOD_CURRENT,
long chart_id_ = 0, int subwindow_ = 0, ulong max_deviation_ = NO_MAX_DEVIATION_DEFINED, string name = "Strategy")
: 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)
{
//---
//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_;
//---
m_trade.LogLevel(LOG_LEVEL_NO);
m_trade.SetExpertMagicNumber(m_magic_number);
m_period_in_seconds = ::PeriodSeconds(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());
}
//+------------------------------------------------------------------+
CStrategyBase::~CStrategyBase() /* ADVERTENCIA SI SE A<EFBFBD>ADEN PUNTEROS EN ITEMS ENTONCES ESTOS SE LIMPIARAN NO HACE FALTA HACERLO EN EL DESTRUCTOE DEL HIJO*/
{
//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;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
// Nota: Tick debede de ser cargado
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CStrategyBase::GetTP(double entry_price, ENUM_POSITION_TYPE position_type, datetime curr_time)
{
double val = m_point_value;
double val_to = m_TP_POINTS;
if(m_tp_sl_mode == TP_SL_ATR)
{
val_to = m_atr_multiplier_tp;
val = m_atr_pointer.base.GetAtrValue(curr_time);
}
const double spr = (m_tick.ask - m_tick.bid);
const double to_sum = ::fmax(m_stops_leevel + spr, (val * val_to));
return position_type == POSITION_TYPE_BUY ? entry_price + to_sum : entry_price - to_sum;
}
//+------------------------------------------------------------------+
double CStrategyBase::GetSL(double entry_price, ENUM_POSITION_TYPE position_type, datetime curr_time)
{
double val = m_point_value;
double val_to = m_SL_POINTS;
if(m_tp_sl_mode == TP_SL_ATR)
{
val_to = m_atr_multiplier_sl;
val = m_atr_pointer.base.GetAtrValue(curr_time);
}
const double spr = (m_tick.ask - m_tick.bid);
const double to_sum = ::fmax(m_stops_leevel + spr, (val * val_to));
return position_type == POSITION_TYPE_BUY ? entry_price - to_sum : entry_price + to_sum;
}
//+------------------------------------------------------------------+
//| Setters |
//+------------------------------------------------------------------+
void CStrategyBase::FixedLotSize(double new_lot)
{
if(new_lot <= STRATEGY_BASE_EMPTY_VALUE)
return;
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;
}
m_LOT_SIZE = new_lot;
}
//+------------------------------------------------------------------+
void CStrategyBase::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_;
}
//+------------------------------------------------------------------+
void CStrategyBase::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;
}
}
//+------------------------------------------------------------------+
void CStrategyBase::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_;
}
#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();
}
*/
//+------------------------------------------------------------------+