//+------------------------------------------------------------------+ //| Article-13911-MQL5-History-Positions-Profit-Loss-Diagram.mq5 | //| Copyright 2026, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property indicator_separate_window #property indicator_buffers 2 #property indicator_plots 1 //--- plot Fill #property indicator_label1 "Profit;ZeroLine" #property indicator_type1 DRAW_FILLING #property indicator_color1 clrGreen,clrRed #property indicator_style1 STYLE_SOLID #property indicator_width1 1 //--- includes #include //--- enums //--- Режимы сортировки объектов сделок enum ENUM_DEAL_SORT_MODE { DEAL_SORT_MODE_TIME_MSC, // Время совершения сделки в миллисекундах DEAL_SORT_MODE_TIME, // Время совершения сделки DEAL_SORT_MODE_TIKET, // Тикет сделки DEAL_SORT_MODE_POS_ID, // Идентификатор позиции DEAL_SORT_MODE_MAGIC, // Magic number для сделки DEAL_SORT_MODE_TYPE, // Тип сделки DEAL_SORT_MODE_ENTRY, // Направление сделки – вход в рынок, выход из рынка или разворот DEAL_SORT_MODE_VOLUME, // Объем сделки DEAL_SORT_MODE_PRICE, // Цена сделки DEAL_SORT_MODE_COMISSION, // Комиссия по сделке DEAL_SORT_MODE_SWAP, // Накопленный своп при закрытии DEAL_SORT_MODE_PROFIT, // Финансовый результат сделки DEAL_SORT_MODE_FEE, // Оплата за проведение сделки DEAL_SORT_MODE_SYMBOL, // Имя символа, по которому произведена сделка }; //--- Режимы сортировки объектов позиций enum ENUM_POS_SORT_MODE { POS_SORT_MODE_TIME_IN_MSC, // Время открытия в миллисекундах POS_SORT_MODE_TIME_OUT_MSC,// Время закрытия в миллисекундах POS_SORT_MODE_TIME_IN, // Время открытия POS_SORT_MODE_TIME_OUT, // Время закрытия POS_SORT_MODE_DEAL_IN, // Тикет сделки открытия POS_SORT_MODE_DEAL_OUT, // Тикет сделки закрытия POS_SORT_MODE_ID, // Идентификатор позиции POS_SORT_MODE_MAGIC, // Магик позиции POS_SORT_MODE_PRICE_IN, // Цена открытия POS_SORT_MODE_PRICE_OUT, // Цена закрытия POS_SORT_MODE_VOLUME, // Объем позиции POS_SORT_MODE_SYMBOL, // Символ позиции }; //--- classes //+------------------------------------------------------------------+ //| Класс сделки | //+------------------------------------------------------------------+ class CDeal : public CObject { private: long m_ticket; // Тикет сделки long m_magic; // Magic number для сделки long m_position_id; // Идентификатор позиции long m_time_msc; // Время совершения сделки в миллисекундах datetime m_time; // Время совершения сделки ENUM_DEAL_TYPE m_type; // Тип сделки ENUM_DEAL_ENTRY m_entry; // Направление сделки – вход в рынок, выход из рынка или разворот double m_volume; // Объем сделки double m_price; // Цена сделки double m_comission; // Комиссия по сделке double m_swap; // Накопленный своп при закрытии double m_profit; // Финансовый результат сделки double m_fee; // Оплата за проведение сделки string m_symbol; // Имя символа, по которому произведена сделка //--- Возвращает описание направления сделки string EntryDescription(void) const { return(this.m_entry==DEAL_ENTRY_IN ? "Entry In" : this.m_entry==DEAL_ENTRY_OUT ? "Entry Out" : this.m_entry==DEAL_ENTRY_INOUT ? "Reverce" : "Close a position by an opposite one"); } //--- Возвращает описание типа сделки string TypeDescription(void) const { switch(this.m_type) { case DEAL_TYPE_BUY : return "Buy"; case DEAL_TYPE_SELL : return "Sell"; case DEAL_TYPE_BALANCE : return "Balance"; case DEAL_TYPE_CREDIT : return "Credit"; case DEAL_TYPE_CHARGE : return "Additional charge"; case DEAL_TYPE_CORRECTION : return "Correction"; case DEAL_TYPE_BONUS : return "Bonus"; case DEAL_TYPE_COMMISSION : return "Additional commission"; case DEAL_TYPE_COMMISSION_DAILY : return "Daily commission"; case DEAL_TYPE_COMMISSION_MONTHLY : return "Monthly commission"; case DEAL_TYPE_COMMISSION_AGENT_DAILY : return "Daily agent commission"; case DEAL_TYPE_COMMISSION_AGENT_MONTHLY: return "Monthly agent commission"; case DEAL_TYPE_INTEREST : return "Interest rate"; case DEAL_TYPE_BUY_CANCELED : return "Canceled buy deal"; case DEAL_TYPE_SELL_CANCELED : return "Canceled sell deal"; case DEAL_DIVIDEND : return "Dividend operations"; case DEAL_DIVIDEND_FRANKED : return "Franked (non-taxable) dividend operations"; case DEAL_TAX : return "Tax charges"; default : return "Unknown: "+(string)this.m_type; } } //--- Возвращает время с миллисекундами string TimeMSCtoString(const long time_msc,int flags=TIME_DATE|TIME_MINUTES|TIME_SECONDS) { return ::TimeToString(time_msc/1000,flags)+"."+::IntegerToString(time_msc%1000,3,'0'); } public: //--- Методы возврата свойств сделки long Ticket(void) const { return this.m_ticket; } // Тикет сделки long Magic(void) const { return this.m_magic; } // Magic number для сделки long PositionID(void) const { return this.m_position_id; } // Идентификатор позиции long TimeMsc(void) const { return this.m_time_msc; } // Время совершения сделки в миллисекундах datetime Time(void) const { return this.m_time; } // Время совершения сделки ENUM_DEAL_TYPE TypeDeal(void) const { return this.m_type; } // Тип сделки ENUM_DEAL_ENTRY Entry(void) const { return this.m_entry; } // Направление сделки – вход в рынок, выход из рынка или разворот double Volume(void) const { return this.m_volume; } // Объем сделки double Price(void) const { return this.m_price; } // Цена сделки double Comission(void) const { return this.m_comission; } // Комиссия по сделке double Swap(void) const { return this.m_swap; } // Накопленный своп при закрытии double Profit(void) const { return this.m_profit; } // Финансовый результат сделки double Fee(void) const { return this.m_fee; } // Оплата за проведение сделки string Symbol(void) const { return this.m_symbol; } // Имя символа, по которому произведена сделка //--- Методы установки свойств сделки void SetTicket(const long ticket) { this.m_ticket=ticket; } // Тикет сделки void SetMagic(const long magic) { this.m_magic=magic; } // Magic number для сделки void SetPositionID(const long id) { this.m_position_id=id; } // Идентификатор позиции void SetTimeMsc(const long time_msc) { this.m_time_msc=time_msc; } // Время совершения сделки в миллисекундах void SetTime(const datetime time) { this.m_time=time; } // Время совершения сделки void SetType(const ENUM_DEAL_TYPE type) { this.m_type=type; } // Тип сделки void SetEntry(const ENUM_DEAL_ENTRY entry) { this.m_entry=entry; } // Направление сделки – вход в рынок, выход из рынка или разворот void SetVolume(const double volume) { this.m_volume=volume; } // Объем сделки void SetPrice(const double price) { this.m_price=price; } // Цена сделки void SetComission(const double comission) { this.m_comission=comission; } // Комиссия по сделке void SetSwap(const double swap) { this.m_swap=swap; } // Накопленный своп при закрытии void SetProfit(const double profit) { this.m_profit=profit; } // Финансовый результат сделки void SetFee(const double fee) { this.m_fee=fee; } // Оплата за проведение сделки void SetSymbol(const string symbol) { this.m_symbol=symbol; } // Имя символа, по которому произведена сделка //--- Метод сравнения двух объектов virtual int Compare(const CObject *node,const int mode=0) const { const CDeal *compared_obj=node; switch(mode) { case DEAL_SORT_MODE_TIME : return(this.Time()>compared_obj.Time() ? 1 : this.Time()compared_obj.TimeMsc() ? 1 : this.TimeMsc()compared_obj.Ticket() ? 1 : this.Ticket()compared_obj.Magic() ? 1 : this.Magic()compared_obj.PositionID() ? 1 : this.PositionID()compared_obj.TypeDeal() ? 1 : this.TypeDeal()compared_obj.Entry() ? 1 : this.Entry()compared_obj.Volume() ? 1 : this.Volume()compared_obj.Price() ? 1 : this.Price()compared_obj.Comission() ? 1 : this.Comission()compared_obj.Swap() ? 1 : this.Swap()compared_obj.Profit() ? 1 : this.Profit()compared_obj.Fee() ? 1 : this.Fee()compared_obj.Symbol() ? 1 : this.Symbol()compared_obj.TimeMsc() ? 1 : this.TimeMsc()0) return this.m_point*this.m_contract_size/array[0]; //--- Если не удалось получить цену закрытия бара по времени time , возвращаем ноль else return 0; } //--- Проверяем наличие символа с названием "Валюта прибыли символа" + "Валюта счёта" string direct=this.m_currency_profit+this.m_account_currency; //--- Если такой символ существует и добавлен в Обзор рынка if(this.SymbolIsExist(direct)) { //--- Если цена закрытия бара по времени time получена, возвращаем размер контракта * Point символа, умноженные на цену Close бара if(::CopyClose(direct,PERIOD_CURRENT,time,1,array)==1) return this.m_point*this.m_contract_size*array[0]; } //--- Не удалось получить символы, у которых валюта прибыли символа не совпадает с валютой счёта, ни обратный, ни прямой - возвращаем ноль return 0; } public: //--- Методы возврата свойств позиции long ID(void) const { return this.m_position_id; } // Идентификатор позиции long Magic(void) const { return this.m_magic; } // Магик позиции long TimeInMsc(void) const { return this.m_time_in_msc; } // Время открытия long TimeOutMsc(void) const { return this.m_time_out_msc; } // Время закрытия datetime TimeIn(void) const { return this.m_time_in; } // Время открытия datetime TimeOut(void) const { return this.m_time_out; } // Время закрытия ulong DealIn(void) const { return this.m_deal_in_ticket; } // Тикет сделки открытия ulong DealOut(void) const { return this.m_deal_out_ticket; } // Тикет сделки закрытия ENUM_POSITION_TYPE TypePosition(void) const { return this.m_type; } // Тип позиции double PriceIn(void) const { return this.m_price_in; } // Цена открытия double PriceOut(void) const { return this.m_price_out; } // Цена закрытия double Volume(void) const { return this.m_volume; } // Объём позиции string Symbol(void) const { return this.m_symbol; } // Символ позиции //--- Методы установки свойств позиции void SetID(long id) { this.m_position_id=id; } // Идентификатор позиции void SetMagic(long magic) { this.m_magic=magic; } // Магик позиции void SetTimeInMsc(long time_in_msc) { this.m_time_in_msc=time_in_msc; } // Время открытия void SetTimeOutMsc(long time_out_msc) { this.m_time_out_msc=time_out_msc; } // Время закрытия void SetTimeIn(datetime time_in) { this.m_time_in=time_in; } // Время открытия void SetTimeOut(datetime time_out) { this.m_time_out=time_out; } // Время закрытия void SetDealIn(ulong ticket_deal_in) { this.m_deal_in_ticket=ticket_deal_in; } // Тикет сделки открытия void SetDealOut(ulong ticket_deal_out) { this.m_deal_out_ticket=ticket_deal_out; } // Тикет сделки закрытия void SetType(ENUM_POSITION_TYPE type) { this.m_type=type; } // Тип позиции void SetPriceIn(double price_in) { this.m_price_in=price_in; } // Цена открытия void SetPriceOut(double price_out) { this.m_price_out=price_out; } // Цена закрытия void SetVolume(double new_volume) { this.m_volume=new_volume; } // Объём позиции void SetSymbol(string symbol) // Символ позиции { this.m_symbol=symbol; this.m_digits=(int)::SymbolInfoInteger(this.m_symbol,SYMBOL_DIGITS); this.m_point=::SymbolInfoDouble(this.m_symbol,SYMBOL_POINT); this.m_contract_size=::SymbolInfoDouble(this.m_symbol,SYMBOL_TRADE_CONTRACT_SIZE); this.m_currency_profit=::SymbolInfoString(this.m_symbol,SYMBOL_CURRENCY_PROFIT); } //--- Добавляет сделку в список сделок bool DealAdd(CDeal *deal) { //--- Объявляем переменную результата добавления сделки в список bool res=false; //--- Устанавливаем списку флаг сортировки по тикету сделки this.m_list_deals.Sort(DEAL_SORT_MODE_TIKET); //--- Если сделки с таким тикетом нет в списке - if(this.m_list_deals.Search(deal)==WRONG_VALUE) { //--- Устанавливаем списку флаг сортировки по времени в миллисекундах и //--- возвращаем результат добавления сделки в список в порядке сортировки по времени this.m_list_deals.Sort(DEAL_SORT_MODE_TIME_MSC); res=this.m_list_deals.InsertSort(deal); } //--- Если сделка уже есть в списке - возвращаем false else this.m_list_deals.Sort(DEAL_SORT_MODE_TIME_MSC); return res; } //--- Возвращает время начала бара (1) открытия, (2) закрытия позиции на текущем периоде графика datetime BarTimeOpenPosition(void) const { return this.BarOpenTime(PERIOD_CURRENT,this.TimeIn()); } datetime BarTimeClosePosition(void) const { return this.BarOpenTime(PERIOD_CURRENT,this.TimeOut()); } //--- Возвращает флаг существования позиции в указанное время bool IsPresentInTime(const datetime time) const { return(time>=this.BarTimeOpenPosition() && time<=this.BarTimeClosePosition()); } //--- Возвращает профит позиции в количестве пунктов или в стоимости количества пунктов относительно цены закрытия double ProfitRelativeClosePrice(const double price_close,const datetime time,const bool points) const { //--- Если на указанном времени позиции не было - возвращаем 0 if(!this.IsPresentInTime(time)) return 0; //--- Рассчитываем количество пунктов прибыли в зависимости от направления позиции int pp=int((this.TypePosition()==POSITION_TYPE_BUY ? price_close-this.PriceIn() : this.PriceIn()-price_close)/this.m_point); //--- Если прибыль в пунктах - возвращаем рассчитанное количество пунктов if(points) return pp; //--- Иначе - возвращаем рассчитанное количество пунктов, умноженное на (стоимость одного пункта * объём позиции) return pp*this.GetOnePointPrice(time)*this.Volume(); } //--- Метод сравнения двух объектов virtual int Compare(const CObject *node,const int mode=0) const { const CPosition *compared_obj=node; switch(mode) { case POS_SORT_MODE_TIME_IN_MSC : return(this.TimeInMsc()>compared_obj.TimeInMsc() ? 1 : this.TimeInMsc()compared_obj.TimeOutMsc() ? 1 : this.TimeOutMsc()compared_obj.TimeIn() ? 1 : this.TimeIn()compared_obj.TimeOut() ? 1 : this.TimeOut()compared_obj.DealIn() ? 1 : this.DealIn()compared_obj.DealOut() ? 1 : this.DealOut()compared_obj.ID() ? 1 : this.ID()compared_obj.Magic() ? 1 : this.Magic()compared_obj.Symbol() ? 1 : this.Symbol()compared_obj.PriceIn() ? 1 : this.PriceIn()compared_obj.PriceOut() ? 1 : this.PriceOut()compared_obj.Volume() ? 1 : this.Volume()compared_obj.TimeInMsc() ? 1 : this.TimeInMsc()1) { //--- Устанавливаем количество баров для расчёта, равное всей доступной истории и инициализируем буферы "пустыми" значениями limit=rates_total-1; ArrayInitialize(BufferFilling1,EMPTY_VALUE); ArrayInitialize(BufferFilling2,EMPTY_VALUE); } //--- В цикле по барам истории символа for(int i=limit;i>=0;i--) { //--- получаем профит позиций, присутствующих на баре с индексом цикла i и записываем в первый буфер полученное значение double profit=Profit(close[i],time[i]); BufferFilling1[i]=profit; //--- Во второй буфер всегда записываем ноль. В зависимости от того, больше или меньше нуля значение в первом буфере, //--- будет меняться цвет рисуемой заливки между массивами 1 и 2 буфера индикатора BufferFilling2[i]=0; } //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+ //| Возвращает профит всех позиций из списка на указанном времени | //+------------------------------------------------------------------+ double Profit(const double price,const datetime time) { //--- Переменная для записи и возврата результата подсчёта прибыли на баре double res=0; //--- В цикле по списку исторических позиций for(int i=0;i