ラインタッチでアラートを鳴らす方法（MQL4

# 目的
MetaTraderで，水平線を引いたとき，現在価格が水平線にタッチしたときアラートを鳴らしたい。

# アラートを出す際の考え方

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531802/7d8812e5-1d66-2922-4783-51da88499492.png)
図のようにNOWという現在価格に対して，上の破線や下の破線と同価格になったときに，アラートを出したいと思います。
上の破線は，下から同価格になったときのみに反応し，下の破線はその逆になるようにしたいため，方向性という考え方を持たせています（ソースコード中のdirection）。

また，アラート制御用のボタンとしてラベルを水平線の近くに配置します。
このラベルは，水平線と紐づけするため，水平線の価格をラベルの説明文に付与してタグ付けしておきます。

例としては，
``` C
//水平線を探した後は，水平線にラベルがセットされてるか探す。
if(StringFind(ObjectDescription(ownObjName),objName,0) > -1)
```
といった具合に，ラベルをチェックし，水平線と紐づけされているか見ています。

基本的には
1. 水平線を探す
1. ラベルが水平線と紐づけされているか調べる
1. ラベルがアラートをオンするか調べる

というのを，価格が更新される都度実施しています。

# 全ソースコード

``` C
#property strict
#property indicator_chart_window

input string msg = "目標価格に到達しました！";//価格到達時のメッセージ
string objName = "HLineTouch";
color backGroundDark = clrYellow;
color backGroundWhite = clrDeepSkyBlue;
extern bool debugMode = false;//サポートモード
string dmsg;
color fontColor;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   dmsg = "\r\n" +msg+" :" + "\r\n";

   ChartSetInteger(0,CHART_EVENT_OBJECT_CREATE, true);

   if(ChartGetInteger(0,CHART_COLOR_BACKGROUND,0) > 800000)
     {
      fontColor = backGroundWhite;
     }
   else
     {
      fontColor = backGroundDark;
     }
   int objNum = ObjectsTotal();
   int i;
   if(objNum > 0)
     {

      //水平線を探す処理
      for(i = 0; i < objNum; i++)
        {
         string ownObjName = ObjectName(i);
         if(ObjectType(ownObjName) == OBJ_HLINE)
           {
            if(debugMode)
               Print("object no setsumei :"+ObjectDescription(ownObjName));
            //水平線を探した後は，水平線にラベルがセットされてるか探す。
            if(StringFind(ObjectDescription(ownObjName),objName,0) > -1)
              {
               float value = (float)ObjectGetDouble(0, ownObjName, OBJPROP_PRICE,0);  //自分の価格を取得
               //long dateTime = ObjectGetInteger(0,ownObjName,OBJPROP_TIME,0);
               moveLabel(ObjectDescription(ownObjName),value,fontColor);
              }



           }
        }
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
  {
//---

//水平線を探してから，labelを探して，Alert ONを探す
   int objNum = ObjectsTotal();
   int i;
   if(objNum > 0)
     {
      //水平線を探す処理
      for(i = 0; i < objNum; i++)
        {
         string ownObjName = ObjectName(i);
         if(ObjectType(ownObjName) == OBJ_HLINE)
           {
            //説明欄が空白であることが条件
            if(StringFind(ObjectDescription(ownObjName),objName,0)>-1)
              {

               for(int j = 0; j< objNum; j++)
                 {
                  string ownObjName2 = ObjectName(j);
                  if(ObjectType(ownObjName2) == OBJ_LABEL)
                    {
                     if(StringFind(ownObjName2,ObjectDescription(ownObjName),0) > -1)
                       {

                        string srcStr = ObjectGetString(0,ownObjName2,OBJPROP_TEXT,0);
                        double value = ObjectGetDouble(0, ownObjName, OBJPROP_PRICE,0);  //自分の価格を取得
                        moveLabel(ownObjName2,(float)value,fontColor);
                        if(srcStr == "Alert ON")
                          {
                           double nowValue = close[0];
                           string name[];
                           int    sep_num = StringSplit(ownObjName2,',',name);
                           int direction = StrToInteger(name[2]);
                           if(direction * (nowValue - value) <= 0)
                             {
                              Alert("Line Touch!!!");

                              ObjectSetString(0,ownObjName2,OBJPROP_TEXT,"Alert OFF");    // 表示するテキスト
                             }
                          }


                       }


                    }
                 }
              }


           }
        }
     }


//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---


   if(ChartGetInteger(0,CHART_COLOR_BACKGROUND,0) > 800000)
     {
      fontColor = backGroundWhite;
     }
   else
     {
      fontColor = backGroundDark;
     }

   int objNum = ObjectsTotal();
   int i;


//新たにオブジェクトが作成された
   if(id == CHARTEVENT_OBJECT_CREATE)
     {

      if(objNum > 0)
        {
         //水平線を探す処理
         for(i = 0; i < objNum; i++)
           {
            string ownObjName = ObjectName(i);
            if(ObjectType(ownObjName) == OBJ_HLINE)
              {
               //説明欄が空白であることが条件
               if(StringFind(ObjectDescription(ownObjName),objName,0)<0)
                 {
                  //水平線にフラグをセットしてラベルを立てる。
                  datetime time = TimeLocal();
                  float value = (float)ObjectGetDouble(0, ownObjName, OBJPROP_PRICE,0);  //自分の価格を取得
                  string lblName = objName+" "+ TimeToStr(time)+ IntegerToString(TimeSeconds(time)) ;

                  int selectBar = iBarShift(NULL,PERIOD_CURRENT,iTime(NULL,PERIOD_CURRENT,0),false);
                  if(debugMode)
                     Print("時間足制御　バー:"+IntegerToString(selectBar));
                  double selectClose;
                  selectClose = iClose(NULL,PERIOD_CURRENT,selectBar);
                  int q = value < selectClose  ? 1:-1;
                  ObjectSetText(ownObjName,lblName,0,NULL,clrNONE);

                  lblName = lblName+","+DoubleToStr(selectClose ) +","+IntegerToString(q);
                  labelCreate(lblName,value,fontColor);
                 }

              }
           }
        }

     }

   if(id == CHARTEVENT_OBJECT_CLICK)
     {
      if(ObjectType(sparam) == OBJ_LABEL)
        {

         //if(debugMode)
         //   Print(sparam);
         string srcStr = ObjectGetString(0,sparam,OBJPROP_TEXT,0);

         //if(debugMode)
         // Print(srcStr);
         if(srcStr == "Alert OFF")
           {
            ObjectSetString(0,sparam,OBJPROP_TEXT,"Alert ON");    // 表示するテキスト
           }
         else
           {
            ObjectSetString(0,sparam,OBJPROP_TEXT,"Alert OFF");    // 表示するテキスト
           }
        }

     }

   if(id == CHARTEVENT_OBJECT_DRAG || id == CHARTEVENT_CHART_CHANGE)
     {
      if(objNum > 0)
        {
         //水平線を探す処理
         for(i = 0; i < objNum; i++)
           {
            string ownObjName = ObjectName(i);
            if(ObjectType(ownObjName) == OBJ_HLINE)
              {
               //水平線を探した後は，水平線にラベルがセットされてるか探す。
               if(StringFind(ObjectDescription(ownObjName),objName,0) > -1)
                 {
                  float value = (float)ObjectGetDouble(0, ownObjName, OBJPROP_PRICE,0);  //自分の価格を取得
                  //long dateTime = ObjectGetInteger(0,ownObjName,OBJPROP_TIME,0);
                  for(int j = 0; j< objNum; j++)
                    {
                     string ownObjName2 = ObjectName(j);
                     if(ObjectType(ownObjName2) == OBJ_LABEL)
                       {
                        if(StringFind(ownObjName2,ObjectDescription(ownObjName),0) > -1)
                          {

                           int selectBar = iBarShift(NULL,PERIOD_CURRENT,iTime(NULL,PERIOD_CURRENT,0),false);
                           if(debugMode)
                              Print("時間足制御　バー:"+IntegerToString(selectBar));
                           double selectClose;
                           selectClose = iClose(NULL,PERIOD_CURRENT,selectBar);

                           moveLabels(ownObjName2,value,fontColor,(float)selectClose);
                           //ObjectDescription(ownObjName)っていう名前のラベルがあるはず
                          }
                       }
                    }
                 }
              }
           }
        }
     }


  }


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int labelCreate(string myObjName,float value,color col)
  {

   int x,y;
   int chartId = 0;
   ChartTimePriceToXY(0,0,TimeCurrent(),value,x,y);
   ObjectCreate(0,myObjName, // オブジェクト作成
                OBJ_LABEL,                                             // オブジェクトタイプ
                0,                                                       // サブウインドウ番号
                0,                                                       // 1番目の時間のアンカーポイント
                0                                                        // 1番目の価格のアンカーポイント
               );

   ObjectSetInteger(chartId,myObjName,OBJPROP_COLOR,col);    // 色設定

   ObjectSetInteger(chartId,myObjName,OBJPROP_BACK,false);           // オブジェクトの背景表示設定
   ObjectSetInteger(chartId,myObjName,OBJPROP_SELECTABLE,false);     // オブジェクトの選択可否設定
   ObjectSetInteger(chartId,myObjName,OBJPROP_SELECTED,false);      // オブジェクトの選択状態
   ObjectSetInteger(chartId,myObjName,OBJPROP_HIDDEN,true);         // オブジェクトリスト表示設定
   ObjectSetInteger(chartId,myObjName,OBJPROP_ZORDER,0);     // オブジェクトのチャートクリックイベント優先順位


   ObjectSetString(chartId,myObjName,OBJPROP_TEXT,"Alert OFF");    // 表示するテキスト
   ObjectSetString(chartId,myObjName,OBJPROP_FONT,"ＭＳ　ゴシック");  // フォント

   ObjectSetInteger(chartId,myObjName,OBJPROP_FONTSIZE,14);                   // フォントサイズ
   ObjectSetInteger(chartId,myObjName,OBJPROP_CORNER,CORNER_RIGHT_UPPER);  // コーナーアンカー設定
   ObjectSetInteger(chartId,myObjName,OBJPROP_XDISTANCE,0);                // X座標
   ObjectSetInteger(chartId,myObjName,OBJPROP_YDISTANCE,y+14+10);                 // Y座標

// オブジェクトバインディングのアンカーポイント設定
   ObjectSetInteger(chartId,myObjName,OBJPROP_ANCHOR,ANCHOR_RIGHT_LOWER);

   return 0;

  }
//+------------------------------------------------------------------+
int moveLabel(string myObjName,float value,color col)
  {
   int x,y;
   int chartId = 0;
   ChartTimePriceToXY(0,0,TimeCurrent(),value,x,y);
   ObjectSetInteger(chartId,myObjName,OBJPROP_COLOR,col);    // 色設定
   ObjectSetInteger(chartId,myObjName,OBJPROP_YDISTANCE,y+14+5);                 // Y座標

   return 0;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int moveLabels(string myObjName,float value,color col,float myClose)
  {
   int x,y;
   int chartId = 0;
   string name[];
   int    sep_num = StringSplit(myObjName,',',name);
   string myNewObjName;
   ChartTimePriceToXY(0,0,TimeCurrent(),value,x,y);
   string alertStatus = ObjectGetString(chartId,myObjName,OBJPROP_TEXT);
   int q = value < myClose ? 1:-1;
   myNewObjName = name[0] +","+DoubleToStr(myClose) +","+IntegerToString(q);



   ObjectDelete(0,myObjName);
   labelCreate(myNewObjName,value,col);
   ObjectSetString(chartId,myNewObjName,OBJPROP_TEXT,alertStatus);
   return 0;
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int moveChangeBackColor(string myObjName)
  {

   return 0;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {


   /*
   REASON_PROGRAM      0  EAがExpertRemove関数を呼び出して操作を終了しました。
   REASON_REMOVE       1  プログラムがチャートから削除されました。
   REASON_RECOMPILE    2  プログラムが再コンパイルされました。
   REASON_CHARTCHANGE  3  通貨ペアか時間足が変更されました。
   REASON_CHARTCLOSE   4  チャートが閉じられました。
   REASON_PARAMETERS   5  パラメータがユーザ—によって変更されました。
   REASON_ACCOUNT      6  別のアカウントが有効化されるか、アカウントの設定変更によってサーバへの再接続が発生しました。
   REASON_TEMPLATE     7  新しいテンプレートが適用されました。
   REASON_INITFAILED   8  OnInit関数がゼロ以外の値を返しました。
   REASON_CLOSE        9  MT4のターミナルが閉じられた。
   */
   if(reason == REASON_REMOVE || reason == REASON_CHARTCLOSE)
     {
      int objNum = ObjectsTotal();
      int i;
      if(objNum > 0)
        {
         //水平線を探す処理
         for(i = 0; i < objNum; i++)
           {
            string ownObjName = ObjectName(i);
            if(ObjectType(ownObjName) == OBJ_HLINE)
              {
               //水平線を探した後は，水平線にラベルがセットされてるか探す。
               if(StringFind(ObjectDescription(ownObjName),objName,0) > -1)
                 {
                  float value = (float)ObjectGetDouble(0, ownObjName, OBJPROP_PRICE,0);  //自分の価格を取得
                  //long dateTime = ObjectGetInteger(0,ownObjName,OBJPROP_TIME,0);
                  for(int j = 0; j< objNum; j++)
                    {
                     string ownObjName2 = ObjectName(j);
                     if(ObjectType(ownObjName2) == OBJ_LABEL)
                       {
                        if(StringFind(ownObjName2,ObjectDescription(ownObjName),0) > -1)
                          {

                           ObjectDelete(0,ownObjName2);
                          }
                       }
                    }
                 }
              }
           }
        }
     }

  }


//+------------------------------------------------------------------+

```



# REFERENCEs

なし

# さいごに

投資は自己責任！

カレンダー参加者募集してます！

https://qiita.com/advent-calendar/2022/mql

Have a good MQL Life!!!