[MT4][programming][intro] 3. How to write trade processing based on the implementation example of OnTick()

Here, we will check how to describe the trade process based on the implementation example for OnTick () called from the Event handler of mql4.
I hope it will be helpful in implementing OnTick().

Also, for the sample code that will be the format for implementing mql4 and the outline, first see the format file and outline of Experts Advisor.

Contents

Implementation example of OnTick() and how to write trade processing

1. Implementation example of OnTick()

First of all, I will introduce a sample implementation example of OnTick() below.
Here, OnInit() etc. are also described so that the code is up to just before it can operate to some extent.

sample code

NOTE 1:You need to set the conditions and values you want to implement.If it is left as it is, a compile error will occur because the setting values and conditions are not described.

/*****************************/
/* propety setting  ・・・ A */
/*****************************/
#property copyright   "2020-2021, x corp."
#property version     "1.00"
#property strict

/*****************************/
/* Input variables  ・・・ B */
/*****************************/
input double	Lots		= 1.0;

/******************************/
/* Global variables  ・・・ C */
/******************************/

/************************************/
/* Event Handing Functions ・・・ D */
/************************************/
int OnInit(void)
{
	int ret = INIT_SUCCEEDED;
	
	// If the number of lots for automatic trading is 0 or less, trading is not possible, so EA is stopped.
	if(Lots <= 0)	{ ret = INIT_PARAMETERS_INCORRECT; }
	
	return ret;
}

void OnDeinit(const int reason)
{
	// (No particular implementation this time)
	
	return;
}

void OnTick(void)
{
	// get the number of market and pending orders.
	int total	= OrdersTotal();
	
	// New position acquisition process
	if(total == 0)	{ CheckNewOrder();			}
	// Processing for acquired positions
	else			{ CheckOpendOrder(total);	}
	
	return;
}

void CheckNewOrder(void)
{
	if(/* Specify new ordering conditions(NOTE 1) */)
	{
		bool	isLong		= /* Set which position to have(NOTE 1) */;
		double	takeProfit	= /* Set take profit value(NOTE 1) */;
		double	stopLoss	= /* Set the stop loss value(NOTE 1) */;
		
		// For LONG position ordering
		if(isLong == true)
		{
			if(OrderSend(Symbol(),OP_BUY,Lots,Ask,3,stopLoss,takeProfit,"EA sample",16384,0,Green) <= 0)
			{
				Print("Error opening BUY order : ",GetLastError());
			}
		}
		// SHORT position order
		else
		{
			if(OrderSend(Symbol(),OP_SELL,Lots,Bid,3,stopLoss,takeProfit,"EA sample",16384,0,Red) <= 0)
			{
				Print("Error opening SELL order : ",GetLastError());
			}
		}
		
	}
	
	return;
}

void CheckOpendOrder(int total)
{
	int cnt;
	
	// Loop processing for the total number obtained above(Processing for each order number)
	for(cnt=0; cnt < total; cnt++)
	{
		// Select one order from the order table being traded.
		if(OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES) == false) { continue; }
		
		if((OrderType() <= OP_SELL) &&		// Is it an acquired position?
			(OrderSymbol() == Symbol()))	// Is it the currency pair of the chart adding EA?
		{
			/**************************************/
			/* Processing for acquired positions */
			/**************************************/
			// Implementation example:
			// 1. Close if Clsoe condition is met
			// 2. Trail processing that brings the stop loss a little closer to the current value when a certain amount of profit is obtained.
			// etc
			
			// Close processing for LONG position
			if(OrderType()==OP_BUY)
			{
				/* 1. Close If the condition is met, close */
				if(/* Specify close condition(NOTE 1) */)
				{
					// Send position close request
					if(OrderClose(OrderTicket() ,OrderLots(),Bid,3,Violet) == false)
					{
						// Error information output processing when Close processing fails
						Print("Error in OrderClose. Error code=",GetLastError());
					}
				}
				
				/* 2. Trail processing */
				if(/* Specify trail conditions(NOTE 1) */)
				{
					double newStopLoss = /* Set the stop loss value here(NOTE 1) */;
					
					if(OrderModify(OrderTicket(), OrderOpenPrice(), newStopLoss, OrderTakeProfit(), 0, CLR_NONE) == false)
					{
						Print("Error in OrderModify. Error code=",GetLastError());
					}
				}
			}
			// Close processing for SHORT position
			else
			{
				/* 1. close if the close condition is met */
				if(/* Specify close condition(NOTE 1) */)
				{
					// Send position close request
					if(OrderClose(OrderTicket() ,OrderLots(),Ask,3,Violet) == false)
					{
						// Error information output processing when Close processing fails
						Print("OrderClose error ",GetLastError());
					}
				}
				
				/* 2. Trail processing */
				if(/* Specify trail conditions(NOTE 1) */)
				{
					double newStopLoss = /* Set the stop loss value here(NOTE 1) */;
					
					if(OrderModify(OrderTicket(), OrderOpenPrice(), newStopLoss, OrderTakeProfit(), 0, CLR_NONE) == false)
					{
						Print("Error in OrderModify. Error code=",GetLastError());
					}
				}
			}
		}
	}
	
	return;
}

2. Explanation of implementation example of OnTick()

Next, let’s check each code based on the above sample code.

2-1. Description of the OnInit() function

This time, not only OnTick() but also OnInit() function is implemented for reference.
The following processing is implemented in the OnInit() function.

// If the number of lots for automatic trading is 0 or less, trading is not possible, so EA is stopped.
if(Lots <= 0)	{ ret = INIT_PARAMETERS_INCORRECT; }

This is checking the parameters that can be entered on the “Parameters” of Expert Advisor.
In this example, it is possible to input a parameter called “Lots”, and I prepared this as a parameter to specify the number of Lots when buying and selling automatically.

If the number of lots is 0 when trading automatically, there is no number to trade, so it is not necessary to operate Expert Advisor in the first place, so the input value of this parameter is checked with OnInit().

If 0 or less is specified, “INIT_PARAMETERS_INCORRECT” is returned as the return value, so Expert Advisor will stop operating after OnDeinit() is called.

2-2. Description of the OnTick() function

The following is a description of the OnTick () function, which is the purpose of this article.
As explained in the Expert Advisor format file and overview, OnTick() implements the core trading process for automated trading.

I think that the expected implementation contents are the following processes.
・Order processing for new positions
・Close processing of the position you are holding
・Trail processing of the position you are holding

This time, in the OnTick() function, in order to improve the readability of the above processing, it is divided into self-made functions as shown below and implemented.

void OnTick(void)
{
	// Acquire the current number of acquired positions and the total number of limit and stop orders
	int total	= OrdersTotal();
	
	// New position acquisition process
	if(total == 0)	{ CheckNewOrder();			}
	// Processing for acquired positions
	else			{ CheckOpendOrder(total);	}
	
	return;
}

CheckNewOrder()
This function is prepared as a function to execute “order processing of new position”.
It is called when there is no position currently held or pending order.

CheckOpendOrder()
This function is prepared as a function to execute “Settlement processing of the position held” and “Trail processing of the position held”.
It is called when there is a position currently held or a pending order.

The above self-made function is switched based on the result of the OrdersTotal() function in the library prepared by MT4.
The OrdersTotal () function is a function that returns the sum of the “number of positions held” and the “number of pending orders for limit / stop” in the current account.
(It is the same as the total number of orders you can see on the “trade” tab of MT4.)

Let’s take a look at the contents of each self-made function.

2-2-1. Description of CheckNewOrder() function

The CheckNewOrder() function is implemented so that long positions and short positions can be ordered respectively.

As for the conditions for taking a new position, please consider and implement the conditions for order processing when each person meets the conditions.
Also, in that case, consider whether to place an order with LONG, SHORT, or TakeProfit / StopLoss.
In the sample code, conditional expressions etc. are not implemented and comments are described.

LONG position ordering sample code
if(OrderSend(Symbol(),OP_BUY,Lots,Ask,3,stopLoss,takeProfit,"EA sample",16384,0,Green) <= 0)
{
	Print("Error opening BUY order : ",GetLastError());
}
SHORT position ordering sample code
if(OrderSend(Symbol(),OP_SELL,Lots,Bid,3,stopLoss,takeProfit,"EA sample",16384,0,Red) <= 0)
{
	Print("Error opening SELL order : ",GetLastError());
}

2-2-2. Description of CheckOpendOrder() function

Finally, a description of the CheckOpendOrder() function.
This function loops for the total number of “number of positions held” and “number of pending orders”, and for each order, “do you want to perform trail processing?” Or “timing to close?” It will be a process like confirming.

2-2-2-1. First, select / copy ORDER

The first process in the loop for each order first uses the OrderSelect() function to select one order.

// Select one order from the order table being traded.
if(OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES) == false) { continue; }

OrderSelect() is explained in MQL4 Reference as follows.

First argument:
Depending on the MODE of the second argument, whether to specify the “order ticket number” or “specify the number indexed with 0 origin of the order” changes.
If you specify an Order Ticket Number, specify that order number because each order is assigned a unique order number.
“Specify an order indexed number with 0 origin” is used when you want to check all orders in a loop for all orders without sticking to the order number.
This time, all orders are looped to check all orders, so the first argument is looped from “0” to “maximum number of orders-1”.

Second argument:
Specify whether the number passed in the first argument is the “order ticket number” or the “order indexed number”.
If you specify the Order Ticket Number, specify SELECT_BY_TICKET.
If you want to specify the indexed number of the order, specify SELECT_BY_POS.
In this sample, we want to specify the “order indexed number”, so we specify SELECT_BY_POS.

Third argument:
Specify whether to get the order that matches the information of the first argument and the second argument from the information in the “trade” tab of MT4 or from the information in the “account history” tab.
If you want to get information from “holding position or pending order in order”, you can get information from “trade” tab and “closed position or canceled pending order”. If you want to come, I try to get the information from the “account history” tab.
Specify MODE_TRADES if you want to get the information from the “trade” tab, and specify MODE_HISTORY if you want to get the information from the “account history” tab.

Once this OrderSelect() is executed, one order information will be copied internally.
Since the order information is copied when OrderSelect() is executed, it may be a little old information depending on the processing timing.If you implement to refer to the order information, try to get the latest information by executing OrderSelect() just before referring to the information.
The copied information can be read by executing the following function.

function nameWhat can be read in the copied order
OrderClosePrice()close price
OrderCloseTime()close time
OrderComment()comment
OrderCommission()commission
OrderExpiration()expiration
OrderLots()num of lots
OrderMagicNumber()identifying (magic) number
OrderOpenPrice()open price
OrderOpenTime()open time
OrderPrint()All of the following transaction information will be output on the “experts” tab of MT4.
ticket number; open time; trade operation; amount of lots; symbol; open price; Stop Loss; Take Profit; close time; close price; commission; swap; profit; comment; magic number; pending order expiration date.
OrderProfit()profit
OrderStopLoss()stop loss
OrderSwap()swap
OrderSymbol()symbol
OrderTakeProfit()take profit
OrderTicket()ticket nmber
OrderType()order tyoe.
The types of order types that can be obtained are as follows.
OP_BUY : buy order,
OP_SELL : sell order,
OP_BUYLIMIT : buy limit pending order,
OP_BUYSTOP : buy stop pending order,
OP_SELLLIMIT : sell limit pending order,
OP_SELLSTOP : sell stop pending order.
Functions that read the information copied by OrderSelect()

2-2-2-2. Is it an order you have?

At this point, you have selected / copied one order.
Next, check if the selected order is an in-holding order.

To check if the order is in possession, use OrderType () introduced in “Functions to read the information copied by OrderSelect ()”.

if((OrderType() <= OP_SELL) &&		// Is it an acquired position?
	(OrderSymbol() == Symbol()))	// Is it a symbol of the chart adding EA?
{
	//処理
}

It describes whether the result of OrderType() is OP_SELL or less.
You can see why such a description can be judged as “in-hold order” by checking the variation of the value returned by OrderType().

The values returned by OrderType() are as follows.

return value(define)define valuemeaning
OP_BUY0buy order
OP_SELL1sell order
OP_BUYLIMIT2buy limit pending order
OP_SELLLIMIT3sell limit pending order
OP_BUYSTOP4buy stop pending order
OP_SELLSTOP5sell stop pending order
return value of OrderType() from https://docs.mql4.com/constants/tradingconstants/orderproperties

Looking at the above, there are two matches for “In-hold orders”: “OP_BUY” and “OP_SELL”.
Looking at these two define values, “0” and “1” are defined, so if you confirm that “it is 1 or less”, you can confirm whether it is an “in-holding order”.

Therefore, if you check “Being less than OP_SELL” defined in 1, you can check whether it is “Order in possession”.

2-2-2-3. check for symbol

Next is the symbol check.
Normally, I think that there are many people who make it work only with the currency pair of Chart to which EA is added, so it check it.

To get the symbol of the Chart to which the EA has been added, use the Symbol () function.

if((OrderType() <= OP_SELL) &&		// Is it an acquired position?
	(OrderSymbol() == Symbol()))	// Is it a symbol of the chart adding EA?
{
	//処理
}

Since the symbol information is returned as a string type (character string), it is checked whether the symbol name of the selected order and the symbol name of the chart match.

2-2-2-4. close process

At this point, the “in-holding order” and “symbol with EA added” orders are selected. For this order, we will check if close processing or trail processing is necessary, and if necessary, execute them.

When the condition to close is met, the OrderClose() function is used to close the selected order.
In order to close the selected order, OrderTicket() is set in the first argument of OrderClose() to notify the ticket number of the selected order.

// Send position close request
if(OrderClose(OrderTicket() ,OrderLots(),Bid,3,Violet) == false)
{
	// Error information output processing when Close processing fails
	Print("Error in OrderClose. Error code=",GetLastError());
}
// Send position close request
if(OrderClose(OrderTicket() ,OrderLots(),Ask,3,Violet) == false)
{
	// Error information output processing when Close processing fails
	Print("OrderClose error ",GetLastError());
}

2-2-2-5. Trail processing

Finally, trail processing.
Let’s try to build / implement the conditions for “what is trigger for trail” and “how much to modify the stop loss” when the order you have is profitable to some extent.
When performing trail processing, use the OrderModify() function, which changes the information of the order currently held.
Again, in order to change the selected order, OrderTicket() is set in the first argument of OrderModify() to notify the ticket number of the selected order.

double newStopLoss = /* Set the stop loss value here(NOTE 1) */;

if(OrderModify(OrderTicket(), OrderOpenPrice(), newStopLoss, OrderTakeProfit(), 0, CLR_NONE) == false)
{
	Print("Error in OrderModify. Error code=",GetLastError());
}

2. Relationship with auto trading button

If you have read this far, you know that you also need to enable the “Auto Trading” button on MT4 in order for EA to work.
What is restricted in detail by this “auto trading” button in the processing of EA, there is the following description in Client Terminal Events of mql4.

The NewTick event is generated irrespective of whether automated trade is allowed or not (“Allow/prohibit Auto trading” button). The prohibition of automated trading denotes only that sending of trade requests from an Expert Advisor is not allowed, while the Expert Advisor keeps working.

https://docs.mql4.com/runtime/event_fire

Since it says “denotes only that sending of trade requests”, it seems that the specifications are such that only order operation requests such as OrderSend(), OrderClose(), and OrderModify() are actually restricted.

3. Finally

By now, you should have understood to some extent how to implement “new order”, “close”, and “trail” processing in the contents of OnTick().
Based on these, I would like to create your own EA and help you in discretionary trading.