forked from nique_372/MQLArticles
713 lines
29 KiB
MQL5
713 lines
29 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| Base.mqh |
| |
//| Copyright 2025, Niquel Mendoza. |
| |
//| https://www.mql5.com/es/users/nique_372/news |
| |
//+------------------------------------------------------------------+
| |
#property copyright "Copyright 2025, Niquel Mendoza."
| |
#property link "https://www.mql5.com/es/users/nique_372/news"
| |
#property strict
| |
| |
| |
#ifndef MQLARTICLES_POSMGMT_CONDPARTIALS_BASE_BASE_MQH
| |
#define MQLARTICLES_POSMGMT_CONDPARTIALS_BASE_BASE_MQH
| |
| |
//+------------------------------------------------------------------+
| |
//| Include |
| |
//+------------------------------------------------------------------+
| |
#include "Defines.mqh"
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
namespace TSN
| |
{
| |
//+------------------------------------------------------------------+
| |
//| Base class for conditional partials implementation |
| |
//+------------------------------------------------------------------+
| |
class CConditionalPartials : public CAccountGestor
| |
{
| |
protected:
| |
//--- General
| |
IConditionPartial* m_partial_condition; // Pointer to IConditionPartial, this pointer will give the condition to close buy and sell positions partially
| |
bool m_delete_partial_condition_in_dstctor;
| |
CTrade m_trade; // CTrade instance, to apply partial closes
| |
| |
//--- Tracked Positions
| |
// Array of ConditionalPartialTrackedPosition type, this array will store the positions
| |
// To which partial closes will be applied
| |
ConditionalPartialTrackedPosition m_tracked_positions_arr[];
| |
int m_tracked_positions_size; // Number of positions being tracked
| |
int m_tracked_positions_r;
| |
| |
int m_indexes_to_remove[]; // Indices to be removed from tracked positions array
| |
int m_indexes_to_remove_s;
| |
int m_indexes_to_remove_r;
| |
| |
//--- Symbol
| |
string m_symbol; // Symbol from which data like min_volume, step, etc. will be obtained
| |
double m_symbol_bid; // Symbol bid
| |
double m_symbol_ask; // Symbol ask
| |
double m_symbol_min_volume; // Minimum volume (opening or closing) for symbol m_symbol
| |
double m_symbol_volume_step; // Volume step for symbol m_symbol
| |
int8_t m_symbol_digits; // Digits of symbol "m_symbol"
| |
| |
//--- Parameters
| |
// Boolean that will indicate to the class if when trades are opened (with the magic number, or magic number = 0), they will be added
| |
// Automatically to the trackable positions array
| |
bool m_auto_add_to_tracked_positions;
| |
CDiff* m_min_distance_to_close_pos; // Pointer to CDiff, this will give the minimum distance between partial closes
| |
ulong m_magic_number; // Magic number
| |
double m_volumes_to_close_arr[]; // Array of percentages, that will close the trades in m_tracked_positions_arr
| |
int m_volume_to_close_size; // Number of partial closes planned for each trade
| |
| |
//--- Min distance
| |
double m_min_distance_value; // Minimum distance between partials (value)
| |
| |
//--- Symbol Functions
| |
void RefreshSymbolData();
| |
| |
//--- Partials closures Functions
| |
virtual void ClosePartialsForBuyPositions();
| |
virtual void ClosePartialsForSellPositions();
| |
| |
| |
public:
| |
CConditionalPartials();
| |
~CConditionalPartials();
| |
| |
//--- Initialization function
| |
bool Init(ConditionalPartialConfig& config);
| |
| |
//--- Add function
| |
virtual void AddPositionToTrack(ENUM_POSITION_TYPE position_type, ulong position_ticket, double open_price);
| |
bool AddPositionToTrack(ulong position_ticket);
| |
| |
//--- Remove
| |
bool RemoveTrackedPosition(ulong position_ticket);
| |
| |
//--- Events functions
| |
void OnOpenPosition(const ROnOpenPosition &pos) override final;
| |
void OnClosePosition(const ROnClosePosition &pos, const int global_pos_index) override final;
| |
| |
//--- Execute function
| |
void Execute(const datetime& current_time); // OnNewBar
| |
| |
//--- Getter and Setters
| |
// Condition
| |
IConditionPartial* PartialCloseCondition() { return m_partial_condition; }
| |
void PartialCloseCondition(IConditionPartial* new_condition, bool delete_prev_ptr);
| |
| |
// Magic number
| |
ulong MagicNumber() const { return m_magic_number; }
| |
void MagicNumber(ulong new_value);
| |
| |
// Minimum distance between partial closures
| |
CDiff* MinDistBetweenPartialClosures() { return m_min_distance_to_close_pos; }
| |
void MinDistBetweenPartialClosures(CDiff* new_value, bool delete_prev_ptr);
| |
| |
// Symbol
| |
string Symbol() { return m_symbol; }
| |
void Symbol(string new_symbol);
| |
| |
// Auto mode
| |
bool IsAutoMode() const { return m_auto_add_to_tracked_positions; }
| |
void AutoMode(bool new_value) { m_auto_add_to_tracked_positions = new_value; }
| |
| |
//--- Tracked positions size
| |
int TrackedPositionsSize() const { return m_tracked_positions_size; }
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| Constructor |
| |
//+------------------------------------------------------------------+
| |
CConditionalPartials::CConditionalPartials()
| |
:
| |
m_partial_condition(NULL), m_symbol(NULL), m_symbol_ask(0.00),
| |
m_symbol_bid(0.00), m_symbol_digits(0), m_symbol_min_volume(0.00), m_symbol_volume_step(0.00),
| |
m_auto_add_to_tracked_positions(true),
| |
m_min_distance_to_close_pos(NULL), m_magic_number(NOT_MAGIC_NUMBER), m_volume_to_close_size(0), m_min_distance_value(0.00),
| |
m_delete_partial_condition_in_dstctor(true),
| |
| |
m_tracked_positions_r(CONDITIONAL_PARTIAL_ARR_MAIN_RESERVE), m_tracked_positions_size(0),
| |
m_indexes_to_remove_r(CONDITIONAL_PARTIAL_ARR_TO_REMOVE_RESERVE), m_indexes_to_remove_s(0)
| |
| |
{
| |
account_status.RegisterEvents(&this, ACCOUNT_STATUS_REG_FLAG_ON_OPEN_POSITION | ACCOUNT_STATUS_REG_FLAG_ON_CLOSE_POSITION);
| |
| |
//---
| |
::ArrayResize(m_tracked_positions_arr, CONDITIONAL_PARTIAL_ARR_MAIN_RESERVE, CONDITIONAL_PARTIAL_ARR_MAIN_RESERVE);
| |
::ArrayResize(m_indexes_to_remove, CONDITIONAL_PARTIAL_ARR_MAIN_RESERVE, CONDITIONAL_PARTIAL_ARR_MAIN_RESERVE);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Destructor |
| |
//+------------------------------------------------------------------+
| |
CConditionalPartials::~CConditionalPartials()
| |
{
| |
//--- Delete of event pool
| |
if(account_status.IsActive())
| |
account_status.UnregisterEvents(&this, ACCOUNT_STATUS_REG_FLAG_ON_OPEN_POSITION | ACCOUNT_STATUS_REG_FLAG_ON_CLOSE_POSITION);
| |
| |
//--- Delete condition
| |
if(m_delete_partial_condition_in_dstctor && CheckPointer(m_partial_condition) == POINTER_DYNAMIC)
| |
{
| |
delete m_partial_condition;
| |
}
| |
| |
//--- Delete diff
| |
if(CheckPointer(m_min_distance_to_close_pos) == POINTER_DYNAMIC)
| |
{
| |
delete m_min_distance_to_close_pos;
| |
}
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Initialization function, the function sets the class parameters |
| |
//| through the "config" variable. |
| |
//| Inputs: |
| |
//| - ConditionalPartialConfig& config: variable that will store the |
| |
//| initial class configuration. |
| |
//| Outputs: |
| |
//| - bool: The function returns true on success, otherwise false. |
| |
//+------------------------------------------------------------------+
| |
bool CConditionalPartials::Init(ConditionalPartialConfig& config)
| |
{
| |
//--- Check parameters are valid
| |
string err = "";
| |
if(!config.IsValid(err))
| |
{
| |
LogError(err, FUNCION_ACTUAL); // Show error
| |
return false;
| |
}
| |
| |
//--- Assignment
| |
m_magic_number = config.magic_number;
| |
m_symbol = config.symbol;
| |
m_min_distance_to_close_pos = config.min_distance_to_close_pos;
| |
m_auto_add_to_tracked_positions = config.auto_mode;
| |
m_trade.SetExpertMagicNumber(m_magic_number);
| |
| |
// Partial condition
| |
m_partial_condition = config.condition;
| |
m_delete_partial_condition_in_dstctor = config.delete_condition;
| |
| |
//--- Symbol refresh
| |
RefreshSymbolData();
| |
| |
//--- Convert str to double array
| |
if(!config.ConvertStrToDoubleArr(',', m_volumes_to_close_arr, err))
| |
{
| |
LogError(err, FUNCION_ACTUAL); // Show error
| |
return false;
| |
}
| |
| |
//--- Convert to "decimal"
| |
int idx_to_remove[];
| |
m_volume_to_close_size = ::ArraySize(m_volumes_to_close_arr);
| |
for(int i = 0; i < m_volume_to_close_size; i++)
| |
{
| |
const double val = m_volumes_to_close_arr[i];
| |
if(val > 100.0)
| |
{
| |
LogWarning(::StringFormat("Found at position %d, value greater than 100.00%%, omitting value", i), FUNCION_ACTUAL);
| |
idx_to_remove.Push(i);
| |
continue;
| |
}
| |
else
| |
if(val < 0.000001)
| |
{
| |
LogWarning(::StringFormat("Found at position %d, value less than 0.000001%%, omitting value", i), FUNCION_ACTUAL);
| |
idx_to_remove.Push(i);
| |
continue;
| |
}
| |
m_volumes_to_close_arr[i] /= 100.00;
| |
}
| |
| |
m_volume_to_close_size = RemoveMultipleIndexes(m_volumes_to_close_arr, idx_to_remove, 0);
| |
| |
//--- Check array size
| |
if(m_volume_to_close_size < 1)
| |
{
| |
LogError("The size of the array of volumes to be closed is less than 1", FUNCION_ACTUAL);
| |
return false;
| |
}
| |
| |
//--- Print array
| |
if(IsInfoLogEnabled())
| |
{
| |
ArrayPrint(m_volumes_to_close_arr, 2, " | ");
| |
}
| |
| |
| |
//--- Finish
| |
return true;
| |
}
| |
| |
//+-------------------------------------------------------------------------------------------+
| |
//| Function that executes the main logic of the class |
| |
//| Inputs: |
| |
//| datetime current_time: Variable of datetime type, which must |
| |
//| store the current time. |
| |
//| Outputs: the function returns nothing. |
| |
//| Notes: |
| |
//| - The function can be called on each tick or each bar, this |
| |
//| will depend on the Execute function of the m_partial_condition variable, |
| |
//| for example if the condition is an indicator, like RSI |
| |
//| the logic could be executed on each candle, or for example if the condition |
| |
//| evaluates a mitigation of an orderblock, in this case it would be executed on each tick |
| |
//| |
| |
//| - The define "CONDITIONAL_PARTIAL_EXECUTE_WHEN_THE_ARE_POSITIONS", |
| |
//| allows conditional code compilation, if this define is defined |
| |
//| the Execute(..) function of the m_partial_condition pointer, will only be called |
| |
//| when there are open positions, otherwise if the define is not defined |
| |
//| it will be called at all times. |
| |
//+-------------------------------------------------------------------------------------------+
| |
//#define CONDITIONAL_PARTIAL_EXECUTE_WHEN_THE_ARE_POSITIONS
| |
| |
//+------------------------------------------------------------------+
| |
void CConditionalPartials::Execute(const datetime& current_time)
| |
{
| |
#ifdef CONDITIONAL_PARTIAL_EXECUTE_WHEN_THE_ARE_POSITIONS
| |
if(m_tracked_positions_size > 0)
| |
{
| |
//--- Execution
| |
m_partial_condition.Execute(current_time);
| |
| |
//--- Close conditions
| |
const bool buy = m_partial_condition.CloseBuy();
| |
const bool sell = m_partial_condition.CloseSell();
| |
| |
//--- Obtain min diff
| |
if(buy || sell)
| |
{
| |
m_min_distance_to_close_pos.OnNewBar();
| |
m_min_distance_value = m_min_distance_to_close_pos.Diff();
| |
| |
//--- Check buy condition
| |
if(buy)
| |
{
| |
m_symbol_bid = ::SymbolInfoDouble(m_symbol, SYMBOL_BID);
| |
ClosePartialsForBuyPositions();
| |
}
| |
| |
//--- Check sell condition
| |
if(sell)
| |
{
| |
m_symbol_ask = ::SymbolInfoDouble(m_symbol, SYMBOL_ASK);
| |
ClosePartialsForSellPositions();
| |
}
| |
}
| |
}
| |
#else
| |
//--- Execution
| |
m_partial_condition.Execute(current_time);
| |
| |
//--- Check conditions
| |
if(m_tracked_positions_size > 0)
| |
{
| |
//--- Close conditions
| |
const bool buy = m_partial_condition.CloseBuy();
| |
const bool sell = m_partial_condition.CloseSell();
| |
| |
//--- Obtain min diff
| |
if(buy || sell)
| |
{
| |
m_min_distance_to_close_pos.OnNewBar();
| |
m_min_distance_value = m_min_distance_to_close_pos.Diff();
| |
| |
//--- Check buy condition
| |
if(buy)
| |
{
| |
m_symbol_bid = ::SymbolInfoDouble(m_symbol, SYMBOL_BID);
| |
ClosePartialsForBuyPositions();
| |
}
| |
| |
//--- Check sell condition
| |
if(sell)
| |
{
| |
m_symbol_ask = ::SymbolInfoDouble(m_symbol, SYMBOL_ASK);
| |
ClosePartialsForSellPositions();
| |
}
| |
}
| |
}
| |
#endif
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Function that executes every time a trade is closed or opened |
| |
//+------------------------------------------------------------------+
| |
void CConditionalPartials::OnOpenPosition(const ROnOpenPosition &pos)
| |
{
| |
if((pos.position.magic == m_magic_number || m_magic_number == NOT_MAGIC_NUMBER) && account_status.LastTransctionSymbol() == m_symbol)
| |
{
| |
LogInfo(::StringFormat("A trade has just been opened with ticket %I64u", pos.position.ticket), FUNCION_ACTUAL);
| |
AddPositionToTrack(pos.position.type, pos.position.ticket, pos.position.open_price);
| |
}
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
void CConditionalPartials::OnClosePosition(const ROnClosePosition &pos, const int global_pos_index)
| |
{
| |
// Once the position is closed we verify if that ticket is in the internal array, if so the function
| |
// will remove it.
| |
RemoveTrackedPosition(pos.position.ticket);
| |
}
| |
| |
//+-------------------------------------------------------------------------+
| |
//| The function creates a new trackable position and adds it |
| |
//| to the internal array. |
| |
//| Inputs: |
| |
//| - ENUM_POSITION_TYPE position_type: position type (buy or sell) |
| |
//| - ulong position_ticket: Position ticket. |
| |
//| - double open_price: Trade opening price. |
| |
//| Outputs: The function returns no value. |
| |
//| Notes: |
| |
//| - The position ticket must be valid, |
| |
//| the function will not check if the ticket exists. |
| |
//+-------------------------------------------------------------------------+
| |
void CConditionalPartials::AddPositionToTrack(ENUM_POSITION_TYPE position_type, ulong position_ticket, double open_price)
| |
{
| |
//---
| |
if(m_tracked_positions_size == 0) // First position to be added to the tracked positions array
| |
m_partial_condition.OnInitPartials(open_price);
| |
| |
//--- Obtain current value
| |
m_min_distance_to_close_pos.OnNewBar();
| |
m_min_distance_value = m_min_distance_to_close_pos.Diff();
| |
| |
//---
| |
if(m_tracked_positions_size >= m_tracked_positions_r)
| |
{
| |
m_tracked_positions_r <<= 1;
| |
ArrayResize(m_tracked_positions_arr, m_tracked_positions_r, m_tracked_positions_r);
| |
}
| |
| |
//--- Creation of a new position to track
| |
m_tracked_positions_arr[m_tracked_positions_size].type = position_type;
| |
m_tracked_positions_arr[m_tracked_positions_size].ticket = position_ticket;
| |
m_tracked_positions_arr[m_tracked_positions_size].next_min_price = position_type == POSITION_TYPE_BUY ?
| |
open_price + m_min_distance_value : open_price - m_min_distance_value;
| |
m_tracked_positions_arr[m_tracked_positions_size++].next_index_to_close = 0;
| |
}
| |
| |
//+-------------------------------------------------------------------------+
| |
//| With a function similar to the AddPositionToTrack(...) function |
| |
//| differing in that this overloaded function only requires the |
| |
//| position ticket (the function internally already queries the |
| |
//| position type, open_price, etc.). |
| |
//| Inputs: |
| |
//| - ulong position_ticket: Position ticket. |
| |
//| Outputs: Returns true if a trackable position could be created with the |
| |
//| ticket, otherwise false |
| |
//| Notes: |
| |
//| - If the position ticket doesn't exist, or is invalid (failure of |
| |
//| PositionSelectByTicket) the function will not create a new trackable |
| |
//| position with the ticket |
| |
//+-------------------------------------------------------------------------+
| |
bool CConditionalPartials::AddPositionToTrack(ulong position_ticket)
| |
{
| |
//--- Select
| |
if(!::PositionSelectByTicket(position_ticket))
| |
{
| |
LogError(::StringFormat("Invalid ticket: %I64u", position_ticket), FUNCION_ACTUAL);
| |
return false;
| |
}
| |
| |
//--- Check symbol
| |
const string sym =::PositionGetString(POSITION_SYMBOL);
| |
if(sym != m_symbol)
| |
{
| |
LogError(::StringFormat("The symbol for position %I64u = %s, does not correspond to that of the class = %s",
| |
position_ticket, sym, m_symbol), FUNCION_ACTUAL);
| |
return false;
| |
}
| |
| |
//--- Add
| |
AddPositionToTrack((ENUM_POSITION_TYPE)::PositionGetInteger(POSITION_TYPE), position_ticket, ::PositionGetDouble(POSITION_PRICE_OPEN));
| |
return true;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Function that iterates over all positions to track (buy) |
| |
//| and partially closes the trade |
| |
//+------------------------------------------------------------------+
| |
void CConditionalPartials::ClosePartialsForBuyPositions(void)
| |
{
| |
//--- Resize to zero
| |
m_indexes_to_remove_s = 0;
| |
| |
//---
| |
MQLARTICLES_CPARTIALES_CHECK_RESIZE_IR
| |
| |
//--- Iteration
| |
for(int i = 0; i < m_tracked_positions_size; i++)
| |
{
| |
if(m_tracked_positions_arr[i].type == POSITION_TYPE_SELL || m_symbol_bid < m_tracked_positions_arr[i].next_min_price)
| |
continue;
| |
| |
if(!::PositionSelectByTicket(m_tracked_positions_arr[i].ticket))
| |
{
| |
// "Delete"
| |
m_indexes_to_remove[m_indexes_to_remove_s++] = i;
| |
| |
#ifdef CONDITIONAL_PARTIAL_MORE_INFO
| |
LogError(::StringFormat("Ticket selection failed %I64u", m_tracked_positions_arr[i].ticket), FUNCION_ACTUAL);
| |
#endif
| |
continue;
| |
}
| |
| |
//---
| |
m_tracked_positions_arr[i].next_min_price += m_min_distance_value;
| |
| |
//--- Initial calculation
| |
const double lot_size = ::PositionGetDouble(POSITION_VOLUME);
| |
double volume_to_close = m_volumes_to_close_arr[m_tracked_positions_arr[i].next_index_to_close] * lot_size;
| |
| |
//--- Check min volume to close
| |
if(volume_to_close < m_symbol_min_volume)
| |
{
| |
//--- Log
| |
LogWarning(::StringFormat("Partial close %d, of position %I64u skipped, volume to close %.*f is less than minimum volume %.*f",
| |
m_tracked_positions_arr[i].next_index_to_close,
| |
m_tracked_positions_arr[i].ticket,
| |
m_symbol_digits,
| |
volume_to_close,
| |
m_symbol_digits,
| |
m_symbol_min_volume), FUNCION_ACTUAL);
| |
| |
//---
| |
m_tracked_positions_arr[i].next_index_to_close++; // Skip this "partial close", advance to next index
| |
if(m_tracked_positions_arr[i].next_index_to_close == m_volume_to_close_size) // Limit reached
| |
{
| |
m_indexes_to_remove[m_indexes_to_remove_s++] = i;
| |
}
| |
continue;
| |
}
| |
| |
//--- Round
| |
volume_to_close = RoundToStep(volume_to_close, m_symbol_volume_step);
| |
| |
#ifdef CONDITIONAL_PARTIAL_MORE_INFO
| |
LogInfo(::StringFormat("Partially closing %.2f lots of position %I64u", volume_to_close, m_tracked_positions_arr[i].ticket), FUNCION_ACTUAL);
| |
#endif
| |
| |
//--- Close
| |
m_trade.PositionClosePartial(m_tracked_positions_arr[i].ticket, volume_to_close);
| |
| |
//--- Next index
| |
m_tracked_positions_arr[i].next_index_to_close++;
| |
| |
//--- Check limit
| |
if(m_tracked_positions_arr[i].next_index_to_close == m_volume_to_close_size) // Limit reached
| |
{
| |
m_indexes_to_remove[m_indexes_to_remove_s++] = i;
| |
}
| |
}
| |
| |
//--- Multiple remove
| |
RemoveMultipleIndexesWithVSizeRemoveCts(m_tracked_positions_arr, m_tracked_positions_size, m_indexes_to_remove,
| |
0, m_indexes_to_remove_s);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Function that iterates over all positions to track (sell) |
| |
//| and partially closes the trade |
| |
//+------------------------------------------------------------------+
| |
void CConditionalPartials::ClosePartialsForSellPositions(void)
| |
{
| |
//--- Resize to zero
| |
m_indexes_to_remove_s = 0;
| |
| |
//---
| |
MQLARTICLES_CPARTIALES_CHECK_RESIZE_IR
| |
| |
//--- Iteration
| |
for(int i = 0; i < m_tracked_positions_size; i++)
| |
{
| |
if(m_tracked_positions_arr[i].type == POSITION_TYPE_BUY || m_symbol_ask > m_tracked_positions_arr[i].next_min_price)
| |
continue;
| |
| |
if(!::PositionSelectByTicket(m_tracked_positions_arr[i].ticket))
| |
{
| |
// "Delete"
| |
m_indexes_to_remove[m_indexes_to_remove_s++] = i;
| |
| |
#ifdef CONDITIONAL_PARTIAL_MORE_INFO
| |
LogError(::StringFormat("Ticket selection failed %I64u", m_tracked_positions_arr[i].ticket), FUNCION_ACTUAL);
| |
#endif
| |
continue;
| |
}
| |
| |
//---
| |
m_tracked_positions_arr[i].next_min_price -= m_min_distance_value;
| |
| |
//--- Initial calculation
| |
const double lot_size = ::PositionGetDouble(POSITION_VOLUME);
| |
double volume_to_close = m_volumes_to_close_arr[m_tracked_positions_arr[i].next_index_to_close] * lot_size;
| |
| |
//--- Check min volume to close
| |
if(volume_to_close < m_symbol_min_volume)
| |
{
| |
//--- Log
| |
LogWarning(::StringFormat("Partial close %d, of position %I64u skipped, volume to close %.*f is less than minimum volume %.*f",
| |
m_tracked_positions_arr[i].next_index_to_close,
| |
m_tracked_positions_arr[i].ticket,
| |
m_symbol_digits,
| |
volume_to_close,
| |
m_symbol_digits,
| |
m_symbol_min_volume), FUNCION_ACTUAL);
| |
| |
//--- Increment
| |
m_tracked_positions_arr[i].next_index_to_close++; // Skip this "partial close", advance to next index
| |
| |
//--- Check limits
| |
if(m_tracked_positions_arr[i].next_index_to_close == m_volume_to_close_size) // Limit reached
| |
{
| |
m_indexes_to_remove[m_indexes_to_remove_s++] = i;
| |
}
| |
continue;
| |
}
| |
| |
//--- Round
| |
volume_to_close = RoundToStep(volume_to_close, m_symbol_volume_step);
| |
| |
#ifdef CONDITIONAL_PARTIAL_MORE_INFO
| |
LogInfo(::StringFormat("Partially closing %.2f lots of position %I64u", volume_to_close, m_tracked_positions_arr[i].ticket), FUNCION_ACTUAL);
| |
#endif
| |
| |
| |
//--- Close
| |
m_trade.PositionClosePartial(m_tracked_positions_arr[i].ticket, volume_to_close);
| |
| |
//--- Next index
| |
m_tracked_positions_arr[i].next_index_to_close++;
| |
| |
//--- Check limit
| |
if(m_tracked_positions_arr[i].next_index_to_close == m_volume_to_close_size) // Limit reached
| |
{
| |
m_indexes_to_remove[m_indexes_to_remove_s++] = i;
| |
}
| |
}
| |
| |
//--- Multiple remove
| |
RemoveMultipleIndexesWithVSizeRemoveCts(m_tracked_positions_arr, m_tracked_positions_size, m_indexes_to_remove,
| |
0, m_indexes_to_remove_s);
| |
}
| |
| |
//+----------------------------------------------------------------------------------+
| |
//| The function sets a new partial close "condition", it changes |
| |
//| the "address" of the m_partial_condition pointer to new_condition, and |
| |
//| if necessary the previous pointer is deleted |
| |
//| Inputs: |
| |
//| - IConditionPartial *new_condition: Pointer to IConditionPartial. |
| |
//| - bool delete_prev_ptr: Boolean indicating if the internal pointer is deleted |
| |
//| Outputs: The function returns nothing. |
| |
//+----------------------------------------------------------------------------------+
| |
void CConditionalPartials::PartialCloseCondition(IConditionPartial *new_condition, bool delete_prev_ptr)
| |
{
| |
//--- Delete prev ptr
| |
if(delete_prev_ptr && ::CheckPointer(m_partial_condition) == POINTER_DYNAMIC)
| |
{
| |
delete m_partial_condition;
| |
}
| |
//---
| |
m_partial_condition = new_condition;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Sets a new internal pointer for the minimum distance |
| |
//| between partials. |
| |
//| Inputs: |
| |
//| - CDiff *new_value: Pointer to CDiff. |
| |
//| - bool delete_prev_ptr: Boolean indicating if the |
| |
//| internal class pointer m_min_distance_to_close_pos is deleted |
| |
//| before assigning it a new "value" (address). |
| |
//| Outputs: The function returns no value |
| |
//+------------------------------------------------------------------+
| |
void CConditionalPartials::MinDistBetweenPartialClosures(CDiff *new_value, bool delete_prev_ptr)
| |
{
| |
//--- Delete prev ptr
| |
if(delete_prev_ptr && ::CheckPointer(m_min_distance_to_close_pos) == POINTER_DYNAMIC)
| |
{
| |
delete m_min_distance_to_close_pos;
| |
}
| |
//---
| |
m_min_distance_to_close_pos = new_value;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Sets a new value for the magic number |
| |
//| Inputs: |
| |
//| - ulong new_value: new magic number value. |
| |
//| Outputs: The function returns no value |
| |
//+------------------------------------------------------------------+
| |
void CConditionalPartials::MagicNumber(ulong new_value)
| |
{
| |
m_magic_number = new_value;
| |
m_trade.SetExpertMagicNumber(new_value);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Sets a new symbol. |
| |
//| Inputs: |
| |
//| - string new_symbol: New symbol. |
| |
//| Outputs: The function returns no value. |
| |
//| Notes: |
| |
//| - The function should only be called when there are no |
| |
//| open positions, to avoid unexpected bugs with |
| |
//| tracked positions from other symbols. |
| |
//+------------------------------------------------------------------+
| |
void CConditionalPartials::Symbol(string new_symbol)
| |
{
| |
m_symbol = new_symbol;
| |
RefreshSymbolData();
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Removes a trackable position from the internal array |
| |
//| Inputs: |
| |
//| ulong position_ticket: Ticket of the trackable position to be |
| |
//| removed |
| |
//| Outputs: The function returns true, if the ticket was |
| |
//| successfully removed, otherwise false (ticket does not |
| |
//| exist). |
| |
//+------------------------------------------------------------------+
| |
bool CConditionalPartials::RemoveTrackedPosition(ulong position_ticket)
| |
{
| |
//---
| |
int index = -1;
| |
for(int i = 0; i < m_tracked_positions_size; i++)
| |
{
| |
if(m_tracked_positions_arr[i].ticket == position_ticket)
| |
{
| |
index = i;
| |
break;
| |
}
| |
}
| |
| |
//---
| |
if(index != -1)
| |
{
| |
m_tracked_positions_size--; // Resize
| |
if(index != m_tracked_positions_size)
| |
{
| |
m_tracked_positions_arr[index] = m_tracked_positions_arr[m_tracked_positions_size];
| |
}
| |
return true; // Success
| |
}
| |
return false; // Error, or position_ticket not exist in array
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| "Re-queries" the values of minimum volume, volume step and |
| |
//| symbol digits. |
| |
//+------------------------------------------------------------------+
| |
void CConditionalPartials::RefreshSymbolData(void)
| |
{
| |
m_symbol_digits = (int8_t)::SymbolInfoInteger(m_symbol, SYMBOL_DIGITS);
| |
m_symbol_min_volume = ::SymbolInfoDouble(m_symbol, SYMBOL_VOLUME_MIN);
| |
m_symbol_volume_step = ::SymbolInfoDouble(m_symbol, SYMBOL_VOLUME_STEP);
| |
}
| |
}
| |
#endif // MQLARTICLES_POSMGMT_CONDPARTIALS_BASE_BASE_MQH
| |
//+------------------------------------------------------------------+
|