How to safely open an order in MQL4

All the functions discussed here are part of the Common library available for purchase.




In trading, it's critical to confirm certain criteria and conditions before opening an order. These checks may impact on your profitability or notify you in case something failed.

My checks


For me, it's important to verify before opening an order in MQL4 the following conditions:
- Is the spread acceptable? i.e. whether it's below a certain fixed value which I predefined.
- Is the trade context busy? This thread is the one which eventually opens the orders, if busy, it will not be possible to open the order anyway.
- Do we have enough margin to trade? As noted here, margin is critical factor when trading and could make a difference between a successful account to wiped one.
- Check open order returned value. As per MQL4 specifications, open order returns the order id which was opened or -1 in case it failed - https://docs.mql4.com/trading/ordersend. It is a good practice to check the value before doing anything with that order.

Coding it all


Let's build the code that does these checks and open an order if all conditions are satisfied. The function signature is:
int OpenOrder(string argSymbol,int argOrderType,double argLotSize,double argSL=0,double argTP=0,string argComment=NULL,int argMagicNumber=0,int argSpread=0) export

The function returns the value received from OrderSend function, or -1 if the function fails from any reason.
1. argSymbol is the symbol we want to open order on.
2. argOrderType whether it's OP_BUY or OP_SELL
3. argLotSize - how many lots we want to trade
4. argSL - optional argument holding the order stop loss level. If the order is a Buy, then the stop loss must be below the open price, otherwise, for a sell, the stop loss must be above the open price.
5. argTP - optional argument holding the order take profit level. If the order is a Buy, then the take profit must be above the open price, otherwise, for a sell, the take profit must be below the open price.
6. argComment - optional string to use as the order's comment. Please note, that If the order comment length is above 31 characters. the mt4 server will automatically reject the order.
7. argMagicNumber - optional integer representing the order magic number, a value which can be used later to link order to an expert adviser.
8. argSpread - an optional value used as a spread threshold to open an order.

putting it all together



  • starting from the last argument, if provided I will not open an order, if the symbol spread is larger than the value:
    if(argSpread>0 && MarketInfo(argSymbol,MODE_SPREAD)>argSpread)
    {
    SendNotification(StringConcatenate("Open order declined, symbol spread ",DoubleToString(MarketInfo(argSymbol,MODE_SPREAD),0)," is larger than ",IntegerToString(argSpread)));
    return -1;
    }

    Using the latest spread value for this symbol, I reject it if it's larger.

  • Next, I am checking whether the trade context, the thread which is responsible for the order themselves is busy. If it is, I will reject the open order request:
    else if(IsTradeContextBusy())
    {
    SendNotification("Open order declined, trade context is busy");
    return -1;
    }

  • If the function was called with an order type different than OP_BUY/OP_SELL this code will reject if(argOrderType!=OP_BUY && argOrderType!=OP_SELL)
    {
    SendNotification(StringConcatenate("Open order declined, illegal order type = ",IntegerToString(argOrderType)));
    return -1;
    }

  • We then check for enough margin to make this order, but calling a function I mentioned in my post about margin:
    else if(ConfirmEnoughMargin(argSymbol,argOrderType,argLotSize)==false)
    {
    SendNotification(StringConcatenate("Open order declined, not enough equity to open new position, remaining equity if opened = ",DoubleToString(AccountFreeMarginCheck(argSymbol,argOrderType,argLotSize),2)," ",AccountCurrency()));
    return -1;
    }

    Where ConfirmEnoughMargin is defined as:
    bool ConfirmEnoughMargin(string argSymbol,int argOrderType,double argLotSize) export
    {
    if(AccountStopoutMode()==0 && MathRound(AccountMargin()+AccountFreeMargin()-AccountFreeMarginCheck(argSymbol,argOrderType,argLotSize))!=0 && (100*AccountEquity()/(AccountMargin()+AccountFreeMargin()-AccountFreeMarginCheck(argSymbol,argOrderType,argLotSize)))<AccountStopoutLevel() && GetLastError()==134)
    {
    SendNotification(StringConcatenate("Confrim enough margin failed with account stopout mode 0, free margin if opened will be ",DoubleToString(AccountFreeMarginCheck(argSymbol,argOrderType,argLotSize),2)),134);
    return false;
    }
    else if(AccountStopoutMode()==1 && AccountFreeMarginCheck(argSymbol,argOrderType,argLotSize)<AccountStopoutLevel() && GetLastError()==134)
    {
    SendNotification(argSymbol,argLotSize,StringConcatenate("Confrim enough margin failed with account stopout mode 1, free margin if opened will be ",DoubleToString(AccountFreeMarginCheck(argSymbol,argOrderType,argLotSize),2)),134);
    return false;
    }
    return true;
    }

  • Finally, if all the checks passed successfully, we call MQL4 internal OrderSend function and return the value:
    else
    {
    int Order=OrderSend(argSymbol,argOrderType,VerifyLotSize(argSymbol,argLotSize),argOrderType==OP_BUY?MarketInfo(argSymbol,MODE_ASK):MarketInfo(argSymbol,MODE_BID),Slippage,NormalizeDouble(argSL,(int)MarketInfo(argSymbol,MODE_DIGITS)),NormalizeDouble(argTP,(int)MarketInfo(argSymbol,MODE_DIGITS)),argComment==NULL?MQLInfoString(MQL_PROGRAM_NAME):argComment,argMagicNumber,0,argOrderType==OP_BUY?clrGreen:clrRed);
    if(Order==-1)
    LogError(argSymbol,argLotSize,"Open order failed");
    return Order;
    }



The whole function is:
int OpenOrder(string argSymbol,int argOrderType,double argLotSize,double argSL=0,double argTP=0,string argComment=NULL,int argMagicNumber=0,int argSpread=0) export
{
if(argSpread>0 && MarketInfo(argSymbol,MODE_SPREAD)>argSpread)
{
LogError(argSymbol,argLotSize,StringConcatenate("Open order declined, symbol spread ",DoubleToString(MarketInfo(argSymbol,MODE_SPREAD),0)," is larger than ",IntegerToString(argSpread)));
return -1;
}
else if(IsTradeContextBusy())
{
LogError(argSymbol,argLotSize,"Open order declined, trade context is busy");
return -1;
}
if(argOrderType!=OP_BUY && argOrderType!=OP_SELL)
{
LogError(argSymbol,argLotSize,StringConcatenate("Open order declined, illegal order type = ",IntegerToString(argOrderType)));
return -1;
}
else if(ConfirmEnoughMargin(argSymbol,argOrderType,argLotSize)==false)
{
LogError(argSymbol,argLotSize,StringConcatenate("Open order declined, not enough equity to open new position, remaining equity if opened = ",DoubleToString(AccountFreeMarginCheck(argSymbol,argOrderType,argLotSize),2)," ",AccountCurrency()));
return -1;
}
else
{
int Order=OrderSend(argSymbol,argOrderType,VerifyLotSize(argSymbol,argLotSize),argOrderType==OP_BUY?MarketInfo(argSymbol,MODE_ASK):MarketInfo(argSymbol,MODE_BID),Slippage,NormalizeDouble(argSL,(int)MarketInfo(argSymbol,MODE_DIGITS)),NormalizeDouble(argTP,(int)MarketInfo(argSymbol,MODE_DIGITS)),argComment==NULL?MQLInfoString(MQL_PROGRAM_NAME):argComment,argMagicNumber,0,argOrderType==OP_BUY?clrGreen:clrRed);
if(Order==-1)
LogError(argSymbol,argLotSize,"Open order failed");
return Order;
}
}

Comments