Article-15026-MQL5-Trade-Hi.../PositionsControl.mqh
2026-03-23 12:53:11 +07:00

494 lines
44 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"
//+------------------------------------------------------------------+
//| Класс исторических позиций |
//+------------------------------------------------------------------+
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; i<total; i++)
{
//--- получаем указатель на очередную позицию и
CPosition *pos=this.m_list_pos.At(i);
if(pos==NULL || pos.ID()==pos_id)
continue;
//--- скрываем графическое представление позиции
pos.Hide();
}
//--- После цикла при установленном флаге обновляем график
if(chart_redraw)
::ChartRedraw(this.m_chart_id);
}
//+------------------------------------------------------------------+
//| Центрирует график на текущей выбранной позиции |
//+------------------------------------------------------------------+
void CPositionsControl::CentersChartByCurrentSelected(void)
{
//--- Получаем индекс первого видимого бара на графике и количество видимых баров
int bar_open=0, bar_close=0;
int first_visible=(int)::ChartGetInteger(this.m_chart_id, CHART_FIRST_VISIBLE_BAR);
int visible_bars =(int)::ChartGetInteger(this.m_chart_id, CHART_VISIBLE_BARS);
//--- Получаем время открытия позиции, а по нему - бар открытия
datetime time_open=this.TimeOpenCurrentSelected();
if(time_open!=0)
bar_open=::iBarShift(this.m_symbol, PERIOD_CURRENT, time_open);
//--- Получаем время закрытия позиции, а по нему - бар закрытия
datetime time_close=this.TimeCloseCurrentSelected();
if(time_close!=0)
bar_close=::iBarShift(this.m_symbol, PERIOD_CURRENT, time_close);
//--- Рассчитаем ширину окна, в котором расположены значки сделки
int width=bar_open-bar_close;
//--- Рассчитаем смещение графика так, чтобы окно со сделками находилось по центру графика
int shift=(bar_open + visible_bars/2 - width/2);
//--- Если ширина окна больше ширины графика, то сделка открытия будет располагаться на втором видимом баре
if(shift-bar_open<0)
shift=bar_open+1;
//--- Если бар сделки открытия левее первого видимого бара графика,
//--- или бар сделки открытия правее последнего видимого бара графика -
//--- прокручиваем график на рассчитанное смещение
if(bar_open>first_visible || bar_open<first_visible+visible_bars)
::ChartNavigate(this.m_chart_id, CHART_CURRENT_POS, first_visible-shift);
}
//+------------------------------------------------------------------+
//| Возвращает время открытия текущей выбранной позиции |
//+------------------------------------------------------------------+
datetime CPositionsControl::TimeOpenCurrentSelected(void)
{
CPosition *pos=this.GetPositionObjByID(this.CurrentSelectedID());
return(pos!=NULL ? pos.Time() : 0);
}
//+------------------------------------------------------------------+
//| Возвращает время закрытия текущей выбранной позиции |
//+------------------------------------------------------------------+
datetime CPositionsControl::TimeCloseCurrentSelected(void)
{
CPosition *pos=this.GetPositionObjByID(this.CurrentSelectedID());
return(pos!=NULL ? pos.TimeClose() : 0);
}
//+------------------------------------------------------------------+
//| Возвращает тип позиции по типу сделки |
//+------------------------------------------------------------------+
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;
}
}
//+------------------------------------------------------------------+
//| Создаёт список исторических позиций |
//+------------------------------------------------------------------+
bool CPositionsControl::Refresh(void)
{
//--- Если запросить историю сделок и ордеров не удалось - возвращаем false
if(!::HistorySelect(0,::TimeCurrent()))
return false;
//--- Ставим списку позиций флаг сортировки по времени в миллисекундах
this.m_list_pos.Sort(SORT_MODE_POSITION_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) || ::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<total; i++)
{
CPosition *pos=this.m_list_pos.At(i);
if(pos==NULL)
continue;
pos.Print();
}
}
//+------------------------------------------------------------------+
//| Возвращает описание текущей выбранной позиции |
//+------------------------------------------------------------------+
string CPositionsControl::CurrentSelectedDescription(void)
{
CPosition *pos=this.GetPositionObjByID(this.CurrentSelectedID());
return(pos!=NULL ? pos.Tooltip() : NULL);
}
//+------------------------------------------------------------------+