Using Trading Channels in an Expert Advisor
In the last module we implemented a cross-over strategy. If you’d like to review that module, click here: Implementing a Cross Over with MQL.
This is the first module of the Advanced Concepts course. In this module we will learn how to implement a channel strategy.
So, what is a channel? Really it’s just any two lines that can form upper and lower boundaries. In this chart below, the top and bottom lines of the Bollinger Bands indicator (iBands) can be used to create a channel.
One channel I like to use is the band formed using the highest high and the lowest low for the last 20 candles or so. This forms a channel with straight horizontal lines. This chart shows this type of channel.
Next, let’s see the MQL code for using a channel within an Expert Advisor …
We’ll use the MQL functions iHigh, https://www.iexpertadvisor.com/iHighest, iLow and iLowest to build our channel. This is the definition for the iHighest MQL function (the iLowest function is similar).
|
The symbol parameter is the currency symbol.
The timeframe parameter is the timeframe of the chart (30 minute, 1 hour, etc).
The type is the series identifier. This doesn’t make a lot of sense to me – I always set it to MODE_HIGH for the iHighest function and MODE_LOW for the iLowest function.
The count parameter is how far back to go – for our example we will look back 24 candles.
The start parameter is where to start. Start set to zero will use the current candle as a start.
Here is the tricky part: the iHighest function returns the index of the candle with the highest value – not the actual high value. So after we get the index of the highest candle, we use the MQL function iHigh to get the actual value. This is the definition of the iHigh function (the iLow function is similar).
double iHigh( | string symbol, int timeframe, int shift) |
The shift parameter is the index we’ll get from the iHighest function. The iHigh function will return the actual high value.
This is the MQL code for finding the highest and lowest values using iHighest and iLowest.
int high_bar = iHighest(Symbol(), Period(), MODE_HIGH, 24, 0);
double high = iHigh(Symbol(), Period(), high_bar );
int low_bar = iLowest(Symbol(), Period(), MODE_LOW, 24, 0);
double low = iLow(Symbol(), Period(), low_bar );
We’ll create a new user-function call “fnCheckChannel“. If the Ask price is higher than the channel, we’ll return OP_BUY. If the Bid price is lower than the channel, “fnCheckChannel” will return OP_SELL, otherwise the function will return -1.
This is the MQL code of our Expert Advisor making full use of the “fnCheckChannel” function. Notice where and how the function is used.
// these are all externs so they can be changed when the EA is attached to a chart
// the values set are default values
extern int stoploss=200;
extern int takeprofit=200;
extern double lots = 1.0;
extern int magic_number=12345;
extern int rsi_period=12;
extern double rsi_buy_level=75.0;
extern double rsi_sell_level=25.0;
extern int close_day=5;
extern int close_hour=14;
int start()
{
// get the rsi value
double rsi_value = iRSI(Symbol(), Period(), rsi_period, PRICE_CLOSE, 0);
// this variable will hold the number of trades open for this EA (as defined by magic number)
int my_trades=0;
// this variable will holds the total number of trades for the entire account
int all_trades=OrdersTotal();
// use a for loop to cycle through all of the trades, from 0 up to all_trades
for( int cnt=0;cnt<all_trades;cnt++ )
{
// use OrderSelect to get the info for each trade, cnt=0 the first time, then 1, 2, .., etc
// if OrderSelect fails it returns false, so we just continue
if( OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES) == false )
continue;
// compare the magic_number of our EA (as passed in as an input parameter) to the order’s magic number
// if they are equal, increment my_trades
if( magic_number == OrderMagicNumber() )
{
my_trades++;
if( OrderType() == OP_BUY )
{
// this is the explicit close logic for a buy order
// if the rsi OR it is friday
if( (rsi_value < rsi_sell_level) || (fnCheckExit(close_day, close_hour) == true) )
{
// use the ticket info to close
// note: the price should be Bid for a Buy order
// using OrderLots()will close the entire order
OrderClose(OrderTicket(), OrderLots(), Bid, 3, Green);
}
}
if( OrderType() == OP_SELL )
{
// this is the explicit close logic for a sell order
// if the rsi OR it is friday
if( (rsi_value > rsi_buy_level) || (fnCheckExit(close_day, close_hour) == true) )
{
// use the ticket info to close
// note: the price should be Ask for a Sell order
// using OrderLots()will close the entire order
OrderClose(OrderTicket(), OrderLots(), Ask, 3, Green);
}
}
}
}
// my_trades should either be 1 or 0. if it is greater than zero, then we just exit
if( my_trades > 0 )
return(0);
// call the fnCheckEntry to see if we should open a buy
if(fnCheckChannel() == OP_BUY)
{
// call our user function with a SELL parameter
fnOpenTrade(Symbol(), OP_BUY);
// exit after trying to open a trade
return(0);
}
// call the fnCheckEntry to see if we should open a sell
if(fnCheckChannel() == OP_SELL)
{
// call our user function with a SELL parameter
fnOpenTrade(Symbol(), OP_SELL);
// exit after trying to open a trade
return(0);
}
return(0);
}
void fnOpenTrade(string symbol, int type)
{
// notice that the symbol is passed in as a parameter
// get the bid value for whatever symbol was sent in
double my_bid = MarketInfo(symbol, MODE_BID);
// get the ask value for whatever symbol was sent in
double my_ask = MarketInfo(symbol, MODE_ASK);
// get the point value for whatever symbol was sent in
double my_point = MarketInfo(symbol, MODE_POINT);
// now instead of using the Bid, Ask and Point values that give the value for the
// symbol that the EA is running, use our values from above
int status =
OrderSend( symbol, // the synbol for this chart
type, // a buy order
lots, // number of lots
my_bid, // use the ask price for a BUY
3, // allow the price up to move 3 points
my_bid + (stoploss*my_point), // stop
my_ask – (takeprofit*my_point), // limit
“My Simple EA”, // comment to see in Terminal
magic_number, // a unique # to id this trade
0, // expiration, doesn’t work
Red // a blue arrow
);
if( status < 0 )
Comment(“OrderSend Failed!! Error=”, GetLastError());
}
// This function returns true to indicate it’s time to close
bool fnCheckExit(int day, int hour)
{
// if today is Friday(5) AND the hour is at, or past, 2:00 PM
if( (DayOfWeek() == day) && (Hour() >= hour) )
return(true);
return(false);
}
int fnCheckEntry()
{
double dx_plus_now = iADX(Symbol(), Period(), 14, PRICE_CLOSE, MODE_PLUSDI, 0);
double dx_plus_before = iADX(Symbol(), Period(), 14, PRICE_CLOSE, MODE_PLUSDI, 1);
double dx_minus_now = iADX(Symbol(), Period(), 14, PRICE_CLOSE, MODE_MINUSDI, 0);
double dx_minus_before = iADX(Symbol(), Period(), 14, PRICE_CLOSE, MODE_MINUSDI, 1);
if( (dx_plus_before < dx_minus_before) && (dx_plus_now > dx_minus_now) )
return(OP_BUY);
if( (dx_plus_before > dx_minus_before) && (dx_plus_now < dx_minus_now) )
return(OP_SELL);
return(-1);
}
int fnCheckChannel()
{
// get the bar (or candle) of the highest high
int high_bar = iHighest(Symbol(), Period(), MODE_HIGH, 24, 0);
// use the bar as an index to get the high value
double high = iHigh(Symbol(), Period(), high_bar );
// get the bar (or candle) of the lowest low
int low_bar = iLowest(Symbol(), Period(), MODE_LOW, 24, 0);
// use the bar as an index to get the low value
double low = iLow(Symbol(), Period(), low_bar );
// buy if the price breaks the high of the channel
if( Ask > high )
return(OP_BUY);
// sell if the price breaks the low of the channel
if( Bid < low )
return(OP_SELL);
return(-1);
}
Copy and paste this code into your MetaEditor and experiment with it. There is a minor flaw in the logic. If the price is flat for the entire channel, you can actually get a Buy and Sell signal at the same time! Try to figure out how to solve this problem. I’ll provide a solution in the next module.