【MQL4関数】OrderTicket関数の使い方!チケット番号の取得方法

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

ポジションのチケット番号を使いたいけど、どうしたら取得できるの?

OrderTicket関数とは

OrderTicket関数は、ポジションのチケット番号を返す関数です。

ポジションを選択した後に使う関数なので、OrderSelect関数の後に使用します。

OrderTicket関数を使う場面は少ないですが、決済注文を出すときに使う関数なので、「そういえばチケット番号を取得する関数があったよなぁ」程度に覚えておきましょう。

 

OrderTicket関数の書き方

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

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

 

基本的な書き方

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

int ticketNum = OrderTicket();

 

このように返り値は int型で、引数はありません。

まずは返り値を見ていきましょう。

 

返り値(戻り値)

OrderTicket関数の返り値は int型です。

チケット番号が返ってきます。

FX会社にもよりますが、具体的には6〜10桁の番号です。

 

OrderTicket関数の具体的な使い方(サンプルあり)

OrderTicket関数は、主に OrderClose関数(決済注文を出す関数)を使うときに使います。

具体的には以下のようなプログラムです。

//保有ポジションを一つ選択
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
   {

      //チケット番号を取得
      int ticketNum = OrderTicket();

      //ポジションを決済
      bool orderClose = OrderClose( ticketNum, OrderLots(), OrderClosePrice(), 10, clrNONE);

   }

 

 

初めに、OrderSelect関数でポジションを選択し、OrderTicket関数でチケット番号を取得します。

その後にチケット番号を利用して、OrderClose関数を実行します。

 

OrderTicket関数は、このように他の関数と組み合わせて使うことが多いです。

決済注文(OrderClose関数)を出すときに、覚えておくと便利な関数なので、覚えておくと良いでしょう。

 

OrderTicket関数の実用的なプログラム例

ここからは、OrderTicket関数を活用した実用的なプログラム例をいくつか紹介します。実際のEA開発で役立つパターンばかりなので、ぜひ参考にしてください。

 

例1:自分のEAのポジションだけを全決済する

複数のEAを同時に稼働させている場合、マジックナンバーを使って自分のEAが建てたポジションだけを決済する必要があります。OrderTicket関数でチケット番号を取得し、該当するポジションのみを決済するプログラムです。

//+------------------------------------------------------------------+
//| 自分のEAのポジションだけを全決済する関数                          |
//+------------------------------------------------------------------+
#define MAGIC_NUMBER 12345  // このEA固有のマジックナンバー

void CloseAllMyPositions()
{
   // ポジション数の後ろから順にループ(決済するとインデックスがずれるため)
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      // ポジションを選択
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
         continue;

      // 自分のEAのマジックナンバーかチェック
      if(OrderMagicNumber() != MAGIC_NUMBER)
         continue;

      // 対象通貨ペアかチェック
      if(OrderSymbol() != Symbol())
         continue;

      // チケット番号を取得
      int ticket = OrderTicket();

      // 決済処理を実行
      bool result = OrderClose(ticket, OrderLots(), OrderClosePrice(), 10, clrNONE);

      if(result)
         Print("チケット番号 ", ticket, " の決済に成功しました");
      else
         Print("チケット番号 ", ticket, " の決済に失敗。エラー: ", GetLastError());
   }
}

ポイント:ポジションを決済するとインデックス番号がずれるため、後ろから順にループするのが重要です。OrderTicket関数で取得したチケット番号をPrint関数に渡せば、ログにどのポジションを処理したか記録できます。

 

例2:チケット番号を配列に保存して管理する

ナンピンEAなどでは、複数のポジションを建てて管理する必要があります。チケット番号を配列に保存しておけば、後からポジションごとの損益確認や部分決済に活用できます。

//+------------------------------------------------------------------+
//| チケット番号を配列に保存して保有ポジションを管理する例            |
//+------------------------------------------------------------------+
#define MAGIC_NUMBER 12345

// チケット番号を格納する配列
int g_tickets[];

//+------------------------------------------------------------------+
//| 自分のEAの全チケット番号を配列に取得する関数                      |
//+------------------------------------------------------------------+
int GetMyTickets(int &tickets[])
{
   int count = 0;
   // まず配列を空にする
   ArrayResize(tickets, 0);

   for(int i = 0; i < OrdersTotal(); i++)
   {
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
         continue;

      // マジックナンバーと通貨ペアでフィルタ
      if(OrderMagicNumber() != MAGIC_NUMBER)
         continue;
      if(OrderSymbol() != Symbol())
         continue;

      // 配列を1つ拡張してチケット番号を格納
      count++;
      ArrayResize(tickets, count);
      tickets[count - 1] = OrderTicket();
   }

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

//+------------------------------------------------------------------+
//| OnTick内での使用例                                                |
//+------------------------------------------------------------------+
void OnTick()
{
   // 全チケット番号を取得
   int posCount = GetMyTickets(g_tickets);

   // 合計損益を計算
   double totalProfit = 0;
   for(int i = 0; i < posCount; i++)
   {
      // チケット番号で直接ポジションを選択
      if(OrderSelect(g_tickets[i], SELECT_BY_TICKET))
      {
         totalProfit += OrderProfit() + OrderSwap() + OrderCommission();
      }
   }

   // 合計利益が一定額を超えたら全決済
   if(totalProfit > 5000)  // 5000円以上の利益で全決済
   {
      for(int i = posCount - 1; i >= 0; i--)
      {
         if(OrderSelect(g_tickets[i], SELECT_BY_TICKET))
         {
            OrderClose(g_tickets[i], OrderLots(), OrderClosePrice(), 10, clrNONE);
         }
      }
   }
}

ポイント:OrderSelect関数はSELECT_BY_TICKETを指定すると、チケット番号で直接ポジションを選択できます。配列にチケット番号を保存しておけば、いつでも特定のポジションにアクセスできるので便利です。

 

例3:新規注文後にチケット番号でSL/TPを変更する

OrderSend関数の返り値もチケット番号です。注文後にOrderTicket関数と組み合わせて、ストップロスやテイクプロフィットを後から変更(OrderModify)する実用的なパターンを紹介します。

//+------------------------------------------------------------------+
//| 新規注文後にチケット番号でSL/TPを変更するEA例                    |
//+------------------------------------------------------------------+
#define MAGIC_NUMBER 12345

//+------------------------------------------------------------------+
//| 自分のEAのポジション数をカウントする関数                          |
//+------------------------------------------------------------------+
int CountMyOrders()
{
   int count = 0;
   for(int i = 0; i < OrdersTotal(); i++)
   {
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
         continue;
      if(OrderMagicNumber() == MAGIC_NUMBER && OrderSymbol() == Symbol())
         count++;
   }
   return count;
}

//+------------------------------------------------------------------+
//| メイン処理                                                        |
//+------------------------------------------------------------------+
void OnTick()
{
   // すでにポジションがあれば何もしない
   if(CountMyOrders() > 0) return;

   // 移動平均のゴールデンクロスでエントリー
   double maFast     = iMA(Symbol(), 0, 10, 0, MODE_SMA, PRICE_CLOSE, 1);
   double maSlow     = iMA(Symbol(), 0, 50, 0, MODE_SMA, PRICE_CLOSE, 1);
   double maFastPrev = iMA(Symbol(), 0, 10, 0, MODE_SMA, PRICE_CLOSE, 2);
   double maSlowPrev = iMA(Symbol(), 0, 50, 0, MODE_SMA, PRICE_CLOSE, 2);

   // ゴールデンクロス発生時に買い注文
   if(maFastPrev < maSlowPrev && maFast > maSlow)
   {
      // まずSL/TPなしで注文を送信
      int ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 10,
                             0, 0, "GoldenCross EA", MAGIC_NUMBER, 0, clrBlue);

      if(ticket > 0)
      {
         Print("注文成功!チケット番号: ", ticket);

         // チケット番号でポジションを選択してSL/TPを設定
         if(OrderSelect(ticket, SELECT_BY_TICKET))
         {
            double openPrice = OrderOpenPrice();
            double sl = openPrice - 500 * Point;  // 50pipsのストップロス
            double tp = openPrice + 1000 * Point;  // 100pipsのテイクプロフィット

            bool modified = OrderModify(ticket, openPrice, sl, tp, 0, clrBlue);

            if(modified)
               Print("チケット番号 ", ticket, " のSL/TP設定に成功");
            else
               Print("SL/TP設定に失敗。エラー: ", GetLastError());
         }
      }
      else
      {
         Print("注文失敗。エラー: ", GetLastError());
      }
   }
}

ポイント:OrderSend関数の返り値は成功時にチケット番号を返します。このチケット番号をそのままSELECT_BY_TICKETで選択すれば、すぐにOrderModify関数でSL/TPを追加設定できます。ECN口座などSL/TP同時指定ができないブローカーでは、この方法が必須になります。

 

例4:最も古いポジションだけを決済する

複数ポジションを保有している状況で、最初に建てた(最も古い)ポジションだけを決済したいケースがあります。OrderTicket関数とOrderOpenTime関数を組み合わせることで実現できます。

//+------------------------------------------------------------------+
//| 最も古いポジションだけを決済する関数                              |
//+------------------------------------------------------------------+
#define MAGIC_NUMBER 12345

bool CloseOldestPosition()
{
   int    oldestTicket = -1;        // 最も古いポジションのチケット番号
   datetime oldestTime = TimeCurrent(); // 比較用の時刻(現在時刻で初期化)

   // 全ポジションをループして最も古いものを探す
   for(int i = 0; i < OrdersTotal(); i++)
   {
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
         continue;

      // マジックナンバーと通貨ペアでフィルタ
      if(OrderMagicNumber() != MAGIC_NUMBER)
         continue;
      if(OrderSymbol() != Symbol())
         continue;

      // オープン時刻が最も古いポジションを記録
      if(OrderOpenTime() < oldestTime)
      {
         oldestTime   = OrderOpenTime();
         oldestTicket = OrderTicket();  // チケット番号を保存
      }
   }

   // 対象ポジションが見つかった場合に決済
   if(oldestTicket > 0)
   {
      if(OrderSelect(oldestTicket, SELECT_BY_TICKET))
      {
         bool result = OrderClose(oldestTicket, OrderLots(), OrderClosePrice(), 10, clrRed);

         if(result)
         {
            Print("最も古いポジション(チケット番号: ", oldestTicket, ")を決済しました");
            return true;
         }
         else
         {
            Print("決済失敗。チケット番号: ", oldestTicket, " エラー: ", GetLastError());
         }
      }
   }

   return false;
}

ポイント:ループの中でOrderOpenTime関数を比較して最も古いポジションのチケット番号を特定し、ループ後にまとめて決済します。探索と決済を分離することで、ループ中のインデックスずれを気にする必要がなくなります。

 

例5:トレーリングストップをチケット番号で管理する

含み益が伸びたときに、ストップロスを利益方向に引き上げる「トレーリングストップ」は人気の手法です。OrderTicket関数で各ポジションを識別しながら、ポジションごとにSLを更新するプログラムです。

//+------------------------------------------------------------------+
//| チケット番号を使ったトレーリングストップ処理                      |
//+------------------------------------------------------------------+
#define MAGIC_NUMBER 12345

input int TrailingStartPips = 30;  // トレーリング開始pips
input int TrailingStopPips  = 20;  // トレーリング幅pips

void TrailingStopByTicket()
{
   double trailingStart = TrailingStartPips * Point * 10; // pipsをポイントに変換(5桁対応)
   double trailingStop  = TrailingStopPips  * Point * 10;

   for(int i = 0; i < OrdersTotal(); i++)
   {
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
         continue;

      // 自分のEAかつ対象通貨ペアかチェック
      if(OrderMagicNumber() != MAGIC_NUMBER) continue;
      if(OrderSymbol() != Symbol())          continue;

      // チケット番号を取得(ログ出力やデバッグに使用)
      int ticket = OrderTicket();

      // 買いポジションのトレーリングストップ
      if(OrderType() == OP_BUY)
      {
         // 現在の含み益がトレーリング開始条件を満たしているか
         double profit = Bid - OrderOpenPrice();
         if(profit >= trailingStart)
         {
            // 新しいSL = 現在のBid - トレーリング幅
            double newSL = NormalizeDouble(Bid - trailingStop, Digits);

            // 現在のSLより高い場合のみ更新(SLは上方向にのみ移動)
            if(newSL > OrderStopLoss() || OrderStopLoss() == 0)
            {
               bool modified = OrderModify(ticket, OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrBlue);
               if(modified)
                  Print("チケット ", ticket, " のSLを ", newSL, " に更新しました");
               else
                  Print("チケット ", ticket, " のSL更新失敗。エラー: ", GetLastError());
            }
         }
      }

      // 売りポジションのトレーリングストップ
      if(OrderType() == OP_SELL)
      {
         // 現在の含み益がトレーリング開始条件を満たしているか
         double profit = OrderOpenPrice() - Ask;
         if(profit >= trailingStart)
         {
            // 新しいSL = 現在のAsk + トレーリング幅
            double newSL = NormalizeDouble(Ask + trailingStop, Digits);

            // 現在のSLより低い場合のみ更新(SLは下方向にのみ移動)
            if(newSL < OrderStopLoss() || OrderStopLoss() == 0)
            {
               bool modified = OrderModify(ticket, OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrRed);
               if(modified)
                  Print("チケット ", ticket, " のSLを ", newSL, " に更新しました");
               else
                  Print("チケット ", ticket, " のSL更新失敗。エラー: ", GetLastError());
            }
         }
      }
   }
}

ポイント:OrderModify関数の第1引数にはチケット番号が必要です。OrderTicket関数で取得した番号をそのまま渡すことで、ポジションごとに正確なSL更新ができます。Print関数でチケット番号を出力すれば、どのポジションのSLが動いたかログで確認できるので、デバッグにも役立ちます。

 

例6:待機注文(指値・逆指値)をチケット番号で削除する

OrderTicket関数は成行注文だけでなく、待機注文(ペンディングオーダー)にも使えます。一定時間が経過した待機注文を自動削除するプログラムを紹介します。

//+------------------------------------------------------------------+
//| 期限切れの待機注文をチケット番号で削除する関数                    |
//+------------------------------------------------------------------+
#define MAGIC_NUMBER 12345

input int PendingExpireMinutes = 60;  // 待機注文の有効期限(分)

void DeleteExpiredPendingOrders()
{
   // 有効期限の基準時刻を計算
   datetime expireTime = TimeCurrent() - PendingExpireMinutes * 60;

   // 後ろから順にループ(削除するとインデックスがずれるため)
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
         continue;

      // 自分のEAかつ対象通貨ペアかチェック
      if(OrderMagicNumber() != MAGIC_NUMBER) continue;
      if(OrderSymbol() != Symbol())          continue;

      // 待機注文かどうかを判定(OP_BUYLIMITからOP_SELLSTOPまで)
      int type = OrderType();
      if(type != OP_BUYLIMIT && type != OP_SELLLIMIT &&
         type != OP_BUYSTOP  && type != OP_SELLSTOP)
         continue;  // 成行ポジションはスキップ

      // 注文時刻が有効期限を過ぎているかチェック
      if(OrderOpenTime() < expireTime)
      {
         // チケット番号を取得して待機注文を削除
         int ticket = OrderTicket();
         bool result = OrderDelete(ticket, clrNONE);

         if(result)
            Print("期限切れの待機注文を削除。チケット番号: ", ticket,
                  " 注文時刻: ", TimeToStr(OrderOpenTime()));
         else
            Print("待機注文の削除に失敗。チケット番号: ", ticket,
                  " エラー: ", GetLastError());
      }
   }
}

ポイント:OrderDelete関数もOrderClose関数と同様に、第1引数にチケット番号が必要です。OrderType関数で注文の種類を判定し、待機注文(OP_BUYLIMIT・OP_SELLLIMIT・OP_BUYSTOP・OP_SELLSTOP)だけを対象にしています。成行ポジションを誤って処理しないよう、タイプのチェックを忘れないようにしましょう。

 

まとめ

OrderTicket関数は、ポジションや待機注文のチケット番号を取得するためのシンプルな関数です。

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

  • OrderSelect関数の後に使う:ポジションを選択してから呼び出す
  • OrderClose関数やOrderModify関数の引数として使う:決済やSL/TP変更に必須
  • OrderDelete関数にも使える:待機注文の削除にもチケット番号が必要
  • 配列に保存して管理できる:複数ポジションの管理に便利
  • Print関数と組み合わせてデバッグに活用:どのポジションを処理したかログに記録できる

OrderTicket関数単体では使う場面が少ないですが、決済や注文変更には必ず必要になる関数です。他の注文関連の関数とセットで覚えておきましょう。