2026-07-28 09:08:36 -05:00
//+------------------------------------------------------------------+
//| Breakeven.mqh |
//| Copyright 2025, Niquel y Leo. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
# property copyright " Copyright 2025, Niquel y Leo. "
# property link " https://www.mql5.com "
# property strict
# ifndef MQLARTICLES_POSMGMT_BREAKEVEN_MQH
# define MQLARTICLES_POSMGMT_BREAKEVEN_MQH
//+------------------------------------------------------------------+
//| Include |
//+------------------------------------------------------------------+
# include "..\\RM\\RiskManagement.mqh"
//+------------------------------------------------------------------+
//| Break Even Structs |
//+------------------------------------------------------------------+
struct pack ( 8 ) position_be
{
ulong ticket ; //Position Ticket
double breakeven_price ; //Be price
double price_to_beat ; //Price to exceed to reach break even
ENUM_POSITION_TYPE type ; //Position type
} ;
//---
enum ENUM_BREAKEVEN_TYPE
{
BREAKEVEN_TYPE_RR = 0 , //By RR
BREAKEVEN_TYPE_FIXED_POINTS = 1 , //By FixedPoints
BREAKEVEN_TYPE_ATR = 2 //By Atr
} ;
//---
const string g_breakevens_types_str [ 3 ]
{
" Breakeven by rr " ,
" Breakeven by fixed points " ,
" Breakeven by atr "
} ;
//---
struct BreakEvenParams
{
string string_value ;
2026-08-04 17:02:36 -05:00
long integer_value ;
2026-07-28 09:08:36 -05:00
double double_value ;
CAtrUltraOptimized * atr_pointer_value ;
} ;
//---
class CBreakEvenBase ;
class CBreakEven ;
class CBreakEvenAtr ;
class CBreakEvenRR ;
class CBreakEvenSimple ;
//---
# define CBREKEVEN_TOTAL 3
2026-08-04 17:02:36 -05:00
//--
# define MQLARTICLES_BREAKEVEN_CHECK_RE \
if ( m_pos_be_s > = m_pos_be_r ) \
{ \
m_pos_be_r < < = 1 ; \
ArrayResize ( m_pos_be , m_pos_be_r , m_pos_be_r ) ; \
}
2026-07-28 09:08:36 -05:00
//---
2026-08-04 17:02:36 -05:00
# define CBREAKEVEN_RESERVE_REMOVE_BE_ARR 16
# define CBREAKEVEN_RESERVE_ARR 16
2026-07-28 09:08:36 -05:00
//+------------------------------------------------------------------+
//| Main class to apply break even |
//+------------------------------------------------------------------+
class CBreakEvenBase : public CAccountGestor
{
private :
int m_indices_to_remove_be [ ] ;
2026-08-04 17:02:36 -05:00
int m_indices_to_remove_be_s ;
int m_indices_to_remove_be_r ;
2026-07-28 09:08:36 -05:00
protected :
2026-08-04 17:02:36 -05:00
//---
2026-07-28 09:08:36 -05:00
CTrade obj_trade ; //CTrade object
string symbol ; //current symbol
double point_value ; //value of the set symbol point
ulong magic ; //magic number of positions to make break even
int num_params ; //Number of parameters the class needs
bool m_automatic ;
2026-08-04 17:02:36 -05:00
//---
position_be m_pos_be [ ] ; //array of positions of type Positions
int m_pos_be_s ;
int m_pos_be_r ;
2026-07-28 09:08:36 -05:00
public :
CBreakEvenBase ( const string & symbol_ , const ulong magic_ ) ;
~ CBreakEvenBase ( ) ;
//---
inline bool Automatic ( ) const { return m_automatic ; }
void Automatic ( bool auto ) { m_automatic = auto ; }
//---
inline string Simbolo ( ) const { return symbol ; }
inline ulong Magic ( ) const { return magic ; }
//---
inline int GetNumParams ( ) const { return num_params ; }
//--- Add
2026-08-04 17:02:36 -05:00
virtual bool Add ( ulong post_ticket , double open_price , double sl_price , ENUM_POSITION_TYPE
position_type , datetime open_time , string sym ) = 0 ;
2026-07-28 09:08:36 -05:00
bool Add ( ulong position_ticket ) ;
//--- Remove
bool Remove ( ulong ticket ) ;
//--- General
void BreakEven ( ) ;
void OnOpenPosition ( const ROnOpenPosition & pos ) override final ;
void OnClosePosition ( const ROnClosePosition & pos , const int global_pos_index ) override final ;
//--- Setter principal
virtual void Set ( BreakEvenParams & params [ ] ) = 0 ;
} ;
//+------------------------------------------------------------------+
//| Contructor \ Destructor |
//+------------------------------------------------------------------+
CBreakEvenBase : : CBreakEvenBase ( const string & symbol_ , const ulong magic_ )
2026-08-04 17:02:36 -05:00
: m_automatic ( true ) ,
m_pos_be_s ( 0 ) , m_pos_be_r ( CBREAKEVEN_RESERVE_ARR ) ,
m_indices_to_remove_be_s ( 0 ) , m_indices_to_remove_be_r ( CBREAKEVEN_RESERVE_REMOVE_BE_ARR )
2026-07-28 09:08:36 -05:00
{
//---
if ( magic_ ! = NOT_MAGIC_NUMBER )
obj_trade . SetExpertMagicNumber ( magic_ ) ;
//---
obj_trade . LogLevel ( LOG_LEVEL_NO ) ;
//---
this . symbol = symbol_ ;
this . num_params = 0 ;
this . magic = magic_ ;
this . point_value = SymbolInfoDouble ( symbol_ , SYMBOL_POINT ) ;
//---
account_status . RegisterEvents ( & this , ACCOUNT_STATUS_REG_FLAG_ON_OPEN_POSITION | ACCOUNT_STATUS_REG_FLAG_ON_CLOSE_POSITION ) ;
//---
2026-08-04 17:02:36 -05:00
: : ArrayResize ( m_pos_be , CBREAKEVEN_RESERVE_ARR , CBREAKEVEN_RESERVE_ARR ) ;
: : ArrayResize ( m_indices_to_remove_be , CBREAKEVEN_RESERVE_REMOVE_BE_ARR , CBREAKEVEN_RESERVE_REMOVE_BE_ARR ) ;
2026-07-28 09:08:36 -05:00
}
//+------------------------------------------------------------------+
CBreakEvenBase : : ~ CBreakEvenBase ( )
{
if ( account_status . IsActive ( ) )
account_status . UnregisterEvents ( & this , ACCOUNT_STATUS_REG_FLAG_ON_OPEN_POSITION | ACCOUNT_STATUS_REG_FLAG_ON_CLOSE_POSITION ) ;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CBreakEvenBase : : Add ( ulong position_ticket )
{
//---
ResetLastError ( ) ;
if ( ! PositionSelectByTicket ( position_ticket ) )
{
LogError ( StringFormat ( " Error selecting ticket %I64u = %d " , position_ticket , GetLastError ( ) ) , FUNCION_ACTUAL ) ;
return false ;
}
//---
if ( Add ( position_ticket , PositionGetDouble ( POSITION_PRICE_OPEN ) , PositionGetDouble ( POSITION_SL ) , ENUM_POSITION_TYPE ( PositionGetInteger ( POSITION_TYPE ) ) ,
PositionGetInteger ( POSITION_TIME ) , PositionGetString ( POSITION_SYMBOL ) ) )
{
LogInfo ( StringFormat ( " Ticket %I64u has been added to the array of positions " , position_ticket ) , FUNCION_ACTUAL ) ;
return true ;
}
else
{
LogWarning ( StringFormat ( " Ticket %I64u could not be added " , position_ticket ) , FUNCION_ACTUAL ) ;
return false ;
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CBreakEvenBase : : Remove ( ulong ticket )
{
2026-08-04 17:02:36 -05:00
//---
int index = -1 ;
for ( int i = 0 ; i < m_pos_be_s ; i + + )
{
if ( m_pos_be [ i ] . ticket = = ticket )
{
index = i ;
break ;
}
}
//---
if ( index ! = -1 )
{
m_pos_be_s - - ;
if ( m_pos_be_s ! = index )
{
m_pos_be [ index ] = m_pos_be [ m_pos_be_s ] ;
}
return true ;
}
return false ;
2026-07-28 09:08:36 -05:00
}
//+------------------------------------------------------------------+
//| OnTradeTransactionEvent |
//+------------------------------------------------------------------+
void CBreakEvenBase : : OnOpenPosition ( const ROnOpenPosition & pos )
{
if ( m_automatic )
{
const ulong position_magic = pos . position . magic ;
if ( ( this . magic = = position_magic | | this . magic = = NOT_MAGIC_NUMBER ) )
{
2026-08-04 17:02:36 -05:00
if ( Add ( pos . position . ticket , pos . position . open_price , pos . position . sl , pos . position . type ,
pos . position . open_time , account_status . LastTransctionSymbol ( ) ) )
2026-07-28 09:08:36 -05:00
LogInfo ( StringFormat ( " Ticket %I64u has been added to the array of positions " , pos . position . ticket ) , FUNCION_ACTUAL ) ;
}
return ;
}
}
//+------------------------------------------------------------------+
2026-08-04 17:02:36 -05:00
void CBreakEvenBase : : OnClosePosition ( const ROnClosePosition & pos , const int global_pos_index )
2026-07-28 09:08:36 -05:00
{
2026-08-04 17:02:36 -05:00
if ( Remove ( pos . position . ticket ) )
LogCaution ( StringFormat ( " Ticket %I64u has been removed from the positions array, breakeven could not be applied " ,
pos . position . ticket ) , FUNCION_ACTUAL ) ;
2026-07-28 09:08:36 -05:00
}
//+------------------------------------------------------------------+
//| Function to make break even |
//+------------------------------------------------------------------+
void CBreakEvenBase : : BreakEven ( void )
{
//---
2026-08-04 17:02:36 -05:00
if ( m_pos_be_s < 1 )
2026-07-28 09:08:36 -05:00
return ;
//---
2026-08-04 17:02:36 -05:00
m_indices_to_remove_be_s = 0 ;
2026-07-28 09:08:36 -05:00
const double ask = SymbolInfoDouble ( symbol , SYMBOL_ASK ) ;
const double bid = SymbolInfoDouble ( symbol , SYMBOL_BID ) ;
//---
2026-08-04 17:02:36 -05:00
if ( m_pos_be_s > = m_indices_to_remove_be_r )
2026-07-28 09:08:36 -05:00
{
2026-08-04 17:02:36 -05:00
m_indices_to_remove_be_r < < = 1 ;
ArrayResize ( m_indices_to_remove_be , m_indices_to_remove_be_r , m_indices_to_remove_be_r ) ;
}
2026-07-28 09:08:36 -05:00
2026-08-04 17:02:36 -05:00
//---
for ( int i = 0 ; i < m_pos_be_s ; i + + )
{
if ( this . m_pos_be [ i ] . type = = POSITION_TYPE_BUY & & ask > = this . m_pos_be [ i ] . price_to_beat )
2026-07-28 09:08:36 -05:00
{
2026-08-04 17:02:36 -05:00
const ulong ticket = this . m_pos_be [ i ] . ticket ;
2026-07-28 09:08:36 -05:00
ResetLastError ( ) ;
if ( ! PositionSelectByTicket ( ticket ) )
{
2026-08-04 17:02:36 -05:00
LogError ( StringFormat ( " Fallo al selecionar el ticket = %I64u, ultimo error = %d " , ticket , : : GetLastError ( ) ) , FUNCION_ACTUAL ) ;
m_indices_to_remove_be [ m_indices_to_remove_be_s + + ] = i ;
2026-07-28 09:08:36 -05:00
continue ;
}
2026-08-04 17:02:36 -05:00
if ( ! obj_trade . PositionModify ( ticket , this . m_pos_be [ i ] . breakeven_price , PositionGetDouble ( POSITION_TP ) ) )
2026-07-28 09:08:36 -05:00
{
2026-08-04 17:02:36 -05:00
LogError ( StringFormat ( " Fallo al modificar la posicion = %I64u, ultimo error = %d " , ticket , : : GetLastError ( ) ) , FUNCION_ACTUAL ) ;
2026-07-28 09:08:36 -05:00
}
2026-08-04 17:02:36 -05:00
m_indices_to_remove_be [ m_indices_to_remove_be_s + + ] = i ;
2026-07-28 09:08:36 -05:00
}
else
2026-08-04 17:02:36 -05:00
if ( this . m_pos_be [ i ] . type = = POSITION_TYPE_SELL & & bid < = this . m_pos_be [ i ] . price_to_beat )
2026-07-28 09:08:36 -05:00
{
2026-08-04 17:02:36 -05:00
const ulong ticket = this . m_pos_be [ i ] . ticket ;
2026-07-28 09:08:36 -05:00
ResetLastError ( ) ;
if ( ! PositionSelectByTicket ( ticket ) )
{
2026-08-04 17:02:36 -05:00
LogError ( StringFormat ( " Fallo al selecionar el ticket = %I64u, ultimo error = %d " , ticket , : : GetLastError ( ) ) , FUNCION_ACTUAL ) ;
m_indices_to_remove_be [ m_indices_to_remove_be_s + + ] = i ;
2026-07-28 09:08:36 -05:00
continue ;
}
2026-08-04 17:02:36 -05:00
if ( ! obj_trade . PositionModify ( ticket , this . m_pos_be [ i ] . breakeven_price , PositionGetDouble ( POSITION_TP ) ) )
2026-07-28 09:08:36 -05:00
{
2026-08-04 17:02:36 -05:00
LogError ( StringFormat ( " Fallo al modificar la posicion = %I64u, ultimo error = %d " , ticket , : : GetLastError ( ) ) , FUNCION_ACTUAL ) ;
2026-07-28 09:08:36 -05:00
}
2026-08-04 17:02:36 -05:00
m_indices_to_remove_be [ m_indices_to_remove_be_s + + ] = i ;
2026-07-28 09:08:36 -05:00
}
}
//---
2026-08-04 17:02:36 -05:00
RemoveMultipleIndexesWithVSizeRemoveCts ( this . m_pos_be , m_pos_be_s ,
m_indices_to_remove_be , 0 , m_indices_to_remove_be_s ) ;
2026-07-28 09:08:36 -05:00
}
//+------------------------------------------------------------------+
//| class CBreakEvenSimple |
//+------------------------------------------------------------------+
class CBreakEvenSimple : public CBreakEvenBase
{
private :
int extra_points_be , points_be ;
public :
CBreakEvenSimple ( string symbol_ , ulong magic_ )
: CBreakEvenBase ( symbol_ , magic_ ) { this . extra_points_be = 0 ; this . points_be = 0 ; this . num_params = 2 ; }
bool Add ( ulong post_ticket , double open_price , double sl_price , ENUM_POSITION_TYPE position_type , datetime open_time , string sym ) override ;
void Set ( BreakEvenParams & params [ ] ) override ;
void SetSetSimple ( int points_be_ , int extra_points_be_ ) ;
} ;
//+----------------------------------------------------------------------------------------------+
//| Create a new structure and add it to the main array using the 'AddToArrayBe' function |
//+----------------------------------------------------------------------------------------------+
bool CBreakEvenSimple : : Add ( ulong post_ticket , double open_price , double sl_price , ENUM_POSITION_TYPE position_type , datetime open_time , string sym )
{
if ( sym ! = symbol )
return false ;
2026-08-04 17:02:36 -05:00
MQLARTICLES_BREAKEVEN_CHECK_RE
m_pos_be [ m_pos_be_s ] . breakeven_price = position_type = = POSITION_TYPE_BUY ? open_price + ( point_value * extra_points_be ) : open_price - ( point_value * extra_points_be ) ;
m_pos_be [ m_pos_be_s ] . type = position_type ;
m_pos_be [ m_pos_be_s ] . price_to_beat = position_type = = POSITION_TYPE_BUY ? open_price + ( point_value * points_be ) : open_price - ( point_value * points_be ) ;
m_pos_be [ m_pos_be_s + + ] . ticket = post_ticket ;
2026-07-28 09:08:36 -05:00
return true ;
}
//+------------------------------------------------------------------+
//| Set attributes of CBreakEvenSimple class with MqlParam array |
//+------------------------------------------------------------------+
void CBreakEvenSimple : : Set ( BreakEvenParams & params [ ] )
{
if ( params .Size ( ) < 2 )
{
LogCriticalError ( StringFormat ( " MqlParams array size is less than 2 " , params .Size ( ) ) , FUNCION_ACTUAL ) ;
ExpertRemove ( ) ;
return ;
}
SetSetSimple ( int ( params [ 0 ] . integer_value ) , int ( params [ 1 ] . integer_value ) ) ;
}
//+------------------------------------------------------------------+
//| Function to set member variables without using MalParams |
//+------------------------------------------------------------------+
void CBreakEvenSimple : : SetSetSimple ( int points_be_ , int extra_points_be_ )
{
if ( points_be_ < = 0 )
{
LogError ( StringFormat ( " The points to set the breakeven %I32d are invalid. " , points_be_ ) , FUNCION_ACTUAL ) ;
ExpertRemove ( ) ;
return ;
}
if ( extra_points_be_ < 0 )
{
LogFatalError ( StringFormat ( " The extra points %I32d for the breakeven price are invalid. " , extra_points_be_ ) , FUNCION_ACTUAL ) ;
ExpertRemove ( ) ;
return ;
}
if ( extra_points_be_ > = points_be_ )
{
LogWarning ( " The break even points (breakeven_price) is greater than the breakeven points (price_to_beat) \n Therefore the value of the extra breakeven points will be modified 0. " , FUNCION_ACTUAL ) ;
this . points_be = points_be_ ; //0
this . extra_points_be = 0 ; //1
return ;
}
this . points_be = points_be_ ; //0
this . extra_points_be = extra_points_be_ ; //1
}
//+------------------------------------------------------------------+
//| Class to apply break even based on atr |
//+------------------------------------------------------------------+
# define POINTER_INSTEAD_OF_PERIOD 0
//--- class CBreakEvenAtr
class CBreakEvenAtr : public CBreakEvenBase
{
private :
CAtrUltraOptimized * atr_ultra ;
double atr_multiplier_be ;
double atr_multiplier_extra_be ;
public :
CBreakEvenAtr ( string symbol_ , ulong magic_ )
: CBreakEvenBase ( symbol_ , magic_ ) , atr_multiplier_be ( 1.0 ) , atr_multiplier_extra_be ( 1.0 )
{ this . num_params = 5 ; }
bool Add ( ulong post_ticket , double open_price , double sl_price , ENUM_POSITION_TYPE position_type , datetime open_time , string sym ) override ;
void Set ( BreakEvenParams & params [ ] ) override ;
void SetSimple ( CAtrUltraOptimized * atr_pointer , double atr_multiplier_extra_be_ , double atr_multiplier_be_ ) ;
void SetSimple ( int atr_period , int atr_idx_ , double atr_multiplier_extra_be_ , double atr_multiplier_be_ , ENUM_TIMEFRAMES timeframe ) ;
} ;
//+------------------------------------------------------------------+
//| Function to set CBreakEvenAtr variables with MqlParams array |
//+------------------------------------------------------------------+
void CBreakEvenAtr : : Set ( BreakEvenParams & params [ ] )
{
if ( params .Size ( ) < 5 )
{
LogError ( StringFormat ( " The size of the array MqlParams %I32u to set the atr is less than 5 " , params .Size ( ) ) , FUNCION_ACTUAL ) ;
ExpertRemove ( ) ;
return ;
}
if ( params [ 4 ] . integer_value = = POINTER_INSTEAD_OF_PERIOD ) //4
{
/*
this . atr_pointer = ( int ) params [ 0 ] . atr_pointer_value ; //0
this . atr_multiplier_be = params [ 1 ] . double_value ; //1
this . atr_multiplier_extra_be = params [ 2 ] . double_value ; //2
* /
SetSimple ( params [ 0 ] . atr_pointer_value , params [ 2 ] . double_value , params [ 1 ] . double_value ) ;
}
else
{
/*
this . atr_idx = ( int ) params [ 0 ] . integer_value ; //0
this . atr_multiplier_be = params [ 1 ] . double_value ; //1
this . atr_multiplier_extra_be = params [ 2 ] . double_value ; //2
ENUM_TIMEFRAMES timeframe = ( ENUM_TIMEFRAMES ) params [ 3 ] . integer_value ; //3
int period = ( int ) params [ 4 ] . integer_value ; //4
* /
SetSimple ( ( int ) params [ 4 ] . integer_value , ( int ) params [ 0 ] . integer_value , params [ 2 ] . double_value , params [ 1 ] . double_value , ( ENUM_TIMEFRAMES ) params [ 3 ] . integer_value ) ;
}
}
//+------------------------------------------------------------------------------------+
//| Function to set the values of the CBreakEvenAtr class without using MqlParams |
//| Using the handle instead of period and timeframe |
//+------------------------------------------------------------------------------------+
void CBreakEvenAtr : : SetSimple ( CAtrUltraOptimized * atr_pointer , double atr_multiplier_extra_be_ , double atr_multiplier_be_ )
{
if ( atr_multiplier_extra_be_ > = atr_multiplier_be_ )
{
LogError ( " The multiplier of the atr to calculate the price be is greater than or equal to the multiplier to set the be " , FUNCION_ACTUAL ) ;
Remover ( ) ;
return ;
}
if ( CheckPointer ( atr_pointer ) = = POINTER_INVALID )
{
LogFatalError ( " El puntero a CAtrUltraOptimized* es invaldio " , FUNCION_ACTUAL ) ;
Remover ( ) ;
return ;
}
this . atr_ultra = atr_pointer ;
this . atr_multiplier_be = atr_multiplier_be_ ;
this . atr_multiplier_extra_be = atr_multiplier_extra_be_ ;
}
//+------------------------------------------------------------------------------------+
//| Function to set the values of the CBreakEvenAtr class without using MqlParams |
//| Configuring the handle with the period and timeframe. |
//+------------------------------------------------------------------------------------+
void CBreakEvenAtr : : SetSimple ( int atr_period , int atr_idx_ , double atr_multiplier_extra_be_ , double atr_multiplier_be_ , ENUM_TIMEFRAMES timeframe )
{
CAtrUltraOptimized * atr_op = new CAtrUltraOptimized ( ) ;
atr_op . SetVariables ( timeframe , this . symbol , atr_idx_ , atr_period ) ;
atr_op . SetInternalPointer ( ) ;
SetSimple ( atr_op , atr_multiplier_extra_be_ , atr_multiplier_be_ ) ;
}
//+------------------------------------------------------------------+
//| Function to add an element to the positions array |
//+------------------------------------------------------------------+
bool CBreakEvenAtr : : Add ( ulong post_ticket , double open_price , double sl_price , ENUM_POSITION_TYPE position_type , datetime open_time , string sym )
{
if ( sym ! = symbol )
return false ;
2026-08-04 17:02:36 -05:00
MQLARTICLES_BREAKEVEN_CHECK_RE
2026-07-28 09:08:36 -05:00
const double val = atr_ultra . base . GetAtrValue ( open_time ) ;
2026-08-04 17:02:36 -05:00
m_pos_be [ m_pos_be_s ] . breakeven_price = position_type = = POSITION_TYPE_BUY ? open_price + ( val * atr_multiplier_extra_be ) : open_price - ( val * atr_multiplier_extra_be ) ;
m_pos_be [ m_pos_be_s ] . type = position_type ;
m_pos_be [ m_pos_be_s ] . price_to_beat = position_type = = POSITION_TYPE_BUY ? open_price + ( val * atr_multiplier_be ) : open_price - ( val * atr_multiplier_be ) ;
m_pos_be [ m_pos_be_s + + ] . ticket = post_ticket ;
2026-07-28 09:08:36 -05:00
return true ;
}
//+------------------------------------------------------------------+
//| CBreakEvenRR Class |
//+------------------------------------------------------------------+
enum ENUM_TYPE_EXTRA_BE_BY_RRR
{
EXTRA_BE_RRR_BY_ATR , //By Atr
EXTRA_BE_RRR_BY_FIXED_POINTS //By Fixed Points
} ;
//--- class CBreakEvenRR
class CBreakEvenRR : public CBreakEvenBase
{
private :
CAtrUltraOptimized * atr_ultra ;
double coefficient_rr ; //Coefficient of rr
ENUM_TYPE_EXTRA_BE_BY_RRR type ;
double extra_value_be ; //Extra value that will be added to the opening price of the position to obtain the breakeven price
//Note: if the type is atr this will contain the atr multiplier, if not it will contain the value already multiplied by the point value
public :
CBreakEvenRR ( string symbol_ , ulong magic_ ) ;
bool Add ( ulong post_ticket , double open_price , double sl_price , ENUM_POSITION_TYPE position_type , datetime open_time , string sym ) override ;
void Set ( BreakEvenParams & params [ ] ) override ;
void SetSimple ( double rr_a_put_the_break_even , ENUM_TYPE_EXTRA_BE_BY_RRR type_extra , double atr_multiplier_or_extra_points , CAtrUltraOptimized * atr_ptr ) ;
void SetSimple ( double rr_a_put_the_break_even , ENUM_TYPE_EXTRA_BE_BY_RRR type_extra , double atr_multiplier_or_extra_points , int idx_atr_ , ENUM_TIMEFRAMES tf_atr , int atr_period_ ) ;
} ;
//+------------------------------------------------------------------+
//| Contructor |
//+------------------------------------------------------------------+
void CBreakEvenRR : : CBreakEvenRR ( string symbol_ , ulong magic_ )
: CBreakEvenBase ( symbol_ , magic_ ) ,
coefficient_rr ( 1.0 ) ,
extra_value_be ( 100.0 ) ,
type ( EXTRA_BE_RRR_BY_FIXED_POINTS )
{
this . num_params = 6 ;
}
//+------------------------------------------------------------------+
//| Function to set break even values by rr with MqlParams |
//+------------------------------------------------------------------+
void CBreakEvenRR : : Set ( BreakEvenParams & params [ ] )
{
if ( ( int ) params .Size ( ) < num_params )
{
LogError ( StringFormat ( " The size of the MqlParams array %I32u to set the be by rr is less than 2 " , params .Size ( ) ) , FUNCION_ACTUAL ) ;
ExpertRemove ( ) ;
return ;
}
if ( params [ 5 ] . integer_value = = POINTER_INSTEAD_OF_PERIOD )
{
//-> (0)double rr_a_put_the_break_even,(1) ENUM_TYPE_EXTRA_BE_BY_RRR type_extra, (2)double atr_multiplier_or_extra_points, (3)int idx_atr_, (4)int atr_handle_
SetSimple ( params [ 0 ] . double_value , ( ENUM_TYPE_EXTRA_BE_BY_RRR ) params [ 1 ] . integer_value , params [ 2 ] . double_value , params [ 4 ] . atr_pointer_value ) ;
}
else
{
//-> (0)double rr_a_put_the_break_even,(1) ENUM_TYPE_EXTRA_BE_BY_RRR type_extra, (2)double atr_multiplier_or_extra_points, (3)int idx_atr_, (4)ENUM_TIMEFRAMES tf_atr, (5)int atr_period_
SetSimple ( params [ 0 ] . double_value , ( ENUM_TYPE_EXTRA_BE_BY_RRR ) params [ 1 ] . integer_value , params [ 2 ] . double_value , ( int ) params [ 3 ] . integer_value , ( ENUM_TIMEFRAMES ) params [ 4 ] . integer_value , ( int ) params [ 5 ] . integer_value ) ;
}
}
//+------------------------------------------------------------------+
//| Function to set break even values by rr without MqlParams |
//+------------------------------------------------------------------+
void CBreakEvenRR : : SetSimple ( double rr_a_put_the_break_even , ENUM_TYPE_EXTRA_BE_BY_RRR type_extra , double atr_multiplier_or_extra_points , CAtrUltraOptimized * atr_ptr )
{
ResetLastError ( ) ;
if ( coefficient_rr < = 0.00 )
{
LogCriticalError ( StringFormat ( " The %+f coefficient of 'reward' is invalid " , coefficient_rr ) , FUNCION_ACTUAL ) ;
ExpertRemove ( ) ;
return ;
}
if ( atr_multiplier_or_extra_points < = 0.00 )
{
LogCriticalError ( StringFormat ( " The atr multiplier or extra points %f is less than or equal to 0 " , atr_multiplier_or_extra_points ) , FUNCION_ACTUAL ) ;
ExpertRemove ( ) ;
return ;
}
if ( type_extra ! = EXTRA_BE_RRR_BY_ATR & & type_extra ! = EXTRA_BE_RRR_BY_FIXED_POINTS )
{
LogCriticalError ( StringFormat ( " The type of extra value %s is invalid " , EnumToString ( type_extra ) ) , FUNCION_ACTUAL ) ;
ExpertRemove ( ) ;
return ;
}
this . type = type_extra ;
this . coefficient_rr = rr_a_put_the_break_even ;
if ( type_extra = = EXTRA_BE_RRR_BY_ATR )
{
if ( CheckPointer ( atr_ptr ) = = POINTER_INVALID )
{
LogFatalError ( " El puntero 'atr_ptr' es invalido " , FUNCION_ACTUAL ) ;
ExpertRemove ( ) ;
return ;
}
this . atr_ultra = atr_ptr ;
this . extra_value_be = atr_multiplier_or_extra_points ;
}
else
this . extra_value_be = atr_multiplier_or_extra_points * this . point_value ;
}
//+------------------------------------------------------------------+
//| Function to set break even values by rr without MqlParams |
//+------------------------------------------------------------------+
void CBreakEvenRR : : SetSimple ( double rr_a_put_the_break_even , ENUM_TYPE_EXTRA_BE_BY_RRR type_extra , double atr_multiplier_or_extra_points , int idx_atr_ , ENUM_TIMEFRAMES tf_atr , int atr_period_ )
{
if ( type_extra ! = EXTRA_BE_RRR_BY_ATR & & type_extra ! = EXTRA_BE_RRR_BY_FIXED_POINTS )
{
LogCriticalError ( StringFormat ( " The type of extra value %s is invalid " , EnumToString ( type_extra ) ) , FUNCION_ACTUAL ) ;
ExpertRemove ( ) ;
return ;
}
if ( atr_period_ < 0 )
{
LogError ( StringFormat ( " The atr period %d is invalid " , atr_period_ ) , FUNCION_ACTUAL ) ;
ExpertRemove ( ) ;
return ;
}
if ( type_extra = = EXTRA_BE_RRR_BY_ATR )
{
CAtrUltraOptimized * atr_ptr = new CAtrUltraOptimized ( ) ;
atr_ptr . SetVariables ( tf_atr , this . symbol , idx_atr_ , atr_period_ ) ;
atr_ptr . SetInternalPointer ( ) ;
SetSimple ( rr_a_put_the_break_even , type_extra , atr_multiplier_or_extra_points , atr_ptr ) ;
return ;
}
SetSimple ( rr_a_put_the_break_even , type_extra , atr_multiplier_or_extra_points , NULL ) ;
}
//+------------------------------------------------------------------+
//| Function to add an element to the positions array |
//+------------------------------------------------------------------+
bool CBreakEvenRR : : Add ( ulong post_ticket , double open_price , double sl_price , ENUM_POSITION_TYPE position_type , datetime open_time , string sym )
{
//---
if ( sym ! = symbol )
{
return false ;
}
//---
if ( sl_price < = DBL_EPSILON )
{
LogError ( StringFormat ( " Position %I64u with stop loss %+f has sl less than 0 " , post_ticket , sl_price ) , FUNCION_ACTUAL ) ;
return false ;
}
//---
double val = this . extra_value_be ;
if ( type = = EXTRA_BE_RRR_BY_ATR )
{
val * = atr_ultra . base . GetAtrValue ( open_time ) ;
}
//---
double diff = fabs ( open_price - sl_price ) ;
if ( ( diff * coefficient_rr ) < = val )
{
LogError ( StringFormat ( " The distance from the opening price %f where the stoploss is located is greater than or equal to the price to trigger the breakeven " , this . extra_value_be ) , FUNCION_ACTUAL ) ;
return false ;
}
//---
2026-08-04 17:02:36 -05:00
MQLARTICLES_BREAKEVEN_CHECK_RE
m_pos_be [ m_pos_be_s ] . breakeven_price = position_type = = POSITION_TYPE_BUY ? open_price + val : open_price - val ;
m_pos_be [ m_pos_be_s ] . type = position_type ;
m_pos_be [ m_pos_be_s ] . price_to_beat = position_type = = POSITION_TYPE_BUY ? open_price + ( coefficient_rr * diff ) : open_price - ( coefficient_rr * diff ) ;
m_pos_be [ m_pos_be_s ] . ticket = post_ticket ;
2026-07-28 09:08:36 -05:00
2026-08-04 17:02:36 -05:00
//---
LogInfo ( StringFormat ( " El precio del nuevo stop loss sera %f, valor actual del atr[%d] = %f " ,
m_pos_be [ m_pos_be_s ] . breakeven_price , atr_ultra . Index ( ) , val ) , FUNCION_ACTUAL ) ;
2026-07-28 09:08:36 -05:00
//---
2026-08-04 17:02:36 -05:00
m_pos_be_s + + ;
2026-07-28 09:08:36 -05:00
return true ;
}
//+------------------------------------------------------------------+
//| CBreakEven class |
//+------------------------------------------------------------------+
struct BreakEvenParamsInfo
{
BreakEvenParams params [ ] ;
2026-08-04 17:02:36 -05:00
bool filled ;
//---
BreakEvenParamsInfo ( )
: filled ( false )
{
ArrayResize ( params , 0 ) ;
}
2026-07-28 09:08:36 -05:00
} ;
//---
class CBreakEven
{
private :
ulong magic ;
string symbol ;
2026-08-04 17:02:36 -05:00
BreakEvenParamsInfo parameters [ CBREKEVEN_TOTAL ] ;
2026-07-28 09:08:36 -05:00
CBreakEvenBase * CreateBreakEven ( ENUM_BREAKEVEN_TYPE type ) ;
public :
CBreakEven ( ulong magic_ , const string symbol_ ) ;
~ CBreakEven ( ) ;
//---
CBreakEvenBase * obj ;
//--- Setters
bool SetInternalPointer ( ENUM_BREAKEVEN_TYPE type ) ;
void SetBeByFixedPoints ( int points_be_ , int extra_points_be_ ) ;
void SetBeByAtr ( int atr_period , int atr_idx_ , double atr_multiplier_extra_be_ , double atr_multiplier_be_ , ENUM_TIMEFRAMES timeframe ) ;
void SetBeByAtr ( double atr_multiplier_be , double atr_multiplier_extra_be , CAtrUltraOptimized * atr_ptr ) ;
void SetBeByRR ( double rr_a_put_the_break_even , ENUM_TYPE_EXTRA_BE_BY_RRR type_extra , double atr_multiplier_or_extra_points , CAtrUltraOptimized * atr_ptr ) ;
void SetBeByRR ( double rr_a_put_the_break_even , ENUM_TYPE_EXTRA_BE_BY_RRR type_extra , double atr_multiplier_or_extra_points , int idx_atr_ , ENUM_TIMEFRAMES tf_atr , int atr_period_ ) ;
} ;
//+------------------------------------------------------------------+
//| Contructor |
//+------------------------------------------------------------------+
CBreakEven : : CBreakEven ( ulong magic_ , const string symbol_ )
{
2026-08-04 17:02:36 -05:00
//---
2026-07-28 09:08:36 -05:00
this . magic = magic_ ;
this . symbol = symbol_ ;
obj = NULL ;
2026-08-04 17:02:36 -05:00
//---
ArrayResize ( parameters [ int ( BREAKEVEN_TYPE_RR ) ] . params , 6 ) ;
ArrayResize ( parameters [ int ( BREAKEVEN_TYPE_ATR ) ] . params , 5 ) ;
ArrayResize ( parameters [ int ( BREAKEVEN_TYPE_FIXED_POINTS ) ] . params , 2 ) ;
2026-07-28 09:08:36 -05:00
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CBreakEven : : ~ CBreakEven ( )
{
if ( CheckPointer ( this . obj ) = = POINTER_DYNAMIC )
{
delete this . obj ;
this . obj = NULL ;
}
}
//+------------------------------------------------------------------+
//| Set rr without handle |
//+------------------------------------------------------------------+
void CBreakEven : : SetBeByRR ( double rr_a_put_the_break_even , ENUM_TYPE_EXTRA_BE_BY_RRR type_extra , double atr_multiplier_or_extra_points , int idx_atr_ , ENUM_TIMEFRAMES tf_atr , int atr_period_ )
{
2026-08-04 17:02:36 -05:00
parameters [ BREAKEVEN_TYPE_RR ] . filled = true ;
2026-07-28 09:08:36 -05:00
parameters [ BREAKEVEN_TYPE_RR ] . params [ 0 ] . double_value = rr_a_put_the_break_even ;
parameters [ BREAKEVEN_TYPE_RR ] . params [ 1 ] . integer_value = ( int ) type_extra ;
parameters [ BREAKEVEN_TYPE_RR ] . params [ 2 ] . double_value = atr_multiplier_or_extra_points ;
parameters [ BREAKEVEN_TYPE_RR ] . params [ 3 ] . integer_value = idx_atr_ ;
parameters [ BREAKEVEN_TYPE_RR ] . params [ 4 ] . integer_value = ( int ) tf_atr ;
parameters [ BREAKEVEN_TYPE_RR ] . params [ 5 ] . integer_value = atr_period_ ;
}
//+------------------------------------------------------------------+
//| Set rr with handle |
//+------------------------------------------------------------------+
void CBreakEven : : SetBeByRR ( double rr_a_put_the_break_even , ENUM_TYPE_EXTRA_BE_BY_RRR type_extra , double atr_multiplier_or_extra_points , CAtrUltraOptimized * atr_ptr )
{
2026-08-04 17:02:36 -05:00
parameters [ BREAKEVEN_TYPE_RR ] . filled = true ;
2026-07-28 09:08:36 -05:00
parameters [ BREAKEVEN_TYPE_RR ] . params [ 0 ] . double_value = rr_a_put_the_break_even ;
parameters [ BREAKEVEN_TYPE_RR ] . params [ 1 ] . integer_value = ( int ) type_extra ;
parameters [ BREAKEVEN_TYPE_RR ] . params [ 2 ] . double_value = atr_multiplier_or_extra_points ;
parameters [ BREAKEVEN_TYPE_RR ] . params [ 3 ] . integer_value = 0 ;
parameters [ BREAKEVEN_TYPE_RR ] . params [ 4 ] . atr_pointer_value = atr_ptr ;
parameters [ BREAKEVEN_TYPE_RR ] . params [ 5 ] . integer_value = POINTER_INSTEAD_OF_PERIOD ;
}
//+------------------------------------------------------------------+
//| Set atr (without handle) |
//+------------------------------------------------------------------+
void CBreakEven : : SetBeByAtr ( int atr_period , int atr_idx_ , double atr_multiplier_extra_be_ , double atr_multiplier_be_ , ENUM_TIMEFRAMES timeframe )
{
2026-08-04 17:02:36 -05:00
parameters [ BREAKEVEN_TYPE_ATR ] . filled = true ;
2026-07-28 09:08:36 -05:00
parameters [ BREAKEVEN_TYPE_ATR ] . params [ 0 ] . integer_value = atr_idx_ ;
parameters [ BREAKEVEN_TYPE_ATR ] . params [ 1 ] . double_value = atr_multiplier_be_ ;
parameters [ BREAKEVEN_TYPE_ATR ] . params [ 2 ] . double_value = atr_multiplier_extra_be_ ;
parameters [ BREAKEVEN_TYPE_ATR ] . params [ 3 ] . integer_value = int ( timeframe ) ;
parameters [ BREAKEVEN_TYPE_ATR ] . params [ 4 ] . integer_value = atr_period ;
}
//+------------------------------------------------------------------+
//| Set atr (with handle) |
//+------------------------------------------------------------------+
void CBreakEven : : SetBeByAtr ( double atr_multiplier_be , double atr_multiplier_extra_be , CAtrUltraOptimized * atr_ptr )
{
2026-08-04 17:02:36 -05:00
parameters [ BREAKEVEN_TYPE_ATR ] . filled = true ;
2026-07-28 09:08:36 -05:00
parameters [ BREAKEVEN_TYPE_ATR ] . params [ 0 ] . atr_pointer_value = atr_ptr ;
parameters [ BREAKEVEN_TYPE_ATR ] . params [ 1 ] . double_value = atr_multiplier_be ;
parameters [ BREAKEVEN_TYPE_ATR ] . params [ 2 ] . double_value = atr_multiplier_extra_be ;
parameters [ BREAKEVEN_TYPE_ATR ] . params [ 3 ] . integer_value = 0 ;
parameters [ BREAKEVEN_TYPE_ATR ] . params [ 4 ] . integer_value = POINTER_INSTEAD_OF_PERIOD ;
}
//+------------------------------------------------------------------+
//| Set Fixed Point be |
//+------------------------------------------------------------------+
void CBreakEven : : SetBeByFixedPoints ( int points_be_ , int extra_points_be_ )
{
2026-08-04 17:02:36 -05:00
parameters [ BREAKEVEN_TYPE_ATR ] . filled = true ;
2026-07-28 09:08:36 -05:00
parameters [ BREAKEVEN_TYPE_FIXED_POINTS ] . params [ 0 ] . integer_value = points_be_ ;
parameters [ BREAKEVEN_TYPE_FIXED_POINTS ] . params [ 1 ] . integer_value = extra_points_be_ ;
}
//+------------------------------------------------------------------+
//| Dynamically create the correct BreakEven |
//+------------------------------------------------------------------+
CBreakEvenBase * CBreakEven : : CreateBreakEven ( ENUM_BREAKEVEN_TYPE type )
{
switch ( type )
{
case BREAKEVEN_TYPE_RR :
return new CBreakEvenRR ( this . symbol , this . magic ) ;
case BREAKEVEN_TYPE_FIXED_POINTS :
return new CBreakEvenSimple ( this . symbol , this . magic ) ;
case BREAKEVEN_TYPE_ATR :
return new CBreakEvenAtr ( this . symbol , this . magic ) ;
default :
return NULL ;
}
}
//+------------------------------------------------------------------+
//| Set Pointer |
//+------------------------------------------------------------------+
bool CBreakEven : : SetInternalPointer ( ENUM_BREAKEVEN_TYPE type )
{
//---
if ( CheckPointer ( this . obj ) = = POINTER_DYNAMIC )
{
delete this . obj ;
this . obj = NULL ;
FastLog ( FUNCION_ACTUAL , WARNING_TEXT , " A new breakeven mode is being re-established " ) ;
}
//---
this . obj = CreateBreakEven ( type ) ;
if ( this . obj = = NULL )
{
FastLog ( FUNCION_ACTUAL , ERROR_TEXT , StringFormat ( " The be type = %s is invalid " , g_breakevens_types_str [ type ] ) ) ;
ExpertRemove ( ) ;
return false ;
}
//---
2026-08-04 17:02:36 -05:00
if ( ! parameters [ type ] . filled | | ( int ) parameters [ type ] . params .Size ( ) ! = obj . GetNumParams ( ) )
2026-07-28 09:08:36 -05:00
{
2026-08-04 17:02:36 -05:00
FastLog ( FUNCION_ACTUAL , ERROR_TEXT , StringFormat ( " The parameter array for %s is too small (%I32u elements), or not seted " ,
g_breakevens_types_str [ type ] , parameters [ type ] . params .Size ( ) ) ) ;
2026-07-28 09:08:36 -05:00
delete obj ;
obj = NULL ;
ExpertRemove ( ) ;
return false ;
}
//---
obj .Set ( parameters [ type ] . params ) ;
return true ;
}
//+------------------------------------------------------------------+
# endif
//+------------------------------------------------------------------+