【MQL4関数】OrderSend関数の使い方!エントリー注文の出し方とサンプルプログラム

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

自動売買EAで、トレードさせるにはどうしたらいいの?

OrderSend関数とは

自動売買を作るからには、自動でトレードさせたいですよね!

「ロジックは考えてあるのに、注文を出す方法が分からない」という方は、OrderSend関数を使ってみましょう。

OrderSend関数は、FX会社に注文を出す関数です。

プログラムで実行することで、注文を出すことができます。

 

対応している注文方法は以下の通りです。

・成り行き注文
・指値注文
・逆指値注文

つまり、エントリー注文だけを出すことができる関数です。

指値の変更や決済の関数は、別の関数がありますので、そちらを利用してください。

 

OrderSend関数の書き方

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

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

 

基本的な書き方

OrderSend関数は、引数が非常に多い関数です。

そのため、難しい部分もあるかと思います。

ですが、成り行き注文のプログラムだけ作ってしまえば、あとは簡単に作れてしまうので、まずは成り行き注文のプログラムを作ってみましょう。

 

さて本題です。

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

分かりやすいように、引数は省略しています。

int ticketNum = OrderSend( ①, ②, ③, ④, ⑤, ⑥, ⑦, ⑧, ⑨, ⑩, ⑪);

 

返り値はint型で、引数は11個あります。

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

 

OrderSend関数の返り値(戻り値)

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

返り値は配列のような連続した値ではなく、値が1つだけ返って来ます。

OrderSend関数の返り値は、関数の中でも特殊で、返ってきた値を使うことは、ほとんどありません。

返り値の具体的な値は以下の通りです。

・注文が成功すればチケット番号(注文番号)が返ってきます。
・注文が失敗すれば-1が返ってきます。

ポジションの管理方法をチケット番号にしている場合は、重要になるのですが、その方法は難易度が高いので、今は気にしなくて大丈夫です。

このようにOrderSend関数は、あくまで注文を出すだけの関数なので、返り値は重要ではありません。

 

OrderSend関数の引数

ここからはOrderSend関数の引数について解説していきます。

数が多いので、ゆっくりと見ていきましょう。

はじめに、OrderSend関数には11個の引数があります。

int ticketNum = OrderSend( ①, ②, ③, ④, ⑤, ⑥, ⑦, ⑧, ⑨, ⑩, ⑪);

 

一覧でザッと見てみます。

番号データ型引数名内容
stringsymbol通貨ペアを設定(NULLなど)
intcmd注文の種類を設定(OP_BUYなど)
doublevolumeロット数を設定
doubleprice注文の価格を設定
intslippage許容するスリッページを設定
doublestoploss損切りを設定
doubletakeprofit利確を設定
stringcommentポジションのコメントを設定
intmagicマジックナンバーを設定
datetimeexpirationポジションの有効期限を設定
colorarrow_colorチャート上のポジションの色を設定

 

1つずつ詳しく見ていきましょう。

 

①symbol

symbolは、注文を実行する通貨ペアを設定します。

例えばドル円の場合、「USDJPY」という値になります。

ですが、具体的に通貨ペアを設定する必要はありません。

というのもMQLには便利なものがあり、「Symbol」または「NULL」と書くだけで、現在の通貨ペアを設定できます。

そのため通常は、下記のように入力すれば大丈夫です。

int ticketNum = OrderSend( NULL, ②, ③, ④, ⑤, ⑥, ⑦, ⑧, ⑨, ⑩, ⑪);

 

②cmd

cmdには注文の種類を設定します。

注文の種類は6種類ありますが、「成り行き買い注文(OP_BUY)」と「成り行き売り注文(OP_SELL)」だけ覚えれば問題なしです。

ここでは成り行き買い注文を出す場合を考えています。

int ticketNum = OrderSend( NULL, OP_BUY, ③, ④, ⑤, ⑥, ⑦, ⑧, ⑨, ⑩, ⑪);

 

実用的なサンプルプログラム

ここからは、OrderSend関数を使った実用的なサンプルプログラムを紹介していきます。

基本の書き方を理解した上で、実際のEAでどのように使うかを見ていきましょう。

 

サンプル①:エラーハンドリング付きの成り行き注文

実際のEA開発では、注文が失敗したときにエラー内容を確認できるようにしておくことが非常に重要です。

以下のプログラムは、注文の成功・失敗を判定し、失敗した場合はエラーコードをログに出力する実用的なサンプルです。

//+------------------------------------------------------------------+
//| エラーハンドリング付き成り行き買い注文のサンプル                  |
//+------------------------------------------------------------------+
void OnTick()
{
   // すでにポジションを持っている場合は何もしない
   if(OrdersTotal() > 0) return;

   // 損切り・利確の価格を計算(現在の買い値から50pips離れた位置)
   double stopLoss   = Ask - 50 * Point;  // 損切り:現在価格の50pips下
   double takeProfit = Ask + 100 * Point;  // 利確:現在価格の100pips上

   // 成り行き買い注文を実行
   int ticket = OrderSend(
      NULL,              // 現在の通貨ペア
      OP_BUY,            // 買いの成り行き注文
      0.1,               // 0.1ロット
      Ask,               // 現在の買い値
      20,                // スリッページ2pips(0.1pips単位)
      stopLoss,          // 損切り価格
      takeProfit,        // 利確価格
      "Buy Order",       // コメント
      12345,             // マジックナンバー
      0,                 // 有効期限なし
      clrBlue            // チャート上の矢印の色
   );

   // 注文結果を判定する
   if(ticket >= 0)
   {
      // 注文成功:チケット番号をログに表示
      Print("注文成功!チケット番号: ", ticket);
   }
   else
   {
      // 注文失敗:エラーコードを取得してログに表示
      int errorCode = GetLastError();
      Print("注文失敗!エラーコード: ", errorCode);
   }
}

GetLastError()を使うことで、なぜ注文が失敗したのかを調べることができます。エラーコードの意味はMQLリファレンスで確認できますので、デバッグに活用してください。

 

サンプル②:移動平均線のゴールデンクロスで買い注文を出すEA

次は、テクニカル指標と組み合わせた実用的なEAのサンプルです。

短期移動平均線が長期移動平均線を下から上に抜けた(ゴールデンクロス)タイミングで買い注文を出します。

//+------------------------------------------------------------------+
//| 移動平均線ゴールデンクロスで買い注文を出すEA                     |
//+------------------------------------------------------------------+
// パラメータ設定(外部から変更可能)
extern double LotSize      = 0.1;   // ロット数
extern int    ShortPeriod  = 5;     // 短期移動平均の期間
extern int    LongPeriod   = 25;    // 長期移動平均の期間
extern int    StopLossPips = 30;    // 損切り幅(pips)
extern int    TakeProfitPips = 60;  // 利確幅(pips)
extern int    MagicNumber  = 1001;  // マジックナンバー

void OnTick()
{
   // 現在のバーの短期・長期移動平均線の値を取得
   double shortMA_current = iMA(NULL, 0, ShortPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
   double longMA_current  = iMA(NULL, 0, LongPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);

   // 1本前のバーの短期・長期移動平均線の値を取得
   double shortMA_prev = iMA(NULL, 0, ShortPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
   double longMA_prev  = iMA(NULL, 0, LongPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);

   // 自分のマジックナンバーのポジションがあるか確認
   bool hasPosition = false;
   for(int i = 0; i < OrdersTotal(); i++)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         // マジックナンバーと通貨ペアが一致するポジションがあるかチェック
         if(OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol())
         {
            hasPosition = true;
            break;
         }
      }
   }

   // すでにポジションがある場合は注文しない
   if(hasPosition) return;

   // ゴールデンクロスの判定
   // 1本前:短期MA ≦ 長期MA かつ 現在:短期MA > 長期MA
   if(shortMA_prev <= longMA_prev && shortMA_current > longMA_current)
   {
      // 損切り・利確の価格を計算
      double sl = Ask - StopLossPips * Point;
      double tp = Ask + TakeProfitPips * Point;

      // 買い注文を実行
      int ticket = OrderSend(
         NULL, OP_BUY, LotSize, Ask, 20,
         sl, tp,
         "GoldenCross Buy",  // ゴールデンクロスの買い注文
         MagicNumber, 0, clrBlue
      );

      if(ticket < 0)
      {
         Print("ゴールデンクロス買い注文失敗 エラー: ", GetLastError());
      }
      else
      {
         Print("ゴールデンクロス買い注文成功 チケット: ", ticket);
      }
   }
}

このサンプルのポイントは、マジックナンバーを使ってポジションの重複を防いでいるところです。すでに自分のEAがポジションを持っている場合は、新たな注文を出さないようにしています。

 

サンプル③:買い注文と売り注文を切り替えるEA

実際のEA開発では、条件に応じて買い注文と売り注文を使い分ける必要があります。

以下のサンプルは、RSIの値に応じて売買方向を切り替えるEAです。買い注文と売り注文でAsk/Bidや損切り・利確の方向が逆になる点に注目してください。

//+------------------------------------------------------------------+
//| RSIの値で売買を切り替えるEA                                      |
//+------------------------------------------------------------------+
extern double LotSize     = 0.1;   // ロット数
extern int    RSI_Period  = 14;    // RSIの期間
extern int    RSI_BuyLevel  = 30;  // RSIがこの値以下なら買い(売られすぎ)
extern int    RSI_SellLevel = 70;  // RSIがこの値以上なら売り(買われすぎ)
extern int    SL_Pips     = 40;    // 損切り幅(pips)
extern int    TP_Pips     = 80;    // 利確幅(pips)
extern int    MagicNumber = 2002;  // マジックナンバー

void OnTick()
{
   // 自分のポジションがすでにあれば何もしない
   for(int i = 0; i < OrdersTotal(); i++)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol())
            return;  // ポジションがあるので処理を中断
      }
   }

   // RSIの値を取得(現在のバー)
   double rsiValue = iRSI(NULL, 0, RSI_Period, PRICE_CLOSE, 0);

   // RSIが30以下 → 売られすぎなので買い注文
   if(rsiValue <= RSI_BuyLevel)
   {
      // 買い注文の場合:Askで注文、SLはAskより下、TPはAskより上
      double sl = Ask - SL_Pips * Point;
      double tp = Ask + TP_Pips * Point;

      int ticket = OrderSend(
         NULL, OP_BUY, LotSize, Ask, 20,
         sl, tp,
         "RSI Buy",       // RSIによる買い注文
         MagicNumber, 0, clrBlue
      );

      if(ticket >= 0)
         Print("RSI買い注文成功 RSI=", rsiValue, " チケット=", ticket);
      else
         Print("RSI買い注文失敗 エラー=", GetLastError());
   }

   // RSIが70以上 → 買われすぎなので売り注文
   if(rsiValue >= RSI_SellLevel)
   {
      // 売り注文の場合:Bidで注文、SLはBidより上、TPはBidより下
      // ※買い注文とは損切り・利確の方向が逆になる点に注意!
      double sl = Bid + SL_Pips * Point;
      double tp = Bid - TP_Pips * Point;

      int ticket = OrderSend(
         NULL, OP_SELL, LotSize, Bid, 20,
         sl, tp,
         "RSI Sell",      // RSIによる売り注文
         MagicNumber, 0, clrRed
      );

      if(ticket >= 0)
         Print("RSI売り注文成功 RSI=", rsiValue, " チケット=", ticket);
      else
         Print("RSI売り注文失敗 エラー=", GetLastError());
   }
}

初心者がよく間違えるポイントですが、買い注文はAsk、売り注文はBidを使います。また、損切り・利確の方向も売買で逆になりますので、必ず確認するようにしましょう。

 

サンプル④:注文関数を共通化して使い回す方法

EA開発に慣れてくると、注文処理を関数として共通化しておくと、コードの見通しが良くなり、バグの防止にもつながります。

以下は、買い注文・売り注文をそれぞれ関数化したサンプルです。OnTick()からシンプルに呼び出せるようになります。

//+------------------------------------------------------------------+
//| 注文関数を共通化して使い回すサンプル                              |
//+------------------------------------------------------------------+
extern double LotSize     = 0.1;   // ロット数
extern int    SL_Pips     = 50;    // 損切り幅(pips)
extern int    TP_Pips     = 100;   // 利確幅(pips)
extern int    Slippage    = 20;    // スリッページ
extern int    MagicNumber = 3003;  // マジックナンバー

//+------------------------------------------------------------------+
//| 買い注文を出す関数                                                |
//+------------------------------------------------------------------+
int SendBuyOrder(string comment)
{
   double sl = Ask - SL_Pips * Point;
   double tp = Ask + TP_Pips * Point;

   int ticket = OrderSend(
      NULL, OP_BUY, LotSize, Ask, Slippage,
      sl, tp,
      comment, MagicNumber, 0, clrBlue
   );

   if(ticket < 0)
      Print("買い注文失敗 エラー: ", GetLastError());
   else
      Print("買い注文成功 チケット: ", ticket);

   return ticket;
}

//+------------------------------------------------------------------+
//| 売り注文を出す関数                                                |
//+------------------------------------------------------------------+
int SendSellOrder(string comment)
{
   double sl = Bid + SL_Pips * Point;
   double tp = Bid - TP_Pips * Point;

   int ticket = OrderSend(
      NULL, OP_SELL, LotSize, Bid, Slippage,
      sl, tp,
      comment, MagicNumber, 0, clrRed
   );

   if(ticket < 0)
      Print("売り注文失敗 エラー: ", GetLastError());
   else
      Print("売り注文成功 チケット: ", ticket);

   return ticket;
}

//+------------------------------------------------------------------+
//| 自分のポジションがあるか確認する関数                              |
//+------------------------------------------------------------------+
bool HasMyPosition()
{
   for(int i = 0; i < OrdersTotal(); i++)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol())
            return true;
      }
   }
   return false;
}

//+------------------------------------------------------------------+
//| メイン処理                                                        |
//+------------------------------------------------------------------+
void OnTick()
{
   // すでにポジションがある場合は何もしない
   if(HasMyPosition()) return;

   // 移動平均線の値を取得
   double shortMA = iMA(NULL, 0, 5, 0, MODE_SMA, PRICE_CLOSE, 0);
   double longMA  = iMA(NULL, 0, 25, 0, MODE_SMA, PRICE_CLOSE, 0);

   // 短期MAが長期MAより上なら買い注文
   if(shortMA > longMA)
   {
      SendBuyOrder("MA Buy");
   }
   // 短期MAが長期MAより下なら売り注文
   else if(shortMA < longMA)
   {
      SendSellOrder("MA Sell");
   }
}

このように注文処理を関数化しておくと、OnTick()の中がスッキリして読みやすくなります。また、買い注文・売り注文の処理が1か所にまとまるので、修正やバグ修正も簡単になります。

特にHasMyPosition()関数のように、ポジションの有無を確認する処理も関数化しておくと、どのEAでも使い回しができて非常に便利です。

 

まとめ

今回はOrderSend関数の使い方を解説しました。

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

・OrderSend関数はエントリー注文を出すための関数
・引数は11個あるが、まずは成り行き注文を覚えればOK
・買い注文はAsk、売り注文はBidを使う
・損切り・利確の方向は、買いと売りで逆になる
・エラーハンドリング(GetLastError)を入れておくとデバッグが楽になる
・注文処理を関数化しておくと、コードの管理がしやすくなる

まずはサンプル①のシンプルな成り行き注文から試してみて、慣れてきたらサンプル④のように関数化していくのがおすすめです。

ぜひ、自分のEA開発に活用してみてください!