このシリーズ記事ではEA(自動売買プログラム)を初めて開発する人に向けて、プログラミングの基礎から実践的な取引戦略をEAに実装する方法まで丁寧に解説しています。

記事を途中から読んでも問題なく理解できるようになっていますが、以下のリストの順番通りに読み進めていくことで、より理解が深まるように構成されています。

EAの教科書

  1. 自動売買の仕組み なぜ自動売買を使うべきなのか
     
  2. MetaTraderとMetaEditor
     
  3. EAのフローチャート
     
  4. mqlの基礎文法 変数・関数宣言方法
     
  5. アラートを表示する方法とEAの基本的な使い方
     
  6. 関数の作り方 OrderSend()を使ってNewOrder()関数を作ってみる
     
  7. Barsで新しいローソク足ができたかを判定する
     
  8. 合計注文数を知る 一度に一つの注文に制限する
     
  9. 全てのトレードを決済注文する関数をつくる
     
  10. ペアごとに異なるpipsの桁数を合わせる
     
  11. 確実に一定の利益を確保する関数を作る
     
  12. トレーリングストップを実装する
     
  13. 本格的にトレードを行うための関数を作る
     
  14. ローソク足を基準に決済する関数
     
  15. エントリーシグナルを見極める関数
     
  16. 自分のトレード戦略をEAに実装する

[itemlink post_id=”2236″]

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

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

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

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

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

これまでEAにトレードを行わせるために必要な関数について解説してきました。今回はそれらを実際にどうやってあなたの取引戦略に活用していくかについて説明します。以下のシリーズ記事を順番に読んでいくと、より理解できるようになっています。

【FX自動売買】EA入門 初めてのEA開発 EAの教科書【MT4】

はじめに

FX市場において取引を行うということは、常に自分の資産がリスクに晒されていることを意味します。
もちろん、相応の見返りを得られる可能性もありますが、それでは単なるギャンブルと変わりません。

FXがギャンブルと大きく異なる点はリスク管理をしっかり行うことで、長期的にみて堅実な利益を得ることができるという点です。ある調査によればプロのトレーダーは年間で平均7%の利益を得ているそうです。厳しいことを言いますが、この値が小さいと思った方はFXに向いていないかもしれません。

たしかに短期でそれ以上に勝つことは意外と簡単に達成できるかもしれません。しかしそれは偶然の産物であり、再現性に乏しいため、これに味を占めた方は長期的には資産を減らしていくことが容易に想像できます。

リスク管理をしっかり行うというのは、自分の資産に合ったトレード手法、ルールを事前に決め、それを徹底して継続することを意味します。そこに人間の感情は邪魔な要素になってくるため、EAにあなたの戦略を自動化させるのです。

それでは次のステップで、これまで解説した関数を使ってあなたのトレード戦略をEAに実装する手順を解説していきます。

トレード戦略を考える手順

考えたトレード戦略が利益を生み出すかは何回もテストを行って検証する必要があります。以下ではこれまでの関数を活用してトレード戦略を検証・実装する手順を紹介します。

  1. まずはじめにエントリーシグナル(買い・売りでエントリーするタイミング)を見つける必要があります。これは以下の記事で紹介したEntrySignal()関数に変更を加えることで、あなたにとってのエントリーシグナルを見つけ、注文を出すことができます。
【FX自動売買】EA入門 エントリーシグナルを見極める関数【MT4】

   次にCandleClose()関数を使って指定した本数のローソク足が完成した後にトレードをクロー
   ズするように設定してみましょう。順番に5本、10本、20本後でクローズするようにテストし、
   どのときに一番利益が出るか確かめてみてください。このときにUseStopLoss、UseTakeProfit
   UseBreakEvenはfalseにしておきましょう。

【FX自動売買】EA入門 ローソク足を基準に決済する関数【MT4】
  1. 様々な期間でテストを行うようにしてください。また上昇トレンドにある期間や下降トレンドにある期間、レンジ状態にある期間なども含んでいることを確認してください。
    異なる通貨ペア・時間軸で同じEAを動かすことで、どれが最も適しているかがわかります。
    またロング(買い)エントリーとショート(売り)エントリーの戦略が同じである必要性は全くありません。上昇トレンドと下降トレンドでは動きが大きく異なることも珍しくないので、戦略もそれぞれ変わってくることがよくあります。
     
  2. どの時間軸・通貨ペアがあなたの取引戦略にとってベストかがわかったら、次は別のクローズ(決済)手法も試してみましょう。先ほどまでは単純に指定したローソク足の数のあとでクローズさせていましたが、ストップロスや利確ポイント、トレーリングストップなどを使ってクローズさせたほうがいい場合もあります。BreakEven()関数も試してみるといいでしょう。
【FX自動売買】EA入門 確実に一定の利益を確保する関数を作る【MT4】
【FX自動売買】EA入門 トレーリングストップを実装する【MT4】
【FX自動売買】EA入門 本格的にトレードを行うための関数を作る【MT4】

  1. 安定して利益が出せる戦略を生み出すためには取引戦略の数値を変えながら、様々な時間軸・通貨ペアで検証する必要があります。トレンド状態では利益を生み出すことができるEAもレンジ状態にあるマーケットで大損失を出すようでは意味がありません。様々なマーケットに対応できる戦略を見つけましょう。レンジ状態では全く取引させないようにするなどというのも一つの手です。
     
  2. まだ最大ドローダウン(一時的に最大資産がどれくらい減ったかを表す値:50%であれば資産が半分になったことを意味する)も考慮しましょう。この値が高すぎると実際の運用では使い物にならないかもしれません。

まとめ

いかがでしたか?

マーケットは常に変動するものなので、いつでも確実に利益をだす戦略というのはありえません。それでも長期的にみれば高い割合で利益を得られる戦略を見つけることは可能です。

そのためには様々な戦略を様々な通貨ペア・時間軸上で検証する必要があります。ぜひパラメーターを細かく調整しながら、あなたにとってベストなEAを開発してみてください。

[itemlink post_id=”2236″]

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

【FX自動売買】EA入門 初めてのEA開発 EAの教科書【MT4】

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

関数の概要

今回作る関数はEAに行わせたい取引戦略を記述し、注文を出すタイミングを見極めるための関数です。今回作る関数でトレードタイミングを見極め、タイミングがきたら以前作成したTrade関数を使って実際にトレードを行わせています。Trade関数は細かい設定をつけて注文を出せる関数ですが、詳しくは以下の記事で解説していますので、興味のある方はご覧ください。

【FX自動売買】EA入門 本格的にトレードを行うための関数を作る【MT4】

 

今回作る関数の名前はEntrySignal()とします。

今回使用するグローバル変数

今回使用するローカル変数

今回はシンプルに短期の移動平均線が長期の移動平均線を上回ったタイミングでロングエントリー、下回ればショートエントリーする戦略をプログラム化していきたいと思います。従って、短期と長期それぞれの直前の移動平均と、二つ前の移動平均を出したうえで、それらを比較する必要があります。

例えば二つ前の短期移動平均が長期移動平均を下回っており、一つ前の短期移動平均が長期移動平均を上回っていればロングでエントリーするということになります。ちなみにこのことをゴールデンクロスと呼びます。

従って、上述した移動平均をそれぞれ保持するためのローカル変数を準備します。

それぞれの移動平均を算出するためにグローバル変数として宣言したLongMAPeriodなどが使われていることを確認してください。

関数のコード

void EntrySignal() //1
{
double ShortMACurrent=iMA(Symbol(),PERIOD_CURRENT,ShortMAPeriod,0,MODE_SMA,PRICE_CLOSE,1); //2
double LongMACurrent=iMA(Symbol(),PERIOD_CURRENT,LongMAPeriod,0,MODE_SMA,PRICE_CLOSE,1);
double ShortMAPrevious=iMA(Symbol(),PERIOD_CURRENT,ShortMAPeriod,0,MODE_SMA,PRICE_CLOSE,2);
double LongMAPrevious=iMA(Symbol(),PERIOD_CURRENT,ShortMAPeriod,0,MODE_SMA,PRICE_CLOSE,2);

   if(TradeLong) //3
      {
      if(ShortMAPrevious<LongMAPrevious && ShortMACurrent>LongMACurrent) //4
         {
         Trade(0); //5
         }
      }
   if(TradeShort)
      {
      if(ShortMAPrevious>LongMAPrevious && ShortMACurrent<LongMACurrent)
         {
         Trade(1);
         }
      }
   return;
}
  1. まずは関数の宣言からです。戻り値はないのでvoidとし、関数名をEntrySignal()としましょう。
     
  2. 先ほど説明したローカル変数を順番に宣言しています。iMA()関数は移動平均を求めるための関数で、最後の引数は現在使用しているチャート上において何本前の移動平均を求めるかを指定しています。1であれば直前、2は2つ前の移動平均です。
     
  3. グローバル変数のTradeLongがtrueであれば買い注文を行わせたいということなので、その後の処理に入ります。ここでは買い注文をするかしないのかを判定し、実際に買いエントリーすべきかはその後の処理でチェックします。
     
  4. このif文ではゴールデンクロスが起こっているかを判定しています。具体的には、二つ前の短期移動平均線が長期移動平均線を下回っており、直前で上回っていることをチェックしています。
    (SBI FXトレード参照)
     
  5. 以前作成したTrade()関数をつかって買い注文を行っています。Trade(0)は買い、Trade(1)は売り注文です。

その後は逆の計算を売り注文のほうで行わせています。

グローバル変数

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;
//今回追加した変数
extern int ShortMAPeriod=50;
extern int LongMAPeriod=100;
extern bool TradeLong=true;
extern bool TradeShort=true;

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

使い方

この関数はOnTick()関数内で使用し、既存の注文がない場合に動作するようにします。

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

注文が一つもないときにはティック毎にEntrySignal()が動作し、チャートをチェックしてくれるので、エントリータイミングが来た瞬間に注文されることになります。

[itemlink post_id=”2236″]

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

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

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

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

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

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

【FX自動売買】EA入門 初めてのEA開発 EAの教科書【MT4】

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

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

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

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

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

今回使うグローバル変数

今回使うローカル変数

この関数の使われ方

この関数はトレード中の注文が一つでもある場合にティック毎に呼び出されるようにします。ですので、以前作成した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″]

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

【FX自動売買】EA入門 初めてのEA開発 EAの教科書【MT4】

これまでに別記事でEAにトレードを行わせるために必要な様々な関数を紹介してきました。本記事ではその中でも最も重要といっても過言でない実際にトレードを行わせるための関数を紹介していきます。

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

関数の概要

今回作成していく関数の名前はシンプルにTradeとします。戻り値はなく、引数にはint型のDirectionをとり、買い注文か売り注文かを指定するために使います。今回は0は買い注文、1は売り注文を表すことにします。

また今回作成するTradeには実践的なオプションがあり、ストップロスを設定するか、利確ポイントを設定するか、決済注文をリスクリワードレシオ(得られる収益と取らなければいけないリスクの割合)で指定するか、注文量を口座にある証拠金に対する割合で指定するかなどを設定できるようにします。

今回使用するグローバル変数

関数のコード

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;
}

上から順番に何をおこなっているのか解説していきます。

  1. まずは関数の宣言からです。戻り値はないので、voidとし、関数名Trade、引数は売りか買いかを示す(int Direction)です。使うときにはTrade(0)などのようにして呼び出します。
     
  2. ストップロスを表すための変数SLを宣言しています。最終的にOrderSend()関数で注文を出すときに使用します。ここでは何も代入しません。
     
  3. 利確ポイントを表すための変数TPを宣言しています。最終的にOrderSend()関数で注文を出すときに使用します。ここでは何も代入しません。
     
  4. 証拠金残高を表すための変数Equityを宣言しています。これには現在の口座にある証拠金残高がAccountEquity()関数によって代入されています。
     
  5. 一回のトレードでとるリスク量を表すための変数を宣言しています。グローバルエリアで宣言しているRiskPercent変数とそれを%に変換するために0.01と先程のEquity変数をかけた値が大ニュされています。
     
  6. 注文量を表すためのLots変数を宣言しています。初期値は0を代入しています。
    2~6で宣言した変数は全てTrade()関数内のローカル変数ですので、Trade()関数内のみで使用できます。
     
  7. 買い注文かを判別しています。買い注文である場合、すなわちTrade(0)で呼び出した場合はこのif文の中の処理が全て実行されます。
     
  8. ストップロスを設定するかを判別しています。UseStopLossがtrueである場合にはストップロスはBid – StopLoss * pipsになります。そうでない場合は0、すなわちストップロスが設定されません。
     
  9. 利確ポイントを設定するかを判別しています。
     
  10. 9で利確ポイントを設定する場合にはさらにリスクリワードレシオを使って利確ポイントを設定するかを判別しています。リスクリワードレシオはストップロスに対する見込み利益の割合なので、必然的にストップロスも使用している必要があります。そのためUseStopLossがtrueかも判定しています。
    もし両者ともtrueの場合、StopLoss変数をpips単位に変換した値にグローバルエリアで設定したRewardRatioをかけた値をBid(現在の売値)に足すことで、ストップロス x RewardRatio倍が利益になるように利確ポイントを設定します。
    リスクリワードレシオを使わない場合は利確ポイントはBid + TakeProfit * pipsになります。
     
  11. 注文量を指定するかをUsePositionSizingとUseStopLossで判定しています。どちらもtrueの場合、注文量(Lots)はRiskedAmountに設定された値とStopLossに設定された値をもとに算出した値に設定されます。
     
  12. 最後にここまでで設定された変数を利用してOrderSend()関数で注文を出しています。
     

グローバルエリアは以下のようになっています。

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;

Trade()関数の使い方

この関数はエントリータイミングが来たタイミングで発注するための関数ですので、エントリータイミングを見極めるための関数と組み合わせて使用します。

エントリータイミングを見極める関数については以下をご覧ください。

【FX自動売買】EA入門 エントリーシグナルを見極める関数【MT4】

[itemlink post_id=”2273″]

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

【FX自動売買】EA入門 初めてのEA開発 EAの教科書【MT4】

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

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

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

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

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

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

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

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

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

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

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

使っていく変数

これらは全てグローバル変数として宣言します。また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()関数に関しては以下の記事を御覧ください。

【FX自動売買】EA入門 確実に一定の利益を確保する関数を作る【MT4】

完成コード

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

//+------------------------------------------------------------------+
//|                                                         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/

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

【FX自動売買】EA入門 初めてのEA開発 EAの教科書【MT4】

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

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

今回作っていく関数の名前はBreakEven()とします。Break evenは収支がトントンになるといったような意味があります。この関数は実行中のトレードが一定の利益を出したら、あらかじめ指定した価格をストップロスに設定し、確実に利益を確保・または損失を出さないようにするために使います。

この関数は実行中のトレードがあるときにのみ、毎ティック実行されます。ですのでOnTick()関数の中でif文で実行中のトレードがあるかを確認し、ある場合にはBreakEven()関数を実行するようにします。

また今回はこの関数自体を使うかどうかを設定できるようにするためのbool値をグローバルエリアにextern(EA実行時に値を変更できる)で宣言しておきます。デフォルト値はtrueに設定します。

グローバルエリアには他にextern int型のMoveToBreakEven、extern int型のPipsProfitLockがあります。

MoveToBreakEvenはいくらのpip数分の利益が出ていれば損切りポイントを変更するかを指定します。

PipsProfitLockはいくらの利益を最低限確保したいかを指定するために使います。0である場合はトントンを意味し、20である場合は確実に20pipsの利益は確保できることを意味します。

つまり、MoveToBreakEvenが40でPipsProfitLockが20である場合、40pips以上の利益が出ている状態であれば、20pipsの利益は確保できるように損切りポイントを変更するということになります。即座に決済注文が出されるわけではないので、さらに含み益が伸びる可能性もありますし、好ましくない方向に市場価格が動いても確実に20pipsの利益は確保できます。

コードの確認

それでは実際にBreakEven()関数のコードを紹介していきます。

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()-Bid>MoveToBreakEven*pips)
            {
            if(OrderOpenPrice()<OrderStopLoss())
               {
               bool res1=OrderModify(OrderTicket(),OrderOpenPrice(),OrderOpenPrice()-PipsProfitLock*pips,OrderTakeProfit(),0,clrNONE);
               }
            }
         }
      }
   }
}

それではコメントにふった番号に沿って説明していきます。

  1. まずは関数の宣言です。戻り値はないのでvoidとし、名前はBreakEven()にします。
     
  2. 注文の総数分だけ繰り返すfor文です。
     
  3. OrderSelect()関数を使ってトレードプールの中から一つのトレードを選択しています。指定した番号のトレードが存在する場合にはtrueを返します。このfor文は注文分繰り返すので、繰り返されている間は必ずtrueを返すはずです。
     
  4. トレードが選択されたら、そのトレードが買い注文かをチェックしています。買い注文の場合はif文の中の処理に移ります。
     
  5. 現在の価格と注文時の価格の差分(pips)がMoveToBreakEvenに設定した値より大きいかをチェックしています。たとえばMoveToBreakEven変数の値を40にしていた場合、この変数自体は単位がpipsになっていないので、pips変数とかけて単位がpipsになるように変換しています。pipsが0.0001だった場合、0.0040になります。そして差分が0.0040より大きいということは、現在の市場価格が自分にとって良い方に40pips以上動いたということになります。この場合は買い注文ですので、40pips以上価格が上がったということです。売り注文の場合は逆になります。そして次の処理に移ります。
    40pips以上動いていない場合はif文の中身を飛ばして次の処理に移ります。

pips変数については以前の記事で解説しているので、詳しく知りたい方は以下の記事を御覧ください。

【FX自動売買】EA入門 ペアごとに異なるpipsの桁数を合わせる【MT4】
  1. このif文は損切り値(Stoploss)がこの関数によって既に動かされていないかをチェックしています。初回は必ずtrueを返すはずなので、if文の中の処理に移ります。そうでない場合は次の売り注文かをチェックするif文に飛ばされます。
     
  2. OrderModify()関数を使って注文内容を変更します。今回変更したいのは損切りポイントです。それ以外は特に変更しません。
    ここでは買い注文なので、利益を確保したい分のpipsをOrderOpenPrice()に足します。再度確認しておきますが、PipsProfitLock変数自体はint値なので、pips変数を使ってpipsを表す値に変換しています。
     
  3. これと同様の処理を売り注文の場合にも行います。ただし売り注文の場合は約定価格から現在の買い価格(Ask)を引いた値がMoveToBreakEvenより大きければ、現時点で一定の利益が出ているということになる点に注意してください。

グローバル変数の確認

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;

上のコードは宣言している全てのグローバル変数になります。pips変数には何も値が代入されていませんが、事前に定義したPipsFunction()によって適切な値がプログラム実行時に代入されるので、問題ありません。

以前の記事ではStopLossやTakeProfitには0.0050や0.0025のような値が入っていましたが、これもpips変数を使って適切な値に変換できるので、整数値に変更しています。

また以前作成したNewOrder()関数内でもNomalizeDouble()関数を使ってすべてを小数点以下4桁に変換しています。NewOrder()関数については以下の記事を御覧ください。

【FX自動売買】EA入門 関数の作り方 OrderSend()を使ってNewOrder()関数を作ってみる【MT4】


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

//+------------------------------------------------------------------+
//|                                                         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;
//+------------------------------------------------------------------+
//| 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を使うかのチェック
         BreakEven();
      }
   }
}
   
  }
//+------------------------------------------------------------------+
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);
               }
            }
         }
      }
   }
}
      

[itemlink post_id=”2361″]

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

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

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

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

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

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

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

通貨ペアの中には価格を表示するのに4桁の数値を使っているところや5桁の数値を使っているところなど、ペアによって価格の表記方法が異なります。つまり同じ価格でも1.5000のように表示されていたり、1.50000のように表示されている場合があるのです。もしpipsを表すために、pips=0.0001などという風に固定していると、別の通貨ペアによっては1pipsを表すつもりが10pipsになってしまったりする場合もあります。

取引通貨ペアによって異なるそれらの表示形式に影響されずに、pips情報を取得するには、小数点以下が4桁の場合には0.0001を取得し、5桁の場合には0.00001を取得するようにできる関数が必要になってきます。今回はその役割を担うPipsFunction()という関数を作成していきます。

今回は先におおまかな関数の仕組みをざっくりと説明します。

実装にあたって、double型のグローバル変数pipsを宣言します。pipsには通貨ペアごとに1pipsをあらわす値を代入しますが、この値が毎回異なるので、それを今回の関数の中で適切な値が代入されるようにしていきます。

重要な点として、この変数にint型の変数をかけることによって、求めたいpipsに変換することができるということです。例えばこれを使って損切りや利確のための幅をpipsで指定することができるようになります。

あとから値を自由に変更できるexternのint型の変数StopLossに50を代入しておけば、50pipsで損切りさせることができるようになります。ただしこのままではただ整数の50を表す変数なので、この変数と変数pipsをかけることで、実際に50pipsを表す値を生成することができます。

この関数はOnInit()関数内で利用し、プログラム起動時に一度のみ実行されるようにします。この関数の最終的な目的はグローバルエリアに宣言したpipsという変数に適切な値を代入し、StopLossやTakeProfitなどがちゃんとpipsを表せるようにすることです。

コードの確認

以下がPipsFunction()のコードになります。

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

順番に解説していきます

  1. まずはいつもどおり関数の宣言からです。戻り値は特にないので、voidとし、関数名をPipsFunction()とします。
     
  2. ticksizeというdouble型の変数を宣言します。これにはMarketInfo(Symbol(),MODE_TICKSIZE)を代入します。MarketInfo()関数はマーケットから情報を取得するための関数で、今回は5桁表示するペアであれば0.00001を、4桁であれば0.0001を取得します。
     
  3. if文で先程代入したticksizeが5桁かチェックしています。もし先程0.00001がticksizeに代入されていれば、5桁の通貨ペアです。その後ticksizeに10をかけた値をグローバルエリアに宣言しておいた変数pipsに代入します。もしticksizeが0.0001であれば、これは4桁の通貨ペアということになるので、そのままpipsに代入します。
     
  4. 5桁だった場合に10をかけてpipsに代入しています。
     
  5. 4桁だった場合はpips=ticksizeとし、そのままticksizeを利用します。上のif文がfalse、つまり5桁でない場合のみにelseの中が実行されるので、必然的に4桁ということになります。
extern double TakeProfit=0.0050;
extern double StopLoss=0.0025;
extern double LotSize=0.01;

double pips = 0; //ここでグローバル変数としてpipsを宣言している
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   PipsFunction(); //OnInit内でプログラム起動時に一度だけ実行
//---
   return(INIT_SUCCEEDED);
  }

pips変数は他の関数内でも使われる変数なので、グローバルエリア(どの関数にも所属していない場所)に宣言することで、グローバル変数としてどの関数内でも使うことができるようになります。

この関数は最初に一度だけ実行できればいいので、OnInit()内におきます。プログラムの初期化時に実行され、pips変数に適切な値が代入されることで、他の関数内で損切り値や利確値を計算させるのに使うことができるようになります。

例えば、プログラムの起動前、pips変数には0が入っています。ストラテジーテスターでこのEAを起動するとプログラムが実行され、OnInit()関数が完了したときにPipsFunction()が呼び出され、pips変数に値が代入されます。

まとめ

今回は通貨ペアごとに桁数が異なる場合でもpipsを合わせるための関数、PipsFunction()の作り方を解説しました。

この関数はOnInit()内で初期化時に一度だけ実行させれば、pips変数に適切な値が代入され、正しい損切りや利確値を計算することができます。

[itemlink post_id=”2350″]

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

【FX自動売買】EA入門 初めてのEA開発 EAの教科書【MT4】

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

関数の詳細

今回作成していく関数は全ての実行中のトレードを決済し、待機中の注文も全てキャンセルするための関数になります。関数名はCloseAllOrders()とします。

先に関数のコードを紹介したあとで順番に解説していきます。

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;
}

コメント部分の数字順に何を行っているか解説していきます。

  1. まず関数の宣言を行います。この関数は何も戻り値がないので、voidとし、関数名をCloseAllOrders()とします。
     
  2. int型のTotal変数を宣言します。OrdersTotal()関数の戻り値を代入します。OrdersTotal()は現在のトレードと待機中の注文の合計数を返します。
     
  3. Total変数の数分だけループを繰り返すfor文を作成します。Total変数をディクリメント(毎回-1する)する形で行っているので、最後の注文から処理されていきます。例えばTotal変数が10という数値なら10番目から行い、9⇛8⇛…1という風になります。
     
  4. オーダープール中の特定の注文を選択するためにOrderSelect()関数を使います。オーダープールの注文は全て異なるインデックス番号を持っており、インデックス番号は0から始まります。たとえば待機中の注文も合わせて10個ある場合、0~9までの10個のインデックス番号に紐づく注文があることになります。ですので、1つ目のインデックス番号を指定する引数ではi-1とすることで、最初に9を指定し、最後に0を指定することができます。
    この関数は正しく注文が選択されるとtrueを返します。注文の数分繰り返すfor文の中で行っているので、ここのif文は必ずtrueになるはずです。
     
  5. ここのif文ではOrderType()関数を使って選択された現在行われている売り注文であるかを確かめています。OrderType()関数は名前の通り、注文の種類を返してくれます。注文の種類はあらかじめmqlに定数で設定されており、OP_SELL(売り注文)やOP_BUY(買い注文)などがあります。このあとも使っていくので随時覚えていくといいでしょう。
     
  6. ここでようやく初めてOrderClose()関数を使って決済注文を行っています。OrderClose()はTrue/Falseを返すので、この値を保存するためのbool型の変数res1を宣言しています。この関数には5つの引数があり、
  7. 5と同様に選択された注文が買い注文であるかを確かめています。買い注文である場合には8に進み、6と同様の処理をします。決済価格がBidであること以外は同じなので、説明は省略します。
  8. 省略
  9. ここでは選択された注文が待機中の注文かを確かめています。OP_BUYLIMITは指値買い注文、OP_BUYSTOPは逆指値買い注文を表します。
     
  10. 9のif文がtureだった場合は指値、逆指値注文が待機中ということですので、これらをキャンセルするために、OrderDelete()を使っています。

まとめ

今回は全ての注文を決済・キャンセルする関数の作り方を解説しました。

またこの関数を使うタイミングは各々のトレード戦略によって異なりますが、ローソク足が何本かでき終わるタイミングで行わせたりなどが考えられます。それらについては今後解説していこうと思います。

[itemlink post_id=”2350″]

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

【FX自動売買】EA入門 初めてのEA開発 EAの教科書【MT4】

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

合計注文数を確認する

【FX自動売買】EA入門 Barsで新しいローソク足ができたかを判定する【MT4】

上の記事で作成したコードに付け足していく形で解説していきます。先に読んでおくとより理解しやすいかもしれませんが、今回解説していく内容とは切り離せるので、以下のコードを使うことだけさらっと目を通して確認しておければと思います。

前回までのコード:

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

extern double TakeProfit=0.0050;
extern double StopLoss=0.0025;
extern double LotSize=0.01;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
if(IsNewCandle()){
   NewOrder();
}
   
  }
//+------------------------------------------------------------------+
void NewOrder()
{

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

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

 

 

今回は既に注文しているトレードの数を調べる関数を作成していきます。

目的は一度に成行注文を複数回出さないようにするために現在までの成行注文の数を知ることです。

関数名はTotalOpenOrders()としましょう。先に関数の中身を公開しておきます。

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);
}

この関数の戻り値は注文数なので、戻り値の型はintにしてください。それでは順番に解説していきます。

  1. はじめにOrdersというint型の変数を宣言し、0を代入します。これは現在までの成行注文の数を代入するための変数になります。
  2. 次にTotalというint型の変数を宣言し、これにOrdersTotal()という関数の戻り値を代入します。この関数は注文済みのトレードと待機中の注文の数の合計を返します。
  3. 次にfor文でループ処理を作成します。これは先程の変数Totalを利用して、待機中・注文済みの注文がある限り、中身が処理されるようにしています。for文の使い方としては、 はじめに変数iを宣言し、これにTotalを代入し、次にi>0という文で、iが0より大きい間処理を繰り返すようにしています。i–でそして一つの処理が終わるごとに、iの値を-1します。こうすることでTotalに代入された値分だけ処理を繰り返させることができます。
  4. bool res=OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES);というfor文の中の最初の処理は、自分のトレードの中からOrderSelect()という関数を使って、ある一つのトレードを選択するためのコードです。この関数はトレードプール(全てのトレードが保存されている場所)に指定したトレードが存在すればtrueを返します。

    OrderSelect()関数を使った処理が2つある場合、1つは注文を選択するためのもので、trueを返したあとで次の処理に制御を委ねることになります。

    OrderSelect関数は3つの引数をとり、1つ目は選択する注文のインデックス番号です。この番号は0から始まるので、i-1にしてあげることでループ文の最後がちょうど0になり、全てのトレードがインデックス場号の大きい方から順に選択されることになります。

    次の引数SELECT_BY_POSはインデックス番号によってトレードを選択することを指定するためのものです。実は1つ目の引数はチケット番号(詳しい説明は省略)を指定することもできるため、この2つ目の引数によってインデックス番号であることを明示する必要があります。

    3つ目のMODE_TRADESは現在のトレードプールを利用することを指定しています。MODE_HISTORYとすると過去のデータを指定することができます。
  5. 次にif文で選択した注文が買い注文または売り注文であることを判定しています。
  6. もし買い注文または売り注文である場合、Orders変数に1を足します。どちらでもない場合は何もせずに次のfor文に進みます。例えば合計で8つの注文がトレードプールにある場合、次のiは7になります。
  7. 全ての注文を確認し終わったら、iの値は0になり、for文を抜けてOrdersを返します。

TotalOpenOrders()の使い方

TotalOpenOrders()についての解説が終わったので、具体的な使い方について説明します。

この関数はOnTick()の中の前回作成したIsNewCandle()がtrueの場合に呼び出し、注文が存在しない場合、つまりTotalOpenOrders()<1である場合のみトレードを行わせるようにします。そうすることで一度に複数のトレードが行われることを回避できます。

確認:IsNewCandle()は現在のチャート上に新しいローソク足が作成されたかを確認するための関数です。時間軸を一時間にしている場合は一時間ごとにtrueを返します。

void OnTick()
  {
//---
if(IsNewCandle()){
   if(TotalOpenOrders()<1){
      NewOrder();
   }
}
   
  }

まとめ

現在行われているトレードの合計数を確認するための関数TotalOpenOrders()について解説しました。この関数は現在行われているトレードの合計数を返すもので、これが0である場合はトレードが何も行われていないということになります。

自動売買で注文を一つずつ行わせたい場合はこの関数を利用して、値が1未満(=0)である場合のみ注文を出す処理を行わせるようにしましょう。

[itemlink post_id=”2273″]