662 lines
58 KiB
MQL5
662 lines
58 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"
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 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
|
|
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_indexes_to_remove[]; // Indices to be removed from tracked positions array
|
|
|
|
//--- 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(void);
|
|
~CConditionalPartials(void);
|
|
|
|
//--- 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 OnOpenClosePosition(const ROnOpenClosePosition &pos) override final;
|
|
void OnNewDay(datetime init_time) override final { m_partial_condition.OnNewDay(); }
|
|
void OnNewWeek(datetime init_time) override final { m_partial_condition.OnNewWeek(); }
|
|
|
|
//--- Execute function
|
|
void Execute(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(void)
|
|
: m_partial_condition(NULL), m_tracked_positions_size(0), 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)
|
|
{
|
|
::ArrayResize(m_tracked_positions_arr, 0, CONDITIONAL_PARTIAL_ARR_MAIN_RESERVE);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
#define CONDITIONAL_PARTIAL_DELETE_PTR_IN_DESTRUCTOR
|
|
CConditionalPartials::~CConditionalPartials()
|
|
{
|
|
//--- Delete condition
|
|
#ifdef CONDITIONAL_PARTIAL_DELETE_PTR_IN_DESTRUCTOR
|
|
if(CheckPointer(m_partial_condition) == POINTER_DYNAMIC)
|
|
{
|
|
delete m_partial_condition;
|
|
}
|
|
#endif
|
|
|
|
//--- 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_partial_condition = config.condition;
|
|
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);
|
|
|
|
//--- 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(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::OnOpenClosePosition(const ROnOpenClosePosition &pos)
|
|
{
|
|
//---
|
|
if(pos.deal_entry_type == DEAL_ENTRY_OUT)
|
|
{
|
|
// Once the position is closed we verify if that ticket is in the internal array, if so the function
|
|
// will remove it.
|
|
if(RemoveIndexFromAnArrayOfPositions(m_tracked_positions_arr, pos.position.ticket, CONDITIONAL_PARTIAL_ARR_MAIN_RESERVE))
|
|
m_tracked_positions_size--; // Change the value of the array size
|
|
}
|
|
else
|
|
if(m_auto_add_to_tracked_positions && pos.deal_entry_type == DEAL_ENTRY_IN
|
|
&& account_status.LastTransctionSymbol() == m_symbol && (pos.position.magic == m_magic_number || m_magic_number == NOT_MAGIC_NUMBER))
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------+
|
|
//| 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();
|
|
|
|
//--- Creation of a new position to track
|
|
ConditionalPartialTrackedPosition new_tracked;
|
|
new_tracked.type = position_type;
|
|
new_tracked.ticket = position_ticket;
|
|
new_tracked.next_min_price = position_type == POSITION_TYPE_BUY ? open_price + m_min_distance_value : open_price - m_min_distance_value;
|
|
new_tracked.next_index_to_close = 0;
|
|
|
|
//--- Add to tracked positions
|
|
::ArrayResize(m_tracked_positions_arr, m_tracked_positions_size + 1, CONDITIONAL_PARTIAL_ARR_MAIN_RESERVE);
|
|
m_tracked_positions_arr[m_tracked_positions_size] = new_tracked;
|
|
m_tracked_positions_size++;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------+
|
|
//| 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
|
|
::ArrayResize(m_indexes_to_remove, 0, CONDITIONAL_PARTIAL_ARR_TO_REMOVE_RESERVE);
|
|
|
|
//--- 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"
|
|
AddArrayNoVerification2(m_indexes_to_remove, i, CONDITIONAL_PARTIAL_ARR_TO_REMOVE_RESERVE)
|
|
|
|
#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
|
|
{
|
|
AddArrayNoVerification2(m_indexes_to_remove, i, CONDITIONAL_PARTIAL_ARR_TO_REMOVE_RESERVE)
|
|
}
|
|
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
|
|
{
|
|
AddArrayNoVerification2(m_indexes_to_remove, i, CONDITIONAL_PARTIAL_ARR_TO_REMOVE_RESERVE)
|
|
}
|
|
}
|
|
|
|
//--- Multiple remove
|
|
m_tracked_positions_size = RemoveMultipleIndexes(m_tracked_positions_arr, m_indexes_to_remove, CONDITIONAL_PARTIAL_ARR_MAIN_RESERVE);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Function that iterates over all positions to track (sell) |
|
|
//| and partially closes the trade |
|
|
//+------------------------------------------------------------------+
|
|
void CConditionalPartials::ClosePartialsForSellPositions(void)
|
|
{
|
|
//--- Resize to zero
|
|
::ArrayResize(m_indexes_to_remove, 0, CONDITIONAL_PARTIAL_ARR_TO_REMOVE_RESERVE);
|
|
|
|
//--- 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"
|
|
AddArrayNoVerification2(m_indexes_to_remove, i, CONDITIONAL_PARTIAL_ARR_TO_REMOVE_RESERVE)
|
|
|
|
#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
|
|
{
|
|
AddArrayNoVerification2(m_indexes_to_remove, i, CONDITIONAL_PARTIAL_ARR_TO_REMOVE_RESERVE)
|
|
}
|
|
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
|
|
{
|
|
AddArrayNoVerification2(m_indexes_to_remove, i, CONDITIONAL_PARTIAL_ARR_TO_REMOVE_RESERVE)
|
|
}
|
|
}
|
|
|
|
//--- Multiple remove
|
|
m_tracked_positions_size = RemoveMultipleIndexes(m_tracked_positions_arr, m_indexes_to_remove, CONDITIONAL_PARTIAL_ARR_MAIN_RESERVE);
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------------+
|
|
//| 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)
|
|
{
|
|
if(RemoveIndexFromAnArrayOfPositions(m_tracked_positions_arr, position_ticket, CONDITIONAL_PARTIAL_ARR_MAIN_RESERVE))
|
|
{
|
|
m_tracked_positions_size--; // Resize
|
|
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
|
|
//+------------------------------------------------------------------+
|