ホーム » FX自動売買基礎と応用 » MT5でインジケーターのサインで自動売買を行うツールを作成する【MQLプログラミングの基礎】

MT5でインジケーターのサインで自動売買を行うツールを作成する【MQLプログラミングの基礎】

1.自動売買ON/OFF切り替え用のボタン追加

この記事では、指定したインジケーターのサイン情報を利用してエントリーする自動売買ツールを作成します。

※この記事はMQL5の内容です。

ファイルは新規作成せず、過去の記事「MT5版のボタン操作でエントリーするEAを作成する【MQLプログラミングの基礎】」で作成した、ボタン操作でエントリーするツール「TradePanel.mq5」を流用します。

「TradePanel.mq5」を改修して機能を追加します。

ファイル「TradePanel.mq5」をMetaEditorで開き、OnInit関数内の下記の行


   ButtonCreate(0, PREFIX + "Exit", 0, 0, 60, 160, 40, CORNER_LEFT_UPPER, "Exit", "Arial Black", 20, clrBlack, clrGold, clrNONE);

の下に次のコードを追記し、自動エントリーのON/OFF切り替え用の「Auto」ボタンを追加します。


   ButtonCreate(0, PREFIX + "Auto", 0, 0, 100, 160, 40, CORNER_LEFT_UPPER, "Auto", "Arial Black", 20, clrWhite, clrGray, clrNONE);

ボタン追加_MT5でインジケーターのサインで自動売買を行うツールを作成する【MQLプログラミングの基礎】

2.ボタンの色変化回路を追加

OnChartEvent関数内の下記の行


         Exit();
      }

に次のコードを追記します。


      if (sparam == PREFIX + "Auto") {
         bool state = ObjectGetInteger(0, sparam, OBJPROP_STATE);
         ObjectSetInteger(0, sparam, OBJPROP_BGCOLOR, state ? clrLimeGreen : clrGray);
         ChartRedraw();
      }

「Auto」ボタンを押すと、ボタンの色がLimeGreenになり、戻すとGrayになるので、ON/OFFの状態が目視確認しやすくなります。

ONでボタン色変化_MT5でインジケーターのサインで自動売買を行うツールを作成する【MQLプログラミングの基礎】

3.インジケーターのサイン情報を取得

パラメーター「input string COMMENT = “”; // コメント」の下に次のコードを追記します。


input group ""
input group "【インジケーター】"
input string NAME = "MA_Cross_Sign.ex5"; // ファイル名
input int BUFFER_BUY = 2;                // 買いサインバッファ番号
input int BUFFER_SELL = 3;               // 売りサインバッファ番号

インジケーターとして利用するファイルの初期設定は、以前に「MT5で移動平均線クロスの矢印サインを表示する【MQLプログラミングの基礎】」という記事で作成した「MA_Cross_Sign.ex5」としています。

「MA_Cross_Sign.ex5」では、買い・売りの矢印サインが表示されるバッファは「2」と「3」なので、今回の初期設定は上記のようにしています。

呼び出したいインジケーターに合わせて、パラメーターを変更してください。

上記パラメーターの下に、インジケーター呼び出し用のハンドルを定義するため、次のコードを追記します。


int handle;

OnInit関数内の「 EventSetMillisecondTimer(100);」の下に下記を追記して、インジケーターを呼び出します。


   handle = iCustom(NULL, 0, NAME);

上記の「NAME」の後に、カンマ区切りでインジケーターのパラメーターを指定することも可能です。

今回は、インジケーターのパラメーターは初期設定のまま利用する仕様で進めていきます。
https://www.mql5.com/ja/docs/indicators/icustom

EAをチャートから削除するときなどに、呼び出したインジケーターハンドルを開放するため、OnDeinit関数内の「 EventKillTimer();」の下に、下記を追記します。


   IndicatorRelease(handle);

OnTick関数内に下記を追記し、インジケーターの売買サイン情報を取得します。


   double buy[], sell[];
   ArraySetAsSeries(buy, true);
   ArraySetAsSeries(sell, true);
   CopyBuffer(handle, BUFFER_BUY, 0, 2, buy);
   CopyBuffer(handle, BUFFER_SELL, 0, 2, sell);

上記にて、買いサインの最新足と確定足の2本の値がbuy[]配列にコピーされます。

売りサイン情報は、sell[]にコピーされます。

「Auto」ボタンが押されているときのみ自動売買が機能するよう、下記のif文を追記します。


   if (ObjectGetInteger(0, PREFIX + "Auto", OBJPROP_STATE)) {
      
   }

1本のローソク足でのエントリーを1回のみに制限するため、上記if文内に次のコードを追記します。


      static datetime timeEntry;

チャート右端から数えて2本目の確定足に買いサインが表示されたとき、買いエントリーが実行されるよう、上記のif文内に、次のコードを追記します。


      if (buy[1] != EMPTY_VALUE && iTime(NULL, 0, 0) > timeEntry) {
         double price = SymbolInfoDouble(NULL, SYMBOL_ASK);
         double sl = NormalizeDouble(price - SL * _Point * 10, _Digits);
         double tp = NormalizeDouble(price + TP * _Point * 10, _Digits);
         Entry(ORDER_TYPE_BUY, LOT, price, sl, tp, int(SLIP * 10), MAGIC, COMMENT);
         timeEntry = iTime(NULL, 0, 0);
      }

「buy[1]の値がEMPTY_VALUE(空の値)ではない」という式で、サインが表示されているという判定をしています。

上記のエントリー回路部分は、「Buy」ボタンが押されたときに実行される回路と同じです。

売りサインで売りエントリーするよう、上記の下に次のコードを追記します。


      if (sell[1] != EMPTY_VALUE && iTime(NULL, 0, 0) > timeEntry) {
         double price = SymbolInfoDouble(NULL, SYMBOL_BID);
         double sl = NormalizeDouble(price + SL * _Point * 10, _Digits);
         double tp = NormalizeDouble(price - TP * _Point * 10, _Digits);
         Entry(ORDER_TYPE_SELL, LOT, price, sl, tp, int(SLIP * 10), MAGIC, COMMENT);
         timeEntry = iTime(NULL, 0, 0);
      }

ここまでの編集で作業は完了です。

コンパイルした後に「Auto」ボタンを押しておくと、サインが表示された足が確定したときにエントリーが実行されます。

サイン表示でエントリー_MT5でインジケーターのサインで自動売買を行うツールを作成する【MQLプログラミングの基礎】

チャート上にインジケーターをセットしていなくても本EAでのエントリーは実行されます。

ただし、データフォルダ内のIndicatorsフォルダに、パラメーター「NAME」で呼び出しているインジケーターファイルが入っている必要があります。

4.ソースコード

今回、作成したソースコードは下記の通りです。


//+------------------------------------------------------------------+
//|                                                   TradePanel.mq5 |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#define PREFIX MQLInfoString(MQL_PROGRAM_NAME) + "_"

input int MAGIC = 1;       // マジックナンバー
input double LOT = 0.1;    // ロット
input double TP = 50;      // TP [pips]
input double SL = 50;      // SL [pips]
input double SLIP = 1;     // 許容スリッページ [pips]
input string COMMENT = ""; // コメント
input group ""
input group "【インジケーター】"
input string NAME = "MA_Cross_Sign.ex5"; // ファイル名
input int BUFFER_BUY = 2;                // 買いサインバッファ番号
input int BUFFER_SELL = 3;               // 売りサインバッファ番号

int handle;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   ButtonCreate(0, PREFIX + "Sell", 0, 0, 20, 80, 40, CORNER_LEFT_UPPER, "Sell", "Arial Black", 20, clrWhite, clrDodgerBlue, clrNONE);
   ButtonCreate(0, PREFIX + "Buy", 0, 80, 20, 80, 40, CORNER_LEFT_UPPER, "Buy", "Arial Black", 20, clrWhite, clrRed, clrNONE);
   ButtonCreate(0, PREFIX + "Exit", 0, 0, 60, 160, 40, CORNER_LEFT_UPPER, "Exit", "Arial Black", 20, clrBlack, clrGold, clrNONE);
   ButtonCreate(0, PREFIX + "Auto", 0, 0, 100, 160, 40, CORNER_LEFT_UPPER, "Auto", "Arial Black", 20, clrWhite, clrGray, clrNONE);
//--- create timer
   EventSetMillisecondTimer(100);
   
   handle = iCustom(NULL, 0, NAME);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ObjectsDeleteAll(0, PREFIX);
//--- destroy timer
   EventKillTimer();
   IndicatorRelease(handle);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   double buy[], sell[];
   ArraySetAsSeries(buy, true);
   ArraySetAsSeries(sell, true);
   CopyBuffer(handle, BUFFER_BUY, 0, 2, buy);
   CopyBuffer(handle, BUFFER_SELL, 0, 2, sell);
   
   if (ObjectGetInteger(0, PREFIX + "Auto", OBJPROP_STATE)) {
      static datetime timeEntry;
      if (buy[1] != EMPTY_VALUE && iTime(NULL, 0, 0) > timeEntry) {
         double price = SymbolInfoDouble(NULL, SYMBOL_ASK);
         double sl = NormalizeDouble(price - SL * _Point * 10, _Digits);
         double tp = NormalizeDouble(price + TP * _Point * 10, _Digits);
         Entry(ORDER_TYPE_BUY, LOT, price, sl, tp, int(SLIP * 10), MAGIC, COMMENT);
         timeEntry = iTime(NULL, 0, 0);
      }
      if (sell[1] != EMPTY_VALUE && iTime(NULL, 0, 0) > timeEntry) {
         double price = SymbolInfoDouble(NULL, SYMBOL_BID);
         double sl = NormalizeDouble(price + SL * _Point * 10, _Digits);
         double tp = NormalizeDouble(price - TP * _Point * 10, _Digits);
         Entry(ORDER_TYPE_SELL, LOT, price, sl, tp, int(SLIP * 10), MAGIC, COMMENT);
         timeEntry = iTime(NULL, 0, 0);
      }
   }
  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---
   static bool stateBuy = false, stateSell = false, stateExit = false;
   if (stateBuy) ObjectSetInteger(0, PREFIX + "Buy", OBJPROP_STATE, false);
   stateBuy = ObjectGetInteger(0, PREFIX + "Buy", OBJPROP_STATE);
   if (stateSell) ObjectSetInteger(0, PREFIX + "Sell", OBJPROP_STATE, false);
   stateSell = ObjectGetInteger(0, PREFIX + "Sell", OBJPROP_STATE);
   if (stateExit) ObjectSetInteger(0, PREFIX + "Exit", OBJPROP_STATE, false);
   stateExit = ObjectGetInteger(0, PREFIX + "Exit", OBJPROP_STATE);
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int32_t id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   if (id == CHARTEVENT_OBJECT_CLICK) {
      if (sparam == PREFIX + "Buy") {
         double price = SymbolInfoDouble(NULL, SYMBOL_ASK);
         double sl = NormalizeDouble(price - SL * _Point * 10, _Digits);
         double tp = NormalizeDouble(price + TP * _Point * 10, _Digits);
         Entry(ORDER_TYPE_BUY, LOT, price, sl, tp, int(SLIP * 10), MAGIC, COMMENT);
      }
      if (sparam == PREFIX + "Sell") {
         double price = SymbolInfoDouble(NULL, SYMBOL_BID);
         double sl = NormalizeDouble(price + SL * _Point * 10, _Digits);
         double tp = NormalizeDouble(price - TP * _Point * 10, _Digits);
         Entry(ORDER_TYPE_SELL, LOT, price, sl, tp, int(SLIP * 10), MAGIC, COMMENT);
      }
      if (sparam == PREFIX + "Exit") {
         Exit();
      }
      if (sparam == PREFIX + "Auto") {
         bool state = ObjectGetInteger(0, sparam, OBJPROP_STATE);
         ObjectSetInteger(0, sparam, OBJPROP_BGCOLOR, state ? clrLimeGreen : clrGray);
         ChartRedraw();
      }
   }
  }
//+------------------------------------------------------------------+ 
//| ボタンを作成する                                                      | 
//+------------------------------------------------------------------+ 
bool ButtonCreate(const long              chart_ID=0,               // チャート識別子 
                const string            name="Button",           // ボタン名 
                const int               sub_window=0,             // サブウィンドウ番号 
                const int               x=0,                     // X 座標 
                const int               y=0,                     // Y 座標 
                const int               width=50,                 // ボタンの幅 
                const int               height=18,               // ボタンの高さ 
                const ENUM_BASE_CORNER  corner=CORNER_LEFT_UPPER, // アンカーに使用されるチャートのコーナー 
                const string            text="Button",           // テキスト 
                const string            font="Arial",             // フォント 
                const int               font_size=10,             // フォントサイズ 
                const color             clr=clrBlack,             // テキストの色 
                const color             back_clr=C'236,233,216', // 背景色 
                const color             border_clr=clrNONE,       // 境界線の色 
                const bool              state=false,             // 押される/放される 
                const bool              back=false,               // 背景で表示する 
                const bool              selection=false,         // 強調表示して移動 
                const bool              hidden=true,             // オブジェクトリストに隠す 
                const long              z_order=0)               // マウスクリックの優先順位 
 { 
//--- エラー値をリセットする 
  ResetLastError(); 
//--- ボタンを作成する 
  if(!ObjectCreate(chart_ID,name,OBJ_BUTTON,sub_window,0,0)) 
    { 
    Print(__FUNCTION__, 
          ": failed to create the button! Error code = ",GetLastError()); 
    return(false); 
    } 
//--- ボタン座標を設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x); 
  ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y); 
//--- ボタンサイズを設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_XSIZE,width); 
  ObjectSetInteger(chart_ID,name,OBJPROP_YSIZE,height); 
//--- ポイント座標が相対的に定義されているチャートのコーナーを設定 
  ObjectSetInteger(chart_ID,name,OBJPROP_CORNER,corner); 
//--- テキストを設定する 
  ObjectSetString(chart_ID,name,OBJPROP_TEXT,text); 
//--- テキストフォントを設定する 
  ObjectSetString(chart_ID,name,OBJPROP_FONT,font); 
//--- フォントサイズを設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_FONTSIZE,font_size); 
//--- テキストの色を設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr); 
//--- 背景色を設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_BGCOLOR,back_clr); 
//--- 境界線の色を設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_BORDER_COLOR,border_clr); 
//--- 前景(false)または背景(true)に表示 
  ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back); 
//--- ボタンの状態を設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_STATE,state); 
//--- マウスでのボタンを移動させるモードを有効(true)か無効(false)にする 
  ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection); 
  ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection); 
//--- オブジェクトリストのグラフィックオブジェクトを非表示(true)か表示(false)にする 
  ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden); 
//--- チャートのマウスクリックのイベントを受信するための優先順位を設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order); 
//--- 実行成功 
  return(true); 
 } 
//+------------------------------------------------------------------+
//| エントリー関数                                                   | 
//+------------------------------------------------------------------+ 
void Entry(ENUM_ORDER_TYPE type, double lot, double price, double sl, double tp, int slip, int magic, string comment)
{
   ENUM_ORDER_TYPE_FILLING filling;
   long mode = SymbolInfoInteger(NULL, SYMBOL_FILLING_MODE);
   if ((mode & SYMBOL_FILLING_IOC) != 0) filling = ORDER_FILLING_IOC;
   else if ((mode & SYMBOL_FILLING_FOK) != 0) filling = ORDER_FILLING_FOK;
   else filling = ORDER_FILLING_RETURN;

//--- リクエストと結果の宣言と初期化
   MqlTradeRequest request = {};
   MqlTradeResult  result = {};
//--- リクエストのパラメータ
   request.action       = TRADE_ACTION_DEAL; // 取引操作タイプ
   request.symbol       = Symbol();          // シンボル
   request.volume       = lot;               // ロットのボリューム
   request.type         = type;              // 注文タイプ
   request.price        = price;             // 発注価格
   request.sl           = sl;                // SL (指定する為に行を追加)
   request.tp           = tp;                // TP (指定する為に行を追加)
   request.deviation    = slip;              // 価格からの許容偏差
   request.type_filling = filling;           // 注文充填
   request.magic        = magic;             // 注文のMagicNumber
//--- リクエストの送信
   if(!OrderSend(request,result))
      PrintFormat("OrderSend error %d",GetLastError());     // リクエストの送信が失敗した場合、エラーコードを出力する
   else PlaySound("ok.wav");
}

//+------------------------------------------------------------------+
//| 全てのポジションを決済                                                  |
//+------------------------------------------------------------------+
void Exit()
  {
   ENUM_ORDER_TYPE_FILLING filling;
   long mode = SymbolInfoInteger(NULL, SYMBOL_FILLING_MODE);
   if ((mode & SYMBOL_FILLING_IOC) != 0) filling = ORDER_FILLING_IOC;
   else if ((mode & SYMBOL_FILLING_FOK) != 0) filling = ORDER_FILLING_FOK;
   else filling = ORDER_FILLING_RETURN;

//--- 結果とリクエストの宣言
  MqlTradeRequest request;
   MqlTradeResult  result;
   int total=PositionsTotal(); // 保有ポジション数   
//--- 全ての保有ポジションの取捨
  for(int i=total-1; i>=0; i--)
     {
     //--- 注文のパラメータ
    ulong  position_ticket=PositionGetTicket(i);                                     // ポジションチケット
    string position_symbol=PositionGetString(POSITION_SYMBOL);                       // シンボル 
    int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);             // 小数点以下の桁数
    ulong  magic=PositionGetInteger(POSITION_MAGIC);                                 // ポジションのMagicNumber
     double volume=PositionGetDouble(POSITION_VOLUME);                                 // ポジションボリューム
    ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);   // ポジションタイプ
    //--- ポジション情報の出力
    PrintFormat("#%I64u %s  %s  %.2f  %s [%I64d]",
                  position_ticket,
                  position_symbol,
                 EnumToString(type),
                  volume,
                 DoubleToString(PositionGetDouble(POSITION_PRICE_OPEN),digits),
                  magic);
     //--- MagicNumberが一致している場合
    if(magic==MAGIC)
        {
         //--- リクエストと結果の値のゼロ化
        ZeroMemory(request);
         ZeroMemory(result);
         //--- 操作パラメータの設定
        request.action   =TRADE_ACTION_DEAL;       // 取引操作タイプ
        request.position =position_ticket;         // ポジションチケット
        request.symbol   =position_symbol;         // シンボル 
        request.volume   =volume;                   // ポジションボリューム
        request.deviation=int(SLIP * 10);          // 価格からの許容偏差
        request.type_filling = filling;            // 注文充填
        request.magic    =MAGIC;                   // ポジションのMagicNumber
         //--- ポジションタイプによる注文タイプと価格の設定
        if(type==POSITION_TYPE_BUY)
           {
            request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID);
            request.type =ORDER_TYPE_SELL;
           }
         else
           {
            request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
            request.type =ORDER_TYPE_BUY;
           }
         //--- 決済情報の出力
        PrintFormat("Close #%I64d %s %s",position_ticket,position_symbol,EnumToString(type));
         //--- リクエストの送信
        if(!OrderSend(request,result))
           PrintFormat("OrderSend error %d",GetLastError()); // リクエストの送信に失敗した場合、エラーコードを出力
        else PlaySound("ok.wav");
        //--- 操作情報  
         PrintFormat("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
         //---
        }
     }
  }
//+------------------------------------------------------------------+

本記事の監修者・HT FX

2013年にFXを開始し、その後専業トレーダーへ。2014年からMT4/MT5のカスタムインジケーターの開発に取り組む。ブログでは100本を超えるインジケーターを無料公開。投資スタイルは自作の秒足インジケーターを利用したスキャルピング。

EA(自動売買)を学びたい方へオススメコンテンツ

EA運用の注意点

OANDAではEA(自動売買)を稼働するプラットフォームMT4/MT5の基本的な使い方について、画像や動画付きで詳しく解説しています。MT4/MT5のインストールからEAの設定方法までを詳しく解説しているので、初心者の方でもスムーズにEA運用を始めることが可能です。またOANDAの口座をお持ちであれば、独自開発したオリジナルインジケーターを無料で利用することもできます。EA運用をお考えであれば、ぜひ口座開設をご検討ください。


本ホームページに掲載されている事項は、投資判断の参考となる情報の提供を目的としたものであり、投資の勧誘を目的としたものではありません。投資方針、投資タイミング等は、ご自身の責任において判断してください。本サービスの情報に基づいて行った取引のいかなる損失についても、当社は一切の責を負いかねますのでご了承ください。また、当社は、当該情報の正確性および完全性を保証または約束するものでなく、今後、予告なしに内容を変更または廃止する場合があります。なお、当該情報の欠落・誤謬等につきましてもその責を負いかねますのでご了承ください。

この記事をシェアする