forked from nique_372/MQLArticles
391 lines
17 KiB
MQL5
391 lines
17 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"
| |
#include "..\\..\\Utils\\FA\\DiffCalc.mqh"
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
namespace TSN
| |
{
| |
//+------------------------------------------------------------------+
| |
//| 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
| |
const ENUM_TIMEFRAMES m_timeframe; //Timeframe de la estrategia
| |
const string m_symbol;
| |
const int m_subwindow;
| |
const long m_chart_id;
| |
const int m_period_in_seconds;
| |
const 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;
| |
const ulong m_max_deviation;
| |
const 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
| |
inline double GetSL(const double entry_price, const ENUM_POSITION_TYPE position_type, const datetime curr_time) const;
| |
inline double GetTP(const double entry_price, const ENUM_POSITION_TYPE position_type, const datetime curr_time) const;
| |
__forceinline double GetSLDistance(const ENUM_POSITION_TYPE position_type, const datetime curr_time) const;
| |
__forceinline double GetTPDistance(const ENUM_POSITION_TYPE position_type, const datetime curr_time) const;
| |
| |
// 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
| |
// Evitamos la modificacion de dirreicon y del obejto y de las variabes miembro de la clase
| |
const CTrade* const TradePointer() const { return &m_trade; }
| |
| |
// Tamaño de lote
| |
__forceinline double FixedLotSize() const { return m_LOT_SIZE; }
| |
void 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; }
| |
void EnableStrategy(const bool enable) { 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),
| |
| |
//---
| |
m_symbol(symbol_), m_magic_number(magic_number_), m_chart_id(chart_id_), m_subwindow(subwindow_), m_timeframe((timeframe_ == PERIOD_CURRENT ? _Period : timeframe_)),
| |
m_period_in_seconds(CTimeframe::PeriodSecondsFast(timeframe_)), m_max_deviation(max_deviation_), m_strategy_name(name)
| |
{
| |
//---
| |
//AddLogFlags(LOG_LEVEL_WARNING);
| |
//---
| |
m_trade.LogLevel(LOG_LEVEL_NO);
| |
m_trade.SetExpertMagicNumber(m_magic_number);
| |
| |
//---
| |
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_);
| |
| |
//---
| |
::MathSrand(::GetTickCount());
| |
| |
//---
| |
m_obj_code = ::StringFormat("%s_%s_%d_", name, CTimeframe::ToStrUpper(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;
| |
}
| |
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
// 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>
| |
inline double CStrategyBaseTemplate::GetTP(const double entry_price, const ENUM_POSITION_TYPE position_type, const datetime curr_time) const
| |
{
| |
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>
| |
inline double CStrategyBaseTemplate::GetSL(const double entry_price, const ENUM_POSITION_TYPE position_type, const datetime curr_time) const
| |
{
| |
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;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
template <typename TPadre>
| |
__forceinline double CStrategyBaseTemplate::GetTPDistance(const ENUM_POSITION_TYPE position_type, const datetime curr_time) const
| |
{
| |
return (m_tp_sl_mode) ? (m_point_value * m_TP_POINTS) : (m_atr_multiplier_tp * m_atr_pointer.base.GetAtrValue(curr_time));
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
template <typename TPadre>
| |
__forceinline double CStrategyBaseTemplate::GetSLDistance(const ENUM_POSITION_TYPE position_type, const datetime curr_time) const
| |
{
| |
return (m_tp_sl_mode) ? (m_point_value * m_SL_POINTS) : (m_atr_multiplier_sl * m_atr_pointer.base.GetAtrValue(curr_time));
| |
}
| |
| |
| |
//+------------------------------------------------------------------+
| |
//| Setters |
| |
//+------------------------------------------------------------------+
| |
template <typename TPadre>
| |
void CStrategyBaseTemplate::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;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
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();
| |
}
| |
*/
| |
//+------------------------------------------------------------------+
|