How to save on commissions by using PositionCloseBy - page 2

 
Vladimir Karputov:

A nested loop is dangerous and wrong.

You need to do one cycle: during the cycle, find opposite positions and memorize their tickets. After the loop, just apply CloseBy.


I gave an example in a post  - this is a simple way. But you can make it even more complicated: search for positions by lot size, by opposite profit ...

Ok, now I made it by using two different loops and I found PositionCloseBy less profitable. These are the lines:

      //+------------------------------------------ Internal loop ---!
      ulong iTicket=0;
      int BuyOrders=0,SellOrders=0;
      for(int i=0;i<PositionsTotal();i++){
         iTicket=PositionGetTicket(i);
         if(PositionSelectByTicket(iTicket)&&
         PositionGetString(POSITION_SYMBOL)==SName){
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY){BuyOrders++;}
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL){SellOrders++;}
            }
         }
      //----------------------------------------- PositionCloseBy ---!
      int OrdersCount=0;
      for(int j=PositionsTotal()-1;j>=0;j--){
         ulong jTicket=PositionGetTicket(j);
         if(PositionSelectByTicket(jTicket)&&
         PositionGetString(POSITION_SYMBOL)==SName){
            OrdersCount++;if(OrdersCount>1){
               if(!trade.PositionCloseBy(jTicket,iTicket)){
                  Print("PositionCloseBy error ",trade.ResultRetcode());
                  return;
                  }
               }
            }
         }

Reversal close result: 3458,15 / 15,79% DD., Sharpe 0,05. PositionCloseBy result: 1543,94 / 7,69% DD., Sharpe: 0,04.

Giving this results may I suppose that I'm having some kind of LotSizing problem by using the CloseBy, don't actually know how. Anyone knows?

 
David Diez :

Ok, now I made it by using two different loops and I found PositionCloseBy less profitable . These are the lines:

Reversal close result: 3458,15 / 15,79% DD., Sharpe 0,05. PositionCloseBy result: 1543,94 / 7,69% DD., Sharpe: 0,04.

Giving this results may I suppose that I'm having some kind of LotSizing problem by using the CloseBy , don't actually know how. Anyone knows?

You persistently make one and the same mistake:

    iTicket= PositionGetTicket  (i);
         if ( PositionSelectByTicket (iTicket)&&

PositionGetTicket selects a position. After that PositionSelectByTicket is NOT NECESSARY TO DO. Why re-select a position ??? It is enough to check the ticket for equality to zero. If zero means an error.

Please correct your algorithm.

 
Vladimir Karputov:

You persistently make one and the same mistake:

PositionGetTicket selects a position. After that PositionSelectByTicket is NOT NECESSARY TO DO. Why re-select a position ??? It is enough to check the ticket for equality to zero. If zero means an error.

Please correct your algorithm.

Ok I've just corrected but the result is the same. Now I'm having the problem of LotSizing which its formula has not been changed.

      //----------------------------------------- PositionCloseBy ---!
      int OrdersCount=0;
      for(int j=PositionsTotal()-1;j>=0;j--){
         ulong jTicket=PositionGetTicket(j);
         if(PositionGetString(POSITION_SYMBOL)==SName){
            OrdersCount++;if(OrdersCount>1){
               if(!trade.PositionCloseBy(jTicket,iTicket)){
                  Print("PositionCloseBy error ",trade.ResultRetcode());
                  return;
                  }
               }
            }
         }
 

I will show the algorithm (the basis is taken from )

We have such positions (pay attention only to the BUY positions):


Now we make a cycle, we enter only the BUY positions into the table (we enter the ticket and the volume).

After that, we sort the table - first by tickets, then by volume:

2020.08.20 14:23:23.848 Position Close By (GBPUSD,M30)  SORT_BY_TICKET
2020.08.20 14:23:23.848 Position Close By (GBPUSD,M30)  688545825 0.02
2020.08.20 14:23:23.848 Position Close By (GBPUSD,M30)  688545924 0.01
2020.08.20 14:23:23.848 Position Close By (GBPUSD,M30)  688545953 0.03
2020.08.20 14:23:23.848 Position Close By (GBPUSD,M30)  688546040 0.01
2020.08.20 14:23:23.848 Position Close By (GBPUSD,M30)  688546073 0.07
2020.08.20 14:23:23.848 Position Close By (GBPUSD,M30)  688546154 0.04
2020.08.20 14:23:23.848 Position Close By (GBPUSD,M30)  SORT_BY_VOLUME
2020.08.20 14:23:23.848 Position Close By (GBPUSD,M30)  688545924 0.01
2020.08.20 14:23:23.848 Position Close By (GBPUSD,M30)  688546040 0.01
2020.08.20 14:23:23.848 Position Close By (GBPUSD,M30)  688545825 0.02
2020.08.20 14:23:23.848 Position Close By (GBPUSD,M30)  688545953 0.03
2020.08.20 14:23:23.848 Position Close By (GBPUSD,M30)  688546154 0.04
2020.08.20 14:23:23.848 Position Close By (GBPUSD,M30)  688546073 0.07

As you can see, the table is perfectly sorted by volume - that means you can create two tables (for BUY and for SELL), sort them by volume and then sequentially bypass the two tables and execute CloseBy.

Сортировка двухмерного массива.
Сортировка двухмерного массива.
  • 2015.03.21
  • www.mql5.com
Автоматические торговые системы: Сортировка двухмерного массива.
Files:
 
Vladimir Karputov:

I will show the algorithm (the basis is taken from )

We have such positions (pay attention only to the BUY positions):


Now we make a cycle, we enter only the BUY positions into the table (we enter the ticket and the volume).

After that, we sort the table - first by tickets, then by volume:

As you can see, the table is perfectly sorted by volume - that means you can create two tables (for BUY and for SELL), sort them by volume and then sequentially bypass the two tables and execute CloseBy.

Thats good but not solving the lotsize problem.
 
David Diez :
Thats good but not solving the lotsize problem.

Did you read it for sure? Everything is solved perfectly: the table is sorted by lot size. All that remains is to create two tables and sort them.

I recommend reading my post again. The problem has been resolved.

 

In general, the task is to divide positions into two sorted tables:


 
Vladimir Karputov:

A nested loop is dangerous and wrong.

You need to do one cycle: during the cycle, find opposite positions and memorize their tickets. After the loop, just apply CloseBy.


I gave an example in a post  - this is a simple way. But you can make it even more complicated: search for positions by lot size, by opposite profit ...

I'm sorry but this is both incorrect and ignorant. First, there is an implied misunderstanding of what CloseBy is and how it works. CloseBy is not a trade operation, it is an accounting operation. The CloseBy operation has absolutely no bearing on position's profit because its obly job is to instruct the trade serve to reconcile offsetting deals to be used as one order closing another. It does not matter what order you close them in, and time has absolutely no effect either. The most important thing you have missed with your code (You need to do one cycle) is because you scanning tickets first you will miss and skip deals still in the pool. You will miss them because when you use a larger one to close a smaller one that deal is modified and issued as a new position. That is not good. In order to account for the mutating order pool you need to either restart the entire loop (ugly and error prone) or use recursion so the function can call itself and restart the iteration over your freshly mutated position pool. David's code didn't work because he didn't use my code. He removed the most important abstractions and turned it into something else. 


In summary:

  1. CloseBy is not a trade operation. It doesn't do anything to P&L. All it does is allow one to treat a hedging account in the same way an opposite order will close another in a netting account. That's all.
  2. The order in which you call closeby does not matter. Sorting by size does not make any difference. Your P&L are locked the second you place the opposite position. CloseBy is only an accounting function. 
  3. Nested loops are not dangerous, but it only works correctly if you know how to make recursive functions, like I did.  
  4. If you don't use recursion you will miss position reconciliations due to the mutating position pool. 
 
nicholish en:


    Forum on trading, automated trading systems and testing trading strategies

    How to save on commissions by using PositionCloseBy

    nicholish en, 2020.08.19 20:23

    To all, 

    When using closeby, ticket order doesn't matter. What does matter is you use some kind of recursion to cleanup all your flattened positions and this is vitally important because if/when you use different position sizes in the closeby it will spawn new positions of smaller sizes. The key is to constantly widdle them down until there is none left. If you're not re-scanning the position pool after each call to closeby then you will have bugs and you will miss positions, I guarantee it. 

    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    bool closeByAll(const string symbol)
      {
       CPositionInfo pos;
       for(int i=PositionsTotal()-1; i>=0; --i)
         {
          if(pos.SelectByIndex(i) && pos.Symbol() == symbol)
            {
             ENUM_POSITION_TYPE type1 = pos.PositionType();
             ulong ticket1 = pos.Ticket();
             for(int j=i-1; j>=0; --j)
               {
                if(pos.SelectByIndex(j) && pos.Symbol() == symbol && pos.PositionType() != type1)
                  {
                   CTrade trade;
                   if(trade.PositionCloseBy(ticket1, pos.Ticket())
                  {
                   return closeByAll(symbol);
                     }
                   return false;
                  }
               }
            }
         }
       return true;
      }

    Misuse of the trade class. Why mindlessly create a new object at every tick at each iteration - after exiting the loop, the object is destroyed and then created again? This line should be moved to the area of global program variables (in the "header" of the advisor). Plus you don't initialize the created object.


    The right approach:

    //+------------------------------------------------------------------+
    //|                                                          ***.mq5 |
    //+------------------------------------------------------------------+
    #property version   "1.000"
    
    #include <Trade\PositionInfo.mqh>
    #include <Trade\Trade.mqh>
    #include <Trade\SymbolInfo.mqh>
    //---
    CPositionInfo  m_position;                   // object of CPositionInfo class
    CTrade         m_trade;                      // object of CTrade class
    CSymbolInfo    m_symbol;                     // object of CSymbolInfo class
    //--- input parameters
    ***
    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //---
       if(!m_symbol.Name(Symbol())) // sets symbol name
         {
          Print(__FILE__," ",__FUNCTION__,", ERROR: CSymbolInfo.Name");
          return(INIT_FAILED);
         }
       RefreshRates();
    //---
    ***
    //---
       m_trade.SetExpertMagicNumber(InpMagic);
       m_trade.SetMarginMode();
       m_trade.SetTypeFillingBySymbol(m_symbol.Name());
       m_trade.SetDeviationInPoints(InpDeviation);
    ***
    //---
       return(INIT_SUCCEEDED);
      }
     
    Vladimir Karputov:

    Misuse of the trade class. Why mindlessly create a new object at every tick at each iteration - after exiting the loop, the object is destroyed and then created again? This line should be moved to the area of global program variables (in the "header" of the advisor). Plus you don't initialize the created object.


    The right approach:


    I know you're better than this so I'm going to assume you overlooked the part of my code where CTrade can only be instantiated once per function call and only if a closeby condition exists. I don't think that global variables are the "right way" to handle anything in a programming project - unless that project is a quick-n-dirty script. Globals are sometimes unavoidable in MQL, I get that, but when you do have to use them you should never use the "m_" prefix because that is the naming convention for class [m]embers only. When you use the wrong naming convention you are only making global variables more dangerous than they already are by adding an extra layer of confusion into your source code. 

    Another thing I don't really understand about this community is the obsession with micro optimizations. Let's say for the sake of argument that the nanosecond gained by using a global dependency mattered. Why, then, are you not running a HFT written in optimized assembly in a custom kernel on custom hardware wired straight to the LP? No...This is an interpreted language running on a retail platform running inside of windows on 'whoknowswhat' hardware while choking on millions of nanoseconds of network latency. These micro optimizations don't matter, and in the case of CloseBy they really don't matter because I could literally introduce minutes of sleep in each iteration and it would not change a thing. Nothing. 

    So please tell me, why would I want to trade an encapsulated, pure, safe and reusable function for one that has a global dependency just to save a nanosecond and a few bytes of memory? 


    Let's pretend that we needed a micro-optimization but we also wanted to make clean reusable code. This is "the right approach"
    bool closeByAll(const string symbol) {
       static CPositionInfo pos;
       static CTrade trade;
       for(int i = PositionsTotal() - 1; i >= 0; --i) {
          if(pos.SelectByIndex(i) && pos.Symbol() == symbol) {
             ENUM_POSITION_TYPE type1 = pos.PositionType();
             ulong ticket1 = pos.Ticket();
             for(int j = i - 1; j >= 0; --j) {
                if(pos.SelectByIndex(j) && pos.Symbol() == symbol && pos.PositionType() != type1) {
                   if(trade.PositionCloseBy(ticket1, pos.Ticket()) {
                      return closeByAll(symbol);
                   }
                   return false;
                }
             }
             break;
          }
       }
       return true;
    }

    Plus you don't initialize the created object.

    Yeah, because you don't need to in order to use CloseBy. 

    Finally, if you take my minimal pure function and you want to make it work with your existing global dependencies then I expect that you should know how to do it. In my opinion, it's not acceptable to call something dangerous just because you don't understand it.

    Reason: