Как написать простейший советник на MQL4?
Подобным вопросом наверняка задается любой новичок желающий освоить написание экспертов для торговли на рынке FOREX. При желании и некоторой сноровке эту задачу можно решить за один вечер.
Для начала сформулируем правила открытия и закрытия позиций.
Торговать будем по валютной паре евродоллар на пятиминутке используя сигналы индикаторов Moving Average и Stochastic. Покупаем, когда Moving Average растет и Stochastic снизился ниже уровня перепроданности. Продаем, когда Moving Average убывает и Stochastic вырос выше уровня перекупленности.
Чтобы облегчить задачу написания советника воспользуемся кодом советника Moving Average.mq4 из стандартной библиотеки MetaTrader 4.
//+------------------------------------------------------------------+//| Moving Average.mq4 |//| Copyright © 2005, MetaQuotes Software Corp. |//| http://www.metaquotes.net |//+------------------------------------------------------------------+#define MAGICMA 20050610extern double Lots = 0.1;extern double MaximumRisk = 0.02;extern double DecreaseFactor = 3;extern double MovingPeriod = 12;extern double MovingShift = 6;//+------------------------------------------------------------------+//| Calculate open positions |//+------------------------------------------------------------------+int CalculateCurrentOrders(string symbol) { int buys=0,sells=0;//---- for(int i=0;i<OrdersTotal();i++) { if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break; if(OrderSymbol()==Symbol() && OrderMagicNumber()==MAGICMA) { if(OrderType()==OP_BUY) buys++; if(OrderType()==OP_SELL) sells++; } }//---- return orders volume if(buys>0) return(buys); else return(-sells); }//+------------------------------------------------------------------+//| Calculate optimal lot size |//+------------------------------------------------------------------+double LotsOptimized() { double lot=Lots; int orders=HistoryTotal(); // history orders total int losses=0; // number of losses orders without a break//---- select lot size lot=NormalizeDouble(AccountFreeMargin()*MaximumRisk/1000.0,1);//---- calcuulate number of losses orders without a break if(DecreaseFactor>0) { for(int i=orders-1;i>=0;i--) { if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)==false) { Print("Error in history!"); break; } if(OrderSymbol()!=Symbol() || OrderType()>OP_SELL) continue; //---- if(OrderProfit()>0) break; if(OrderProfit()<0) losses++; } if(losses>1) lot=NormalizeDouble(lot-lot*losses/DecreaseFactor,1); }//---- return lot size if(lot<0.1) lot=0.1; return(lot); }//+------------------------------------------------------------------+//| Check for open order conditions |//+------------------------------------------------------------------+void CheckForOpen() { double ma; int res;//---- go trading only for first tiks of new bar if(Volume[0]>1) return;//---- get Moving Average ma=iMA(NULL,0,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE,0);//---- sell conditions if(Open[1]>ma && Close[1]<ma) { res=OrderSend(Symbol(),OP_SELL,LotsOptimized(),Bid,3,0,0,"",MAGICMA,0,Red); return; }//---- buy conditions if(Open[1]<ma && Close[1]>ma) { res=OrderSend(Symbol(),OP_BUY,LotsOptimized(),Ask,3,0,0,"",MAGICMA,0,Blue); return; }//---- }//+------------------------------------------------------------------+//| Check for close order conditions |//+------------------------------------------------------------------+void CheckForClose() { double ma;//---- go trading only for first tiks of new bar if(Volume[0]>1) return;//---- get Moving Average ma=iMA(NULL,0,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE,0);//---- for(int i=0;i<OrdersTotal();i++) { if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break; if(OrderMagicNumber()!=MAGICMA || OrderSymbol()!=Symbol()) continue; //---- check order type if(OrderType()==OP_BUY) { if(Open[1]>ma && Close[1]<ma) OrderClose(OrderTicket(),OrderLots(),Bid,3,White); break; } if(OrderType()==OP_SELL) { if(Open[1]<ma && Close[1]>ma) OrderClose(OrderTicket(),OrderLots(),Ask,3,White); break; } }//---- }//+------------------------------------------------------------------+//| Start function |//+------------------------------------------------------------------+void start() {//---- check for history and trading if(Bars<100 || IsTradeAllowed()==false) return;//---- calculate open orders by current symbol if(CalculateCurrentOrders(Symbol())==0) CheckForOpen(); else CheckForClose();//---- }//+------------------------------------------------------------------+
Немного изменим шапку эксперта и укажем свои внешние переменные:
//+------------------------------------------------------------------+//| alya-bob.mq4 |//| Copyright © 2011, AM2. |//| www.forexsystems.biz |//+------------------------------------------------------------------+#property copyright "Copyright © 2011, AM2."#property link "www.forexsystems.biz"#define MAGICMA 20110426extern double StopLoss = 0.0035;extern double TakeProfit = 0.007;extern double MovingPeriod = 20;extern double MovingShift = 0;extern int StochOversold = 30;extern int StochOverbought = 70;
Так как советник у нас простейший убираем из кода функции LotsOptimized() для расчета лота и CheckForClose() предназначенную для закрытия позиций при определенном условии.
Далее запишем значения необходимых индикаторов и условия открытия позиций в функции CheckForOpen():
double ma0=iMA(NULL,0,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE,0); double ma1=iMA(NULL,0,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE,1); double ma2=iMA(NULL,0,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE,2); double st=iStochastic(NULL,0,5,3,3,MODE_SMA,0,MODE_MAIN,0);//---- buy if(ma1>ma2 && ma0>ma1 && Close[1]>ma0 && st<StochOversold) { res=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,Ask-StopLoss,Ask+TakeProfit,"",MAGICMA,0,Blue); return; } //---- sell if(ma1<ma2 && ma0<ma1 && Close[1]<ma0 && st>StochOverbought) { res=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,Bid+StopLoss,Bid-TakeProfit,"",MAGICMA,0,Red); return; }
И в завершение нашего задания подкорректируем содержание функции start():
//+------------------------------------------------------------------+//| Start function |//+------------------------------------------------------------------+void start() {//---- check for history and trading if(Bars<100 || IsTradeAllowed()==false) return;//---- calculate open orders by current symbol if(CalculateCurrentOrders(Symbol())==0) CheckForOpen(); }//+------------------------------------------------------------------+
Вот и весь эксперт!
//+------------------------------------------------------------------+//| alya-bob.mq4 |//| Copyright © 2011, AM2. |//| www.forexsystems.biz |//+------------------------------------------------------------------+#property copyright "Copyright © 2011, AM2."#property link "www.forexsystems.biz"#define MAGICMA 20110426extern double StopLoss = 0.0035;extern double TakeProfit = 0.007;extern double MovingPeriod = 20;extern double MovingShift = 0;extern int StochOversold = 30;extern int StochOverbought = 70;extern double Lots = 0.1;//+------------------------------------------------------------------+//| Calculate open positions |//+------------------------------------------------------------------+int CalculateCurrentOrders(string symbol) { int buys=0,sells=0;//---- for(int i=0;i<OrdersTotal();i++) { if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break; if(OrderSymbol()==Symbol() && OrderMagicNumber()==MAGICMA) { if(OrderType()==OP_BUY) buys++; if(OrderType()==OP_SELL) sells++; } }//---- return orders volume if(buys>0) return(buys); else return(-sells); }//+------------------------------------------------------------------+//| Check for open order conditions |//+------------------------------------------------------------------+void CheckForOpen() {//---- go trading only for first tiks of new bar if(Volume[0]>1) return;//---- get Moving Average int res; //---- double ma0=iMA(NULL,0,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE,0); // Значение Moving Average на текущем баре double ma1=iMA(NULL,0,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE,1); // Значение Moving Average на предыдущем баре double ma2=iMA(NULL,0,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE,2); // Значение Moving Average на предпредыдущем баре double st=iStochastic(NULL,0,5,3,3,MODE_SMA,0,MODE_MAIN,0);//---- buy if(ma1>ma2 && ma0>ma1 && Close[1]>ma0 && st<StochOversold) // Moving Average растет, Stochastic ниже уровня перепроданности и цена выше Moving Average { res=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,Ask-StopLoss,Ask+TakeProfit,"",MAGICMA,0,Blue); return; } //---- sell if(ma1<ma2 && ma0<ma1 && Close[1]<ma0 && st>StochOverbought) // Moving Average убывает, Stochastic выше уровня перекупленности и цена ниже Moving Average { res=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,Bid+StopLoss,Bid-TakeProfit,"",MAGICMA,0,Red); return; } } //+------------------------------------------------------------------+//| Start function |//+------------------------------------------------------------------+void start() {//---- check for history and trading if(Bars<100 || IsTradeAllowed()==false) return;//---- calculate open orders by current symbol if(CalculateCurrentOrders(Symbol())==0) CheckForOpen(); }//+----------------------------------------------------------------