MQLArticles/PosMgmt/ConditionalPartial/Base/Base.mqh
Nique_372 b1d782a2b6
2025-11-28 17:21:07 -05:00

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
//+------------------------------------------------------------------+