Close a Basket of Trades on a Target Profit

This simple expert advisor will demonstrate techniques to monitor your trades as a basket and to close all when a target profit is reached.

The example expert itself will trade only in a selected direction and will open a trade if there are no currently open trades or if the market price has fallen far enough behind the lowest priced (for buy) trade or above the highest priced (for sell) trade. This will create the basket of trades which can be used to create an average position.

This code is for MT4 or MT5, differences are noted.

First create an expert advisor using the wizard.

For MT5 I will be using the CTrade class for opening and closing positions. To include that add this line after the properties.

#include <Trade/Trade.mqh>

Next the inputs

input int             InpMaxTrades = 10;         // Max number of trades
input double          InpTradeGap = 0.005;       // Minimum gap between trades
input ENUM_ORDER_TYPE InpType = ORDER_TYPE_BUY;  // Order type;
input double          InpMinProfit = 1.00;       // Profit in base currency
input int             InpMagicNumber = 1111;     // Magic number
input string          InpTradeComment = __FILE__; // Trade comment
input double          InpVolume = 0.01;          // Volume per order

Then I’m going to define a structure to use to store the total profit, count and trailing price

struct STradeSum {
   int count;
   double profit;
   double trailPrice;
};

For MT5, to use the CTrade class I have to create a variable of type CTrade

CTrade	Trade;

Then, because CTrade does not accept magic numbers in the position open methods I have to assign the magic number to the objact. Do this once only in the OnInit section

	Trade.SetExpertMagicNumber(InpMagicNumber);

The OnTick section is the same for both MT4 and MT5 but it does call on 3 functions (described further down) that do have differences between MT5 and MT4.

void OnTick() {
   STradeSum  sum;
	GetSum(sum);
	
	if (sum.profit>InpMinProfit) { // target reached
		CloseAll();
	} else
	if (sum.count==0) {	// no trades
		OpenTrade();
	} else
	if (sum.count<InpMaxTrades) {
		if (  InpType==ORDER_TYPE_BUY
            && SymbolInfoDouble(Symbol(), SYMBOL_ASK)<=(sum.trailPrice-InpTradeGap)
            ) {	//	Far enough below
			OpenTrade();
		} else
		if (  InpType==ORDER_TYPE_SELL
            && SymbolInfoDouble(Symbol(), SYMBOL_BID)>=(sum.trailPrice+InpTradeGap)
            ) {	//	Far enough above
			OpenTrade();
		}
	}
		
}

First this declares a variable to hold the summary data, then a call to GetSum to populate the data.

Next a series of conditional statements to determine what to do.

If the total profit has reached the target profit then close all trades, effectively starting again.

If there are no trades open (count==0) then just open a trade.

Finally, if the number of trades open is less than the maximum and the current price is far enough behind the trailing trade open another trade.

The OpenTrade function first determines the current opening price based on the trade type (buy or sell) then simply opens the order.

For MT4

void	OpenTrade() {
	double	price	=	(InpType==ORDER_TYPE_BUY) ?
                        SymbolInfoDouble(Symbol(), SYMBOL_ASK) :
                        SymbolInfoDouble(Symbol(), SYMBOL_BID);
	OrderSend(Symbol(), InpType, InpVolume, price, 0, 0, 0, InpTradeComment, InpMagicNumber);
	
}

For MT5 replace the OrderSend Line with

Trade.PositionOpen(Symbol(), InpType, InpVolume, price, 0, 0, InpTradeComment);

Both statements are similar and ask to open an order/position for the current chart symbol, input order type, input trade volume, zero stop loss and take profit and with the supplied trade comment. The MT4 call includes zero slippage and the magic number. For MT5 the Trade object does not include an argument for magic number but that was set earlier in the OnInit section.

The CloseAll function loop through all trades, matching the symbol, magic number and type against the expert advisor, and closing the trade if it matches. The decision to close all trades was done in the OnTick function so CloseAll only has to match the trades for this expert.

For MT4

void	CloseAll() {
	int	count		=	OrdersTotal();
	for (int i = count-1; i>=0; i--) {
		if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
			if (  OrderSymbol()==Symbol()
               && OrderMagicNumber()==InpMagicNumber
               && OrderType()==InpType
               ) {
				OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 0);
			}
		}
	}
}

For MT5

void	CloseAll() {
	int	count		=	PositionsTotal();
	for (int i = count-1; i>=0; i--) {
		ulong	ticket	=	PositionGetTicket(i);
		if (ticket>0) {
			if (	PositionGetString(POSITION_SYMBOL)==Symbol() 
					&& PositionGetInteger(POSITION_MAGIC)==InpMagicNumber
					&& PositionGetInteger(POSITION_TYPE)==InpType
					) {
				Trade.PositionClose(ticket);
			}
		}
	}
}

And the last part of the code is the GetSum function. This takes an argument of type STradeSum by reference.

First zero out the counters in the structure, then loop through all trades, very much like the CloseAll function, with the same loop technique and conditions.

At each trade simply increase count by one, add the trade profit to the running profit total with += and compare the price of the trade with the trail price to decide if this is a new trailing price

For MT4

void	GetSum(STradeSum &sum) {
   sum.count      =  0;
   sum.profit     =  0.0;
   sum.trailPrice =  0.0;
	
	int	count		=	OrdersTotal();
	for (int i = count-1; i>=0; i--) {
		if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
			if (  OrderSymbol()==Symbol()
               && OrderMagicNumber()==InpMagicNumber
               && OrderType()==InpType
               ) {
				sum.count++;
				sum.profit	+=	OrderProfit()+OrderSwap()+OrderCommission();
				if (InpType==ORDER_TYPE_BUY) {
					if (sum.trailPrice==0 || OrderOpenPrice()<sum.trailPrice) {
						sum.trailPrice	=	OrderOpenPrice();
					}
				} else
				if (InpType==ORDER_TYPE_SELL) {
					if (sum.trailPrice==0 || OrderOpenPrice()>sum.trailPrice) {
						sum.trailPrice	=	OrderOpenPrice();
					}
				}
			}
		}
	}
	
	return;
	
}

For MT5

void	GetSum(STradeSum &sum) {
   sum.count      =  0;
   sum.profit     =  0.0;
   sum.trailPrice =  0.0;
	
	int		count		=	PositionsTotal();
	for (int i = count-1; i>=0; i--) {
		ulong	ticket	=	PositionGetTicket(i);
		if (ticket>0) {
			if (	PositionGetString(POSITION_SYMBOL)==Symbol()
					&& PositionGetInteger(POSITION_MAGIC)==InpMagicNumber
					&& PositionGetInteger(POSITION_TYPE)==InpType) {
				sum.count++;
				sum.profit	+= PositionGetDouble(POSITION_PROFIT)+PositionGetDouble(POSITION_SWAP);
				if (InpType==ORDER_TYPE_BUY) {
					if (sum.trailPrice==0 || PositionGetDouble(POSITION_PRICE_OPEN)<sum.trailPrice) {
						sum.trailPrice	=	PositionGetDouble(POSITION_PRICE_OPEN);
					}
				} else
				if (InpType==ORDER_TYPE_SELL) {
					if (sum.trailPrice==0 || PositionGetDouble(POSITION_PRICE_OPEN)>sum.trailPrice) {
						sum.trailPrice	=	PositionGetDouble(POSITION_PRICE_OPEN);
					}
				}
			}
		}
	}
	
	return;
	
}

 

 

5 Comments

  1. You will get faster execution of the close if you instead of closing in a loop, set the TP for each order to the calculated average profit level.

  2. really appreciate your works in sharing the knowledge
    i do learn a lot from your video for my self learning thru open source tutorial in the web
    i am doing a trial in running martingale strategy on EUR USD , with often target profit around 0.0001 ~ 0.0002 .
    by having a bunch of open trade on different direction same time (maybe 20, 30 trade , mix of buy and sell ) , its taking too long time to close when the profit target achieved (i.e around 5 second for 10 trades)
    may i seek your help , if may assist with some codes that allowing update in MT4 of TP with calculation of profit target by reversed count

    its somewhere in the head but i could not really figure out the exact ways of doing it
    something like

    BuyLot = 3
    Total buy position : 15
    Targetprofit = usd 30
    Current price is 1.00000

    so the E.A will modified all 15 position TP to 1.00010 at once

    and when the position added , lets say become 4 lot, it will automatically re-update the TP again to allowed Targetprofit of 40 .

    1. Not sure what help is needed. You just want to calculate the average closing price, weighted by the size of each trade. simple (sum of (price*lots)) / total lots

    1. You should be able to just change the condition instead of total profit > target profit use (total profit)*-1 > (allowable loss). Since the total profit will be a negative number, specify the allowable loss as a positive (because it is more natural to input that way) then multiple the actual profit(loss) by -1.

Leave a Reply