Code review of OrderSend

 

Hello

To the coders: Is there anything I particularly need to watch out for in creating an OrderSend for a live account? I've attached a piece of code that seems to work fine - in StrategyTester, probably even in Demo. I just want to make sure bases are covered.

Note: At this point I have no intention of running more that one EA at a time.

I also have questions about stop and limit orders:

a) Can I assume they wont be subject to requotes, unless price is moving fast and the requested fill level is close to the current market prices?

b) Do these types of orders (stop entry orders and limit orders) get filled at the requested level if the price 'gaps' past it, like over the weeked, for example?


Many thanks (for your constructive comments)!

/* ==========================================================================
|
|   Function :      CreateOrders
|
========================================================================== */
bool _CreateOrders(int type, double level, double slPrice, double tpPrice, string comment, double volume)
{ 
    bool rs = true; int check = 5;
          
    level = NormalizeDouble(level, Digits);
    slPrice = NormalizeDouble(slPrice, Digits);
    tpPrice = NormalizeDouble(tpPrice, Digits);
    
    if (type > 0) {
        if (AccountFreeMarginCheck(Symbol(), OP_BUY, volume) < 0) {
            Print("Not enough free margin");
            return(false); 
        }
    }
    if (type < 0) {
        if (AccountFreeMarginCheck(Symbol(), OP_SELL, volume) < 0) {
            Print("Not enough free margin");
            return(false); 
        }
    }
    while (check > 0) {
        if (IsTradeAllowed() && IsConnected()) {
            RefreshRates(); 
            rs = true;
            if (type == 1)
                ticket = OrderSend(Symbol(), OP_BUY, volume, Ask, slippage, slPrice, tpPrice, comment, magicNum);
            if (type == -1)
                ticket = OrderSend(Symbol(), OP_SELL, volume, Bid, slippage, slPrice, tpPrice, comment, magicNum);
            if (type == 2)
                ticket = OrderSend(Symbol(), OP_BUYSTOP, volume, level, slippage, slPrice, tpPrice, comment, magicNum);
            if (type == -2)
                ticket = OrderSend(Symbol(), OP_SELLSTOP, volume, level, slippage, slPrice, tpPrice, comment, magicNum);
            if (type == 3)
                ticket = OrderSend(Symbol(), OP_BUYLIMIT, volume, level, slippage, slPrice, tpPrice, comment, magicNum);
            if (type == -3)
                ticket = OrderSend(Symbol(), OP_SELLLIMIT, volume, level, slippage, slPrice, tpPrice, comment, magicNum);
    
            if (ticket < 0) {
                Print(ErrorDescription(GetLastError()));
                Print(type," ",volume," ",level," ",slPrice," ",tpPrice," ",comment," Bid: ",Bid," Ask: ",Ask);
                
                rs = false;
                Sleep(500); check--; 
            }
            else
                break;
        }
        else { 
            Sleep(500); check--; 
        }
    }
    return(rs);
}
 
HarriMQL4:

Hello

1.  To the coders: Is there anything I particularly need to watch out for in creating an OrderSend for a live account? I've attached a piece of code that seems to work fine - in StrategyTester, probably even in Demo. I just want to make sure bases are covered.

Note: At this point I have no intention of running more that one EA at a time.

I also have questions about stop and limit orders:

2. a) Can I assume they wont be subject to requotes, unless price is moving fast and the requested fill level is close to the current market prices?

2. b) Do these types of orders (stop entry orders and limit orders) get filled at the requested level if the price 'gaps' past it, like over the weeked, for example?


Many thanks (for your constructive comments)!

1.  this code won't work Live or Demo  if your Broker is an ECN type Broker . . .  more info: ECN

2. a   is your Slippage figure correctly adjusted for 4 and 5 digit Brokers ?

2. b   yes I believe they do.

To make your code more readable consider using the  Standard Constants for Trading instead of the way you curently use your type variable,  instead of if (type > 0)  you would use  if (type%2 == 0 )  for BuyXXX orders and  if (type%2 == 1)  for the SellXXX  orders . . .  and then use if (type == OP_BUY), if (type == OP_SELL), etc.

In the event of an issue with your OrderSend you report the error and relevant vaariables,  that is very good :-)  you might want to print the variables with the correct precision though,  on a 5 digit pair your Printed prices will be missing their last digit  . . .  use something like this . . .    DoubleToStr(Ask, Digits) 

You need to take notice of the StopLevel and FreezeLevel when trying to place orders . . .  you should read, understand and add code to comply with what is at this link:   Requirements and Limitations in Making Trades

 
HarriMQL4:

a) Can I assume they wont be subject to requotes, unless price is moving fast and the requested fill level is close to the current market prices?

b) Do these types of orders (stop entry orders and limit orders) get filled at the requested level if the price 'gaps' past it, like over the weeked, for example?

  1. Stop and Limit order become Market orders when price hits the open price. Won't be subject to requotes. Will be filled at the next available price. If price gaps past it, you'll be filled way past it, at the first available price.
  2. Your free margin check only checks if there is sufficient margin to open the order. As the market moves against you, free margin drops. To prevent a margin call you need to verify there will be free margin at the most adverse excursion (i.e. SL) for ALL open orders. See my code.
 

Thank you both for your comments, I really appreciate it!

@RaptorUk: Well-spotted on my slippage parameter now being adjusted for 5-digit broker! And I am not using an ECN broker.

@WHRoeder: I have the maximum leverage allowable set on the account, but only use a fraction of it when placing orders. This ensures that margin call is a very far distant possibility. Ie, stoploss will hit long before margin call.

 

New version:

bool _CreateOrders(int type, double level, double slPrice, double tpPrice, string comment, double volume)
{ 
    bool rs = true; int check = 5; double price;

    price = NormalizePrice(level);
    if (type == OP_BUY) price = Ask; if (type == OP_SELL) price = Bid;
    slPrice = NormalizePrice(slPrice);
    tpPrice = NormalizePrice(tpPrice);

    if (!checkOrder(1, type, price, slPrice, tpPrice, volume)) return(false);
        
    while (check > 0) {
        if (IsTradeAllowed() && IsConnected()) {
            RefreshRates(); 
            rs = true;
            
            if (requote && (MathRand() % 100) < RQF_TEST) {
                ticket = -1;
            }
            else
            {
                ticket = OrderSend(Symbol(), type, volume, price, slippage, slPrice, tpPrice, comment, magicNum);
            }    
            if (ticket < 0) {
                logError("_CreateOrders", "Could not open order");
                Print(type," Vol: ",volume," OpenPrice: "+DoubleToStr(price, Digits)+" SL: "+DoubleToStr(slPrice, Digits)+" TP: ",
                    DoubleToStr(tpPrice, Digits)+" Comment: ",comment," Bid: "+DoubleToStr(Bid, Digits)," Ask: "+DoubleToStr(Ask, Digits));
                
                rs = false;
                Sleep(500); check--; 
            }
            else
                break;
        }
        else { 
            logError("_CreateOrders", "Trade context busy");
            Sleep(500); check--; 
        }
    }
    return(rs);
}

Notes:

1. Lines about requote are to simulate requote in live environment. Lines wont be executed in Live

2. Changed NormalizeDouble -> NormalizePrice (from WHRoeder)  - https://www.mql5.com/en/forum/135572

3. checkOrder() fn - Requirements & Limitations in making trade operations

bool checkOrder(int opn, int type, double price, double slPrice, double tpPrice, double volume)
{
    bool rs = true;
    int x;
    //Assume normalized prices...
    rs = (rs && assert("checkOrder", (price != 0.0), "Zero level price!"));
    x = type%2;     
    rs = (rs && assert("checkOrder", (AccountFreeMarginCheck(Symbol(), x, volume) > 0), "Not enough free margin"));
    RefreshRates();
    
    if (opn == 1) { //OPENING ORDERS
        switch(type) {
            case OP_BUY: 
                rs = (rs && assert("checkOrder", (price == Ask), "You must open BUY @ Ask price"));
                rs = (rs && assert("checkOrder", (checkDbls(Bid-slPrice, ">=", stopL)), "Distance of stoploss to Bid price"));
                if (tpPrice != 0)
                    rs = (rs && assert("checkOrder", (checkDbls(tpPrice-Bid, ">=", stopL)), "Distance of takeprofit to Bid price"));
                break;
            case OP_SELL: 
                rs = (rs && assert("checkOrder", (price-Bid == 0), "You must open SELL @ Bid price"));
                if (slPrice != 0.0)
                    rs = (rs && assert("checkOrder", (checkDbls(slPrice-Ask, ">=", stopL)), "Distance of stoploss to Ask price"));
                if (tpPrice != 0.0)
                    rs = (rs && assert("checkOrder", (checkDbls(Ask-tpPrice, ">=", stopL)), "Distance of takeprofit to Ask price"));
                break;
            case OP_BUYLIMIT:
                rs = (rs && assert("checkOrder", (checkDbls(Ask-price, ">=", stopL)), "Ask to open price less than StopLevel"));
                rs = (rs && assert("checkOrder", (checkDbls(price-slPrice, ">=", stopL)), "StopLoss to open price less than StopLevel"));
                if (tpPrice != 0.0)
                    rs = (rs && assert("checkOrder", (checkDbls(tpPrice-price, ">=", stopL)), "Ask to open price less than StopLevel"));
                break;
            case OP_SELLLIMIT:
                rs = (rs && assert("checkOrder", (checkDbls(price-Bid, ">=", stopL)), "Open price to Bid is less than StopLevel"));
                if (slPrice != 0.0)
                    rs = (rs && assert("checkOrder", (checkDbls(slPrice-price, ">=", stopL)), "Open price to StopLoss is less than StopLevel"));
                rs = (rs && assert("checkOrder", (checkDbls(price-tpPrice, ">=", stopL)), "Open price to TakeProfit is less than StopLevel"));
                break;
            case OP_BUYSTOP:
                rs = (rs && assert("checkOrder", (checkDbls(price-Ask, ">=", stopL)), "Open price to Ask is less than StopLevel"));
                if (slPrice != 0.0)
                    rs = (rs && assert("checkOrder", (checkDbls(price-slPrice, ">=", stopL)), "Open price to StopLoss is less than StopLevel"));
                rs = (rs && assert("checkOrder", (checkDbls(tpPrice-price, ">=", stopL)), "Open price to TakeProfit is less than StopLevel"));
                break;
            case OP_SELLSTOP:
                rs = (rs && assert("checkOrder", (checkDbls(Bid-price, ">=", stopL)), "Bid to open price less than StopLevel"));
                if (slPrice != 0.0)
                    rs = (rs && assert("checkOrder", (checkDbls(slPrice-price, ">=", stopL)), "StopLoss to open price less than StopLevel"));
                if (tpPrice != 0.0)
                    rs = (rs && assert("checkOrder", (checkDbls(price-tpPrice, ">=", stopL)), "TakeProfit to open price less than StopLevel"));
                break;
        }
    }
    if (opn <= 0) { // CLOSING OR DELETING ORDERS
        switch(type) {
            case OP_BUYSTOP:
            case OP_BUYLIMIT:
                rs = (rs && assert("checkOrder", (MathAbs(Ask-price) > freeze), "Ask to OpenPrice less than Freeze"));
            case OP_BUY:
                rs = (rs && assert("checkOrder", (Bid-slPrice > MathMax(freeze, stopL)), "StopLoss to Bid less than Freeze/Stop"));
                if (tpPrice != 0.0)
                    rs = (rs && assert("checkOrder", (tpPrice-Bid > MathMax(freeze, stopL)), "TakeProfit to Bid less than Freeze/Stop"));
                break;

            case OP_SELLSTOP:
            case OP_SELLLIMIT:
                rs = (rs && assert("checkOrder", (MathAbs(Bid-price) > freeze), "Bid to OpenPrice less than Freeze"));
            case OP_SELL:
                rs = (rs && assert("checkOrder", (Ask-tpPrice > MathMax(freeze, stopL)), "Ask to TakeProfit less than Freeze/Stop"));
                if (slPrice != 0.0)
                    rs = (rs && assert("checkOrder", (slPrice-Ask > MathMax(freeze, stopL)), "StopLoss to Ask less than Freeze/Stop"));
                break;
        }
    }
    return(rs);
}

Right, I think I'm about ready to test!

Reason: