Closing multiple positions according FIFO rule

 

Hello,

According the new FIFO rules of NFA the first open position must be closed first.


At first glance I wrote this code:

int ClosePositions(string symbol)
{
    int orderstotal = OrdersTotal();
    int orders      = 0;
    int ordticket[100];
    
    for (int i = 0; i < orderstotal; i++) 
    {
        OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        
        if (OrderMagicNumber() != Expert_Magic || OrderSymbol() != symbol)
            continue;
        
        int orderType = OrderType();
        if (orderType != OP_BUY && orderType != OP_SELL)
            continue;
        
        ordticket[orders] = OrderTicket();
        orders++;
    }

    for (i = 0; i < orders; i++) 
        ClosePositionByTicket(ordticket[i]);
}

But this code has an issue. It assumes that the oldest open position has the lower index. It's true in the general case but when we have a partially closed position it acquires a new ticket and goes last in the index array.


Is there an elegant solution to arrange open positions' ticket in a sorted array?
 
Miroslav_Popov:

[...] Is there an elegant solution to arrange open positions' ticket in a sorted array?

I don't know about "elegant", but there's been previous discussion of this towards the end of https://www.mql5.com/en/forum/118657

 
Thank you!
 
Miroslav_Popov:
Is there an elegant solution to arrange open positions' ticket in a sorted array?

I don't know about 'elegant' either (that's a matter of opinion, isn't it?), but I imagine that using the built-in ArraySort() would be the most efficient way to do it since it's part of MQL4.

Something like this (i have not tested it, so maybe i missed something):


int ClosePositions(string symbol)
{
    int orderstotal = OrdersTotal();
    int orders      = 0;
    int ordticket[100][2];
    
    for (int i = 0; i < orderstotal; i++) 
    {
        OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        
        if (OrderMagicNumber() != Expert_Magic || OrderSymbol() != symbol)
            continue;
        
        int orderType = OrderType();
        if (orderType != OP_BUY && orderType != OP_SELL)
            continue;
        
        ordticket[orders][0] = OrderOpenTime();          // casting of datetime -> int, should work fine...
        ordticket[orders][1] = OrderTicket();
        orders++;
    }
    
    ArrayResize(ordticket,orders);                       // we have to remove rest of array otherwise it's contents will be sorted as well
    ArraySort(ordticket);                                // sorts ascending by first dimension -> by OrderOpenTime()

    for (i = 0; i < orders; i++) 
        ClosePositionByTicket(ordticket[i][1]);
}


Note that ArrayResize() and ArraySort() function's return value can be checked for error handling if needed (ArrayResize() returns # of elements in array or -1 if fail... not sure what ArraySort() returns, it's undocumented).

 
gordon:

Something like this (i have not tested it, so maybe i missed something): [...]

Very similar to https://www.mql5.com/en/forum/118657, except basically that it includes a check on symbol and magic-number, and assumes no more than 100 orders. The check on the order symbol is very sensible (and I can't think why I didn't include this). But part of the problem and confusion of the NFA's FIFO rule is that orders are offset per account, not per EA, so to speak. Therefore, I don't think it's appropriate to include the check on magic-number. The orders from different EAs on the same symbol are offset regardless of the fact that they're from different trading systems, and therefore the magic-number check will presumably be either (a) irrelevant (if only one EA is being run on the symbol), or (b) will cause things not to work properly (if multiple EAs are being run on the same symbol, and having their orders offset against each other).


But I've never personally been exposed to the effects of the FIFO rule, so I'm guessing.

 

Therefore, I don't think it's appropriate to include the check on magic-number.

Jjc, your note is completely reasonable. But this code is a part of much complex expert / application (if interested google Forex Strategy Trader).


By default the magic number is the same for all instances of the expert. That means several experts can run on different chart of one and the same symbol resulting to a more complex strategy. In that case the expert who receive a close order will close all the positions for that symbol in FIFO order. In other case, a user can separate the experts by individual magic number so he/she can run different strategies simultaneously on different charts. You are right, in that case the FIFO will be a problem if the broker complies with the rule.


FIFO must be considered when setting SL and TP also. The program doesn't use hedging and open multiple positions only due to the lack of adding functionality of MT4. After that the program calculates an aggregate position and control it. So after every change of the aggregate position (adding, reducing, modifying), the program modifies the SL and TP of all open positions in MetaTrader for the selected symbol / magic number. In that way all SL and TP are equal and all the positions must be closed simultaneously. I didn't test how this will be implemented in FIFO broker (if there is any working with MT4).

Files:
 
jjc:

Very similar to https://www.mql5.com/en/forum/118657, except basically that it includes a check on symbol and magic-number, and assumes no more than 100 orders. The check on the order symbol is very sensible (and I can't think why I didn't include this).

Sorry, didn't read that thread (my bad). I just modified Miroslav's code from above... So the magic number / symbol parts where already there.


Regarding using a fixed array size with one ArrayResize() call at the end vs. calling ArrayResize() on every iteration - there are pros and cons for each, specifically if the EA will be optimized extensively, ArrayResize() calls are relatively time consuming and in that case the first method might be preferable. Of course with the first method, the limit is there, but I don't know many EA's which have more than a 100 opened orders simultaneously... I guess it also comes down to preferred programming style.


Edit: ignore that. How many times is the EA going to close all orders? Not too many. Your method makes more sense.


But part of the problem and confusion of the NFA's FIFO rule is that orders are offset per account, not per EA, so to speak. Therefore, I don't think it's appropriate to include the check on magic-number. The orders from different EAs on the same symbol are offset regardless of the fact that they're from different trading systems, and therefore the magic-number check will presumably be either (a) irrelevant (if only one EA is being run on the symbol), or (b) will cause things not to work properly (if multiple EAs are being run on the same symbol, and having their orders offset against each other).

Good point. Agreed.

 
Miroslav_Popov:

FIFO must be considered when setting SL and TP also.

Just curious, what would happen if u try to modify TP/SL and they do not comply with the rule? Would the server return a error and not modify as u asked, or will it modify but not close later?

 

Just curious, what would happen if u try to modify TP/SL and they do not comply with the rule?


As I said, SL and TP in my expert are equal for all the open positions (called orders in MT4!!). I'm also curious how does a FIFO broker proceed in that case. Actually I think MT4 is not the best choice here. MT5 with the averaging capability will be much more appropriate.

 
Miroslav_Popov:

MT5 with the averaging capability will be much more appropriate

Averaging capability?

 

"Averaging capability?"


Adding to a currently open position, if you prefer.

Reason: