698 lines
52 KiB
MQL5
698 lines
52 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| 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;
|
|
//+------------------------------------------------------------------+
|
|
//| 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("");
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Custom indicator iteration function |
|
|
//+------------------------------------------------------------------+
|
|
int OnCalculate(const int rates_total,
|
|
const int prev_calculated,
|
|
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[])
|
|
{
|
|
//--- Устанавливаем индексацию массивов 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;
|
|
}
|
|
//--- 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<history.PositionsTotal();i++)
|
|
{
|
|
//--- получаем указатель на очередную позицию
|
|
CPosition *pos=history.GetPositionObjByIndex(i);
|
|
if(pos==NULL)
|
|
continue;
|
|
//--- добавляем к результату значение расчёта прибыли текущей позиции относительно цены price на баре со временем time
|
|
res+=pos.ProfitRelativeClosePrice(price,time,InpProfitPoints);
|
|
}
|
|
//--- Возвращаем рассчитанную сумму прибыли всех позиций относительно цены price на баре со временем time
|
|
return res;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
|