Видится неплохой вариант в использовании HistorySelectByPosition - запрос истории сделок и ордеров, имеющих указанный идентификатор позиции.
Скрипт "HistorySelectByPosition.mql5" - это изменённый "HistoryDealGetTicket.mq5". Изменение: при помощи HistorySelectByPosition создаём список ордеров и список сделок с указанным идентификатором позиции. Дальше работа уже не отличается.
Пример работы скрипта "HistorySelectByPosition.mql5" - в качестве параметра был задан идентификатор первой позиции (из первого поста в этой ветке) - 132873637:
HistorySelectByPosition (EURUSD,M30) 1: deal #116488540 at 2017.02.07 12:56:38 Закрытие встречной позицией, sell 0.01 EURUSD (order #132873727, position ID 132873637)
HistorySelectByPosition (EURUSD,M30) 2: deal #116488549 at 2017.02.07 12:56:42 Выход из рынка, sell 0.02 EURUSD (order #132873736, position ID 132873637)
и второй запуск - с идентификатором второй позиции - 132873695:
HistorySelectByPosition (EURUSD,M30) 1: deal #116488539 at 2017.02.07 12:56:38 Закрытие встречной позицией, buy 0.01 EURUSD (order #132873727, position ID 132873695)
Функция HistorySelectByPosition хорошо справляется с задачей отбора сделок по идентификатору позиции.
...
Если без closeBy, то совсем просто. В общем же случае надо изучать реализацию closeBy в MT5 и сопоставлять ее с MT4, т.к. в четверке этот момент за многие годы был отточен до идеала.
У меня руки так и не дошли, т.к. возни много, а используется closeBy редко. Правда, разработчики чуть ли не вынуждают использовать closeBy, потому как у MT5-позиций нет TP в виде лимитника, как это практикуется в MT4.
Наверное стоит предусмотреть такую ситуацию, когда запрос истории сделок и ордеров с указанным идентификатором позиции может оказаться неполным. Например:
- ошибки в истории (история была свёрнута брокером)
- или позиция ещё открыта в терминале
- для указанного идентификатора есть, как минимум, сделка DEAL_ENTRY_IN (перечисление ENUM_DEAL_ENTRY) - то есть первоначальная сделка - вход в рынок
- должна быть ещё и сделка DEAL_ENTRY_OUT и/или DEAL_ENTRY_OUT_BY - то есть должен обязательно быть выход
- суммарный объём ("+" DEAL_ENTRY_IN "-" DEAL_ENTRY_OUT "-" DEAL_ENTRY_OUT_BY) сделок должен быть равен нулю
Скрипт "HistorySelectByPosition.mql5" - Версия 1.001 - проверка целостности позиции.
Итог работы:
0: deal #116488443 at 2017.02.07 12:55:56 Вход в рынок, buy 0.03 EURUSD (order #132873637, position ID 132873637)
1: deal #116488540 at 2017.02.07 12:56:38 Закрытие встречной позицией, sell 0.01 EURUSD (order #132873727, position ID 132873637)
2: deal #116488549 at 2017.02.07 12:56:42 Выход из рынка, sell 0.02 EURUSD (order #132873736, position ID 132873637)
position ID 132873637 - целостность подтверждена
position ID 132873695:
0: deal #116488502 at 2017.02.07 12:56:22 Вход в рынок, sell 0.01 EURUSD (order #132873695, position ID 132873695)
1: deal #116488539 at 2017.02.07 12:56:38 Закрытие встречной позицией, buy 0.01 EURUSD (order #132873727, position ID 132873695)
position ID 132873695 - целостность подтверждена
Видно нужно теперь оба скрипта доработать и поместить в один класс. Например в "CHistoryPositionInfo".
Так как позиция состоит из нескольких сделок, имеет смысл в классе "CHistoryPositionInfo" объявить класс для хранения сделок - "CMyDeal". В классе "CMyDeal" будут такие поля (исходя из задачи в названии этой ветки):
//| Класс - сделка |
//+------------------------------------------------------------------+
class CMyDeal : public CObject
{
private:
long m_deal_entry; // направление сделки – вход в рынок, выход из рынка или разворот
double m_deal_price; // цена сделки
double m_deal_volume; // объём сделки
long m_deal_id; // идентификатор позиции, в открытии, изменении или закрытии которой участвовала эта сделка
double m_deal_profit; // финансовый результат сделки
public:
CMyDeal(void);
~CMyDeal(void);
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CMyDeal::CMyDeal(void) : m_deal_entry(0),
m_deal_price(0.0),
m_deal_volume(0.0),
m_deal_id(0),
m_deal_profit(0.0)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CMyDeal::~CMyDeal(void)
{
}
Если без closeBy, то совсем просто. В общем же случае надо изучать реализацию closeBy в MT5 и сопоставлять ее с MT4, т.к. в четверке этот момент за многие годы был отточен до идеала.
У меня руки так и не дошли, т.к. возни много, а используется closeBy редко. Правда, разработчики чуть ли не вынуждают использовать closeBy, потому как у MT5-позиций нет TP в виде лимитника, как это практикуется в MT4.
Видно нужно теперь оба скрипта доработать и поместить в один класс. Например в "CHistoryPositionInfo".
Так как позиция состоит из нескольких сделок, имеет смысл в классе "CHistoryPositionInfo" объявить класс для хранения сделок - "CMyDeal". В классе "CMyDeal" будут такие поля (исходя из задачи в названии этой ветки):
//| Класс - сделка |
//+------------------------------------------------------------------+
class CMyDeal : public CObject
{
private:
long m_deal_entry; // направление сделки – вход в рынок, выход из рынка или разворот
double m_deal_price; // цена сделки
double m_deal_volume; // объём сделки
long m_deal_id; // идентификатор позиции, в открытии, изменении или закрытии которой участвовала эта сделка
double m_deal_profit; // финансовый результат сделки
public:
CMyDeal(void);
~CMyDeal(void);
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CMyDeal::CMyDeal(void) : m_deal_entry(0),
m_deal_price(0.0),
m_deal_volume(0.0),
m_deal_id(0),
m_deal_profit(0.0)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CMyDeal::~CMyDeal(void)
{
}

- 2015.03.23
- Vasiliy Sokolov
- www.mql5.com
Я очень широко использую для закрытия множества локированных ордеров. Все же получается немалая экономия на спреде. Надо будет в КБ скрипт выложить для такого закрытия..
Класс "HistoryPositionInfo.mqh" и проверочный скрипт "TestHistoryPositionInfo.mq5" должны лежать в одной папке.
На данный момент класс "HistoryPositionInfo.mqh" проверяет целостность и принтует все сделки относящиеся к искомой позиции - это больше для проверки работы сделано. Остаётся доделать класс для расчёта прибыли в пунктах...
Класс "HistoryPositionInfo.mqh":
//| HistoryPositionInfo.mqh |
//| Copyright © 2017, Vladimir Karputov |
//| http://wmua.ru/slesar/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2017, Vladimir Karputov"
#property link "http://wmua.ru/slesar/"
#property version "1.000"
#include <Arrays\ArrayObj.mqh>
#property description "Информация о позициях из истории сделок"
//+------------------------------------------------------------------+
//| Класс - сделка |
//+------------------------------------------------------------------+
class CMyDeal : public CObject
{
private:
long m_deal_entry; // направление сделки – вход в рынок, выход из рынка или разворот
double m_deal_price; // цена сделки
double m_deal_volume; // объём сделки
double m_deal_profit; // финансовый результат сделки
public:
CMyDeal(void);
~CMyDeal(void);
//--- методы доступа к защищённым данным
void Entry_deal(const long value) { m_deal_entry=value; }
long Entry_deal(void) const { return(m_deal_entry); }
void Price_deal(const double value) { m_deal_price=value; }
double Price_deal(void) const { return(m_deal_price); }
void Volume_deal(const double value) { m_deal_volume=value; }
double Volume_deal(void) const { return(m_deal_volume); }
void Profit_deal(const double value) { m_deal_profit=value; }
double Profit_deal(void) const { return(m_deal_profit); }
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CMyDeal::CMyDeal(void) : m_deal_entry(0),
m_deal_price(0.0),
m_deal_volume(0.0),
m_deal_profit(0.0)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CMyDeal::~CMyDeal(void)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CHistoryPositionInfo : public CArrayObj
{
private:
long m_position_id; // идентификатор позиции
CArrayObj m_arr_deals; // объкт динамического массива указателей на объекты класса CMyDeal
public:
CHistoryPositionInfo();
~CHistoryPositionInfo();
//--- метод инициализации
void Init(const long value) { m_position_id=value; }
//--- метод получения прибыли в пунктах
bool GetProfitInPoints(const double &profit_in_points);
protected:
//--- проверка целостности позиции
bool IsIntegrity(void);
//--- сравнение двух чисел
bool CompareDoubles(double number1,double number2);
//--- распечатка сделок, которые входят в позицию
void PrintDeals(void);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CHistoryPositionInfo::CHistoryPositionInfo() : m_position_id(0)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CHistoryPositionInfo::~CHistoryPositionInfo()
{
}
//+------------------------------------------------------------------+
//| Расчёт прибыли в пунктах |
//+------------------------------------------------------------------+
bool CHistoryPositionInfo::GetProfitInPoints(const double &profit_in_points)
{
bool result=true;
if(!IsIntegrity())
return(false);
//--- тестирование: распечатаем все позиции
PrintDeals();
//---
return(result);
}
//+------------------------------------------------------------------+
//| Проверка целостности позиции |
//+------------------------------------------------------------------+
bool CHistoryPositionInfo::IsIntegrity(void)
{
bool result=true;
//--- request trade history
if(!HistorySelectByPosition(m_position_id))
{
Print("Error HistorySelectByPosition");
return(false);
}
uint total=HistoryDealsTotal();
//--- for all deals
int count_IN = 0;
double volume_IN = 0.0;
int count_OUT = 0;
double volume_OUT = 0.0;
for(uint i=0;i<total;i++)
{
ulong deal_ticket = HistoryDealGetTicket(i);
long deal_entry = HistoryDealGetInteger(deal_ticket,DEAL_ENTRY);
double deal_price = HistoryDealGetDouble(deal_ticket,DEAL_PRICE);
double deal_volume = HistoryDealGetDouble(deal_ticket,DEAL_VOLUME);
double deal_profit = HistoryDealGetDouble(deal_ticket,DEAL_PROFIT);
//--- объявим указатель на объект класса CMyDeal и присвоим ему вновь созданный объект класса CMyDeal
CMyDeal *my_deal=new CMyDeal;
if(my_deal==NULL)
{
//--- ошибка создания
Print(__FUNCTION__,". Object create error (\"CMyDeal\")");
return(false);
}
my_deal.Entry_deal(deal_entry);
my_deal.Price_deal(deal_price);
my_deal.Volume_deal(deal_volume);
my_deal.Profit_deal(deal_profit);
Add(my_deal);
//---
if(deal_entry==DEAL_ENTRY_IN)
{
count_IN++;
volume_IN+=deal_volume;
}
else if(deal_entry==DEAL_ENTRY_OUT || deal_entry==DEAL_ENTRY_OUT_BY)
{
count_OUT++;
volume_OUT+=deal_volume;
}
}
//--- проверка целостности
if(count_IN==1 && count_OUT>0) // первая проверка - обязательно должна быть одна IN и не менее одной OUT
{
if(!CompareDoubles(volume_IN-volume_OUT,0.0)) // вторая проверка - суммарный объём IN минус OUT должен быть равен нулю
{
Print("Вторая проверка не пройдена. IN=",DoubleToString(volume_IN,2),
", OUT=",DoubleToString(volume_OUT,2));
return(false);
}
}
else
{
Print("Первая проверка не пройдена. IN=",count_IN,", OUT=",count_OUT);
return(false);
}
//---
return(result);
}
//+------------------------------------------------------------------+
//| Сравнение двух чисел на равенство |
//+------------------------------------------------------------------+
bool CHistoryPositionInfo::CompareDoubles(double number1,double number2)
{
if(NormalizeDouble(number1-number2,8)==0) return(true);
else return(false);
}
//+------------------------------------------------------------------+
//| Распечатка сделок, которые входят в позицию |
//+------------------------------------------------------------------+
void CHistoryPositionInfo::PrintDeals(void)
{
for(int i=0;i<Total();i++)
{
//--- объявим указатель на объект класса CMyDeal и присвоим ему элемент из указанной позиции массива
CMyDeal *my_deal=At(i);
if(my_deal==NULL)
{
//--- ошибка чтения из массива
Print(__FUNCTION__,". Get element error (\"CMyDeal\")");
return;
}
Print(EnumToString((ENUM_DEAL_ENTRY)my_deal.Entry_deal()),
", price ",DoubleToString(my_deal.Price_deal(),Digits()),
", Deal ",Symbol(),
", volume ",DoubleToString(my_deal.Volume_deal(),2),
", profit ",DoubleToString(my_deal.Profit_deal(),2));
}
}
//+------------------------------------------------------------------+
и проверочный скрипт "TestHistoryPositionInfo.mq5":
//| TestHistoryPositionInfo.mq5 |
//| Copyright © 2017, Vladimir Karputov |
//| http://wmua.ru/slesar/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2017, Vladimir Karputov"
#property link "http://wmua.ru/slesar/"
#property version "1.00"
#property script_show_inputs
//---
input long position_id=0; // идентификатор позиции - POSITION_IDENTIFIER
#include "HistoryPositionInfo.mqh"
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
//---
Print("Поиск сделок с POSITION_IDENTIFIER ",IntegerToString(position_id));
CHistoryPositionInfo HistoryPositionInfo;
HistoryPositionInfo.Init(position_id);
double profit_in_points=0.0;
if(HistoryPositionInfo.GetProfitInPoints(profit_in_points))
Print("GetProfitInPoints=true");
else
Print("GetProfitInPoints=false");
}
//+------------------------------------------------------------------+
Результаты, когда целостность позиции подтверждена:
2017.02.08 08:09:17.692 TestHistoryPositionInfo (EURUSD,M30) DEAL_ENTRY_IN, price 1.06719, Deal EURUSD, volume 0.01, profit 0.00
2017.02.08 08:09:17.692 TestHistoryPositionInfo (EURUSD,M30) DEAL_ENTRY_OUT_BY, price 1.06725, Deal EURUSD, volume 0.01, profit -0.06
2017.02.08 08:09:17.692 TestHistoryPositionInfo (EURUSD,M30) GetProfitInPoints=true
и вот ситуация, когда позиция открыта и ещё не закрыта:
2017.02.08 08:10:19.052 TestHistoryPositionInfo (EURUSD,M30) Первая проверка не пройдена. IN=1, OUT=0
2017.02.08 08:10:19.052 TestHistoryPositionInfo (EURUSD,M30) GetProfitInPoints=false
Для определения прибыльности в пунктах нужна дополнительная информация:
тип (DEAL_TYPE_BUY или DEAL_TYPE_SELL) сделки IN - то есть банально нужно знать, это была позиция входа в Buy или в Sell.
Для этого добавим переменную в защищённую область класса и методы для работы с этой переменной
{
private:
long m_deal_entry; // направление сделки – вход в рынок, выход из рынка или разворот
long m_deal_type; // тип сделки
double m_deal_price; // цена сделки
double m_deal_volume; // объём сделки
double m_deal_profit; // финансовый результат сделки
public:
CMyDeal(void);
~CMyDeal(void);
//--- методы доступа к защищённым данным
void Entry_deal(const long value) { m_deal_entry=value; }
long Entry_deal(void) const { return(m_deal_entry); }
void Type_deal(const long value) { m_deal_type=value; }
long Type_deal(void) const { return(m_deal_type); }
void Price_deal(const double value) { m_deal_price=value; }
double Price_deal(void) const { return(m_deal_price); }
Изменение в блок определения целостности (по совместительству этот же блок наполняет нашу позицию сделками)
long deal_entry = HistoryDealGetInteger(deal_ticket,DEAL_ENTRY);
long deal_type = HistoryDealGetInteger(deal_ticket,DEAL_TYPE);
double deal_price = HistoryDealGetDouble(deal_ticket,DEAL_PRICE);
double deal_volume = HistoryDealGetDouble(deal_ticket,DEAL_VOLUME);
double deal_profit = HistoryDealGetDouble(deal_ticket,DEAL_PROFIT);
//--- объявим указатель на объект класса CMyDeal и присвоим ему вновь созданный объект класса CMyDeal
CMyDeal *my_deal=new CMyDeal;
if(my_deal==NULL)
{
//--- ошибка создания
Print(__FUNCTION__,". Object create error (\"CMyDeal\")");
return(false);
}
my_deal.Entry_deal(deal_entry);
my_deal.Type_deal(deal_type);
my_deal.Price_deal(deal_price);
my_deal.Volume_deal(deal_volume);
my_deal.Profit_deal(deal_profit);
Add(my_deal);
Также внесём изменение в GetProfitInPoints():
double price_IN = 0.0;
double price_OUT= 0.0;
long type_IN=-1;
for(int i=0;i<Total();i++)
{
//--- объявим указатель на объект класса CMyDeal и присвоим ему элемент из указанной позиции массива
CMyDeal *my_deal=At(i);
if(my_deal==NULL)
{
//--- ошибка чтения из массива
Print(__FUNCTION__,". Get element error (\"CMyDeal\")");
return(false);
}
if(my_deal.Entry_deal()==DEAL_ENTRY_IN)
{
price_IN=my_deal.Price_deal();
type_IN=my_deal.Type_deal();
}
price_OUT=my_deal.Price_deal();
}
if(type_IN==DEAL_TYPE_BUY)
profit_in_points=price_OUT-price_IN;
else if(type_IN==DEAL_TYPE_SELL)
profit_in_points=price_IN-price_OUT;
else
return(false);
//---
return(result);
}
а также в блок вывода вспомогательной информации PrintDeals:
", ",EnumToString((ENUM_DEAL_TYPE)my_deal.Type_deal()),
", price ",DoubleToString(my_deal.Price_deal(),Digits()),
", Deal ",Symbol(),
", volume ",DoubleToString(my_deal.Volume_deal(),2),
", profit ",DoubleToString(my_deal.Profit_deal(),2));
В итоге получаем, для наших двух проверочных позиции такой вывод информации:
Поиск сделок с POSITION_IDENTIFIER 132873695
DEAL_ENTRY_IN, DEAL_TYPE_SELL, price 1.06719, Deal EURUSD, volume 0.01, profit 0.00
DEAL_ENTRY_OUT_BY, DEAL_TYPE_BUY, price 1.06725, Deal EURUSD, volume 0.01, profit -0.06
Профит в пунктах равен -0.00006000
Поиск сделок с POSITION_IDENTIFIER 132873637
DEAL_ENTRY_IN, DEAL_TYPE_BUY, price 1.06725, Deal EURUSD, volume 0.03, profit 0.00
DEAL_ENTRY_OUT_BY, DEAL_TYPE_SELL, price 1.06719, Deal EURUSD, volume 0.01, profit 0.00
DEAL_ENTRY_OUT, DEAL_TYPE_SELL, price 1.06720, Deal EURUSD, volume 0.02, profit -0.10
Профит в пунктах равен -0.00005000
...

- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Условности: рассматривается только hedge-торговые счета.
Стоит задача по торговой истории в терминале определить, сколько пунктов прибыли/убытка у полностью закрытых позиции?
Пока вариант получения списка торговых операций на определённый период времени - скрипт "HistoryDealGetTicket.mq5".
Для примера были такие торговые операции:
Скрипт "HistoryDealGetTicket.mq5" даёт такую распечатку:
HistoryDealGetTicket (EURUSD,M30) 1: deal #116488502 at 2017.02.07 12:56:22 Вход в рынок, sell 0.01 EURUSD (order #132873695, position ID 132873695)
HistoryDealGetTicket (EURUSD,M30) 2: deal #116488539 at 2017.02.07 12:56:38 Закрытие встречной позицией, buy 0.01 EURUSD (order #132873727, position ID 132873695)
HistoryDealGetTicket (EURUSD,M30) 3: deal #116488540 at 2017.02.07 12:56:38 Закрытие встречной позицией, sell 0.01 EURUSD (order #132873727, position ID 132873637)
HistoryDealGetTicket (EURUSD,M30) 4: deal #116488549 at 2017.02.07 12:56:42 Выход из рынка, sell 0.02 EURUSD (order #132873736, position ID 132873637)