MQLArticles/PosMgmt/Breakeven.mqh

800 lines
65 KiB
MQL5

2025-11-10 09:31:13 -05:00
<EFBFBD><EFBFBD>//+------------------------------------------------------------------+
//| Breakeven.mqh |
//| Copyright 2025, Niquel y Leo. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Niquel y Leo."
2025-11-12 16:03:11 -05:00
#property link "https://www.mql5.com"
#property strict
2025-11-10 09:31:13 -05:00
#ifndef MQLARTICLES_POSMGMT_BREAKEVEN_MQH
#define MQLARTICLES_POSMGMT_BREAKEVEN_MQH
//+------------------------------------------------------------------+
//| Include |
2025-11-28 20:21:30 -05:00
//+------------------------------------------------------------------+
2025-11-10 09:31:13 -05:00
#include "..\\RM\\RiskManagement.mqh"
//+------------------------------------------------------------------+
2025-11-28 20:21:30 -05:00
//| Break Even Structs |
//+------------------------------------------------------------------+
struct position_be
{
2025-11-10 09:31:13 -05:00
2025-11-28 20:21:30 -05:00
ulong ticket; //Position Ticket
2025-11-10 09:31:13 -05:00
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
{
2025-11-28 20:21:30 -05:00
BREAKEVEN_TYPE_RR = 0, //By RR
2025-11-10 09:31:13 -05:00
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"
};
2025-11-28 17:21:07 -05:00
2025-11-10 09:31:13 -05:00
//---
struct BreakEvenParams
{
long integer_value;
string string_value;
double double_value;
2025-11-28 20:21:30 -05:00
2025-11-28 17:21:07 -05:00
CAtrUltraOptimized* atr_pointer_value;
2025-11-28 20:21:30 -05:00
};
//---
2025-11-10 09:31:13 -05:00
class CBreakEvenBase;
class CBreakEven;
class CBreakEvenAtr;
class CBreakEvenRR;
class CBreakEvenSimple;
2025-11-28 20:21:30 -05:00
2025-11-10 09:31:13 -05:00
//---
#define CBREKEVEN_TOTAL 3
//---
#define CBREAKEVEN_RESERVE_REMOVE_BE_ARR 3
#define CBREAKEVEN_RESERVE_ARR 3
//+------------------------------------------------------------------+
//| Main class to apply break even |
//+------------------------------------------------------------------+
2025-11-28 17:21:07 -05:00
class CBreakEvenBase : public CAccountGestor
{
private:
2025-11-28 20:21:30 -05:00
2025-11-28 17:21:07 -05:00
int m_indices_to_remove_be[];
2025-11-28 20:21:30 -05:00
2025-11-28 17:21:07 -05:00
2025-11-28 20:21:30 -05:00
protected:
2025-11-28 17:21:07 -05:00
CTrade obj_trade; //CTrade object
2025-11-28 20:21:30 -05:00
MqlTick tick; //tick structure
string symbol; //current symbol
double point_value; //value of the set symbol point
position_be PostionsBe[]; //array of positions of type Positions
ulong magic; //magic number of positions to make break even
int num_params; //Number of parameters the class needs
bool m_automatic;
public:
2025-11-28 17:21:07 -05:00
CBreakEvenBase(string symbol_, ulong magic_);
~CBreakEvenBase() {}
2025-11-10 09:31:13 -05:00
//---
2025-11-28 17:21:07 -05:00
2025-11-10 09:31:13 -05:00
inline bool Automatic() const { return m_automatic; }
void Automatic(bool auto) { m_automatic = auto; }
2025-11-28 17:21:07 -05:00
2025-11-10 09:31:13 -05:00
//---
inline string Simbolo() const { return symbol; }
inline ulong Magic() const { return magic; }
2025-11-28 17:21:07 -05:00
2025-11-10 09:31:13 -05:00
//---
inline int GetNumParams() const { return num_params; }
//--- Add
virtual bool Add(ulong post_ticket, double open_price, double sl_price, ENUM_POSITION_TYPE position_type, datetime open_time, string sym) = 0;
bool Add(ulong position_ticket);
2025-11-28 17:21:07 -05:00
2025-11-28 20:21:30 -05:00
2025-11-10 09:31:13 -05:00
//--- Remove
2025-11-28 17:21:07 -05:00
2025-11-10 09:31:13 -05:00
bool Remove(ulong ticket);
//--- General
void BreakEven();
void OnOpenClosePosition(const ROnOpenClosePosition &pos) override;
//--- Setter principal
virtual void Set(BreakEvenParams &params[]) = 0;
};
//+------------------------------------------------------------------+
2025-11-28 17:21:07 -05:00
//| Contructor |
2025-11-10 09:31:13 -05:00
//+------------------------------------------------------------------+
CBreakEvenBase::CBreakEvenBase(string symbol_, ulong magic_)
: m_automatic(true)
{
if(magic_ != NOT_MAGIC_NUMBER)
obj_trade.SetExpertMagicNumber(magic_);
2025-11-28 17:21:07 -05:00
obj_trade.LogLevel(LOG_LEVEL_NO);
2025-11-10 09:31:13 -05:00
//---
this.symbol = symbol_;
2025-11-28 17:21:07 -05:00
this.num_params = 0;
2025-11-10 09:31:13 -05:00
2025-11-28 17:21:07 -05:00
this.magic = magic_;
this.point_value = SymbolInfoDouble(symbol_, SYMBOL_POINT);
2025-11-10 09:31:13 -05:00
2025-11-28 20:21:30 -05:00
//---
2025-11-10 09:31:13 -05:00
ArrayResize(PostionsBe, 0, CBREAKEVEN_RESERVE_ARR);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
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;
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2025-11-28 17:21:07 -05:00
bool CBreakEvenBase::Remove(ulong ticket)
2025-11-10 09:31:13 -05:00
{
return (RemoveIndexFromAnArrayOfPositions(PostionsBe, ticket, CBREAKEVEN_RESERVE_ARR));
}
//+------------------------------------------------------------------+
//| OnTradeTransactionEvent |
//+------------------------------------------------------------------+
void CBreakEvenBase::OnOpenClosePosition(const ROnOpenClosePosition &pos)
{
//---
if(m_automatic && pos.deal_entry_type == DEAL_ENTRY_IN)
{
const ulong position_magic = pos.position.magic;
if((this.magic == position_magic || this.magic == NOT_MAGIC_NUMBER))
{
if(Add(pos.position.ticket, pos.position.open_price, pos.position.sl, pos.position.type, pos.position.open_time, account_status.LastTransctionSymbol()))
LogInfo(StringFormat("Ticket %I64u has been added to the array of positions", pos.position.ticket), FUNCION_ACTUAL);
}
return;
}
//---
if(pos.deal_entry_type == DEAL_ENTRY_OUT)
{
if(RemoveIndexFromAnArrayOfPositions(PostionsBe, pos.position.ticket, CBREAKEVEN_RESERVE_ARR))
LogCaution(StringFormat("Ticket %I64u has been removed from the positions array, breakeven could not be applied", pos.position.ticket), FUNCION_ACTUAL);
}
}
//+------------------------------------------------------------------+
//| Function to make break even |
//+------------------------------------------------------------------+
void CBreakEvenBase::BreakEven(void)
{
//---
if(this.PostionsBe.Size() < 1)
return;
2025-11-28 17:21:07 -05:00
//---
2025-11-10 09:31:13 -05:00
2025-11-28 17:21:07 -05:00
SymbolInfoTick(this.symbol, tick);
2025-11-10 09:31:13 -05:00
//---
ArrayResize(m_indices_to_remove_be, 0, CBREAKEVEN_RESERVE_REMOVE_BE_ARR);
//---
for(int i = 0 ; i < ArraySize(this.PostionsBe) ; i++)
{
ulong ticket = this.PostionsBe[i].ticket;
if(tick.ask >= this.PostionsBe[i].price_to_beat && this.PostionsBe[i].type == POSITION_TYPE_BUY)
{
double position_tp = PositionGetDouble(POSITION_TP);
obj_trade.PositionModify(ticket, this.PostionsBe[i].breakeven_price, position_tp);
AddArrayNoVerification(m_indices_to_remove_be, i, CBREAKEVEN_RESERVE_REMOVE_BE_ARR);
}
else
if(tick.bid <= this.PostionsBe[i].price_to_beat && this.PostionsBe[i].type == POSITION_TYPE_SELL)
{
2025-11-28 17:21:07 -05:00
2025-11-10 09:31:13 -05:00
double position_tp = PositionGetDouble(POSITION_TP);
obj_trade.PositionModify(ticket, this.PostionsBe[i].breakeven_price, position_tp);
AddArrayNoVerification(m_indices_to_remove_be, i, CBREAKEVEN_RESERVE_REMOVE_BE_ARR);
}
}
//---
RemoveMultipleIndexes(this.PostionsBe, m_indices_to_remove_be, CBREAKEVEN_RESERVE_REMOVE_BE_ARR);
}
//+------------------------------------------------------------------+
//| 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;
position_be new_pos;
new_pos.breakeven_price = position_type == POSITION_TYPE_BUY ? open_price + (point_value * extra_points_be) : open_price - (point_value * extra_points_be);
new_pos.type = position_type;
new_pos.price_to_beat = position_type == POSITION_TYPE_BUY ? open_price + (point_value * points_be) : open_price - (point_value * points_be) ;
new_pos.ticket = post_ticket;
AddArrayNoVerification(this.PostionsBe, new_pos, CBREAKEVEN_RESERVE_ARR);
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 |
2025-11-28 17:21:07 -05:00
//+------------------------------------------------------------------+
2025-11-10 09:31:13 -05:00
2025-11-28 17:21:07 -05:00
void CBreakEvenSimple::SetSetSimple(int points_be_, int extra_points_be_)
{
if(points_be_ <= 0)
{
2025-11-28 20:21:30 -05:00
2025-11-10 09:31:13 -05:00
LogError(StringFormat("The points to set the breakeven %I32d are invalid.", points_be_), FUNCION_ACTUAL);
ExpertRemove();
return;
2025-11-28 17:21:07 -05:00
2025-11-10 09:31:13 -05:00
}
if(extra_points_be_ < 0)
2025-11-28 17:21:07 -05:00
{
2025-11-10 09:31:13 -05:00
LogFatalError(StringFormat("The extra points %I32d for the breakeven price are invalid.", extra_points_be_), FUNCION_ACTUAL);
ExpertRemove();
return;
2025-11-28 17:21:07 -05:00
}
2025-11-10 09:31:13 -05:00
if(extra_points_be_ >= points_be_)
{
2025-11-28 17:21:07 -05:00
LogWarning("The break even points (breakeven_price) is greater than the breakeven points (price_to_beat)\nTherefore the value of the extra breakeven points will be modified 0.", FUNCION_ACTUAL);
2025-11-10 09:31:13 -05:00
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
}
//+------------------------------------------------------------------+
2025-11-28 20:21:30 -05:00
//| Class to apply break even based on atr |
2025-11-10 09:31:13 -05:00
//+------------------------------------------------------------------+
#define POINTER_INSTEAD_OF_PERIOD 0
2025-11-28 20:21:30 -05:00
2025-11-10 09:31:13 -05:00
//--- class CBreakEvenAtr
2025-11-28 20:21:30 -05:00
class CBreakEvenAtr : public CBreakEvenBase
2025-11-10 09:31:13 -05:00
{
private:
CAtrUltraOptimized* atr_ultra;
double atr_multiplier_be;
double atr_multiplier_extra_be;
public:
CBreakEvenAtr(string symbol_, ulong magic_)
2025-11-28 20:21:30 -05:00
2025-11-10 09:31:13 -05:00
: 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);
}
}
2025-11-28 20:21:30 -05:00
//+------------------------------------------------------------------------------------+
2025-11-10 09:31:13 -05:00
//| 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_)
2025-11-28 20:21:30 -05:00
{
2025-11-10 09:31:13 -05:00
2025-11-28 20:21:30 -05:00
if(atr_multiplier_extra_be_ >= atr_multiplier_be_)
2025-11-10 09:31:13 -05:00
{
2025-11-28 20:21:30 -05:00
2025-11-10 09:31:13 -05:00
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);
2025-11-28 20:21:30 -05:00
Remover();
2025-11-10 09:31:13 -05:00
return;
2025-11-28 20:21:30 -05:00
2025-11-10 09:31:13 -05:00
}
2025-11-28 20:21:30 -05:00
2025-11-10 09:31:13 -05:00
if(CheckPointer(atr_pointer) == POINTER_INVALID)
2025-11-28 20:21:30 -05:00
2025-11-10 09:31:13 -05:00
{
2025-11-28 20:21:30 -05:00
LogFatalError("El puntero a CAtrUltraOptimized* es invaldio", FUNCION_ACTUAL);
2025-11-10 09:31:13 -05:00
Remover();
2025-11-28 20:21:30 -05:00
return;
2025-11-10 09:31:13 -05:00
}
2025-11-28 20:21:30 -05:00
2025-11-10 09:31:13 -05:00
this.atr_ultra = atr_pointer;
2025-11-28 20:21:30 -05:00
2025-11-10 09:31:13 -05:00
this.atr_multiplier_be = atr_multiplier_be_;
this.atr_multiplier_extra_be = atr_multiplier_extra_be_;
2025-11-28 20:21:30 -05:00
}
2025-11-10 09:31:13 -05:00
//+------------------------------------------------------------------------------------+