forked from nique_372/MQLArticles
905 lines
35 KiB
MQL5
905 lines
35 KiB
MQL5
//+------------------------------------------------------------------+
| ||
//| 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"
| ||
#include "..\\Utils\\FA\\Atr.mqh"
| ||
| ||
//+------------------------------------------------------------------+
| ||
//| |
| ||
//+------------------------------------------------------------------+
| ||
namespace TSN
| ||
{
| ||
//+------------------------------------------------------------------+
| ||
//| 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;
| ||
long integer_value;
| ||
double double_value;
| ||
CAtrUltraOptimized* atr_pointer_value;
| ||
};
| ||
| ||
//---
| ||
class CBreakEvenBase;
| ||
class CBreakEven;
| ||
class CBreakEvenAtr;
| ||
class CBreakEvenRR;
| ||
class CBreakEvenSimple;
| ||
| ||
//---
| ||
#define CBREKEVEN_TOTAL 3
| ||
| ||
//--
| ||
#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);\
| ||
}
| ||
| ||
//---
| ||
#define CBREAKEVEN_RESERVE_REMOVE_BE_ARR 16
| ||
#define CBREAKEVEN_RESERVE_ARR 16
| ||
//+------------------------------------------------------------------+
| ||
//| Main class to apply break even |
| ||
//+------------------------------------------------------------------+
| ||
class CBreakEvenBase : public CAccountGestor
| ||
{
| ||
private:
| ||
int m_indices_to_remove_be[];
| ||
int m_indices_to_remove_be_s;
| ||
int m_indices_to_remove_be_r;
| ||
| ||
protected:
| ||
//---
| ||
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;
| ||
| ||
//---
| ||
position_be m_pos_be[]; //array of positions of type Positions
| ||
int m_pos_be_s;
| ||
int m_pos_be_r;
| ||
| ||
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
| ||
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);
| ||
| ||
//--- 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 ¶ms[]) = 0;
| ||
};
| ||
| ||
| ||
//+------------------------------------------------------------------+
| ||
//| Contructor \ Destructor |
| ||
//+------------------------------------------------------------------+
| ||
CBreakEvenBase::CBreakEvenBase(const string& symbol_, const ulong magic_)
| ||
: 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)
| ||
{
| ||
//---
| ||
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);
| ||
| ||
//---
| ||
::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);
| ||
}
| ||
| ||
| ||
//+------------------------------------------------------------------+
| ||
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)
| ||
{
| ||
//---
| ||
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;
| ||
}
| ||
| ||
//+------------------------------------------------------------------+
| ||
//| 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))
| ||
{
| ||
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;
| ||
}
| ||
}
| ||
| ||
//+------------------------------------------------------------------+
| ||
void CBreakEvenBase::OnClosePosition(const ROnClosePosition &pos, const int global_pos_index)
| ||
{
| ||
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);
| ||
}
| ||
| ||
| ||
| ||
//+------------------------------------------------------------------+
| ||
//| Function to make break even |
| ||
//+------------------------------------------------------------------+
| ||
void CBreakEvenBase::BreakEven(void)
| ||
{
| ||
//---
| ||
if(m_pos_be_s < 1)
| ||
return;
| ||
| ||
//---
| ||
m_indices_to_remove_be_s = 0;
| ||
const double ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
| ||
const double bid = SymbolInfoDouble(symbol, SYMBOL_BID);
| ||
| ||
//---
| ||
if(m_pos_be_s >= m_indices_to_remove_be_r)
| ||
{
| ||
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);
| ||
}
| ||
| ||
//---
| ||
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)
| ||
{
| ||
const ulong ticket = this.m_pos_be[i].ticket;
| ||
ResetLastError();
| ||
if(!PositionSelectByTicket(ticket))
| ||
{
| ||
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;
| ||
continue;
| ||
}
| ||
| ||
if(!obj_trade.PositionModify(ticket, this.m_pos_be[i].breakeven_price, PositionGetDouble(POSITION_TP)))
| ||
{
| ||
LogError(StringFormat("Fallo al modificar la posicion = %I64u, ultimo error = %d", ticket, ::GetLastError()), FUNCION_ACTUAL);
| ||
}
| ||
| ||
m_indices_to_remove_be[m_indices_to_remove_be_s++] = i;
| ||
}
| ||
else
| ||
if(this.m_pos_be[i].type == POSITION_TYPE_SELL && bid <= this.m_pos_be[i].price_to_beat)
| ||
{
| ||
const ulong ticket = this.m_pos_be[i].ticket;
| ||
ResetLastError();
| ||
if(!PositionSelectByTicket(ticket))
| ||
{
| ||
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;
| ||
continue;
| ||
}
| ||
| ||
if(!obj_trade.PositionModify(ticket, this.m_pos_be[i].breakeven_price, PositionGetDouble(POSITION_TP)))
| ||
{
| ||
LogError(StringFormat("Fallo al modificar la posicion = %I64u, ultimo error = %d", ticket, ::GetLastError()), FUNCION_ACTUAL);
| ||
}
| ||
| ||
m_indices_to_remove_be[m_indices_to_remove_be_s++] = i;
| ||
}
| ||
}
| ||
| ||
//---
| ||
RemoveMultipleIndexesWithVSizeRemoveCts(this.m_pos_be, m_pos_be_s,
| ||
m_indices_to_remove_be, 0, m_indices_to_remove_be_s);
| ||
}
| ||
| ||
| ||
//+------------------------------------------------------------------+
| ||
//| 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 ¶ms[]) 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;
| ||
| ||
| ||
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;
| ||
| ||
return true;
| ||
}
| ||
| ||
//+------------------------------------------------------------------+
| ||
//| Set attributes of CBreakEvenSimple class with MqlParam array |
| ||
//+------------------------------------------------------------------+
| ||
void CBreakEvenSimple::Set(BreakEvenParams ¶ms[])
| ||
{
| ||
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)\nTherefore 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 ¶ms[]) 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 ¶ms[])
| ||
{
| ||
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;
| ||
| ||
MQLARTICLES_BREAKEVEN_CHECK_RE
| ||
const double val = atr_ultra.base.GetAtrValue(open_time);
| ||
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;
| ||
| ||
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 ¶ms[]) 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 ¶ms[])
| ||
{
| ||
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;
| ||
}
| ||
| ||
//---
| ||
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;
| ||
| ||
//---
| ||
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);
| ||
| ||
//---
| ||
m_pos_be_s++;
| ||
return true;
| ||
}
| ||
| ||
| ||
//+------------------------------------------------------------------+
| ||
//| CBreakEven class |
| ||
//+------------------------------------------------------------------+
| ||
struct BreakEvenParamsInfo
| ||
{
| ||
BreakEvenParams params[];
| ||
bool filled;
| ||
| ||
//---
| ||
BreakEvenParamsInfo()
| ||
: filled(false)
| ||
{
| ||
ArrayResize(params, 0);
| ||
}
| ||
};
| ||
| ||
//---
| ||
class CBreakEven
| ||
{
| ||
private:
| ||
ulong magic;
| ||
string symbol;
| ||
BreakEvenParamsInfo parameters[CBREKEVEN_TOTAL];
| ||
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_)
| ||
{
| ||
//---
| ||
this.magic = magic_;
| ||
this.symbol = symbol_;
| ||
obj = NULL;
| ||
| ||
//---
| ||
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);
| ||
}
| ||
//+------------------------------------------------------------------+
| ||
//| 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_)
| ||
{
| ||
parameters[BREAKEVEN_TYPE_RR].filled = true;
| ||
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)
| ||
{
| ||
parameters[BREAKEVEN_TYPE_RR].filled = true;
| ||
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)
| ||
{
| ||
parameters[BREAKEVEN_TYPE_ATR].filled = true;
| ||
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)
| ||
{
| ||
parameters[BREAKEVEN_TYPE_ATR].filled = true;
| ||
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_)
| ||
{
| ||
parameters[BREAKEVEN_TYPE_ATR].filled = true;
| ||
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;
| ||
}
| ||
| ||
//---
| ||
if(!parameters[type].filled || (int)parameters[type].params.Size() != obj.GetNumParams())
| ||
{
| ||
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()));
| ||
delete obj;
| ||
obj = NULL;
| ||
ExpertRemove();
| ||
return false;
| ||
}
| ||
| ||
//---
| ||
obj.Set(parameters[type].params);
| ||
return true;
| ||
}
| ||
}
| ||
//+------------------------------------------------------------------+
| ||
#endif
| ||
//+------------------------------------------------------------------+
|