This is an example of writing an expert advisor using a very basic strategy. The key information here is the comparison of 2 indicators which occur at different times.
The strategy and detail about the code are explained fully in the video.
This covers both MT4 and MT5, there are some sections that are modified between MT4 and MT5.
Begin by using the wizard to create the expert outline.
For MQL5 begin by including the CTrade and CPositionInfo classes and create variables to use them
#include <Trade/Trade.mqh> CTrade Trade; CPositionInfo PositionInfo;
Then add inputs for the CCI and moving averages and the basic information for the expert. This is common between MQL4 and MQL5.
// CCI Inputs input int InpCCIPeriod = 14; // CCI Period input ENUM_APPLIED_PRICE InpCCIPrice = PRICE_CLOSE; // CCI Applied price // Fast MA Inputs input int InpFastMAPeriods = 14; // Fast MA periods input ENUM_MA_METHOD InpFastMAMethod = MODE_EMA; // Fast MA Method input ENUM_APPLIED_PRICE InpFastMAPrice = PRICE_CLOSE; // Fast MA Price // Slow MA Inputs input int InpSlowMAPeriods = 28; // Slow MA periods input ENUM_MA_METHOD InpSlowMAMethod = MODE_EMA; // Slow MA Method input ENUM_APPLIED_PRICE InpSlowMAPrice = PRICE_CLOSE; // Slow MA Price // Standard trading inputs input double InpVolume = 0.01; // Volume input int InpMagicNumber = 212121; // Magic Number input string InpTradeComment = "CCI2MA"; // Trade comment
Now for MQL5 add handles and buffers for the CCI and 2 moving average indicators.
// Indicator handles and buffers int HandleFastMA; int HandleSlowMA; int HandleCCI; double BufferFastMA[]; double BufferSlowMA[]; double BufferCCI[];
Also only for MQL5 add the following inside the OnInit section to initialise the indicator handles, set the buffers as series, and add the magic number to the Trade object
HandleFastMA = iMA(Symbol(), Period(), InpFastMAPeriods, 0, InpFastMAMethod, InpFastMAPrice);
HandleSlowMA = iMA(Symbol(), Period(), InpSlowMAPeriods, 0, InpSlowMAMethod, InpSlowMAPrice);
HandleCCI = iCCI(Symbol(), Period(), InpCCIPeriod, InpCCIPrice);
if (HandleFastMA==INVALID_HANDLE || HandleSlowMA==INVALID_HANDLE || HandleCCI==INVALID_HANDLE) {
Print("Failed to initialise indicator buffers");
return(INIT_FAILED);
}
ArraySetAsSeries(BufferFastMA, true);
ArraySetAsSeries(BufferSlowMA, true);
ArraySetAsSeries(BufferCCI, true);
Trade.SetExpertMagicNumber(InpMagicNumber);
and also for MQL5 remove the handles in the OnDeinit function
IndicatorRelease(HandleFastMA); IndicatorRelease(HandleSlowMA); IndicatorRelease(HandleCCI);
For both MQL4 and MQL5 add the NewBar, SymbolAsk and SymbolBid functions. I mention them here because they will be used in the OnTick section next but I also keep them at the end of the code because they are trivial utility functions
bool NewBar() {
static datetime current = 0;
datetime now = iTime(Symbol(), Period(), 0);
if (now == current) return(false);
current = now;
return(true);
}
double SymbolAsk() { return(SymbolInfoDouble(Symbol(), SYMBOL_ASK)); }
double SymbolBid() { return(SymbolInfoDouble(Symbol(), SYMBOL_BID)); }
Back to the OnTick function, the structure is the same for MQL4 and MQL5 but there are differences in the way the indicator values are retrieved. First for MQL4
void OnTick() {
if (!(bool)TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) return;
static int maCross = 0;
static int cciCross = 0;
bool newBar = NewBar();
if (newBar) {
double cci1 = iCCI(Symbol(), Period(), InpCCIPeriod, InpCCIPrice, 1);
double cci2 = iCCI(Symbol(), Period(), InpCCIPeriod, InpCCIPrice, 2);
if (cci1>0 && cci2<=0) cciCross = 1;
if (cci1<0 && cci2>=0) cciCross = -1;
double fastMa1 = iMA(Symbol(), Period(), InpFastMAPeriods, 0, InpFastMAMethod, InpFastMAPrice, 1);
double fastMa2 = iMA(Symbol(), Period(), InpFastMAPeriods, 0, InpFastMAMethod, InpFastMAPrice, 2);
double slowMa1 = iMA(Symbol(), Period(), InpSlowMAPeriods, 0, InpSlowMAMethod, InpSlowMAPrice, 1);
double slowMa2 = iMA(Symbol(), Period(), InpSlowMAPeriods, 0, InpSlowMAMethod, InpSlowMAPrice, 2);
if (fastMa1>slowMa1 && fastMa2<=slowMa2) {
maCross = 1;
CloseTrades(ORDER_TYPE_SELL);
}
if (fastMa1<slowMa1 && fastMa2>=slowMa2) {
maCross = -1;
CloseTrades(ORDER_TYPE_BUY);
}
if (maCross!=0 && maCross==cciCross) {
if (OpenTrade(maCross)>0) {
maCross = 0;
cciCross = 0;
}
}
}
}
and now the MQL5 version
void OnTick() {
if (!(bool)TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) return;
static int maCross = 0;
static int cciCross = 0;
bool newBar = NewBar();
if (newBar) {
CopyBuffer(HandleCCI, 0, 0, 3, BufferCCI);
double cci1 = BufferCCI[1];
double cci2 = BufferCCI[2];
if (cci1>0 && cci2<=0) cciCross = 1;
if (cci1<0 && cci2>=0) cciCross = -1;
CopyBuffer(HandleFastMA, 0, 0, 3, BufferFastMA);
CopyBuffer(HandleSlowMA, 0, 0, 3, BufferSlowMA);
double fastMa1 = BufferFastMA[1];
double fastMa2 = BufferFastMA[2];
double slowMa1 = BufferSlowMA[1];
double slowMa2 = BufferSlowMA[2];
if (fastMa1>slowMa1 && fastMa2<=slowMa2) {
maCross = 1;
CloseTrades(POSITION_TYPE_SELL);
}
if (fastMa1<slowMa1 && fastMa2>=slowMa2) {
maCross = -1;
CloseTrades(POSITION_TYPE_BUY);
}
if (maCross!=0 && maCross==cciCross) {
if (OpenTrade(maCross)>0) {
maCross = 0;
cciCross = 0;
}
}
}
}
In MQL5 the indicators are already established so the code is to retrieve the values into the buffer arrays and get the needed values there
That completes the expert except for the 2 functions Close Trades and OpenTrade
For MQL4
bool CloseTrades(ENUM_ORDER_TYPE type) {
bool result = true;
double closingPrice = (type==ORDER_TYPE_BUY) ? SymbolBid() : SymbolAsk();
int cnt = OrdersTotal();
for(int i = cnt - 1; i >= 0; i--) {
if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
result = false;
continue;
}
if (OrderSymbol()!=Symbol() || OrderMagicNumber()!=InpMagicNumber) continue;
if ( OrderType()==type) {
result &= OrderClose(OrderTicket(), OrderLots(), closingPrice, 0);
}
}
return(result);
}
int OpenTrade(int maCross) {
if ( maCross<0 ) {
return(OrderSend(Symbol(), ORDER_TYPE_SELL, InpVolume, SymbolBid(), 0, 0, 0, InpTradeComment, InpMagicNumber));
}
if ( maCross>0 ) {
return(OrderSend(Symbol(), ORDER_TYPE_BUY, InpVolume, SymbolAsk(), 0, 0, 0, InpTradeComment, InpMagicNumber));
}
return(0);
}
and for MQL5
bool CloseTrades(ENUM_POSITION_TYPE type) {
bool result = true;
int cnt = PositionsTotal();
for(int i = cnt - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (ticket<=0) {
result = false;
continue;
}
if (PositionInfo.Symbol()!=Symbol() || PositionInfo.Magic()!=InpMagicNumber) continue;
if (PositionInfo.PositionType()==type) {
result &= Trade.PositionClose(ticket);
}
}
return(result);
}
bool OpenTrade(int maCross) {
if ( maCross<0 ) {
return(Trade.PositionOpen(Symbol(), ORDER_TYPE_SELL, InpVolume, SymbolBid(), 0, 0, InpTradeComment));
}
if ( maCross>0 ) {
return(Trade.PositionOpen(Symbol(), ORDER_TYPE_BUY, InpVolume, SymbolAsk(), 0, 0, InpTradeComment));
}
return(true);
}
