【MQL4関数】OrderLots関数の使い方!ポジションのロット数の取得方法

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

ポジションを決済したいとき、ロット数を取得しないとね!

OrderLots関数とは

OrderLots関数とは、選択したポジションのロット数を取得する関数です。

OrderLots関数を実行する前に、ポジションを選択する必要があるので、OrderSelect関数と組み合わせて使います。

使う場面は限られていますが、ポジションを決済するときに使う重要な関数なので、覚えておきましょう。

 

OrderLots関数の書き方

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

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

 

基本的な書き方

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

double lots = OrderLots();

 

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

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

 

返り値(戻り値)

OrderLots関数の返り値は double型です。

選択したポジションのロット数が返ってきます。

注意することは1つです。

ロット数は、全てのポジションの合計値ではなく、1つのポジションのロット数が返ってきます。

そのため、全てのポジションの合計値を使いたい場合は、for文で OrderLots関数の返り血を合計すると良いでしょう。

 

OrderLots関数の具体的な使い方

それでは、OrderLots関数の具体的な使い方を見ていきましょう。

OrderLots関数は、主に決済注文(OrderClose関数)を出すときに使用します。

具体的には以下のようなプログラムを使います。

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

      //ロット数を取得
      double lots = OrderLots();

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

   }

 

 

初めに、OrderSelect関数で決済したいポジションを選択します。

その後にOrderLots関数で、ポジションのロット数を取得し、OrderClose関数の引数に書いています。

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

 

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

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

 

例1:全ポジションの合計ロット数を計算する

ナンピンEAやマーチンゲールEAでは、保有中の全ポジションの合計ロット数を把握することが重要です。以下のプログラムでは、現在の通貨ペアの合計ロット数を計算しています。

//+------------------------------------------------------------------+
//| 現在の通貨ペアの合計ロット数を計算する関数                         |
//+------------------------------------------------------------------+
double GetTotalLots(int magicNumber)
  {
   double totalLots = 0.0; // 合計ロット数を格納する変数

   // 全ポジションをループで確認
   for(int i = OrdersTotal() - 1; i >= 0; i--)
     {
      // ポジションを選択
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
         // 通貨ペアとマジックナンバーが一致するか確認
         if(OrderSymbol() == Symbol() && OrderMagicNumber() == magicNumber)
           {
            // OrderLots関数でロット数を取得し、合計に加算
            totalLots += OrderLots();
           }
        }
     }

   return totalLots; // 合計ロット数を返す
  }

//+------------------------------------------------------------------+
//| 使用例:OnTick内で合計ロット数を確認                              |
//+------------------------------------------------------------------+
void OnTick()
  {
   int myMagic = 12345;

   // 合計ロット数を取得
   double total = GetTotalLots(myMagic);

   // 合計ロット数が上限を超えていたら新規注文を出さない
   double maxLots = 1.0; // 最大合計ロット数
   if(total >= maxLots)
     {
      Comment("合計ロット数が上限に達しています: ", DoubleToString(total, 2), " lots");
      return; // 新規注文を行わない
     }

   // ここに通常のエントリーロジックを記述
   Comment("現在の合計ロット数: ", DoubleToString(total, 2), " lots");
  }

このように、for文でOrderLots関数の戻り値を合計することで、リスク管理に役立てることができます。

 

例2:全ポジションを一括決済するEA

複数のポジションをまとめて決済する場面は非常に多いです。以下のプログラムでは、自分のEAが出した全ポジションをOrderLots関数で取得したロット数を使って一括決済しています。

#define MAGIC_NUMBER 12345 // マジックナンバーの定義

//+------------------------------------------------------------------+
//| 全ポジションを一括決済する関数                                     |
//+------------------------------------------------------------------+
void CloseAllPositions()
  {
   // ポジション数が0になるまで繰り返す(逆順ループ)
   for(int i = OrdersTotal() - 1; i >= 0; i--)
     {
      // ポジションを選択できるか確認
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
         continue;

      // 自分のEAのポジションかつ、現在の通貨ペアか確認
      if(OrderSymbol() != Symbol() || OrderMagicNumber() != MAGIC_NUMBER)
         continue;

      // OrderLots関数でロット数を取得
      double lots = OrderLots();

      // 注文タイプに応じて決済価格を設定
      double closePrice = 0;
      if(OrderType() == OP_BUY)
         closePrice = Bid;  // 買いポジションはBidで決済
      else if(OrderType() == OP_SELL)
         closePrice = Ask;  // 売りポジションはAskで決済
      else
         continue; // 指値・逆指値注文はスキップ

      // ポジションを決済(取得したロット数を使用)
      bool result = OrderClose(OrderTicket(), lots, closePrice, 10, clrRed);

      // 決済結果をログに出力
      if(result)
         Print("決済成功 チケット:", OrderTicket(), " ロット:", DoubleToString(lots, 2));
      else
         Print("決済失敗 チケット:", OrderTicket(), " エラー:", GetLastError());
     }
  }

//+------------------------------------------------------------------+
//| OnTick - 利益が一定額を超えたら全決済する例                       |
//+------------------------------------------------------------------+
void OnTick()
  {
   double totalProfit = 0.0;

   // 全ポジションの損益を合計
   for(int i = OrdersTotal() - 1; i >= 0; i--)
     {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
         if(OrderSymbol() == Symbol() && OrderMagicNumber() == MAGIC_NUMBER)
           {
            totalProfit += OrderProfit() + OrderSwap() + OrderCommission();
           }
        }
     }

   // 利益が10,000円を超えたら全決済
   if(totalProfit >= 10000)
     {
      Print("利益目標達成!全ポジション決済開始 利益:", DoubleToString(totalProfit, 0), "円");
      CloseAllPositions();
     }

   Comment("現在の合計損益: ", DoubleToString(totalProfit, 0), "円");
  }

一括決済は、利益確定や損切りの自動化で非常によく使うパターンです。OrderLots関数でロット数を正しく取得できないと決済が失敗するので、必ずセットで覚えておきましょう。

 

例3:ポジションの半分だけ部分決済するEA

利益が出たポジションの半分だけを決済して利益を確保し、残りのポジションで利益を伸ばす「部分決済」は、裁量トレーダーにも人気のテクニックです。OrderLots関数で取得したロット数を半分にすることで実現できます。

#define MAGIC_NUMBER 12345 // マジックナンバー
input double PartialCloseProfit = 50.0; // 部分決済を行う利益pips

//+------------------------------------------------------------------+
//| ポジションの半分を部分決済する関数                                 |
//+------------------------------------------------------------------+
bool PartialClose(int ticket)
  {
   // チケット番号でポジションを選択
   if(!OrderSelect(ticket, SELECT_BY_TICKET))
     {
      Print("ポジション選択失敗 チケット:", ticket);
      return false;
     }

   // OrderLots関数で現在のロット数を取得
   double currentLots = OrderLots();

   // 半分のロット数を計算(最小ロット数以上になるよう調整)
   double halfLots = NormalizeDouble(currentLots / 2.0, 2);
   double minLot   = MarketInfo(Symbol(), MODE_MINLOT); // 最小ロット数を取得

   // 半分のロット数が最小ロット未満なら部分決済できない
   if(halfLots < minLot)
     {
      Print("ロット数が小さすぎるため部分決済できません。現在:", 
            DoubleToString(currentLots, 2), " 半分:", DoubleToString(halfLots, 2));
      return false;
     }

   // 決済価格を注文タイプに応じて設定
   double closePrice = 0;
   if(OrderType() == OP_BUY)
      closePrice = MarketInfo(Symbol(), MODE_BID);
   else if(OrderType() == OP_SELL)
      closePrice = MarketInfo(Symbol(), MODE_ASK);
   else
      return false;

   // 半分のロット数だけ決済を実行
   bool result = OrderClose(ticket, halfLots, closePrice, 10, clrBlue);

   if(result)
      Print("部分決済成功 チケット:", ticket, 
            " 決済ロット:", DoubleToString(halfLots, 2),
            " 残りロット:", DoubleToString(currentLots - halfLots, 2));
   else
      Print("部分決済失敗 チケット:", ticket, " エラー:", GetLastError());

   return result;
  }

//+------------------------------------------------------------------+
//| OnTick - 利益が一定pipsを超えたら半分決済                         |
//+------------------------------------------------------------------+
void OnTick()
  {
   for(int i = OrdersTotal() - 1; i >= 0; i--)
     {
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
         continue;

      // 自分のEAかつ現在の通貨ペアのポジションか確認
      if(OrderSymbol() != Symbol() || OrderMagicNumber() != MAGIC_NUMBER)
         continue;

      // 現在の利益pipsを計算
      double profitPips = 0;
      if(OrderType() == OP_BUY)
         profitPips = (Bid - OrderOpenPrice()) / Point / 10;
      else if(OrderType() == OP_SELL)
         profitPips = (OrderOpenPrice() - Ask) / Point / 10;

      // OrderLots関数でロット数を確認し、初期ロットから減っていなければ部分決済
      double initialLot = 0.10; // 初期エントリーロット数
      double currentLots = OrderLots();

      // 利益が目標に達し、かつまだ部分決済していない場合
      if(profitPips >= PartialCloseProfit && currentLots >= initialLot)
        {
         PartialClose(OrderTicket());
        }
     }
  }

部分決済では、OrderLots関数で取得したロット数を元に「半分のロット数」を計算しています。ブローカーの最小ロット数を下回らないようチェックすることがポイントです。

 

例4:ポジション情報をチャート上に一覧表示するインジケーター

デバッグや運用監視に便利な、保有中のポジション情報をチャート上に表示するインジケーターです。OrderLots関数を使ってロット数を取得し、各ポジションの詳細を一覧で確認できます。

//+------------------------------------------------------------------+
//| ポジション情報一覧表示インジケーター                               |
//+------------------------------------------------------------------+
#property indicator_chart_window // チャートウィンドウに表示

//+------------------------------------------------------------------+
//| カスタムインジケーター初期化関数                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   // 表示更新用のタイマーを1秒ごとに設定
   EventSetTimer(1);
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| カスタムインジケーター終了処理                                     |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   // チャートのコメントをクリア
   Comment("");
   EventKillTimer();
  }

//+------------------------------------------------------------------+
//| タイマーイベントで定期的に情報を更新                               |
//+------------------------------------------------------------------+
void OnTimer()
  {
   DisplayPositionInfo();
  }

//+------------------------------------------------------------------+
//| メインの計算関数(インジケーター必須)                             |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   return(rates_total);
  }

//+------------------------------------------------------------------+
//| ポジション情報をチャートに表示する関数                             |
//+------------------------------------------------------------------+
void DisplayPositionInfo()
  {
   string info = "===== ポジション情報一覧 =====\n"; // 表示用文字列
   double totalLots   = 0.0; // 合計ロット数
   double totalProfit = 0.0; // 合計損益
   int    posCount    = 0;   // ポジション数カウンター

   // 全ポジションをループ
   for(int i = 0; i < OrdersTotal(); i++)
     {
      // ポジションを選択
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
         continue;

      // 現在の通貨ペアのポジションのみ対象
      if(OrderSymbol() != Symbol())
         continue;

      // 成行注文(BUYまたはSELL)のみ対象
      if(OrderType() != OP_BUY && OrderType() != OP_SELL)
         continue;

      posCount++; // ポジション数をカウント

      // OrderLots関数でロット数を取得
      double lots   = OrderLots();
      double profit = OrderProfit() + OrderSwap() + OrderCommission();

      // 注文タイプを文字列に変換
      string typeStr = (OrderType() == OP_BUY) ? "BUY" : "SELL";

      // 各ポジションの情報を1行ずつ追加
      info += StringFormat("#%d | %s | %.2f lots | 建値:%.5f | 損益:%.0f円 | Magic:%d\n",
                           OrderTicket(),           // チケット番号
                           typeStr,                  // 売買方向
                           lots,                     // ロット数(OrderLots関数)
                           OrderOpenPrice(),         // エントリー価格
                           profit,                   // 損益
                           OrderMagicNumber());      // マジックナンバー

      // 合計値を加算
      totalLots   += lots;
      totalProfit += profit;
     }

   // 合計情報を追加
   info += "==============================\n";
   info += StringFormat("ポジション数: %d\n", posCount);
   info += StringFormat("合計ロット数: %.2f lots\n", totalLots);
   info += StringFormat("合計損益: %.0f円\n", totalProfit);

   // チャートに表示
   Comment(info);
  }

このインジケーターをチャートにセットすると、保有中の全ポジションのロット数・損益・チケット番号などがリアルタイムで一覧表示されます。複数のEAを同時に稼働させているときの状況把握に非常に役立ちます。

 

例5:ナンピンEAで前回ロットの倍でエントリーする

マーチンゲール戦略のナンピンEAでは、前回のポジションのロット数を取得して、次のエントリーを倍のロット数で行います。OrderLots関数を使って最新ポジションのロット数を確認する実用的なパターンです。

#define MAGIC_NUMBER 99999 // マジックナンバー
input double BaseLot       = 0.01; // 基本ロット数
input double LotMultiplier = 2.0;  // ロット倍率(マーチンゲール係数)
input double MaxLot        = 1.0;  // 最大ロット数(安全上限)
input int    NanpinPips    = 30;   // ナンピン間隔(pips)

//+------------------------------------------------------------------+
//| 最新ポジションのロット数を取得する関数                             |
//+------------------------------------------------------------------+
double GetLastPositionLots()
  {
   datetime latestTime = 0; // 最新のオープン時間を保持
   double   lastLots   = 0; // 最新ポジションのロット数

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

      // 自分のEAかつ現在の通貨ペアか確認
      if(OrderSymbol() != Symbol() || OrderMagicNumber() != MAGIC_NUMBER)
         continue;

      // 成行注文のみ対象
      if(OrderType() != OP_BUY && OrderType() != OP_SELL)
         continue;

      // より新しいポジションを見つけたら更新
      if(OrderOpenTime() > latestTime)
        {
         latestTime = OrderOpenTime();
         // OrderLots関数で最新ポジションのロット数を取得
         lastLots = OrderLots();
        }
     }

   return lastLots; // 最新ポジションのロット数を返す
  }

//+------------------------------------------------------------------+
//| 最新ポジションの建値を取得する関数                                 |
//+------------------------------------------------------------------+
double GetLastPositionPrice()
  {
   datetime latestTime  = 0;
   double   lastPrice   = 0;

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

      if(OrderSymbol() != Symbol() || OrderMagicNumber() != MAGIC_NUMBER)
         continue;

      if(OrderType() != OP_BUY && OrderType() != OP_SELL)
         continue;

      if(OrderOpenTime() > latestTime)
        {
         latestTime = OrderOpenTime();
         lastPrice  = OrderOpenPrice();
        }
     }

   return lastPrice;
  }

//+------------------------------------------------------------------+
//| 保有ポジション数を取得する関数                                     |
//+------------------------------------------------------------------+
int CountPositions()
  {
   int count = 0;

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

      if(OrderSymbol() == Symbol() && OrderMagicNumber() == MAGIC_NUMBER)
        {
         if(OrderType() == OP_BUY || OrderType() == OP_SELL)
            count++;
        }
     }

   return count;
  }

//+------------------------------------------------------------------+
//| OnTick - ナンピン買いのロジック例                                  |
//+------------------------------------------------------------------+
void OnTick()
  {
   int posCount = CountPositions();

   // ポジションが0のとき:基本ロットで初回エントリー
   if(posCount == 0)
     {
      // ここにエントリー条件を記述(例として常にBUY)
      int ticket = OrderSend(Symbol(), OP_BUY, BaseLot, Ask, 10, 0, 0,
                             "Nanpin EA", MAGIC_NUMBER, 0, clrBlue);
      if(ticket > 0)
         Print("初回エントリー成功 ロット:", DoubleToString(BaseLot, 2));
      return;
     }

   // ポジションが1つ以上あるとき:ナンピン条件を確認
   double lastPrice = GetLastPositionPrice();
   double lastLots  = GetLastPositionLots();

   // 最新ポジションからナンピン間隔分だけ逆行したか確認
   double nanpinDistance = NanpinPips * Point * 10;

   if(Ask <= lastPrice - nanpinDistance)
     {
      // OrderLots関数で取得した前回ロットに倍率をかける
      double nextLot = NormalizeDouble(lastLots * LotMultiplier, 2);

      // 最大ロット数を超えないよう制限
      if(nextLot > MaxLot)
         nextLot = MaxLot;

      // ナンピンエントリー
      int ticket = OrderSend(Symbol(), OP_BUY, nextLot, Ask, 10, 0, 0,
                             "Nanpin EA", MAGIC_NUMBER, 0, clrGreen);
      if(ticket > 0)
         Print("ナンピンエントリー成功 ロット:", DoubleToString(nextLot, 2),
               " 前回ロット:", DoubleToString(lastLots, 2));
     }

   // チャートに情報を表示
   Comment("ポジション数: ", posCount,
           "\n最新ロット: ", DoubleToString(lastLots, 2),
           "\n最新建値: ", DoubleToString(lastPrice, 5));
  }

このプログラムでは、GetLastPositionLots関数の中でOrderLots関数を使い、最新ポジションのロット数を取得しています。そのロット数に倍率をかけることで、マーチンゲール方式のナンピンを実現しています。MaxLotで上限を設けることで、ロット数が際限なく増えることを防いでいる点もポイントです。

 

まとめ

OrderLots関数は、選択したポジションのロット数を取得するシンプルな関数ですが、EA開発では非常に多くの場面で活躍します。

特に以下の3つの場面で使うことが多いので、しっかり覚えておきましょう。

  1. ポジションの決済時:OrderClose関数の引数としてロット数を指定する
  2. リスク管理:合計ロット数を計算して、上限を超えないよう制御する
  3. ナンピン・マーチンゲール:前回のロット数を基に次のエントリーロットを計算する

OrderLots関数を使う前には、必ずOrderSelect関数でポジションを選択することを忘れないようにしましょう。