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

https://www.youtube.com/watch?v=cKJweUr0fTw

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;
	
}

 

 

Leave a Reply