Article-13911-MQL5-History-.../Article-13911-MQL5-History-Positions-Profit-Loss-Diagram.mq5

698 lines
52 KiB
MQL5
Raw Permalink Normal View History

//+------------------------------------------------------------------+
2026-03-22 00:16:34 +07:00
//| 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 <Arrays\ArrayObj.mqh>
//--- 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.Time() ? -1 : 0);
case DEAL_SORT_MODE_TIME_MSC : return(this.TimeMsc()>compared_obj.TimeMsc() ? 1 : this.TimeMsc()<compared_obj.TimeMsc() ? -1 : 0);
case DEAL_SORT_MODE_TIKET : return(this.Ticket()>compared_obj.Ticket() ? 1 : this.Ticket()<compared_obj.Ticket() ? -1 : 0);
case DEAL_SORT_MODE_MAGIC : return(this.Magic()>compared_obj.Magic() ? 1 : this.Magic()<compared_obj.Magic() ? -1 : 0);
case DEAL_SORT_MODE_POS_ID : return(this.PositionID()>compared_obj.PositionID() ? 1 : this.PositionID()<compared_obj.PositionID() ? -1 : 0);
case DEAL_SORT_MODE_TYPE : return(this.TypeDeal()>compared_obj.TypeDeal() ? 1 : this.TypeDeal()<compared_obj.TypeDeal() ? -1 : 0);
case DEAL_SORT_MODE_ENTRY : return(this.Entry()>compared_obj.Entry() ? 1 : this.Entry()<compared_obj.Entry() ? -1 : 0);
case DEAL_SORT_MODE_VOLUME : return(this.Volume()>compared_obj.Volume() ? 1 : this.Volume()<compared_obj.Volume() ? -1 : 0);
case DEAL_SORT_MODE_PRICE : return(this.Price()>compared_obj.Price() ? 1 : this.Price()<compared_obj.Price() ? -1 : 0);
case DEAL_SORT_MODE_COMISSION : return(this.Comission()>compared_obj.Comission() ? 1 : this.Comission()<compared_obj.Comission() ? -1 : 0);
case DEAL_SORT_MODE_SWAP : return(this.Swap()>compared_obj.Swap() ? 1 : this.Swap()<compared_obj.Swap() ? -1 : 0);
case DEAL_SORT_MODE_PROFIT : return(this.Profit()>compared_obj.Profit() ? 1 : this.Profit()<compared_obj.Profit() ? -1 : 0);
case DEAL_SORT_MODE_FEE : return(this.Fee()>compared_obj.Fee() ? 1 : this.Fee()<compared_obj.Fee() ? -1 : 0);
case DEAL_SORT_MODE_SYMBOL : return(this.Symbol()>compared_obj.Symbol() ? 1 : this.Symbol()<compared_obj.Symbol() ? -1 : 0);
default : return(this.TimeMsc()>compared_obj.TimeMsc() ? 1 : this.TimeMsc()<compared_obj.TimeMsc() ? -1 : 0);
}
}
//--- Распечатывает в журнал свойства сделки
void Print(void)
{
::PrintFormat(" Deal: %s type %s #%lld at %s",this.EntryDescription(),this.TypeDescription(),this.Ticket(),this.TimeMSCtoString(this.TimeMsc()));
}
//--- Конструктор
CDeal(const long deal_ticket)
{
this.m_ticket=deal_ticket;
this.m_magic=::HistoryDealGetInteger(deal_ticket,DEAL_MAGIC); // Magic number для сделки
this.m_position_id=::HistoryDealGetInteger(deal_ticket,DEAL_POSITION_ID); // Идентификатор позиции
this.m_time_msc=::HistoryDealGetInteger(deal_ticket,DEAL_TIME_MSC); // Время совершения сделки в миллисекундах
this.m_time=(datetime)::HistoryDealGetInteger(deal_ticket,DEAL_TIME); // Время совершения сделки
this.m_type=(ENUM_DEAL_TYPE)::HistoryDealGetInteger(deal_ticket,DEAL_TYPE); // Тип сделки
this.m_entry=(ENUM_DEAL_ENTRY)::HistoryDealGetInteger(deal_ticket,DEAL_ENTRY); // Направление сделки – вход в рынок, выход из рынка или разворот
this.m_volume=::HistoryDealGetDouble(deal_ticket,DEAL_VOLUME); // Объем сделки
this.m_price=::HistoryDealGetDouble(deal_ticket,DEAL_PRICE); // Цена сделки
this.m_comission=::HistoryDealGetDouble(deal_ticket,DEAL_COMMISSION); // Комиссия по сделке
this.m_swap=::HistoryDealGetDouble(deal_ticket,DEAL_SWAP); // Накопленный своп при закрытии
this.m_profit=::HistoryDealGetDouble(deal_ticket,DEAL_PROFIT); // Финансовый результат сделки
this.m_fee=::HistoryDealGetDouble(deal_ticket,DEAL_FEE); // Оплата за проведение сделки
this.m_symbol=::HistoryDealGetString(deal_ticket,DEAL_SYMBOL); // Имя символа, по которому произведена сделка
}
};
//+------------------------------------------------------------------+
//| Класс позиции |
//+------------------------------------------------------------------+
class CPosition : public CObject
{
private:
CArrayObj m_list_deals; // Список сделок позиции
long m_position_id; // Идентификатор позиции
long m_time_in_msc; // Время открытия в миллисекундах
long m_time_out_msc; // Время закрытия в миллисекундах
long m_magic; // Магик позиции
datetime m_time_in; // Время открытия
datetime m_time_out; // Время закрытия
ulong m_deal_in_ticket; // Тикет сделки открытия
ulong m_deal_out_ticket; // Тикет сделки закрытия
double m_price_in; // Цена открытия
double m_price_out; // Цена закрытия
double m_volume; // Объём позиции
ENUM_POSITION_TYPE m_type; // Тип позиции
string m_symbol; // Символ позиции
int m_digits; // Digits символа
double m_point; // Значение одного пункта символа
double m_contract_size; // Размер торгового контракта символа
string m_currency_profit; // Валюта прибыли символа
string m_account_currency; // Валюта депозита
//--- Возвращает время с миллисекундами
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');
}
//--- Рассчитывает и возвращает время открытия заведомо существующего бара на указанном периоде графика по заданному времени
//--- (https://www.mql5.com/ru/forum/170952/page234#comment_50523898)
datetime BarOpenTime(const ENUM_TIMEFRAMES timeframe,const datetime time) const
{
ENUM_TIMEFRAMES period=(timeframe==PERIOD_CURRENT ? ::Period() : timeframe);
//--- Расчёт времени открытия бара на периодах, меньших W1
if(period<PERIOD_W1)
return time-time%::PeriodSeconds(period);
//--- Расчёт времени открытия бара на периоде W1
if(period==PERIOD_W1)
return time-(time+4*24*60*60)%::PeriodSeconds(period);
//--- Расчёт времени открытия бара на периоде MN1
else
{
MqlDateTime dt;
::ResetLastError();
if(!::TimeToStruct(time,dt))
{
::PrintFormat("%s: TimeToStruct failed. Error %lu",__FUNCTION__,::GetLastError());
return 0;
}
return time-(time%(24*60*60))-(dt.day-1)*(24*60*60);
}
}
//--- Возвращает флаг наличия символа на сервере. Добавляет символ в окно MarketWatch
bool SymbolIsExist(const string symbol) const
{
bool custom=false;
if(!::SymbolExist(symbol,custom))
return false;
return ::SymbolSelect(symbol,true);
}
//--- Возвращает стоимость одного пункта
double GetOnePointPrice(const datetime time) const
{
if(time==0)
return 0;
//--- Если валюта прибыли символа совпадает с валютой счёта, возвращаем размер контракта * Point символа
if(this.m_currency_profit==this.m_account_currency)
return this.m_point*this.m_contract_size;
//--- Иначе проверяем наличие символа с названием "Валюта счёта" + "Валюта прибыли символа"
double array[1];
string reverse=this.m_account_currency+this.m_currency_profit;
//--- Если такой символ существует и добавлен в Обзор рынка
if(this.SymbolIsExist(reverse))
{
//--- Если цена закрытия бара по времени time получена, возвращаем размер контракта * Point символа, делённые на цену Close бара
if(::CopyClose(reverse,PERIOD_CURRENT,time,1,array)==1 && array[0]>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.TimeInMsc() ? -1 : 0);
case POS_SORT_MODE_TIME_OUT_MSC : return(this.TimeOutMsc()>compared_obj.TimeOutMsc() ? 1 : this.TimeOutMsc()<compared_obj.TimeOutMsc() ? -1 : 0);
case POS_SORT_MODE_TIME_IN : return(this.TimeIn()>compared_obj.TimeIn() ? 1 : this.TimeIn()<compared_obj.TimeIn() ? -1 : 0);
case POS_SORT_MODE_TIME_OUT : return(this.TimeOut()>compared_obj.TimeOut() ? 1 : this.TimeOut()<compared_obj.TimeOut() ? -1 : 0);
case POS_SORT_MODE_DEAL_IN : return(this.DealIn()>compared_obj.DealIn() ? 1 : this.DealIn()<compared_obj.DealIn() ? -1 : 0);
case POS_SORT_MODE_DEAL_OUT : return(this.DealOut()>compared_obj.DealOut() ? 1 : this.DealOut()<compared_obj.DealOut() ? -1 : 0);
case POS_SORT_MODE_ID : return(this.ID()>compared_obj.ID() ? 1 : this.ID()<compared_obj.ID() ? -1 : 0);
case POS_SORT_MODE_MAGIC : return(this.Magic()>compared_obj.Magic() ? 1 : this.Magic()<compared_obj.Magic() ? -1 : 0);
case POS_SORT_MODE_SYMBOL : return(this.Symbol()>compared_obj.Symbol() ? 1 : this.Symbol()<compared_obj.Symbol() ? -1 : 0);
case POS_SORT_MODE_PRICE_IN : return(this.PriceIn()>compared_obj.PriceIn() ? 1 : this.PriceIn()<compared_obj.PriceIn() ? -1 : 0);
case POS_SORT_MODE_PRICE_OUT : return(this.PriceOut()>compared_obj.PriceOut() ? 1 : this.PriceOut()<compared_obj.PriceOut() ? -1 : 0);
case POS_SORT_MODE_VOLUME : return(this.Volume()>compared_obj.Volume() ? 1 : this.Volume()<compared_obj.Volume() ? -1 : 0);
default : return(this.TimeInMsc()>compared_obj.TimeInMsc() ? 1 : this.TimeInMsc()<compared_obj.TimeInMsc() ? -1 : 0);
}
}
//--- Возвращает описание типа позиции
string TypeDescription(void) const
{
return(this.m_type==POSITION_TYPE_BUY ? "Buy" : this.m_type==POSITION_TYPE_SELL ? "Sell" : "Unknown");
}
//--- Распечатывает в журнале свойства позиции и её сделок
void Print(void)
{
//--- Выводим заголовок с описанием позиции
::PrintFormat
(
"Position %s %s #%lld, Magic %lld\n-Opened at %s at a price of %.*f\n-Closed at %s at a price of %.*f:",
this.TypeDescription(),this.Symbol(),this.ID(),this.Magic(),
this.TimeMSCtoString(this.TimeInMsc()), this.m_digits,this.PriceIn(),
this.TimeMSCtoString(this.TimeOutMsc()),this.m_digits,this.PriceOut()
);
//--- В цикле по всем сделкам позиции выводим их описания
for(int i=0;i<this.m_list_deals.Total();i++)
{
CDeal *deal=this.m_list_deals.At(i);
if(deal==NULL)
continue;
deal.Print();
}
}
//--- Конструктор
CPosition(const long position_id) : m_time_in(0), m_time_in_msc(0),m_time_out(0),m_time_out_msc(0),m_deal_in_ticket(0),m_deal_out_ticket(0),m_type(WRONG_VALUE)
{
this.m_list_deals.Sort(DEAL_SORT_MODE_TIME_MSC);
this.m_position_id=position_id;
this.m_account_currency=::AccountInfoString(ACCOUNT_CURRENCY);
}
};
//+------------------------------------------------------------------+
//| Класс списка исторических позиций |
//+------------------------------------------------------------------+
class CHistoryPosition
{
private:
CArrayObj m_list_pos; // Список исторических позиций
public:
//--- Создаёт список исторических позиций
bool CreatePositionList(const string symbol=NULL);
//--- Возвращает объект-позицию из списка по (1) индексу, (2) идентификатору
CPosition *GetPositionObjByIndex(const int index)
{
return this.m_list_pos.At(index);
}
CPosition *GetPositionObjByID(const long id)
{
//--- Создаём временный объект позиции
CPosition *tmp=new CPosition(id);
//--- Устанавливаем списку позиций сортировку по идентификатору позиции
this.m_list_pos.Sort(POS_SORT_MODE_ID);
//--- Получаем индекс объекта в списке позиций с таким идентификатором
int index=this.m_list_pos.Search(tmp);
//--- Удаляем временный объект и устанавливаем списку флаг сортировки по времени в миллисекундах.
delete tmp;
this.m_list_pos.Sort(POS_SORT_MODE_TIME_IN_MSC);
//--- Возвращаем указатель на объект в списке по полученному индексу, либо NULL, если индекс не получен
return this.m_list_pos.At(index);
}
//--- Добавляет сделку в список сделок
bool DealAdd(const long position_id,CDeal *deal)
{
CPosition *pos=this.GetPositionObjByID(position_id);
return(pos!=NULL ? pos.DealAdd(deal) : false);
}
//--- Возвращает флаг нахождения указанной позиции в указанном времени
bool IsPresentInTime(CPosition *pos,const datetime time) const
{
return pos.IsPresentInTime(time);
}
//--- Возвращает профит позиции относительно цены закрытия
double ProfitRelativeClosePrice(CPosition *pos,const double price_close,const datetime time,const bool points) const
{
return pos.ProfitRelativeClosePrice(price_close,time,points);
}
//--- Возвращает количество исторических позиций
int PositionsTotal(void) const { return this.m_list_pos.Total(); }
//--- Распечатывает в журнале свойства позиций и их сделок
void Print(void)
{
//--- В цикле по списку исторических позиций
for(int i=0;i<this.m_list_pos.Total();i++)
{
//--- получаем указатель на объект позиции и распечатываем свойства позиции и её сделок
CPosition *pos=this.m_list_pos.At(i);
if(pos==NULL)
continue;
pos.Print();
}
}
//--- Конструктор
CHistoryPosition(void)
{
this.m_list_pos.Sort(POS_SORT_MODE_TIME_IN_MSC);
}
};
//+------------------------------------------------------------------+
//| CHistoryPosition::Создаёт список исторических позиций |
//+------------------------------------------------------------------+
bool CHistoryPosition::CreatePositionList(const string symbol=NULL)
{
//--- Если запросить историю сделок и ордеров не удалось - возвращаем false
if(!::HistorySelect(0,::TimeCurrent()))
return false;
//--- Объявляем переменную результата и указатель на объект позиции
bool res=true;
CPosition *pos=NULL;
//--- В цикле по количеству сделок истории
int total=::HistoryDealsTotal();
for(int i=0;i<total;i++)
{
//--- получаем тикет очередной сделки в списке
ulong ticket=::HistoryDealGetTicket(i);
//--- Если тикет сделки не получен, или это балансовая операция, или если указан стмвол сделки, но сделка не по этому символу - идём дальше
if(ticket==0 || ::HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_BALANCE || (symbol!=NULL && symbol!="" && ::HistoryDealGetString(ticket,DEAL_SYMBOL)!=symbol))
continue;
//--- Создаём объект-сделку и, если объект создать не удалось - добавляем к переменной res значение false и идём далее
CDeal *deal=new CDeal(ticket);
if(deal==NULL)
{
res &=false;
continue;
}
//--- Получаем из сделки значение идентификатора позиции
long pos_id=deal.PositionID();
//--- Получаем указатель на объект-позицию из списка
pos=this.GetPositionObjByID(pos_id);
//--- Если такой позиции в списке ещё нет
if(pos==NULL)
{
//--- создаём новый объект позиции и, если объект создать не удалось, - добавляем к переменной res значение false, удаляем объект сделки и идём далее
pos=new CPosition(pos_id);
if(pos==NULL)
{
res &=false;
delete deal;
continue;
}
//--- Ставим списку позиций флаг сортировки по времени в миллисекундах и добавляем объект позиции в соответствующее место списка
this.m_list_pos.Sort(POS_SORT_MODE_TIME_IN_MSC);
//--- Если объект позиции не удалось добавить в список - добавляем к переменной res значение false, удаляем объекты сделки и позиции и идём далее
if(!this.m_list_pos.InsertSort(pos))
{
res &=false;
delete deal;
delete pos;
continue;
}
}
//--- Если объект позиции создан и добавлен в список
//--- Если объект сделки не удалось добавить в список сделок объекта позиции - добавляем к переменной res значение false, удаляем объект сделки и идём далее
if(!pos.DealAdd(deal))
{
res &=false;
delete deal;
continue;
}
//--- Всё успешно.
//--- В зависимости от типа сделки устанавливаем свойства позиции
if(deal.Entry()==DEAL_ENTRY_IN)
{
pos.SetSymbol(deal.Symbol());
pos.SetDealIn(deal.Ticket());
pos.SetTimeIn(deal.Time());
pos.SetTimeInMsc(deal.TimeMsc());
ENUM_POSITION_TYPE type=ENUM_POSITION_TYPE(deal.TypeDeal()==DEAL_TYPE_BUY ? POSITION_TYPE_BUY : deal.TypeDeal()==DEAL_TYPE_SELL ? POSITION_TYPE_SELL : WRONG_VALUE);
pos.SetType(type);
pos.SetPriceIn(deal.Price());
pos.SetVolume(deal.Volume());
}
if(deal.Entry()==DEAL_ENTRY_OUT || deal.Entry()==DEAL_ENTRY_OUT_BY)
{
pos.SetDealOut(deal.Ticket());
pos.SetTimeOut(deal.Time());
pos.SetTimeOutMsc(deal.TimeMsc());
pos.SetPriceOut(deal.Price());
}
if(deal.Entry()==DEAL_ENTRY_INOUT)
{
ENUM_POSITION_TYPE type=ENUM_POSITION_TYPE(deal.TypeDeal()==DEAL_TYPE_BUY ? POSITION_TYPE_BUY : deal.TypeDeal()==DEAL_TYPE_SELL ? POSITION_TYPE_SELL : WRONG_VALUE);
pos.SetType(type);
pos.SetVolume(deal.Volume()-pos.Volume());
}
}
//--- Возвращаем результат создания и добавления позиции в список
return res;
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Индикатор |
//+------------------------------------------------------------------+
//--- input parameters
input bool InpProfitPoints = true; // Profit in Points
//--- indicator buffers
double BufferFilling1[];
double BufferFilling2[];
//--- global variables
CHistoryPosition *history=NULL;
2026-03-22 00:16:34 +07:00
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- indicator buffers mapping
SetIndexBuffer(0,BufferFilling1,INDICATOR_DATA);
SetIndexBuffer(1,BufferFilling2,INDICATOR_DATA);
//--- Устанавливаем индексацию массивов буферов как в таймсерии
ArraySetAsSeries(BufferFilling1,true);
ArraySetAsSeries(BufferFilling2,true);
//--- Устанавливаем Digits данных индикатора, равный 2 и один уровень, равный 0
IndicatorSetInteger(INDICATOR_DIGITS,2);
IndicatorSetInteger(INDICATOR_LEVELS,1);
IndicatorSetDouble(INDICATOR_LEVELVALUE,0);
//--- Создаём новый объект исторических данных
history=new CHistoryPosition();
//--- Возвращаем результат создания объекта исторических данных
return(history!=NULL ? INIT_SUCCEEDED : INIT_FAILED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- Удаляем объект исторических позиций и комментарии на графике
if(history!=NULL)
delete history;
Comment("");
2026-03-22 00:16:34 +07:00
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
2026-03-22 00:16:34 +07:00
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
2026-03-22 00:16:34 +07:00
{
//--- Устанавливаем индексацию массивов close и time как в таймсерии
ArraySetAsSeries(close,true);
ArraySetAsSeries(time,true);
//--- Флаг успешного создания списка позиций
static bool done=false;
//--- Если объект данных позиций создан
if(history!=NULL)
{
//--- Если ещё не создавался список позиций
if(!done)
{
//--- Если список позиций по текущему инструменту успешно создан,
if(history.CreatePositionList(Symbol()))
{
//--- распечатываем в журнале позиции и устанавливаем флаг успешного создания списка позиций
history.Print();
done=true;
}
}
}
//--- Количество баров, необходимое для расчёта индикатора
int limit=rates_total-prev_calculated;
//--- Если limit больше 1 - значит это первый запуск или изменение в исторических данных
if(limit>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;
}
2026-03-22 00:16:34 +07:00
//--- return value of prev_calculated for next call
return(rates_total);
}
//+------------------------------------------------------------------+
//| Возвращает профит всех позиций из списка на указанном времени |
2026-03-22 00:16:34 +07:00
//+------------------------------------------------------------------+
double Profit(const double price,const datetime time)
2026-03-22 00:16:34 +07:00
{
//--- Переменная для записи и возврата результата подсчёта прибыли на баре
double res=0;
//--- В цикле по списку исторических позиций
for(int i=0;i<history.PositionsTotal();i++)
{
//--- получаем указатель на очередную позицию
CPosition *pos=history.GetPositionObjByIndex(i);
if(pos==NULL)
continue;
//--- добавляем к результату значение расчёта прибыли текущей позиции относительно цены price на баре со временем time
res+=pos.ProfitRelativeClosePrice(price,time,InpProfitPoints);
}
//--- Возвращаем рассчитанную сумму прибыли всех позиций относительно цены price на баре со временем time
return res;
2026-03-22 00:16:34 +07:00
}
//+------------------------------------------------------------------+