Order Management & Trade Execution

CTrade class, order types, stop-loss, take-profit, trailing stops, and partial close operations.

Order Types in MT5

MT5 supports six order types, each serving a different purpose:

  • Market Orders — Execute immediately at the current price. ORDER_TYPE_BUY and ORDER_TYPE_SELL.
  • Limit Orders — Execute at a specified price or better. ORDER_TYPE_BUY_LIMIT (buy below current price) and ORDER_TYPE_SELL_LIMIT (sell above current price).
  • Stop Orders — Execute when price reaches a specified level. ORDER_TYPE_BUY_STOP (buy above current price) and ORDER_TYPE_SELL_STOP (sell below current price).
  • Stop-Limit Orders — A combination: when price reaches the stop level, a limit order is placed. ORDER_TYPE_BUY_STOP_LIMIT and ORDER_TYPE_SELL_STOP_LIMIT.

The CTrade Class

The standard library's CTrade class simplifies order execution:

#include <Trade/Trade.mqh>

CTrade trade;

void OnInit()
{
    trade.SetExpertMagicNumber(12345);
    trade.SetDeviationInPoints(10);  // max allowed slippage
    trade.SetTypeFilling(ORDER_FILLING_FOK);  // fill or kill
}

// Market orders
trade.Buy(0.1, _Symbol, ask, sl, tp, "Buy signal");
trade.Sell(0.1, _Symbol, bid, sl, tp, "Sell signal");

// Pending orders
trade.BuyLimit(0.1, limitPrice, _Symbol, sl, tp);
trade.SellStop(0.1, stopPrice, _Symbol, sl, tp);

// Close position
trade.PositionClose(_Symbol);

// Modify position SL/TP
trade.PositionModify(ticket, newSL, newTP);

Stop-Loss and Take-Profit

Always set stop-loss on every trade. It is your maximum acceptable loss per trade.

double CalculateSL(ENUM_ORDER_TYPE orderType, double entryPrice,
                   int slPoints)
{
    if(orderType == ORDER_TYPE_BUY)
        return NormalizeDouble(entryPrice - slPoints * _Point, _Digits);
    else
        return NormalizeDouble(entryPrice + slPoints * _Point, _Digits);
}

double CalculateTP(ENUM_ORDER_TYPE orderType, double entryPrice,
                   int tpPoints)
{
    if(orderType == ORDER_TYPE_BUY)
        return NormalizeDouble(entryPrice + tpPoints * _Point, _Digits);
    else
        return NormalizeDouble(entryPrice - tpPoints * _Point, _Digits);
}

Trailing Stop Implementation

A trailing stop moves the stop-loss in the direction of profit, locking in gains:

input int TrailingStop = 50;     // Trailing Stop (points)
input int TrailingStep = 10;    // Trailing Step (points)

void ManageTrailingStop()
{
    if(!PositionSelect(_Symbol)) return;

    double currentSL = PositionGetDouble(POSITION_SL);
    long posType = PositionGetInteger(POSITION_TYPE);
    ulong ticket = PositionGetInteger(POSITION_TICKET);

    if(posType == POSITION_TYPE_BUY)
    {
        double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
        double newSL = bid - TrailingStop * _Point;

        // Only move SL up, never down
        if(newSL > currentSL + TrailingStep * _Point)
        {
            double tp = PositionGetDouble(POSITION_TP);
            trade.PositionModify(ticket, NormalizeDouble(newSL, _Digits), tp);
        }
    }
    else if(posType == POSITION_TYPE_SELL)
    {
        double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
        double newSL = ask + TrailingStop * _Point;

        // Only move SL down, never up
        if(newSL < currentSL - TrailingStep * _Point || currentSL == 0)
        {
            double tp = PositionGetDouble(POSITION_TP);
            trade.PositionModify(ticket, NormalizeDouble(newSL, _Digits), tp);
        }
    }
}

Partial Close

Close part of a position to lock in partial profits:

void PartialClose(double closePercent)
{
    if(!PositionSelect(_Symbol)) return;

    double volume = PositionGetDouble(POSITION_VOLUME);
    double closeVolume = NormalizeDouble(volume * closePercent / 100.0, 2);

    // Ensure minimum lot size
    double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
    if(closeVolume < minLot) closeVolume = minLot;

    // Cannot close more than we have
    if(closeVolume >= volume) closeVolume = volume;

    ulong ticket = PositionGetInteger(POSITION_TICKET);
    trade.PositionClosePartial(ticket, closeVolume);
}

Error Handling

Always check the result of trade operations:

bool OpenBuyPosition(double lots, double sl, double tp)
{
    double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

    if(!trade.Buy(lots, _Symbol, ask, sl, tp, "Buy"))
    {
        Print("Buy failed. Error: ", GetLastError(),
              " Retcode: ", trade.ResultRetcode(),
              " Comment: ", trade.ResultComment());
        return false;
    }

    Print("Buy opened. Ticket: ", trade.ResultOrder(),
          " Price: ", trade.ResultPrice());
    return true;
}

Iterating Through Positions

void ListAllPositions()
{
    int total = PositionsTotal();
    for(int i = 0; i < total; i++)
    {
        ulong ticket = PositionGetTicket(i);
        if(ticket > 0)
        {
            string symbol = PositionGetString(POSITION_SYMBOL);
            double profit = PositionGetDouble(POSITION_PROFIT);
            long magic = PositionGetInteger(POSITION_MAGIC);

            Print("Ticket: ", ticket,
                  " Symbol: ", symbol,
                  " Profit: ", DoubleToString(profit, 2),
                  " Magic: ", magic);
        }
    }
}