257 lines
21 KiB
MQL5
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();
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|