Article-15346-MQL5-Trade-Mo.../PositionsControl.mqh
2026-03-23 13:19:06 +07:00

257 lines
21 KiB
MQL5

//+------------------------------------------------------------------+
//| PositionsControl.mqh |
//| Copyright 2024, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
#include "Position.mqh"
#include "Select.mqh"
//+------------------------------------------------------------------+
//| Класс-коллекция исторических позиций |
//+------------------------------------------------------------------+
class CPositionsControl : public CObject
{
private:
//--- Возвращает (1) тип позиции, (2) причину открытия по типу сделки
ENUM_POSITION_TYPE PositionTypeByDeal(const CDeal *deal);
ENUM_POSITION_REASON PositionReasonByDeal(const CDeal *deal);
protected:
CPosition m_temp_pos; // Временный объект позиции для поиска
CArrayObj m_list_pos; // Список позиций
//--- Возвращает объект-позицию из списка по идентификатору
CPosition *GetPositionObjByID(const long id);
//--- Возвращает флаг того, что позиция рыночная
bool IsMarketPosition(const long id);
public:
//--- Создаёт и обновляет список позиций. Может быть переопределён в наследуемых классах
virtual bool Refresh(void);
//--- Возвращает (1) список, (2) количество позиций в списке
CArrayObj *GetPositionsList(void) { return &this.m_list_pos; }
int PositionsTotal(void) const { return this.m_list_pos.Total(); }
//--- Распечатывает в журнале свойства всех позиций в списке и их сделок
void Print(void);
//--- Конструктор/деструктор
CPositionsControl(void);
~CPositionsControl();
};
//+------------------------------------------------------------------+
//| Конструктор |
//+------------------------------------------------------------------+
CPositionsControl::CPositionsControl(void)
{
this.m_list_pos.Sort(POSITION_PROP_TIME_CLOSE_MSC);
}
//+------------------------------------------------------------------+
//| Деструктор |
//+------------------------------------------------------------------+
CPositionsControl::~CPositionsControl()
{
this.m_list_pos.Shutdown();
}
//+------------------------------------------------------------------+
//| Возвращает объект-позицию из списка по идентификатору |
//+------------------------------------------------------------------+
CPosition *CPositionsControl::GetPositionObjByID(const long id)
{
//--- Устанавливаем временному объекту идентификатор позиции, а списку - флаг сортировки по идентификатору позиции
this.m_temp_pos.SetID(id);
this.m_list_pos.Sort(POSITION_PROP_IDENTIFIER);
//--- Получаем из списка индекс объекта-позиции с указанным идентификатором (либо -1 при его отсутствии)
//--- По полученному индексу получаем указатель на объект позицию из списка (либо NULL при значении индекса -1)
int index=this.m_list_pos.Search(&this.m_temp_pos);
CPosition *pos=this.m_list_pos.At(index);
//--- Возвращаем списку флаг сортировки по времени закрытия позиции в миллисекундах и
//--- возвращаем указатель на объект-позицию (либо NULL при его отсутствии)
this.m_list_pos.Sort(POSITION_PROP_TIME_CLOSE_MSC);
return pos;
}
//+------------------------------------------------------------------+
//| Возвращает флаг того, что позиция рыночная |
//+------------------------------------------------------------------+
bool CPositionsControl::IsMarketPosition(const long id)
{
//--- В цикле по списку действующих позиций в терминале
for(int i=::PositionsTotal()-1; i>=0; i--)
{
//--- получаем тикет позиции по индексу цикла
ulong ticket=::PositionGetTicket(i);
//--- Если тикет получен и позицию можно выбрать и её идентификатор равен переданному в метод -
//--- это искомая рыночная позиция, возвращаем true
if(ticket!=0 && ::PositionSelectByTicket(ticket) && ::PositionGetInteger(POSITION_IDENTIFIER)==id)
return true;
}
//--- Нет такой рыночной позиции - возвращаем false
return false;
}
//+------------------------------------------------------------------+
//| Возвращает тип позиции по типу сделки |
//+------------------------------------------------------------------+
ENUM_POSITION_TYPE CPositionsControl::PositionTypeByDeal(const CDeal *deal)
{
if(deal==NULL)
return WRONG_VALUE;
switch(deal.TypeDeal())
{
case DEAL_TYPE_BUY : return POSITION_TYPE_BUY;
case DEAL_TYPE_SELL : return POSITION_TYPE_SELL;
default : return WRONG_VALUE;
}
}
//+------------------------------------------------------------------+
//| Возвращает причину открытия позиции по типу сделки |
//+------------------------------------------------------------------+
ENUM_POSITION_REASON CPositionsControl::PositionReasonByDeal(const CDeal *deal)
{
if(deal==NULL)
return WRONG_VALUE;
switch(deal.Reason())
{
case DEAL_REASON_CLIENT : return POSITION_REASON_CLIENT;
case DEAL_REASON_MOBILE : return POSITION_REASON_MOBILE;
case DEAL_REASON_WEB : return POSITION_REASON_WEB;
case DEAL_REASON_EXPERT : return POSITION_REASON_EXPERT;
default : return WRONG_VALUE;
}
}
//+------------------------------------------------------------------+
//| Создаёт список исторических позиций |
//+------------------------------------------------------------------+
bool CPositionsControl::Refresh(void)
{
//--- Если запросить историю сделок и ордеров не удалось - возвращаем false
if(!::HistorySelect(0,::TimeCurrent()))
return false;
//--- Ставим списку позиций флаг сортировки по времени в миллисекундах
this.m_list_pos.Sort(POSITION_PROP_TIME_MSC);
//--- Объявляем переменную результата и указатель на объект позиции
bool res=true;
CPosition *pos=NULL;
//--- В цикле по количеству сделок истории
int total=::HistoryDealsTotal();
for(int i=total-1; i>=0; i--)
{
//--- получаем тикет очередной сделки в списке
ulong ticket=::HistoryDealGetTicket(i);
//--- Если тикет сделки не получен, или это не операция покупки/продажи - идём дальше
ENUM_DEAL_TYPE deal_type=(ENUM_DEAL_TYPE)::HistoryDealGetInteger(ticket, DEAL_TYPE);
if(ticket==0 || (deal_type!=DEAL_TYPE_BUY && deal_type!=DEAL_TYPE_SELL))
continue;
//--- Получаем из сделки значение идентификатора позиции
long pos_id=::HistoryDealGetInteger(ticket, DEAL_POSITION_ID);
//--- Если это рыночная позиция - идём далее
if(this.IsMarketPosition(pos_id))
continue;
//--- Получаем указатель на объект-позицию из списка
pos=this.GetPositionObjByID(pos_id);
//--- Если позиции с таким идентификатором в списке ещё нет
if(pos==NULL)
{
//--- Создаём новый объект позиции и, если объект создать не удалось, добавляем к переменной res значение false и идём далее
string pos_symbol=HistoryDealGetString(ticket, DEAL_SYMBOL);
pos=new CPosition(pos_id, pos_symbol);
if(pos==NULL)
{
res &=false;
continue;
}
//--- Если объект позиции не удалось добавить в список - добавляем к переменной res значение false, удаляем объект позиции и идём далее
if(!this.m_list_pos.InsertSort(pos))
{
res &=false;
delete pos;
continue;
}
}
//--- Если объект сделки не удалось добавить в список сделок объекта позиции - добавляем к переменной res значение false и идём далее
CDeal *deal=pos.DealAdd(ticket);
if(deal==NULL)
{
res &=false;
continue;
}
//--- Всё успешно.
//--- В зависимости от типа сделки устанавливаем свойства позиции
if(deal.Entry()==DEAL_ENTRY_IN)
{
pos.SetTicket(deal.Order());
pos.SetMagic(deal.Magic());
pos.SetTime(deal.Time());
pos.SetTimeMsc(deal.TimeMsc());
ENUM_POSITION_TYPE type=this.PositionTypeByDeal(deal);
pos.SetTypePosition(type);
ENUM_POSITION_REASON reason=this.PositionReasonByDeal(deal);
pos.SetReason(reason);
pos.SetPriceOpen(deal.Price());
pos.SetVolume(deal.Volume());
}
if(deal.Entry()==DEAL_ENTRY_OUT || deal.Entry()==DEAL_ENTRY_OUT_BY)
{
pos.SetPriceCurrent(deal.Price());
pos.SetPriceClose(deal.Price());
pos.SetTimeClose(deal.Time());
pos.SetTimeCloseMsc(deal.TimeMsc());
}
if(deal.Entry()==DEAL_ENTRY_INOUT)
{
ENUM_POSITION_TYPE type=this.PositionTypeByDeal(deal);
pos.SetTypePosition(type);
pos.SetVolume(deal.Volume()-pos.Volume());
}
}
//--- Все исторические позиции созданы, а соответствующие сделки добавлены в списки сделок объектов-позиций
//--- Устанавливаем списку позиций флаг сортировки по времени закрытия в миллисекундах
this.m_list_pos.Sort(POSITION_PROP_TIME_CLOSE_MSC);
//--- В цикле по созданному списку закрытых позиций устанавливаем каждой позиции значения Commissions и Fee
for(int i=0; i<this.m_list_pos.Total(); i++)
{
CPosition *pos=this.m_list_pos.At(i);
if(pos==NULL)
continue;
pos.SetCommissions();
pos.SetFee();
}
//--- Возвращаем результат создания и добавления позиции в список
return res;
}
//+------------------------------------------------------------------+
//| Распечатывает в журнале свойства позиций и их сделок |
//+------------------------------------------------------------------+
void CPositionsControl::Print(void)
{
int total=this.m_list_pos.Total();
for(int i=0; i<total; i++)
{
CPosition *pos=this.m_list_pos.At(i);
if(pos==NULL)
continue;
pos.Print();
}
}
//+------------------------------------------------------------------+