【FX自動売買】EA入門 トレーリングストップを実装する【MT4】

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

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

トレーリングストップとは

はじめにトレーリングストップについてカンタンに説明したいと思います、

トレーリングストップとは、利益を大きく、損失を小さくするために編み出された取引戦略の一つで、マーケットの価格変動に合わせて損切りポイントを変更する仕組みのことを言います。トレール(trail)には追従といった意味があり、価格の変動に追従して損切りポイントを変更することからこの名前が付きました。

たとえば101円でドル円を買いエントリーし、1円下の100円を損切りポイントとしました。その後、ドル円の価格はあなたの思惑通り上昇し、102円に達しました。常に1円下に損切りポイントを設定したいので、次は損切りポイントを100円から101円に変更しました。ここで暴落したとしても100円で決済されるので、損失を出さずに済みます。これがトレーリングストップの考え方です。

同じように103円に達したら102円が損切りポイントになるので、仮に103円まで達したあとで暴落が始まったとしても102円で決済され、1円分の利益を確保することができます。

売り注文の場合はこれの逆だと考えてください。

今回作っていく関数の概要

関数名はTrailingStop()とします。戻り値はありません。

冒頭でも説明したように、トレーリングストップを自動で行うための関数です。例えば買いエントリーの場合であれば、現在の売値(Bid)の50pips下に損切りポイントを起きたいとします。売値が50pips上がった時点で、損切りポイントも合わせて50pips上にずらされます。

売りエントリーであれば、買値が50pips下落したら損切りポイントも50pips下にずらされます。

使っていく変数

  • extern bool UseTrailingStop :トレーリングストップを使うか使わないかを決めるための真偽値です。デフォルトではtrueに設定します。
     
  • extern int TrailTrigger:どれくらい価格が自分にとって有利な方向に動いたらトレーリングストップを実行するかをpipsで指定するための変数です。50にした場合、マーケット価格が50pips有利な方向に動いた時点でトレーリングストップが実行され、下で説明するTrailAmount分損切りポイントが移されます。デフォルトでは50にしておきます。
     
  • extern int TrailAmount:現在価格から何pips分離れた場所に損切りポイントを置くかを指定するための変数です。50にした場合、買いエントリーであればトレーリングストップ実行時に損切りポイントが現在価格より50pips上にずらされた価格で更新されます。デフォルトでは50にしておきます。

これらは全てグローバル変数として宣言します。またexternで宣言することによって、ストラテジーテスター実行時に値を自由に設定できるので、EAの最適化を行う際に便利です。

関数のコード

それではTrailingStop()関数のコードを確認していきましょう。

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;
}
  1. まずはいつもどおり関数の宣言からです。今回は戻り値がないので型をvoidとし、名前はTrailingStop()です。
     
  2. 注文の総数分だけ繰り返すfor文です。
     
  3. OrderSelect()関数を使ってトレードプールの中から一つのトレードを選択しています。指定した番号のトレードが存在する場合にはtrueを返します。このfor文は注文分繰り返すので、繰り返されている間は必ずtrueを返すはずです。
     
  4. トレードが選択されたら、そのトレードが買い注文かをチェックしています。買い注文の場合はif文の中の処理に移ります。
     
  5. このif文では現在価格と約定価格の差がTrailingTrigger変数に設定した値よりも大きいかをチェックしています。TrailingTrigger変数自体は整数値なので、pips変数をかけて実際のpips値に変換しています。もしTrailingTrigger変数が50であれば、市場価格が50pips上昇したことになり、if文の中の処理が実行されます。
     
  6. ここでは買い注文について扱っているので、このif文では現在の損切りポイントがトレーリングストップで移動したい先の損切りポイントよりも低い(安い)かをチェックしています。もし現在の損切りポイントがトレーリングストップによって移される損切りポイントより高い場合、トレーリングストップを行う意味がないので、トレーリングストップは行われません。
     
  7. ここではOrderModify()関数を使って損切りポイントを変更しています。変更後はBid-TrailAmount*pipsとし、現在の売り価格からTrailAmountに指定した値分のpipsを引いた値が新しい損切りポイントになります。

買い注文の場合の処理が終わったら売り注文のチェックを行っています。処理はほぼ同じですが、損切りポイントの計算方法が買い注文の場合と逆になっていることに注意してください。

グローバル変数の確認

現在までのグローバル変数は以下のようになっています。

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;

別記事で紹介した関数用の変数なども含まれているので、興味がある方は確認してみてください。FX自動売買(EA)カテゴリから別記事を参照できます。

TrailingStop()関数の使い方

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

この関数もBreakEven()関数と同じく、OnTick()関数の中でトレードが一つでも存在し、UseTrailingStop変数がtrueの場合に実行するように設定しています。

BreakEven()関数に関しては以下の記事を御覧ください。

完成コード

最後にここまでのコード全体を載せておきます。

//+------------------------------------------------------------------+
//|                                                         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;
//+------------------------------------------------------------------+
//| 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();
      }
   }
}
}
//+------------------------------------------------------------------+
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;
}

別記事で紹介した関数なども含まれているので、上記コードだけで理解するのが難しい方はFX自動売買(EA)カテゴリに含まれている記事を日付順に読んでいくことをおすすめします。

[itemlink post_id=”2350″]

完全無料で通えるプログラミングスクール

プログラミング学習はどうしても一人だとつまづいてしまう時がきます。調べればわかることも少なくないですが、最初のうちは調べ方もわからないことが多いため、あまり効率的ではありません。

効率的かつ挫折せずにプログラミングを学習したい方はスクールを検討してみるのも一つの手です。

中には無料で通えるスクールや、就職保証をしてくれるスクールなどもあるので、きっとあなたの目的に応じて最適のスクールが見つかります!以下の記事で評判がよく特におすすめのスクールをいくつかピックアップしているので、スクール選びで後悔したくない方は御覧ください!

https://codelabsjp.net/best-programming-school/