Встречное закрытие ордеров.

 

Периодически встречаю упоминание об использовании такой торговой функции, как OrderCloseBy(). Сам эту функцию раньше не пользовал. Народ иногда утверждает, что с её помощью можно сэкономить spread. Решил сам "потрогать" такую возможность.

Добавил в свою библиотеку процедуру встречного закрытия, вывел комменты и запустил в тестере. Что имеем в логе:

17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: Ticket = 8 | Type = BUY | Lots 0.72
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: Ticket = 7 | Type = BUY | Lots 0.48
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: Ticket = 6 | Type = BUY | Lots 0.32
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: Ticket = 5 | Type = BUY | Lots 0.21
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: Ticket = 2 | Type = SELL | Lots 0.06
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: Ticket = 1 | Type = SELL | Lots 0.02
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: order #2 buy was closed by order #5
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: Bid = 1.2754; Ask = 1.2756; New: Bid = 1.2754; Ask = 1.2756; 
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: b-Trade:     fClose_AllOrdersBy(): ticket #2/SELL | Lots 0.06 | PriceClose = 1.2768 | Profit = $ -59.21
closed By ticket #5/BUY | Lots 0.21 | PriceClose = 1.2768
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: Ticket = 8 | Type = BUY | Lots 0.72
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: Ticket = 7 | Type = BUY | Lots 0.48
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: Ticket = 6 | Type = BUY | Lots 0.32
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: Ticket = 1 | Type = SELL | Lots 0.02
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: order #1 buy was closed by order #6
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: Bid = 1.2754; Ask = 1.2756; New: Bid = 1.2754; Ask = 1.2756; 
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: b-Trade:     fClose_AllOrdersBy(): ticket #1/SELL | Lots 0.02 | PriceClose = 1.2789 | Profit = $ -19.14
closed By ticket #6/BUY | Lots 0.32 | PriceClose = 1.2789
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: Ticket = 8 | Type = BUY | Lots 0.72
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: Ticket = 7 | Type = BUY | Lots 0.48
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: close #8 buy 0.72 EURUSD at 1.2663 at price 1.2754
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: close #7 buy 0.48 EURUSD at 1.2810 at price 1.2754
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: Ticket = 10 | Type = BUY | Lots 0.30
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: Ticket = 9 | Type = BUY | Lots 0.15
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: close #10 buy 0.30 EURUSD at 1.2789 at price 1.2754
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: close #9 buy 0.15 EURUSD at 1.2768 at price 1.2754
17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: b-ManagerPA:     fTrail_PA(): Closed from TrailProfit - 8 orders. 
Profit[Plan $ 182.21] = $ 183.81 | TS_Profit = 104.8 % | TrailProfit_Start = 105.0 %

Что произвожу (по порядку):

 * перед встречным закрытием получаю текущие цены;

* произвожу встречное закрытие;

* опять получаю текущие цены.

Это цены по инструменту (до и после): 

17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: Bid = 1.2754; Ask = 1.2756; New: Bid = 1.2754; Ask = 1.2756;

а здесь цены закрытия ордеров:

17:03:15 2012.01.12 13:30  e-PSI@SAR_Pro EURUSD,M15: b-Trade:     fClose_AllOrdersBy(): ticket #2/SELL | Lots 0.06 | PriceClose = 1.2768 | Profit = $ -59.21
closed By ticket #5/BUY | Lots 0.21 | PriceClose = 1.2768

Я затрудняюсь привести логику, по которой формируется цена закрытия.

У кого-нибудь есть конструктивные мысли по этому вопросу?

 

Вот сама функция:  

//IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII+
//|  Автор : TarasBY, taras_bulba@tut.by                                              |
//+-----------------------------------------------------------------------------------+
//|        Функция закрытия ордеров встречными ордерами                               |
//IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII+
int fClose_AllOrdersBy (double& fd_Pribul,         // Возвращаемый профит закрытых ордеров
                        int fi_Type = -2,          // тип закрываемых ордеров
                        int fi_NBars_Life = 0,     // минимальная "жизнь" ордера в барах на fi_Period: 0 - параметр не учитывается
                        int fi_Period = 0,         // Период
                        int fi_TypeProfit = 0)     // Идентификатор профитности ордера: > 0 - профитный; < 0 - лоссовый
{
    int li_Total = OrdersTotal();
    if (li_Total < 1) return (0);
//----
    int    li_Type, li_ord = 0, li_Ticket, li_size, li_OP;
    double ld_ClosePrice, ld_Profit, lda_MyTiсkets[][2][2], lda_Lots[2];
    string ls_Symbol;
//----
    bs_libNAME = "b-Trade";
    fd_Pribul = 0.0;
    bi_Error = GetLastError();
    //---- Определяем условия взаимного закрытия
    if (fi_Type == -2)
    {
        int li_MyOrders = li_Total;
        while (li_MyOrders > 0)
        {
            ArrayInitialize (lda_Lots, 0.0);
            ArrayResize (lda_MyTiсkets, li_MyOrders);
            ArrayInitialize (lda_MyTiсkets, 0.0);
            li_MyOrders = 0;
            li_size = 0;
            //---- Сначала проводим "инвентаризацию"
            for (int li_ORD = li_Total - 1; li_ORD >= 0; li_ORD--)
            {
                if (!fCheck_MyOrdersWithConditions (li_ORD, -2, fi_NBars_Life, fi_Period, fi_TypeProfit)) continue;
                ls_Symbol = OrderSymbol();
                //---- По очереди закрываем разные инструменты
                if (li_MyOrders == 0) bs_Symbol = ls_Symbol;
                else if (bs_Symbol != ls_Symbol) continue;
                li_Type = OrderType();
                li_Ticket = OrderTicket();
                double ld_Lots = OrderLots();
                int    li_IND = 0;
                //---- Производим сортировку массива ордеров по размеру лота и заносим ордер в "реестр"
                while (lda_MyTiсkets[li_IND][li_Type][0] >= ld_Lots)
                {
                    if (lda_MyTiсkets[li_IND][li_Type][0] == 0.0) break;
                    li_IND++;
                    if (li_IND >= li_MyOrders) break;
                    else if (lda_MyTiсkets[li_IND][li_Type][0] < ld_Lots)
                    {
                        //---- Сортировка
                        for (int li_int = li_size - 1; li_int > li_IND; li_int--)
                        {
                            for (int li_int1 = 0; li_int1 < 2; li_int1++)
                            {lda_MyTiсkets[li_int][li_Type][li_int1] = lda_MyTiсkets[li_int-1][li_Type][li_int1];}
                        }
                        break;
                    }
                }
                lda_MyTiсkets[li_IND][li_Type][0] = ld_Lots;
                lda_MyTiсkets[li_IND][li_Type][1] = li_Ticket;
                //---- Подсчитываем совокупный лот на закрытие
                lda_Lots[li_Type] += ld_Lots;
                li_size = MathMax (li_size, li_IND + 1);
                li_MyOrders++;
            }
            //---- Если удалять больше нечего
            if (li_MyOrders == 0) break;
            ArrayResize (lda_MyTiсkets, li_size);
            //---- Проверка заполнения массива
            /*for (li_OP = 0; li_OP < 2; li_OP++)
            {
                for (li_ORD = 0; li_ORD < li_size; li_ORD++)
                {
                    if (lda_MyTiсkets[li_ORD][li_OP][1] > 0)
                    {Print ("Ticket = ", lda_MyTiсkets[li_ORD][li_OP][1], " | Type = ", fGet_NameOP (li_OP), " | Lots ", DSDig (lda_MyTiсkets[li_ORD][li_OP][0]));}
                }
            }*/
            //---- Организуем взаимное закрытие (если есть что)
            if (lda_Lots[0] > 0.0) if (lda_Lots[1] > 0.0)
            {
                int li_CMD = 1, li_Opposite;
                if (lda_MyTiсkets[0][0][0] <= lda_MyTiсkets[0][1][0]) li_Type = 0; else {li_Type = 1; li_CMD = 0;}
                li_Ticket = lda_MyTiсkets[0][li_Type][1];
                li_IND = li_size - 1;
                //---- Подбираем нужный объём для встречного ордера
                while (lda_MyTiсkets[0][li_Type][0] > lda_MyTiсkets[li_IND][li_CMD][0]) {li_IND--;}
                li_Opposite = lda_MyTiсkets[li_IND][li_CMD][1];
                //---- Фиксируем текущие цены перед встречным закрытием
                if (!bb_VirtualTrade)
                {
                    string lsa_Price[] = {"Bid","Ask"}, ls_txt = StringConcatenate (bs_Symbol, ": ");
                    RefreshRates();
                    for (li_OP = 0; li_OP < 2; li_OP++)
                    {
                        bda_Price[li_OP] = fGet_TradePrice (li_OP, bb_RealTrade, bs_Symbol);
                        ls_txt = StringConcatenate (ls_txt, lsa_Price[li_OP], " = ", DSD (bda_Price[li_OP]), "; ");
                    }
                }
                if (fOrderCloseBy (li_Ticket, li_Opposite))
                {
                    if (!bb_VirtualTrade)
                    {
                        //---- Фиксируем текущие цены после встречного закрытия
                        RefreshRates();
                        ls_txt = StringConcatenate (ls_txt, "New: ");
                        for (li_OP = 0; li_OP < 2; li_OP++)
                        {
                            bda_Price[li_OP] = fGet_TradePrice (li_OP, bb_RealTrade, bs_Symbol);
                            ls_txt = StringConcatenate (ls_txt, lsa_Price[li_OP], " = ", DSD (bda_Price[li_OP]), "; ");
                        }
                        Print (ls_txt);
                        if (!OrderSelect (li_Ticket, SELECT_BY_TICKET, MODE_HISTORY)) continue;
                        ld_Profit = (OrderProfit() + OrderSwap() + OrderCommission());
                        fd_Pribul += ld_Profit;
                                  fSet_Comment (li_Ticket, 0, 42, "fClose_AllOrdersBy()", True, li_Ticket, li_Opposite, ld_Profit, lda_MyTiсkets[li_IND][li_CMD][0]);
                              }
                    li_ord += 2;
                    //---- Отправляем на новый "круг"
                    li_Total = OrdersTotal();
                    li_MyOrders = li_Total;
                    continue;
                }
            }
            li_Type = -1;
            //---- Определяем тип оставшихся ордеров
            if (lda_Lots[0] > 0.0) li_Type = 0; else if (lda_Lots[1] > 0.0) li_Type = 1;
            if (li_Type == -1) continue;
            //---- Закрываем то, что осталось
            for (li_IND = 0; li_IND < li_size; li_IND++)
            {
                li_Ticket = lda_MyTiсkets[li_IND][li_Type][1];
                //---- Если ордера закончились - выходим
                if (li_Ticket == 0) break;
                if (!OrderSelect (li_Ticket, SELECT_BY_TICKET)) continue;
                RefreshRates();
                ld_ClosePrice = fGet_TradePrice (li_Type, bb_RealTrade, OrderSymbol());
                if (fOrderClose (li_Ticket, OrderLots(), ld_ClosePrice, Slippage))
                {
                    li_ord++;
                    if (!OrderSelect (li_Ticket, SELECT_BY_TICKET, MODE_HISTORY)) continue;
                    fd_Pribul += (OrderProfit() + OrderSwap() + OrderCommission());
                    continue;
                }
            }
            //---- Отправляем на новый "круг"
            li_Total = OrdersTotal();
            //---- Если ордеров не осталось - выходим
            if (li_Total == 0) break;
            li_MyOrders = li_Total;
        }
    }
    else li_ord = fClose_AllOrders (fd_Pribul, fi_Type, fi_NBars_Life, fi_Period, fi_TypeProfit);
    //---- Контролируем возможные ошибки
    fGet_LastErrorInArray (bsa_Comment, "fClose_AllOrdersBy()", bi_indERR);
//----
    return (li_ord);
}
 

Оказалось всё банально просто (и вполне логично): пара закрывается по цене открытия встречного ордрера. Если лоты встречно закрываемых ордеров не идентичны, то происходит частичное закрытие.

 

Спасибо Автору за нужную функцию!

Странно только, что никто ещё не среагировал, даже практикующие локи, которые я избегал, а с этой функцией можно быть посмелее, поскольку поможет закрыться хотя бы в минимальном плюсе.

Как быть с 8-ю параметрами не заявленными, но фигурирующие в функции?

bs_libName--------//-Можно закомментировать или есть связь с библиотекой?

bs_Symbol---------//-Можно обойтись без него или необходим?

bs_Delimiter-------//-Нужно заявить глобальной и как, int или bool? 

bb_VirtualTrade---//-Тоже неясно как заявить.

bda_Price----------//-Эта цена относится закрывающей позиции?

bb_RealTrade-----//-Тоже как?

bsa_Comment-----//-Наверно не нужен, но всё-таки...

bi_indERR----------//-Можно заменить на имеющиеся в моём советнике или как-то связано с вашей библиотекой?

Проясните, пожалуйста, что и как нужно сделать! Благодарю за помощь!

 
borilunad:

Спасибо Автору за нужную функцию!

Странно только, что никто ещё не среагировал, даже практикующие локи, которые я избегал, а с этой функцией можно быть посмелее, поскольку поможет закрыться хотя бы в минимальном плюсе.

Как быть с 8-ю параметрами не заявленными, но фигурирующие в функции?

bs_libName--------//-Можно закомментировать или есть связь с библиотекой?

bs_Symbol---------//-Можно обойтись без него или необходим?

bs_Delimiter-------//-Нужно заявить глобальной и как, int или bool? 

bb_VirtualTrade---//-Тоже неясно как заявить.

bda_Price----------//-Эта цена относится закрывающей позиции?

bb_RealTrade-----//-Тоже как?

bsa_Comment-----//-Наверно не нужен, но всё-таки...

bi_indERR----------//-Можно заменить на имеющиеся в моём советнике или как-то связано с вашей библиотекой?

Проясните, пожалуйста, что и как нужно сделать! Благодарю за помощь!

Один из модераторушек взял статью почитать и уже почти месяц её читает - я про публикацию новой версии своей библиотеки b-PSI@Trade. В ней присутствуют ещё функции, использованные в функции встречного закрытия. В прицепе ложу простейший шаблон для возможности использовать упомянутую библиотеку в своих советниках. Там есть и упомянутая библиотека.

Использование функции встречного закрытия НИКАК не отличается от использования любой другой пользовательской функции. Передаваемые в неё параметры описаны. Если будут вопросы, - спрашивайте.

Файлы:
 
TarasBY:

Один из модераторушек взял статью почитать и уже почти месяц её читает - я про публикацию новой версии своей библиотеки https://www.mql5.com/ru/code/10659. В ней присутствуют ещё функции, использованные в функции встречного закрытия. В прицепе ложу простейший шаблон для возможности использовать упомянутую библиотеку в своих советниках. Там есть и упомянутая библиотека.

Использование функции встречного закрытия НИКАК не отличается от использования любой другой пользовательской функции. Передаваемые в неё параметры описаны. Если будут вопросы, - спрашивайте.

Спасибо! А в https://www.mql5.com/en/code всё это смогу найти? А то до сих пор не располагаю архиватором типа zip, т.к. не могу найти в Инете без условий дачи им моих данных. А потом "залавинят" спамом!

Вообще, мне многого не нужно, работаю с одним инструментом, с одним магиком, с одним Тrade и необходимой обработкой ошибок. Хотелось бы как-то попроще и гибче. До этого использовал всё от Кима и др. и на их основе делал свои функции, отвечающие моим условиям. Но у них нет функции CloseBy, а с Вашей функцией не удаётся добиться необходимых мне условий. Всё как-то слишком заорганизовано, потому не поддаётся нужным мне изменениям.

Если не можете помочь практически, тогда извините за беспокойство! Спасибо!

 
borilunad:

Спасибо! А в https://www.mql5.com/en/code всё это смогу найти? А то до сих пор не располагаю архиватором типа zip, т.к. не могу найти в Инете без условий дачи им моих данных. А потом "залавинят" спамом!

Вообще, мне многого не нужно, работаю с одним инструментом, с одним магиком, с одним Тrade и необходимой обработкой ошибок. Хотелось бы как-то попроще и гибче. До этого использовал всё от Кима и др. и на их основе делал свои функции, отвечающие моим условиям. Но у них нет функции CloseBy, а с Вашей функцией не удаётся добиться необходимых мне условий. Всё как-то слишком заорганизовано, потому не поддаётся нужным мне изменениям.

Если не можете помочь практически, тогда извините за беспокойство! Спасибо!

Вот попроще (автор getch):

void LockOFF( )
 {
  int BuyTicket, SellTicket;
    
  BuyTicket  = GetOrderTicket1(Symbol(), OP_BUY, MagicNumber);
  SellTicket = GetOrderTicket1(Symbol(), OP_SELL, MagicNumber);
  
  while ((BuyTicket != -1) && (SellTicket != -1))
  {
    OrderCloseBy(BuyTicket, SellTicket);
    BuyTicket = GetOrderTicket1(Symbol(), OP_BUY, MagicNumber);
    SellTicket = GetOrderTicket1(Symbol(), OP_SELL, MagicNumber);
  } 
  return;
 }
int GetOrderTicket1( string inSymbol, int Type,  int mn=-1 )
{
  int Pos, Total = OrdersTotal();
  
  for (Pos = 0; Pos < Total; Pos++)
  {
   OrderSelect(Pos, SELECT_BY_POS);
   if (OrderMagicNumber()==mn)  
    if (OrderSymbol() == inSymbol)
      if (OrderType() == Type)
        return(OrderTicket());
  }
  return(-1);
}
 
khorosh:

Вот попроще (автор getch):

 

 

Спасибо большое! Сразу видно, что могу добавить и свои условия, не нарушая логики алгоритма функции Автора, и всё очень ясно без комментариев! :)
 
borilunad:
Спасибо большое! Сразу видно, что могу добавить и свои условия, не нарушая логики алгоритма функции Автора, и всё очень ясно без комментариев! :)
Лаконичность - стиль большого мастера. Всё гениальное просто.)
 
khorosh:
Лаконичность - стиль большого мастера. Всё гениальное просто.)
Это точно! Легче потом добавить, что нужно, чем избавиться от досадных ненужностей!
 
borilunad:

Спасибо! А в https://www.mql5.com/en/code всё это смогу найти? А то до сих пор не располагаю архиватором типа zip, т.к. не могу найти в Инете без условий дачи им моих данных. А потом "залавинят" спамом!

Вообще, мне многого не нужно, работаю с одним инструментом, с одним магиком, с одним Тrade и необходимой обработкой ошибок. Хотелось бы как-то попроще и гибче. До этого использовал всё от Кима и др. и на их основе делал свои функции, отвечающие моим условиям. Но у них нет функции CloseBy, а с Вашей функцией не удаётся добиться необходимых мне условий. Всё как-то слишком заорганизовано, потому не поддаётся нужным мне изменениям.

Если не можете помочь практически, тогда извините за беспокойство! Спасибо!

Когда новая версия пройдёт проверку модератором, библиотека по ссылке будет доступна.

Иногда, ответить на вопрос USERa сложнее, чем просто промолчать: не понимаю, как можно придумывать причины на неспособность открыть ЛЮБОЙ архив???

Чего проще - выложил для Вас шаблон, через который можно пользоваться НЕ ТОЛЬКО функцией встречного закрытия, НО и другими функциями торговых операций из моей библиотеки, не заморачиваясь с её структурой.

ХОРОШО, когда у каждого из нас существует выбор. Но нужно понимать - есть структура на уровне функции, есть на уровне комплекса функций (библиотека), выполняющих единую задачу, а есть структура на уровне комплекса библиотек - это РАЗНЫЕ вещи.

В Вашем случае - ничем помочь не смогу.

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