【MQL4関数】OrderMagicNumber関数の使い方!マジックナンバーとは?ポジション管理のやり方

【中級編】MQLプログラムの読み方・書き方
朝日奈りさ
朝日奈りさ

マジックナンバーって何?裁量のときは聞いたことがないけど、自動売買では重要なの?

マジックナンバーとは

裁量のときは、「自分がエントリーしたポジションは、自分で決済」していたと思います。

自動売買を使うからには、「自動売買がエントリーしたポジションは、自動売買で決済」して欲しいですよね!

そこで、マジックナンバーを使ってみましょう!

 

マジックナンバーとは、自動売買がポジションを管理するための番号です。

具体的には、「10」とか「20」とか、好きな数字を設定することができます。

設定することで、自動売買がエントリーしたポジションに、マジックナンバーが付きます。

マジックナンバーが付いていると、その自動売買がエントリーしたポジションなのかを、自動売買が判別することができるようになります。

判別できるようになると、別の自動売買が決済してしまうことや、裁量のポジションを決済してしまうことを防ぐことができるようになるのです。

 

例えば、自動売買①と自動売買②を同時に動かしている場合を考えてみましょう。

  • マジックナンバーが付いていない場合
自動売買は、どちらの自動売買でエントリーしたポジションなのか判別できません。
そのため、自動売買①でエントリーしたのに、自動売買②が決済してしまうことが起こる可能性があります。
  • マジックナンバーが付いている場合
自動売買は、どちらの自動売買でエントリーしたポジションなのか判別できます。
そのため、自動売買①でエントリーしたのに、自動売買②が決済してしまうことは起こりません。

 

このように、マジックナンバーを設定しておくことで、別の自動売買が決済してしまうことを防ぐことができます。

 

ちなみにマジックナンバーは、エントリーするときに設定します。

OrderSend関数の9番目の引数(⑨magic)でマジックナンバーを設定できます。

 

マジックナンバーを設定する方法

「マジックナンバーを設定したいけどどうしたらいいの?」という方が多いと思います。

マジックナンバーは、自動売買プログラムに直接書いてしまうと良いでしょう。

例えば、以下のように書きます。

//大域変数にマジックナンバーの数値を書く
int magicNumber = 10;

//エントリーするときに、マジックナンバーを設定する
//OrderSend関数の9番目の引数に設定
int ticketNum = OrderSend( NULL, OP_BUY, 0.1, Ask, 20, 105.000, 110.000, "自動売買の注文", magicNumber, 0, clrRed);

 

プログラムを省略していますが、上記のようにマジックナンバーを設定します。

大域変数にマジックナンバー用の変数を準備し、好きな数値を代入します。

そして、エントリーするときにマジックナンバーを設定します。

このような流れで、マジックナンバーを設定すると良いでしょう。

 

ちなみに裁量でエントリーしたポジションには、マジックナンバーを付けることができませんので、ご注意ください。

 

ここまでは、マジックナンバーを設定する方法を解説しました。

ここからはマジックナンバーを使う方法を見ていきましょう。

 

マジックナンバーを使う方法(OrderMagicNumber関数)

マジックナンバーを設定するだけではなく、使うことで力を発揮します。

マジックナンバーを使うには、OrderMagicNumber関数を使います。

 

OrderMagicNumber関数は、ポジションに付いているマジックナンバーを取得する関数です。

マジックナンバーを取得することで、自動売買に設定しているマジックナンバーと、ポジションのマジックナンバーが同じかどうか、判別できます。

例えば、ポジションのマジックナンバーが「10」で、自動売買のマジックナンバーが「10」の時、数値が同じなので、自動売買が決済することができます。

マジックナンバーが異なっている場合は、自動売買は決済することができません。

このように、マジックナンバーとOrderMagicNumber関数を使うことで、想定外のポジションを決済してしまうことを防ぐことができます。

 

OrderMagicNumber関数の書き方

OrderMagicNumber関数の使い方は、基本的な関数と同じです。

関数をマスターしていない方はこちらの記事が参考になります。

 

基本的な書き方

OrderMagicNumber関数の基本的な書き方は以下の通りです。

int orderMagicNumber = OrderMagicNumber();

 

返り値は int型で、引数はありません。

ポジションのマジックナンバーは、返り値として取得することができます。

具体的には「10」や「20」など、エントリーするときに設定した数値が返ってきます。

 

事前にポジションを選択しておく

OrderMagicNumber関数は、引数が無いため、どのポジションのマジックナンバーを取得すれば良いのか、判断することができません。

そのため、事前にポジションを選択しておく必要があります。

ポジションを選択するには、OrderSelect関数を使います。

 

必ず OrderSelect関数 → OrderMagicNumber関数 の順にプログラムするので、以下のようなプログラムになります。

//大域変数にマジックナンバーの数値を書く
int magicNumber = 10;

//保有ポジションを一つ選択
if(OrderSelect(0,SELECT_BY_POS,MODE_TRADES))
{
   //選択したポジションのマジックナンバーが
   //自動売買のマジックナンバーと同じかどうかチェック
   if(OrderMagicNumber() == magicNumber)
   {
      //ここにポジションを決済するプログラムを書く
   }
}

 

OrderMagicNumber関数を実行する前に、OrderSelect関数でポジションを選択しておきましょう。

もう少し詳しく解説します。

上記のプログラムの 10行目を見てください。

  • 「OrderMagicNumber( )」はポジションのマジックナンバーです
  • 「magicNumber」は自動売買のマジックナンバーです

ふたつとも必ず一緒じゃないの? と思うかもしれませんが、別物です。

自動売買を複数起動している場合、両者は異なります。

というより、異なるようにマジックナンバーを設定してください。

異なるマジックナンバーを設定することで、別のポジションを決済してしまうことを防いでいます。

 

OrderMagicNumber関数の具体的な使い方

マジックナンバーを設定する方法と、マジックナンバーを使う方法を解説しました。

ここからは、エントリーしてからマジックナンバーを使うところまで、具体的にプログラムを見てみましょう。

早速ですが、プログラムは以下の通りです。

//大域変数にマジックナンバーの数値を書く
int magicNumber = 10;

void OnTick()
{
//---

   //エントリーするときに、マジックナンバーを設定する
   //OrderSend関数の9番目の引数に設定
   int ticketNum = OrderSend(NULL, OP_BUY, 0.1, Ask, 20, 105.000, 110.000, "自動売買の注文", magicNumber, 0, clrRed);

   //保有ポジションを一つずつチェックしていく
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      //保有ポジションを一つ選択
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         //選択したポジションが、実行されている通貨ペアと同じかどうかチェック
         if(OrderSymbol() == Symbol())
         {
            //選択したポジションが、この自動売買のマジックナンバーと同じかチェック
            if(OrderMagicNumber() == magicNumber)
            {
               //ここにポジションを決済するプログラムを書く
               //わかりやすいようにprintしておきます。
               Print("OrderSymbol : " + OrderSymbol());
               Print("Symbol : " + Symbol());
               Print("OrderMagicNumber : " + IntegerToString(OrderMagicNumber()));
            }
         }
      }
   }

}

 

注文関係の関数をたくさん使用しています。

簡単に解説すると、以下の通りです。

  • 2行目で、マジックナンバーを決めています。
  • 10行目で、マジックナンバーが付いたポジションをエントリーしています。
  • 26行目で、ポジションのマジックナンバーをチェックしています。

このように書くことで、全てのポジションをチェックし、その中から自動売買のマジックナンバーと同じポジションだけを、決済することができます。

この書き方は、自動売買を作るときによく使う書き方なので、どこかにコピーしておくと良いでしょう。

 

実用的なプログラム例

ここからは、OrderMagicNumber関数を使った実用的なプログラム例をいくつかご紹介します。

実際のEA開発でよく使う場面を想定していますので、ぜひ参考にしてください。

 

応用例1:マジックナンバーで自分のポジション数をカウントする

EAを作るとき、「今、自分のEAが何個ポジションを持っているか?」を知りたい場面は非常に多いです。

例えば、「ポジションが0個のときだけエントリーする」という条件を作るには、まずポジション数を数える必要があります。

以下のプログラムでは、マジックナンバーと通貨ペアを使って、自分のEAのポジション数をカウントしています。

// マジックナンバーの定義
int magicNumber = 12345;

//+------------------------------------------------------------------+
//| 自分のEAが保有しているポジション数をカウントする関数              |
//+------------------------------------------------------------------+
int CountMyPositions()
{
   int count = 0; // カウント用の変数を初期化

   // 全ポジションをループでチェック
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      // ポジションを選択(選択に失敗したらスキップ)
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
         continue;

      // 通貨ペアが一致するかチェック
      if(OrderSymbol() != Symbol())
         continue;

      // マジックナンバーが一致するかチェック
      if(OrderMagicNumber() != magicNumber)
         continue;

      // すべての条件を満たしたらカウントを増やす
      count++;
   }

   return count; // ポジション数を返す
}

//+------------------------------------------------------------------+
//| OnTick関数                                                        |
//+------------------------------------------------------------------+
void OnTick()
{
   // 自分のEAのポジション数を取得
   int myPositions = CountMyPositions();

   // ポジションが0個のときだけエントリーする
   if(myPositions == 0)
   {
      // ここにエントリー条件を書く
      // 例:買いエントリー
      int ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 20, 0, 0,
                             "MyEA", magicNumber, 0, clrBlue);

      if(ticket > 0)
         Print("エントリー成功!チケット番号: ", ticket);
      else
         Print("エントリー失敗。エラーコード: ", GetLastError());
   }
   else
   {
      Print("既にポジションを ", myPositions, " 個保有中です。");
   }
}

 

ポイントは、CountMyPositions関数を作っている点です。

この関数は、通貨ペアとマジックナンバーの両方が一致するポジションだけをカウントします。

こうすることで、「ポジションが無いときだけエントリー」という制御が簡単にできるようになります。

 

応用例2:マジックナンバーを使って自分のポジションだけを全決済する

「特定のEAが持っているポジションだけを全部決済したい」という場面もよくあります。

例えば、損失が一定額を超えたときに、そのEAのポジションだけをすべて閉じるといった使い方です。

以下のプログラムは、マジックナンバーが一致するポジションだけを全決済します。

// マジックナンバーの定義
int magicNumber = 12345;

//+------------------------------------------------------------------+
//| 自分のEAのポジションを全決済する関数                              |
//+------------------------------------------------------------------+
void CloseAllMyPositions()
{
   // 後ろからループすることで、決済後のインデックスずれを防ぐ
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      // ポジションを選択
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
         continue;

      // 通貨ペアが一致するかチェック
      if(OrderSymbol() != Symbol())
         continue;

      // マジックナンバーが一致するかチェック
      if(OrderMagicNumber() != magicNumber)
         continue;

      // ポジションの種類に応じて決済する
      bool result = false;

      if(OrderType() == OP_BUY)
      {
         // 買いポジションの決済(決済価格はBid)
         result = OrderClose(OrderTicket(), OrderLots(), Bid, 20, clrRed);
      }
      else if(OrderType() == OP_SELL)
      {
         // 売りポジションの決済(決済価格はAsk)
         result = OrderClose(OrderTicket(), OrderLots(), Ask, 20, clrBlue);
      }

      // 決済結果をログに出力
      if(result)
         Print("決済成功!チケット番号: ", OrderTicket());
      else
         Print("決済失敗。チケット番号: ", OrderTicket(),
               " エラーコード: ", GetLastError());
   }
}

//+------------------------------------------------------------------+
//| OnTick関数                                                        |
//+------------------------------------------------------------------+
void OnTick()
{
   // 例:合計損益が -10000円を下回ったら全決済する
   double totalProfit = 0;

   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
         continue;
      if(OrderSymbol() != Symbol())
         continue;
      if(OrderMagicNumber() != magicNumber)
         continue;

      // そのポジションの損益を合計に加算
      totalProfit += OrderProfit() + OrderSwap() + OrderCommission();
   }

   // 合計損益が -10000 を下回ったら全決済
   if(totalProfit < -10000)
   {
      Print("合計損益が ", totalProfit, " 円のため、全決済を実行します。");
      CloseAllMyPositions();
   }
}

 

このプログラムのポイントは2つあります。

  • ループを後ろから回している:ポジションを決済するとインデックスがずれるため、後ろから処理することでエラーを防ぎます。
  • 買いと売りで決済価格が異なる:買いポジションはBidで、売りポジションはAskで決済します。

マジックナンバーでフィルタリングしているので、他のEAや裁量のポジションに影響を与えることはありません。

 

応用例3:外部パラメータでマジックナンバーを変更できるようにする

同じEAを複数のチャートに適用する場合、それぞれ異なるマジックナンバーを設定する必要があります。

そのようなとき、外部パラメータ(extern や input)を使うと、チャートに適用するときにマジックナンバーを自由に変更できるようになります。

以下のプログラムは、外部パラメータでマジックナンバーを設定する例です。

// 外部パラメータとしてマジックナンバーを定義
// EAをチャートに適用するときに、数値を変更できる
input int MagicNumber = 12345;

//+------------------------------------------------------------------+
//| OnTick関数                                                        |
//+------------------------------------------------------------------+
void OnTick()
{
   // 保有ポジションをチェック
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
         continue;

      if(OrderSymbol() != Symbol())
         continue;

      // 外部パラメータのマジックナンバーと比較
      if(OrderMagicNumber() != MagicNumber)
         continue;

      // マジックナンバーが一致するポジションの情報を表示
      Print("チケット番号: ", OrderTicket(),
            " マジックナンバー: ", OrderMagicNumber(),
            " 損益: ", OrderProfit());
   }
}

 

input を使って宣言することで、EAをチャートに適用するときのパラメータ画面で数値を変更できます。

例えば、USDJPYのチャートではマジックナンバーを「12345」に、EURUSDのチャートでは「12346」に設定するといった使い方ができます。

同じEAでもマジックナンバーが異なれば、お互いのポジションに干渉しないため、安全に運用できます。

 

まとめ

今回は、マジックナンバーとOrderMagicNumber関数について解説しました。

ポイントをまとめると以下の通りです。

  • マジックナンバーは、自動売買がポジションを管理するための番号です。
  • マジックナンバーは、OrderSend関数の9番目の引数で設定します。
  • OrderMagicNumber関数で、ポジションに付いているマジックナンバーを取得できます。
  • OrderMagicNumber関数を使う前に、OrderSelect関数でポジションを選択しておく必要があります。
  • 複数のEAを同時に動かす場合は、異なるマジックナンバーを設定しましょう。
  • 外部パラメータ(input)を使うと、マジックナンバーを柔軟に変更できます。

 

マジックナンバーは、EA開発において必須の知識です。

正しく設定・活用することで、安全にポジションを管理できるようになりますので、ぜひ今回の内容を参考にしてみてください。