//+------------------------------------------------------------------+ //| 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" //+------------------------------------------------------------------+ //| Класс исторических позиций | //+------------------------------------------------------------------+ class CPositionsControl : public CObject { private: string m_symbol; // Инструмент, по которому открыта позиция long m_current_id; // Идентификатор текущей отображённой на графике позиции bool m_key_ctrl; // Флаг разрешение на управление графиком с помощью клавиатуры //--- Возвращает тип позиции по типу сделки ENUM_POSITION_TYPE PositionTypeByDeal(const CDeal *deal); protected: CPosition m_temp_pos; // Временный объект позиции для поиска CArrayObj m_list_pos; // Список позиций long m_chart_id; // Идентификатор графика //--- Возвращает объект-позицию из списка по идентификатору CPosition *GetPositionObjByID(const long id); //--- Возвращает флаг того, что позиция рыночная bool IsMarketPosition(const long id); //--- Возвращает указатель на (1) первую, (2) последнюю закрытую позицию в списке CPosition *GetFirstClosedPosition(void); CPosition *GetLastClosedPosition(void); //--- Возвращает указатель на (1) предыдущую, (2) следующую закрытую позицию в списке CPosition *GetPrevClosedPosition(CPosition *current); CPosition *GetNextClosedPosition(CPosition *current); //--- Отображает на графике графическое представление указанной позиции void Show(CPosition *pos, const bool chart_redraw=false); //--- Центрирует график на текущей выбранной позиции void CentersChartByCurrentSelected(void); //--- Возвращает идентификатор текущей выбранной позиции long CurrentSelectedID(void) const { return this.m_current_id; } //--- Возвращает время (1) открытия, (2) закрытия текущей выбранной позиции datetime TimeOpenCurrentSelected(void); datetime TimeCloseCurrentSelected(void); //--- Скрывает на графике графическое представление всех позиций, кроме указанной void HideAllExceptOne(const long pos_id, const bool chart_redraw=false); public: //--- Возвращает (1) символ, (2) идентификатор графика string Symbol(void) const { return this.m_symbol; } long ChartID(void) const { return this.m_chart_id; } //--- Создаёт и обновляет список позиций. Может быть переопределён в наследуемых классах virtual bool Refresh(void); //--- Возвращает количество позиций в списке int Total(void) const { return this.m_list_pos.Total(); } //--- (1) Скрывает, (2) отображает на графике графическое представление первой позиции void HideFirst(const bool chart_redraw=false); void ShowFirst(const bool chart_redraw=false); //--- (1) Скрывает, (2) отображает на графике графическое представление последней позиции void HideLast(const bool chart_redraw=false); void ShowLast(const bool chart_redraw=false); //--- Отображает на графике графическое представление (1) текущей, (2) предыдущей, (3) следующей позиции void ShowCurrent(const bool chart_redraw=false); void ShowPrev(const bool chart_redraw=false); void ShowNext(const bool chart_redraw=false); //--- Возвращает описание текущей выбранной позиции string CurrentSelectedDescription(void); //--- Распечатывает в журнале свойства всех позиций в списке и их сделок void Print(void); //--- Конструктор/деструктор CPositionsControl(const string symbol=NULL); ~CPositionsControl(); }; //+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ CPositionsControl::CPositionsControl(const string symbol=NULL) { this.m_list_pos.Sort(SORT_MODE_POSITION_TIME_CLOSE_MSC); this.m_symbol = (symbol==NULL ? ::Symbol() : symbol); this.m_chart_id = ::ChartID(); this.m_current_id = 0; this.m_key_ctrl = ::ChartGetInteger(this.m_chart_id, CHART_KEYBOARD_CONTROL); } //+------------------------------------------------------------------+ //| Деструктор | //+------------------------------------------------------------------+ CPositionsControl::~CPositionsControl() { this.m_list_pos.Shutdown(); ::ChartSetInteger(this.m_chart_id, CHART_KEYBOARD_CONTROL, this.m_key_ctrl); } //+------------------------------------------------------------------+ //| Возвращает объект-позицию из списка по идентификатору | //+------------------------------------------------------------------+ CPosition *CPositionsControl::GetPositionObjByID(const long id) { //--- Устанавливаем временному объекту идентификатор позиции, а списку - флаг сортировки по идентификатору позиции this.m_temp_pos.SetID(id); this.m_list_pos.Sort(SORT_MODE_POSITION_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(SORT_MODE_POSITION_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; } //+------------------------------------------------------------------+ //| Возвращает указатель на первую закрытую позицию в списке | //+------------------------------------------------------------------+ CPosition *CPositionsControl::GetFirstClosedPosition(void) { this.m_list_pos.Sort(SORT_MODE_POSITION_TIME_CLOSE_MSC); return this.m_list_pos.At(0); } //+------------------------------------------------------------------+ //| Возвращает указатель на последнюю закрытую позицию в списке | //+------------------------------------------------------------------+ CPosition *CPositionsControl::GetLastClosedPosition(void) { this.m_list_pos.Sort(SORT_MODE_POSITION_TIME_CLOSE_MSC); return this.m_list_pos.At(this.m_list_pos.Total()-1); } //+------------------------------------------------------------------+ //| Возвращает указатель на предыдущую закрытую позицию в списке | //+------------------------------------------------------------------+ CPosition *CPositionsControl::GetPrevClosedPosition(CPosition *current) { this.m_list_pos.Sort(SORT_MODE_POSITION_TIME_CLOSE_MSC); int prev=this.m_list_pos.SearchLess(current); return this.m_list_pos.At(prev); } //+------------------------------------------------------------------+ //| Возвращает указатель на следующую закрытую позицию в списке | //+------------------------------------------------------------------+ CPosition *CPositionsControl::GetNextClosedPosition(CPosition *current) { this.m_list_pos.Sort(SORT_MODE_POSITION_TIME_CLOSE_MSC); int next=this.m_list_pos.SearchGreat(current); return this.m_list_pos.At(next); } //+------------------------------------------------------------------+ //| Выводит на график графическое представление указанной позиции | //+------------------------------------------------------------------+ void CPositionsControl::Show(CPosition *pos,const bool chart_redraw=false) { if(pos!=NULL) { pos.Show(chart_redraw); this.m_current_id=pos.ID(); } } //+------------------------------------------------------------------+ //| Скрывает с графика графическое представление первой позиции | //+------------------------------------------------------------------+ void CPositionsControl::HideFirst(const bool chart_redraw=false) { CPosition *pos=this.GetFirstClosedPosition(); if(pos!=NULL) pos.Hide(chart_redraw); } //+------------------------------------------------------------------+ //| Отображает на графике графическое представление первой позиции | //+------------------------------------------------------------------+ void CPositionsControl::ShowFirst(const bool chart_redraw=false) { //--- Получаем указатель на первую закрытую позицию CPosition *pos=this.GetFirstClosedPosition(); if(pos==NULL) return; //--- Скрываем значки всех позиций, кроме первой по её идентификатору this.HideAllExceptOne(pos.ID()); //--- Отображаем значки первой позиции на графике и //--- центрируем график по значкам текущей выбранной позиции this.Show(pos,chart_redraw); this.CentersChartByCurrentSelected(); } //+------------------------------------------------------------------+ //| Скрывает с графика графическое представление последней позиции | //+------------------------------------------------------------------+ void CPositionsControl::HideLast(const bool chart_redraw=false) { CPosition *pos=this.GetLastClosedPosition(); if(pos!=NULL) pos.Hide(chart_redraw); } //+------------------------------------------------------------------+ //| Отображает на графике графическое представление последней позиции| //+------------------------------------------------------------------+ void CPositionsControl::ShowLast(const bool chart_redraw=false) { //--- Получаем указатель на последнюю закрытую позицию CPosition *pos=this.GetLastClosedPosition(); if(pos==NULL) return; //--- Скрываем значки всех позиций, кроме последней по её идентификатору this.HideAllExceptOne(pos.ID(), false); //--- Отображаем значки последней позиции на графике и //--- центрируем график по значкам текущей выбранной позиции this.Show(pos,chart_redraw); this.CentersChartByCurrentSelected(); } //+------------------------------------------------------------------+ //| Отображает на графике графическое представление текущей позиции | //+------------------------------------------------------------------+ void CPositionsControl::ShowCurrent(const bool chart_redraw=false) { //--- Получаем указатель на текущую выбранную закрытую позицию CPosition *curr=this.GetPositionObjByID(this.CurrentSelectedID()); if(curr==NULL) return; //--- Отображаем значки текущей позиции на графике и //--- центрируем график по значкам текущей выбранной позиции this.Show(curr,chart_redraw); this.CentersChartByCurrentSelected(); } //+------------------------------------------------------------------+ //|Отображает на графике графическое представление предыдущей позиции| //+------------------------------------------------------------------+ void CPositionsControl::ShowPrev(const bool chart_redraw=false) { //--- Получаем указатель на текущую и предыдущую позиции CPosition *curr=this.GetPositionObjByID(this.CurrentSelectedID()); CPosition *prev=this.GetPrevClosedPosition(curr); if(curr==NULL || prev==NULL) return; //--- Текущую позицию скрываем, предыдущую отображаем и //--- центрируем график по значкам текущей выбранной позиции curr.Hide(); this.Show(prev,chart_redraw); this.CentersChartByCurrentSelected(); } //+------------------------------------------------------------------+ //| Отображает на графике графическое представление следующей позиции| //+------------------------------------------------------------------+ void CPositionsControl::ShowNext(const bool chart_redraw=false) { //--- Получаем указатель на текущую и следующую позиции CPosition *curr=this.GetPositionObjByID(this.CurrentSelectedID()); CPosition *next=this.GetNextClosedPosition(curr); if(curr==NULL || next==NULL) return; //--- Текущую позицию скрываем, следующую отображаем и //--- центрируем график по значкам текущей выбранной позиции curr.Hide(); this.Show(next,chart_redraw); this.CentersChartByCurrentSelected(); } //+------------------------------------------------------------------+ //| Скрывает с графика графическое представление | //| всех позиций, кроме указанной | //+------------------------------------------------------------------+ void CPositionsControl::HideAllExceptOne(const long pos_id,const bool chart_redraw=false) { //--- В цикле по списку позиций int total=this.m_list_pos.Total(); for(int i=0; ifirst_visible || bar_open=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) || ::HistoryDealGetString(ticket, DEAL_SYMBOL)!=this.m_symbol) 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 и идём далее pos=new CPosition(pos_id, this.m_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.SetTime(deal.Time()); pos.SetTimeMsc(deal.TimeMsc()); ENUM_POSITION_TYPE type=this.PositionTypeByDeal(deal); pos.SetTypePosition(type); pos.SetPriceOpen(deal.Price()); pos.SetVolume(deal.Volume()); } if(deal.Entry()==DEAL_ENTRY_OUT || deal.Entry()==DEAL_ENTRY_OUT_BY) { pos.SetPriceCurrent(deal.Price()); } 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(SORT_MODE_POSITION_TIME_CLOSE_MSC); //--- Возвращаем результат создания и добавления позиции в список return res; } //+------------------------------------------------------------------+ //| Распечатывает в журнале свойства позиций и их сделок | //+------------------------------------------------------------------+ void CPositionsControl::Print(void) { int total=this.m_list_pos.Total(); for(int i=0; i