2026-09-12 19:41:52 -05:00 | | | //+------------------------------------------------------------------+
|
| | | //| 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"
|
| | |
|
2026-09-13 08:47:52 -05:00 | | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | namespace TSN
|
| | | {
|
2026-09-12 19:41:52 -05:00 | | | //+------------------------------------------------------------------+
|
| | | //| 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);
|
| | | }
|
2026-09-13 08:47:52 -05:00 | | | }
|
2026-09-12 19:41:52 -05:00 | | | #endif // MQLARTICLES_POSMGMT_CONDPARTIALS_BASE_BASE_MQH
|
| | | //+------------------------------------------------------------------+
|