【FX自動売買】EA入門 ローソク足を基準に決済する関数【MT4】

この記事は【EAの教科書シリーズ】の一部です。全記事は以下から確認できます。

MT4をまだダウンロードしていない方はこちらのページでダウンロード方法を案内しているので、事前にMT4を用意しておいてください。

今回作る関数の目的と概要

トレードの終え方は人によって異なります。一定の損切りポイントや利確ポイントで取引を終了するトレーダーもいれば、チャート上のローソク足のルールに従って終了するトレーダーもいます。

今回この関数を作る目的は今後解説する予定の取引戦略にローソク足を基準とした取引手法が含まれているためです。

関数名はCandleClose()とします。

今回使うグローバル変数

  • extern bool UseCandleClose:これまで紹介した関数と同様、この関数を使用するかどうかを設定するための真偽値です。デフォルト値はtrueとします。
     
  • extern int CloseAfterCandles:ローソク足が何本でき終わったらトレードを終了したいかを指定するための変数です。これが1の場合はローソク足が1本出来上がるタイミングでトレードを終了することになります。

今回使うローカル変数

  • int period:これには関数Period()の戻り値が代入されます。Period()関数はEAが動作中のチャートの時間軸の値を返します。例えば1分足チャート上でこのEAを動作させていれば1が返り、5分足チャート上であれば5が返されます。単位が分なので、4時間足チャートで実行していた場合、60 x 4 = 240で240が返されます。
     
  • int period2:この変数には後にスイッチ文で適切な値を代入するので、初期値は0にしておきます。この変数は時間軸の単位を秒にした値が代入されます。たとえば1分足上でEAを実行していた場合は60、1時間足であれば3600が代入されます。

この関数の使われ方

この関数はトレード中の注文が一つでもある場合にティック毎に呼び出されるようにします。ですので、以前作成したBreakEven()関数とTrailingStop()関数が配置されている場所と同じ括弧内に置かれることになります。

関数のコード

以下が今回導入する関数のコードになります。

void CandleClose() //1
{
int period=Period(); //2
int period2=0; //3

   switch(period) //4
      {
      case 1:period2=60; break;
      case 5:period2=300; break;
      case 15:period2=900; break;
      case 30:period2=1800; break;
      case 60:period2=3600; break;
      case 240:period2=14400; break;
      case 1440:period2=86400; break;
      case 10080:period2=604800; break;
      case 43200:period2=2592000; break;
      }
   
   for(int i=OrdersTotal();i>0;i--) //5
      {
      if(OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES)) //6
         {
         if(TimeCurrent()-OrderOpenTime()>period2*CloseAfterCandles) //7
            {
            CloseAllOrders(); //8
            }
         }
      }
   return;
}
  1. まずは関数の宣言からです。戻り値はないのでvoidとし、名前をCandleClose()とします。
     
  2. EAの動作しているチャートの時間軸の分単位を保存するための変数periodを宣言します。Period()関数の戻り値を代入することで、現在の時間軸の分が得られます。
     
  3. 次に時間軸を秒単位で保存するための変数period2を宣言します。あとでスイッチ文で適切な値を代入するので0で初期化しておきます。
     
  4. period2に適切な値を代入するためのスイッチ文です。switchのあとの()内に入っている値で、条件分岐することができるので、今回はperiodを使います。caseのあとに値を指定し、periodがその値だった場合の処理をその後に書きます。
    case 1:period2=60;であれば、periodの値が1だった場合、period2に60を代入するという意味です。それぞれのcase文の最後にあるbreak;はスイッチ文から抜け出すことを意味します。これがない場合、そのあとのcase文の中身も実行されてしまうので必ずbreakをつけることを忘れないでください。
     
  5. 注文の数だけ繰り返すfor文です。
     
  6. 全ての注文の中から一つの注文を選択しています。for文は全ての注文の数だけ繰り返されるので、最終的には一つずつ全ての注文を指定することになります。
     
  7. 注文した後に指定した本数分のローソク足が出来上がったかをチェックするif文です。

    TimeCurrent()は1970年からカウントした現在までの経過時間を秒で返します。
    OrderOpenTime()は1970年から注文が約定した時点までの経過時間を秒で返します。
    したがってTimeCurrent()-OrderOpenTime()は注文が約定してから今まで何秒経過したかを意味しています。

    その後period2にグローバル変数CloseAfterCandlesに設定した値をかけることで、指定した本数のローソク足ができるのにかかる秒数を算出しています。例えば、1分足のチャートでローソク足が2本出来上がったら決済したい場合、period2が60(時間軸)CloseAfterCandles(ローソク足の本数)が2になり、それらをかけた値は120(秒)になります。
    この値よりTimeCurrent()-OrderOpenTime()が大きいということは、注文が約定してからローソク足が2本完成したということになります。
     
  8. 7のif文がtrue、すなわち注文が完了してから指定した本数のローソク足が完成していた場合にはCloseAllOrder()関数を使って全ての注文を決済しています。待機中の注文は削除されます。

グローバルエリア

以前までの記事の内容も含めたここまでのグローバルエリアは以下のようになっています。

extern int TakeProfit=50;
extern int StopLoss=25;
extern double LotSize=0.01;

extern bool UseBreakEven=true;
extern int MoveToBreakEven=40;
extern int PipsProfitLock=20;

double pips;

extern bool UseTrailingStop=true;
extern int TrailTrigger=50;
extern int TrailAmount=50;
extern bool UseStopLoss=true;
extern bool UseTakeProfit=true;
extern bool UseRiskReward=true;
extern bool UsePositionSizing=true;
extern int RiskPercent=1;
extern int RewardRatio=2;
//今回追加したグローバル変数
extern bool UseCandleClose=true;
extern int CloseAfterCandles=1;

この関数の使い方

この関数を使う場合はグローバル変数UseCandleCloseをtrueにしておく必要があります。
またCloseAfterCandlesに何本ローソク足が出来上がったら決済したいかを指定する数値を設定しておきます。

またこの関数を使う際には以前作成したUseStopLossとUseTakeProfitをfalseにしてください。そうしないと決済するためのプログラムが複数出来上がってしまいます。

前述したとおり、この関数はOnTick()の中で注文が一つ以上ある場合に実行します。

void OnTick()
  {
//---
if(IsNewCandle()){
   if(TotalOpenOrders()<1){
      NewOrder();
   }
   
   if(TotalOpenOrders()>0){
      if(UseBreakEven){
         BreakEven();
      }
      if(UseTrailingStop){
         TrailingStop();
      }
      if(UseCandleClose){
         CandleClose();
      }
   }
}
}

完成コード

ここまでのコード全体は以下のようになっています。

//+------------------------------------------------------------------+
//|                                                         test.mq4 |
//|                                                         Kanrinin |
//|                                           https://codelabsjp.net |
//+------------------------------------------------------------------+
#property copyright "Kanrinin"
#property link      "https://codelabsjp.net"
#property version   "1.00"
#property strict

extern int TakeProfit=50;
extern int StopLoss=25;
extern double LotSize=0.01;

extern bool UseBreakEven=true;
extern int MoveToBreakEven=40;
extern int PipsProfitLock=20;

double pips;

extern bool UseTrailingStop=true;
extern int TrailTrigger=50;
extern int TrailAmount=50;
extern bool UseStopLoss=true;
extern bool UseTakeProfit=true;
extern bool UseRiskReward=true;
extern bool UsePositionSizing=true;
extern int RiskPercent=1;
extern int RewardRatio=2;
extern bool UseCandleClose=true;
extern int CloseAfterCandles=1;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   PipsFunction();
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
if(IsNewCandle()){
   if(TotalOpenOrders()<1){
      NewOrder();
   }
   
   if(TotalOpenOrders()>0){
      if(UseBreakEven){
         BreakEven();
      }
      if(UseTrailingStop){
         TrailingStop();
      }
      if(UseCandleClose){
         CandleClose();
      }
   }
}
}
//+------------------------------------------------------------------+
void NewOrder()
{

int Result=OrderSend(Symbol(),OP_BUY,LotSize,Ask,3,NormalizeDouble(Ask-StopLoss*pips,4),NormalizeDouble(Ask+TakeProfit*pips,4),NULL,0123,0,clrNONE);
return;
}

bool IsNewCandle()
{
   static int BarsOnChart=0;
   if(Bars==BarsOnChart){
      BarsOnChart = Bars;
      return(false);
   }
   BarsOnChart = Bars;
   return(true);
}

int TotalOpenOrders()
{
int Orders=0;
int Total=OrdersTotal();
   for(int i=Total; i>0; i--)
      {
      bool res=OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES);
      if(OrderType()==OP_BUY || OrderType()==OP_SELL)
         {
         Orders++;
         }
      }
   return(Orders);
}

void CloseAllOrders()   //1
{
int Total=OrdersTotal();   //2
   for(int i=Total;i>0;i--)   //3
      {
      if(OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES))  //4
         {
         if(OrderType()==OP_SELL)   //5
            {
            bool res1=OrderClose(OrderTicket(),OrderLots(),Ask,3,clrNONE); //6
            }
         if(OrderType()==OP_BUY) //7
            {
            bool res2=OrderClose(OrderTicket(),OrderLots(),Bid,3,clrNONE); //8
            }
         if(OrderType()==OP_BUYLIMIT || OrderType()==OP_BUYSTOP || OrderType()==OP_SELLLIMIT || OrderType()==OP_SELLSTOP)  //9
            {
            bool res3=OrderDelete(OrderTicket(),clrNONE);   //10
            }
         }
      }
return;
}

void PipsFunction() //1
{
double ticksize=MarketInfo(Symbol(),MODE_TICKSIZE); //2
   if(ticksize == 0.00001) //3
   {
   pips = ticksize*10; //4
   }
   else
   {
   pips = ticksize; //5
   }
return;
}

void BreakEven() //1
{
for(int i=OrdersTotal();i>0;i--) //2
   {
   if(OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES)) //3
      {
      if(OrderType()==OP_BUY) //4
         {
         if(Bid-OrderOpenPrice()>MoveToBreakEven*pips) //5
            {
            if(OrderOpenPrice()>OrderStopLoss()) //6
               {
               bool res1=OrderModify(OrderTicket(),OrderOpenPrice(),OrderOpenPrice()+PipsProfitLock*pips,OrderTakeProfit(),0,clrNONE); //7
               Alert("Yes");
               }
            }
         }
      if(OrderType()==OP_SELL)
         {
         if(OrderOpenPrice()-Ask>MoveToBreakEven*pips)
            {
            if(OrderOpenPrice()<OrderStopLoss())
               {
               bool res1=OrderModify(OrderTicket(),OrderOpenPrice(),OrderOpenPrice()-PipsProfitLock*pips,OrderTakeProfit(),0,clrNONE);
               }
            }
         }
      }
   }
}

void TrailingStop() //1
{
for(int i=OrdersTotal();i>0;i--) //2
   {
   if(OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES)) //3
      {
      if(OrderType()==OP_BUY)//4
         {
         if(Bid-OrderOpenPrice()>TrailTrigger*pips) //5
            {
            if(OrderStopLoss()<Bid-TrailAmount*pips) //6
               {
               bool res1=OrderModify(OrderTicket(),OrderOpenPrice(),Bid-TrailAmount*pips,OrderTakeProfit(),0,clrNONE); //7
               }
            }
         }
      if(OrderType()==OP_SELL)
         {
         if(OrderOpenPrice()-Bid>TrailTrigger*pips)
            {
            if(OrderStopLoss()>Bid+TrailAmount*pips)
               {
               bool res1=OrderModify(OrderTicket(),OrderOpenPrice(),Bid+TrailAmount*pips,OrderTakeProfit(),0,clrNONE); 
               }
            }
         }
      }
   }
return;
}

void Trade(int Direction) //1
{
double SL; //2
double TP; //3
double Equity=AccountEquity(); //4
double RiskedAmount=Equity*RiskPercent*0.01; //5
double Lots=0; //6

   //買い注文の場合
   if(Direction==0) //7
      {
      if(UseStopLoss) //8
         {
         SL=Bid-StopLoss*pips;
         }
      else
         {
         SL=0;
         }
      if(UseTakeProfit) //9
         {
         if(UseRiskReward && UseStopLoss) //10
            {
            TP=StopLoss*pips*RewardRatio+Bid;
            }
         else
            {
            TP=Bid+TakeProfit*pips;
            }
         }
      else
         {
         TP=0;
         }
      if(UsePositionSizing && UseStopLoss) //11
         {
         Lots=(RiskedAmount/StopLoss)/10;
         }
      else
         {
         Lots=LotSize;
         }
      int res=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,NormalizeDouble(SL,4),NormalizeDouble(TP,4),NULL,0,0,clrNONE); //12
      }
   //売り注文の場合   
   if(Direction==1)
      {
      if(UseStopLoss) //8
         {
         SL=Ask+StopLoss*pips;
         }
      else
         {
         SL=0;
         }
      if(UseTakeProfit) //9
         {
         if(UseRiskReward && UseStopLoss) //10
            {
            TP=Ask-StopLoss*pips*RewardRatio;
            }
         else
            {
            TP=Ask-TakeProfit*pips;
            }
         }
      else
         {
         TP=0;
         }
      if(UsePositionSizing && UseStopLoss) //11
         {
         Lots=(RiskedAmount/StopLoss)/10;
         }
      else
         {
         Lots=LotSize;
         }
      int res=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,NormalizeDouble(SL,4),NormalizeDouble(TP,4),NULL,0,0,clrNONE); //12
      }
return;
}

void CandleClose() //1
{
int period=Period(); //2
int period2=0; //3

   switch(period) //4
      {
      case 1:period2=60; break;
      case 5:period2=300; break;
      case 15:period2=900; break;
      case 30:period2=1800; break;
      case 60:period2=3600; break;
      case 240:period2=14400; break;
      case 1440:period2=86400; break;
      case 10080:period2=604800; break;
      case 43200:period2=2592000; break;
      }
   
   for(int i=OrdersTotal();i>0;i--) //5
      {
      if(OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES)) //6
         {
         if(TimeCurrent()-OrderOpenTime()>period2*CloseAfterCandles) //7
            {
            CloseAllOrders(); //8
            }
         }
      }
   return;
}

[itemlink post_id=”2273″]