2026-03-22 00:39:05 +07:00
//+------------------------------------------------------------------+
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 |
//+------------------------------------------------------------------+
2026-03-22 00:39:05 +07:00
# 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
2026-03-22 00:39:05 +07:00
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 |
//+------------------------------------------------------------------+
2026-03-22 00:39:05 +07:00
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 [ ] ,
2026-03-22 00:39:05 +07:00
const int & spread [ ] )
2026-03-22 00:16:34 +07:00
{
2026-03-22 00:39:05 +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:39:05 +07:00
//| Возвращает профит всех позиций из списка на указанном времени |
2026-03-22 00:16:34 +07:00
//+------------------------------------------------------------------+
2026-03-22 00:39:05 +07:00
double Profit ( const double price , const datetime time )
2026-03-22 00:16:34 +07:00
{
2026-03-22 00:39:05 +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
}
//+------------------------------------------------------------------+
2026-03-22 00:39:05 +07:00