Hedging. Работа с историей. Расчёт прибыли/убытков ПОЗИЦИИ в пунктах.

 

Задача: на Hedge счёте по истории СДЕЛОК собрать ПОЗИЦИИ и определить прибыль/убыток в пунктах.

Если сильно упростить задачу, то всё сводится к тому, что нужно от цены закрытия позиции отнять цену открытия позиции (пока не смотрим на полученный знак результата: "+" или "-" и на тип позиции: Buy или Sell). А вот в реальных условиях возникают нюансы.

Нюансы: 

  1. Вариант из двух шагов: шаг первый - открыли позицию, шаг второй - закрыли позицию - это самый идеальный вариант, но бывает и больше шагов. Пример: открыли Buy объёмом 0.05, выполнили частичное закрытие объёмом 0.03 (при этом осталась позиция Buy объёмом 0.02) и потом только окончательно закрыли позицию объёмом 0.02.
  2. В запрошенную историю (HistorySelect) могут попасть неполные части позиций:
    HistorySelect
    Пояснения: от позиции #1 в запрашиваемый период вошли только часть сделок - значит реконструировать позицию #1 не выйдет. Аналогичная ситуация с позицией #4 - это текущая позиция, которая ещё не закрыта и, значит, невозможно посчитать её ИТОГОВЫЙ финансовый результат.
    fgfg
  3. Оставлю место, может ещё есть нюансы...
Далее будет...
 

Идеальный вариант: открытых позиций нет, а по запрашиваемому интервалу торговой истории можно полностью восстановить ПОЗИЦИИ. Вот распечатка торговой истории сделок

1.DEAL_ENTRY_IN , type DEAL_TYPE_BUY , price 1.04552, Deal EURUSD volume 0.03 DEAL_POSITION_ID #119707903 profit 0.00
2.DEAL_ENTRY_IN , type DEAL_TYPE_SELL , price 1.04562, Deal EURUSD volume 0.02 DEAL_POSITION_ID #119708383 profit 0.00
3.DEAL_ENTRY_OUT_BY , type DEAL_TYPE_SELL , price 1.04562, Deal EURUSD volume 0.02 DEAL_POSITION_ID #119707903 profit 0.20
3.DEAL_ENTRY_OUT_BY , type DEAL_TYPE_BUY , price 1.04552, Deal EURUSD volume 0.02 DEAL_POSITION_ID #119708383 profit 0.00
4.DEAL_ENTRY_IN , type DEAL_TYPE_SELL , price 1.04534, Deal EURUSD volume 0.05 DEAL_POSITION_ID #119708643 profit 0.00
5.DEAL_ENTRY_OUT_BY , type DEAL_TYPE_SELL , price 1.04534, Deal EURUSD volume 0.01 DEAL_POSITION_ID #119707903 profit -0.18
5.DEAL_ENTRY_OUT_BY , type DEAL_TYPE_BUY , price 1.04552, Deal EURUSD volume 0.01 DEAL_POSITION_ID #119708643 profit 0.00
6.DEAL_ENTRY_OUT , type DEAL_TYPE_BUY , price 1.04524, Deal EURUSD volume 0.04 DEAL_POSITION_ID #119708643 profit 0.40

По шагам:

  1. Открыта позиция Buy объёмом 0.03 (119707903).
  2. Открыта позиция Sell объёмом 0.02 (119708383).
  3. Позиция 119707903 закрыта встречной позицией 119708383 (в итоге осталась позиция Buy (119707903) объёмом 0.01).
  4. Открыта позиция Sell объёмом 0.05 (119708643).
  5. Позиция 119707903 закрыта встречной позицией 119708643 (в итоге осталась позиция Sell (119708643) объёмом 0.04).
  6. Окончательное закрытие позиции Sell (119708643) объёмом 0.04).

Такая распечатка получена при помощи скрипта, в котором указывается с какого момента запрашивать историю

//+------------------------------------------------------------------+
//|                                               HistorySelect.mq5  |
//|                              Copyright © 2016, Vladimir Karputov |
//|                                           http://wmua.ru/slesar/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2016, Vladimir Karputov"
#property link      "http://wmua.ru/slesar/"
#property version   "1.00"
#property description "Реверс позиции и исследование \"DEAL_POSITION_ID\" истории сделок"
#property script_show_inputs
//---
input datetime start=D'2016.08.05 09:00:00';
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   if(AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
     {
      Print("This script cannot be run on a hedge; Этот скрипт нельзя запускать на хедж");
      return;
     }
   Print_IDs();
  }
//+------------------------------------------------------------------+
//| List all positions and deals                                     |
//+------------------------------------------------------------------+
void Print_IDs(void)
  {
//--- запрашиваем историю сделок и ордеров за указанный период серверного времени
   HistorySelect(start,TimeCurrent()+86400);
   uint     total    =HistoryDealsTotal();   // количество сделок в истории
   ulong    ticket   =0;                     // тикет сделки в истории
   long     type     =0;                     // тип сделки
   long     deal_id=0;   // идентификатор позиции, в открытии, изменении или закрытии которой участвовала эта сделка
   double   volume   =0.0;                   // объём сделки
   double   profit   =0.0;                   // финансовый результат сделки
   double   price    =0.0;                   // цена сделки
   string   symbol   =NULL;                  // имя символа, по которому произведена сделка
   long     entry    =0;                     // направление сделки – вход в рынок, выход из рынка или разворот
//--- for all deals
   for(uint i=0;i<total;i++)
     {
      //--- try to get deals ticket
      if((ticket=HistoryDealGetTicket(i))>0)
        {
         //--- get deals properties
         type     =HistoryDealGetInteger(ticket,DEAL_TYPE);
         deal_id  =HistoryDealGetInteger(ticket,DEAL_POSITION_ID);
         volume   =HistoryDealGetDouble(ticket,DEAL_VOLUME);
         profit   =HistoryDealGetDouble(ticket,DEAL_PROFIT);
         price    =HistoryDealGetDouble(ticket,DEAL_PRICE);
         symbol   =HistoryDealGetString(ticket,DEAL_SYMBOL);
         entry    =HistoryDealGetInteger(ticket,DEAL_ENTRY);
         Print(EnumToString((ENUM_DEAL_ENTRY)entry),
               ", type ",EnumToString((ENUM_DEAL_TYPE)type),
               ", price ",DoubleToString(price,Digits()),
               ", Deal ",symbol," volume ",DoubleToString(volume,2),
               ", DEAL_POSITION_ID #",deal_id,
               ", profit ",DoubleToString(profit,2));
        }
     }
   Print("");
  }
//+------------------------------------------------------------------+


Файлы:
 

Думаю лучшим способом будет такой вариант: один проход по полученному списку сделок. В процесс прохода сделки сортировать по POSITION_ID. В итоге должно выйти несколько объектов - позиций, в каждом из которых содержаться ЕГО сделки:

HistorySelect 1 

Осталось придумать, как лучше провернуть (обернуть) это. 

 

Было ранее (скрипт "HistorySelect.mq5" из поста ) давал такую распечатку:

1.DEAL_ENTRY_IN , type DEAL_TYPE_BUY , price 1.04552, Deal EURUSD volume 0.03 DEAL_POSITION_ID #119707903 profit 0.00
2.DEAL_ENTRY_IN , type DEAL_TYPE_SELL , price 1.04562, Deal EURUSD volume 0.02 DEAL_POSITION_ID #119708383 profit 0.00
3.DEAL_ENTRY_OUT_BY , type DEAL_TYPE_SELL , price 1.04562, Deal EURUSD volume 0.02 DEAL_POSITION_ID #119707903 profit 0.20
3.DEAL_ENTRY_OUT_BY , type DEAL_TYPE_BUY , price 1.04552, Deal EURUSD volume 0.02 DEAL_POSITION_ID #119708383 profit 0.00
4.DEAL_ENTRY_IN , type DEAL_TYPE_SELL , price 1.04534, Deal EURUSD volume 0.05 DEAL_POSITION_ID #119708643 profit 0.00
5.DEAL_ENTRY_OUT_BY , type DEAL_TYPE_SELL , price 1.04534, Deal EURUSD volume 0.01 DEAL_POSITION_ID #119707903 profit -0.18
5.DEAL_ENTRY_OUT_BY , type DEAL_TYPE_BUY , price 1.04552, Deal EURUSD volume 0.01 DEAL_POSITION_ID #119708643 profit 0.00
6.DEAL_ENTRY_OUT , type DEAL_TYPE_BUY , price 1.04524, Deal EURUSD volume 0.04 DEAL_POSITION_ID #119708643 profit 0.40

и сейчас реконструкция даёт такую распечатку:

position #0
DEAL_ENTRY_IN, type DEAL_TYPE_BUY, price 1.04552, Deal EURUSD, volume 0.03, DEAL_POSITION_ID #119707903, profit 0.00
DEAL_ENTRY_OUT_BY, type DEAL_TYPE_SELL, price 1.04562, Deal EURUSD, volume 0.02, DEAL_POSITION_ID #119707903, profit 0.20
DEAL_ENTRY_OUT_BY, type DEAL_TYPE_SELL, price 1.04534, Deal EURUSD, volume 0.01, DEAL_POSITION_ID #119707903, profit -0.18
position #1
DEAL_ENTRY_IN, type DEAL_TYPE_SELL, price 1.04562, Deal EURUSD, volume 0.02, DEAL_POSITION_ID #119708383, profit 0.00
DEAL_ENTRY_OUT_BY, type DEAL_TYPE_BUY, price 1.04552, Deal EURUSD, volume 0.02, DEAL_POSITION_ID #119708383, profit 0.00
position #2
DEAL_ENTRY_IN, type DEAL_TYPE_SELL, price 1.04534, Deal EURUSD, volume 0.05, DEAL_POSITION_ID #119708643, profit 0.00
DEAL_ENTRY_OUT_BY, type DEAL_TYPE_BUY, price 1.04552, Deal EURUSD, volume 0.01, DEAL_POSITION_ID #119708643, profit 0.00
DEAL_ENTRY_OUT, type DEAL_TYPE_BUY, price 1.04524, Deal EURUSD, volume 0.04, DEAL_POSITION_ID #119708643, profit 0.40


Нужно проверить распечатки. Если всё верно подготовлю код. 

 

Скрипт "Reconstruction of positions.mq5" реконструирования позиции  (пока просто группировка сделок в позиции, без учета целостности - смотреть рисунок из самого первого поста).

//+------------------------------------------------------------------+
//|                                 Reconstruction of positions.mq5  |
//|                              Copyright © 2016, Vladimir Karputov |
//|                                           http://wmua.ru/slesar/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2016, Vladimir Karputov"
#property link      "http://wmua.ru/slesar/"
#property version   "1.000"
#include <Arrays\ArrayObj.mqh>
#property description "Реконструкция ПОЗИЦИЙ из истории сделок"
#property script_show_inputs
//---
input datetime start=D'2016.12.22 16:17:00';
//+------------------------------------------------------------------+
//| Класс - позиция - совокупность всех сделок                       |
//| с одинаковым POSITION_ID                                         |
//+------------------------------------------------------------------+
class CMyPosition : public CArrayObj
  {
private:
   long              m_pos_id;            // идентификатор позиции, в открытии, изменении или закрытии которой участвовала эта сделка
public:
   // CMyPosition(void);
   //~CMyPosition(void);
   //--- методы доступа к защищённым данным
   void              ID(const long value) { m_pos_id=value;               }
   long              ID(void) const       { return(m_pos_id);             }
  };
//+------------------------------------------------------------------+
//| Класс - сделка                                                   |
//+------------------------------------------------------------------+
class CMyDeal : public CObject
  {
private:
   long              m_deal_entry;        // направление сделки – вход в рынок, выход из рынка или разворот
   long              m_deal_type;         // тип сделки
   double            m_deal_price;        // цена сделки
   double            m_deal_volume;       // объём сделки
   long              m_deal_id;           // идентификатор позиции, в открытии, изменении или закрытии которой участвовала эта сделка
   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);             }

   void              Volume_deal(const double value) { m_deal_volume=value;              }
   double            Volume_deal(void) const         { return(m_deal_volume);            }

   void              ID_deal(const long value)       { m_deal_id=value;                  }
   long              ID_deal(void) const             { return(m_deal_id);                }

   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_type(0),
                         m_deal_price(0.0),
                         m_deal_volume(0.0),
                         m_deal_id(0),
                         m_deal_profit(0.0)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CMyDeal::~CMyDeal(void)
  {
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   if(AccountInfoInteger(ACCOUNT_MARGIN_MODE)!=ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
     {
      Print("This script can be started only on a hedge; Этот скрипт можно запускать только на хедж");
      return;
     }
   Print_IDs();
  }
//+------------------------------------------------------------------+
//| List all positions and deals                                     |
//+------------------------------------------------------------------+
void Print_IDs(void)
  {
   CArrayObj arr_positions;                  // объкт динамического массива указателей

//--- запрашиваем историю сделок и ордеров за указанный период серверного времени
   HistorySelect(start,TimeCurrent()+86400);
   uint     total    =HistoryDealsTotal();   // количество сделок в истории
   ulong    ticket   =0;                     // тикет сделки в истории
   long     magic    =0;
   long     type     =0;                     // тип сделки
   long     deal_id  =0;                     // идентификатор позиции, в открытии, изменении или закрытии которой участвовала эта сделка
   double   volume   =0.0;                   // объём сделки
   double   profit   =0.0;                   // финансовый результат сделки
   double   price    =0.0;                   // цена сделки
   string   symbol   =NULL;                  // имя символа, по которому произведена сделка
   long     entry    =0;                     // направление сделки – вход в рынок, выход из рынка или разворот
//--- for all deals
   for(uint i=0;i<total;i++)
     {
      //--- try to get deals ticket
      if((ticket=HistoryDealGetTicket(i))>0)
        {
         //--- get deals properties
         magic    =HistoryDealGetInteger(ticket,DEAL_MAGIC);
         type     =HistoryDealGetInteger(ticket,DEAL_TYPE);
         deal_id  =HistoryDealGetInteger(ticket,DEAL_POSITION_ID);
         volume   =HistoryDealGetDouble(ticket,DEAL_VOLUME);
         profit   =HistoryDealGetDouble(ticket,DEAL_PROFIT);
         price    =HistoryDealGetDouble(ticket,DEAL_PRICE);
         symbol   =HistoryDealGetString(ticket,DEAL_SYMBOL);
         entry    =HistoryDealGetInteger(ticket,DEAL_ENTRY);
         if(symbol==Symbol() /*&& magic==m_magic*/)
           {
            bool find=false;
            for(int j=0;j<arr_positions.Total();j++)
              {
               //--- объявим указатель на объект класса CMyPosition и присвоим ему элемент из указанной позиции массива  
               CMyPosition *my_position=arr_positions.At(j);

               if(my_position==NULL)
                 {
                  //--- ошибка чтения из массива
                  Print("Get element error (\"arr_positions\")");
                  return;
                 }
               if(my_position.ID()==deal_id)
                 {
                  find=true;
                  //--- объявим указатель на объект класса CMyDeal и присвоим ему вновь созданный объект класса CMyDeal
                  CMyDeal *my_deal=new CMyDeal;
                  if(my_deal==NULL)
                    {
                     //--- ошибка создания
                     Print("Object create error (\"my_deal\")");
                     return;
                    }
                  my_deal.Entry_deal(entry);
                  my_deal.Type_deal(type);
                  my_deal.Price_deal(price);
                  my_deal.Volume_deal(volume);
                  my_deal.ID_deal(deal_id);
                  my_deal.Profit_deal(profit);

                  my_position.Add(my_deal);
                 }
              }
            if(!find)
              {
               //--- объявим указатель на объект класса CMyPosition и присвоим ему вновь созданный объект класса CMyPosition
               CMyPosition *my_position=new CMyPosition;
               if(my_position==NULL)
                 {
                  //--- ошибка создания
                  Print("Object create error (\"arr_positions\")");
                  return;
                 }
               my_position.ID(deal_id);
               //--- объявим указатель на объект класса CMyDeal и присвоим ему вновь созданный объект класса CMyDeal
               CMyDeal *my_deal=new CMyDeal;
               if(my_deal==NULL)
                 {
                  //--- ошибка создания
                  Print("Object create error (\"my_deal\")");
                  return;
                 }
               my_deal.Entry_deal(entry);
               my_deal.Type_deal(type);
               my_deal.Price_deal(price);
               my_deal.Volume_deal(volume);
               my_deal.ID_deal(deal_id);
               my_deal.Profit_deal(profit);

               my_position.Add(my_deal);

               arr_positions.Add(my_position);
              }
           }
        }
     }
   for(int k=0;k<arr_positions.Total();k++)
     {
      //--- объявим указатель на объект класса CMyPosition и присвоим ему элемент из указанной позиции массива
      CMyPosition *my_position=arr_positions.At(k);
      if(my_position==NULL)
        {
         //--- ошибка чтения из массива
         Print("Get element error (\"arr_positions\")");
         return;
        }
      Print("position #",k);
      for(int m=0;m<my_position.Total();m++)
        {
         //--- объявим указатель на объект класса CMyDeal и присвоим ему элемент из указанной позиции массива
         CMyDeal *my_deal=my_position.At(m);
         if(my_deal==NULL)
           {
            //--- ошибка чтения из массива
            Print("Get element error (\"my_deal\")");
            return;
           }
         Print(EnumToString((ENUM_DEAL_ENTRY)my_deal.Entry_deal()),
               ", type ",EnumToString((ENUM_DEAL_TYPE)my_deal.Type_deal()),
               ", price ",DoubleToString(my_deal.Price_deal(),Digits()),
               ", Deal ",Symbol(),
               ", volume ",DoubleToString(my_deal.Volume_deal(),2),
               ", DEAL_POSITION_ID #",my_deal.ID_deal(),
               ", profit ",DoubleToString(my_deal.Profit_deal(),2));
        }
     }
   Print("");
  }
//+------------------------------------------------------------------+


 

 Теперь нужно вводить проверки целостности позиций.

Файлы:
 
Vladimir Karputov:

Задача: на Hedge счёте по истории СДЕЛОК собрать ПОЗИЦИИ и определить прибыль/убыток в пунктах.


  1. Вариант из двух шагов: шаг первый - открыли позицию, шаг второй - закрыли позицию - это самый идеальный вариант, но бывает и больше шагов. Пример: открыли Buy объёмом 0.05, выполнили частичное закрытие объёмом 0.03 (при этом осталась позиция Buy объёмом 0.02) и потом только окончательно закрыли позицию объёмом 0.02.
  2. В запрошенную историю (HistorySelect) могут попасть неполные части позиций:

не совсем понял это были вопросы или это ветка для новичков

но в любом случае - https://www.mql5.com/ru/docs/trading/historyselectbyposition

Документация по MQL5: Торговые функции / HistorySelectByPosition
Документация по MQL5: Торговые функции / HistorySelectByPosition
  • www.mql5.com
Торговые функции / HistorySelectByPosition - справочник по языку алгоритмического/автоматического трейдинга для MetaTrader 5
 
o_O:

не совсем понял это были вопросы или это ветка для новичков

но в любом случае - https://www.mql5.com/ru/docs/trading/historyselectbyposition

Чтобы использовать HistorySelectByPosition нужно задать POSITION_IDENTIFIER  - а, чтобы его получить нужно всё равно обратится к торговой истории. Поэтому я решил за один проход по запрошенной торговой истории собирать позиции из сделок. Без HistorySelectByPosition.
 
Vladimir Karputov:

Задача: на Hedge счёте по истории СДЕЛОК собрать ПОЗИЦИИ и определить прибыль/убыток в пунктах.

Зачем?
 

Реконструированная ПОЗИЦИЯ может считаться целостной, если соблюдаются такие условия:

  1. Обязательное присутствие сделки с типом DEAL_ENTRY_IN
  2. Должно быть количество сделок больше одной
  3. Объём сделки DEAL_ENTRY_IN минус объёмы сделок DEAL_ENTRY_OUT и DEAL_ENTRY_OUT_BY должен быть равен нулю.

 

Функция проверки целостности РЕКОНСТРУИРОВАННОЙ ПОЗИЦИИ "IntegrityChecking":

//+------------------------------------------------------------------+
//| Проверка целостности реконструированной позиции                  |
//|  Реконструированная ПОЗИЦИЯ может считаться целостной,           |
//|  если соблюдаются такие условия:                                 |
//|   Количество сделок должно быть  больше одной                    |
//|   Обязательное присутствие сделки с типом DEAL_ENTRY_IN          |
//|   Объём сделки DEAL_ENTRY_IN минус объёмы сделок DEAL_ENTRY_OUT  |
//|   и DEAL_ENTRY_OUT_BY должен быть равен нулю.                    |
//+------------------------------------------------------------------+
bool CMyPosition::IntegrityChecking(void)
  {
   bool result=false;

   int total_deals=Total();
   if(total_deals<2)
      return(false);
   int count_ENTRY_IN=0;
   double volume=0;
   for(int m=0;m<total_deals;m++)
     {
      //--- объявим указатель на объект класса CMyDeal и присвоим ему элемент из указанной позиции массива
      CMyDeal *my_deal=At(m);
      if(my_deal==NULL)
        {
         //--- ошибка чтения из массива
         Print("Get element error (\"my_deal\")");
         return(false);
        }
      if(my_deal.Type_deal()==DEAL_ENTRY_IN)
        {
         count_ENTRY_IN++;
         volume+=my_deal.Volume_deal();
        }
      else if(my_deal.Type_deal()==DEAL_ENTRY_OUT || my_deal.Type_deal()==DEAL_ENTRY_OUT_BY)
        {
         volume-=my_deal.Volume_deal();
        }
     }
//--- Checking
   Print("volume: ",volume);
   if(count_ENTRY_IN!=0 && CompareDoubles(volume,0.0))
      return(true);

   return(result);
  }
Vladimir Karputov:

Реконструированная ПОЗИЦИЯ может считаться целостной, если соблюдаются такие условия:

  1. Обязательное присутствие сделки с типом DEAL_ENTRY_IN
  2. Должно быть количество сделок больше одной
  3. Объём сделки DEAL_ENTRY_IN минус объёмы сделок DEAL_ENTRY_OUT и DEAL_ENTRY_OUT_BY должен быть равен нулю.

Немного изменена последовательность проверок. Сначала проверка на "2. Должно быть количество сделок больше одной":

   int total_deals=Total();
   if(total_deals<2)
      return(false);

Оставшиеся две проверки делаются в самом конце:

//--- Checking
   Print("volume: ",volume);
   if(count_ENTRY_IN!=0 && CompareDoubles(volume,0.0))
      return(true);

Обратите внимание, что сравнивать в лоб объёмы:

//--- Checking
   Print("volume: ",volume);
   if(count_ENTRY_IN!=0 && volume==0.0)
      return(true);

некорректно, так как имеем дело с вещественными числами. Поэтому сравнение производим в функции "CompareDoubles":

//+------------------------------------------------------------------+
//| Сравнение двух чисел double                                      |
//+------------------------------------------------------------------+
bool CMyPosition::CompareDoubles(double number1,double number2)
  {
   if(NormalizeDouble(number1-number2,8)==0)
      return(true);
   else
      return(false);
  }

здесь сравнивается нормализованная разность двух вещественных чисел с нулевым значение (способ два). 

Файлы:
 

Итак, версия 1.001:

position #0
volume: -0.03
Check of integrity isnt undergone
DEAL_ENTRY_OUT_BY, type DEAL_TYPE_SELL, price 1.04392, Deal EURUSD, volume 0.02, DEAL_POSITION_ID #119697329, profit -0.36
DEAL_ENTRY_OUT, type DEAL_TYPE_SELL, price 1.04341, Deal EURUSD, volume 0.01, DEAL_POSITION_ID #119697329, profit -0.69
position #1
Check of integrity isnt undergone
DEAL_ENTRY_OUT_BY, type DEAL_TYPE_BUY, price 1.04410, Deal EURUSD, volume 0.02, DEAL_POSITION_ID #119697601, profit 0.00
position #2
volume: -1.734723475976807e-18
DEAL_ENTRY_IN, type DEAL_TYPE_BUY, price 1.04552, Deal EURUSD, volume 0.03, DEAL_POSITION_ID #119707903, profit 0.00
DEAL_ENTRY_OUT_BY, type DEAL_TYPE_SELL, price 1.04562, Deal EURUSD, volume 0.02, DEAL_POSITION_ID #119707903, profit 0.20
DEAL_ENTRY_OUT_BY, type DEAL_TYPE_SELL, price 1.04534, Deal EURUSD, volume 0.01, DEAL_POSITION_ID #119707903, profit -0.18
position #3
volume: 0.0
DEAL_ENTRY_IN, type DEAL_TYPE_SELL, price 1.04562, Deal EURUSD, volume 0.02, DEAL_POSITION_ID #119708383, profit 0.00
DEAL_ENTRY_OUT_BY, type DEAL_TYPE_BUY, price 1.04552, Deal EURUSD, volume 0.02, DEAL_POSITION_ID #119708383, profit 0.00
position #4
volume: 0.0
DEAL_ENTRY_IN, type DEAL_TYPE_SELL, price 1.04534, Deal EURUSD, volume 0.05, DEAL_POSITION_ID #119708643, profit 0.00
DEAL_ENTRY_OUT_BY, type DEAL_TYPE_BUY, price 1.04552, Deal EURUSD, volume 0.01, DEAL_POSITION_ID #119708643, profit 0.00
DEAL_ENTRY_OUT, type DEAL_TYPE_BUY, price 1.04524, Deal EURUSD, volume 0.04, DEAL_POSITION_ID #119708643, profit 0.40

Обратите внимание на позицию #2 - здесь очень хорошо видно, что простое сравнение в лоб объёмом привело бы к ошибке.
 

Причина обращения: