LINE Notifyを使ってMT4からLINEに通知を送るEAのサンプルコードを探すと、動くには動くけれど「通知が届かないことがある」「エラーの原因がよく分からない」というコードによく出会います。多くの場合、原因はWebRequest()関数の引数の渡し方にあります。
この記事では、LINE Notifyを使った通知の仕組みの作り方を、実際に見つかった3つの不具合(受信結果の取り違え・タイムアウト設定・ヘッダーの書き方)の直し方とあわせて解説します。
LINE Notifyを活用してLINEに通知を送るEA
LINE Notifyとは、外部のwebサービスと連携させて、LINEのトークルームで受信できる通信機能になります。
LINE NotifyのURLにメッセージを投げる(POSTする)ことで、自分のLINEのトークルームにそのメッセージを送ることができるので、見逃すことが少なくなります。
LINE Notifyの設定
LINE Notifyにアクセスしましょう。
ログイン画面からLINEのアカウントでログインします。LINEのアカウントがあれば、新規登録などは不要です。

LINEログインした後の画面で、右上のニックネームをクリックしてマイページに移動します。

マイページ内のトークンを発行するボタンをクリックすると、トークン名と通知の送信先を選択するページになります。

発行したトークンは後で必要なのでコピーしておきましょう。

MT4の設定
MT4画面上部のツール→オプションを選択します。
表示されたウィンドウにてエキスパートアドバイザタブを選択します。
WebRequestを許可するURLリストにチェックを入れます。
新しいURLを追加にをクリック以下のURLを入力しましょう。

メタエディタ(MetaEditor)を立ち上げる
メタエディタ(MetaEditor)を立ち上げましょう。今回は名前を「LINE-EA」で作成します。
LINE Notify API Documentは以下に記載しておきます。
パラメーターを記述する
パラメーターを記述しましょう。
Line_tokenには取得したトークンを貼り、Send_MessageにはLINEに送りたいメッセージを記載します。
input string Line_token = "";//LINEのアクセストークン input string Send_Message = "";//LINEに送りたいメッセージ
OnInit()関数を記述する
OnInit()関数を記述しましょう。LineNotify関数はユーザー定義関数になります。
LineNotify()の関数を呼び出して引数をLine_tokenとSend_Messageを送ります。
これでOnInit()関数は完了です。
LineNotify(Line_token,Send_Message);//LineNotifyを呼び出し
OnInit()はEAをチャートにセットした瞬間・パラメーターを変更した瞬間に一度だけ呼ばれる関数です。そのためこのコードのままだと、LINE通知はEAをセットしたタイミングで1回届くだけになります。実際の運用では、価格やポジションの条件を判定するOnTick()の中からLineNotify()を呼び出すことになりますが、その場合は条件が成立している間ずっと毎ティック通知が送られ続けないよう、通知済みフラグで最初の1回だけに絞る工夫が必要です(gc-dc.mdの「ポジションを重複して持たないようにする」やmaginecole-alert-line.mdの「通知が連打され続けないようにする」と同じ考え方です)。この記事ではLineNotify関数そのものの作り方に絞って解説します。
LineNotify関数を記述する
LineNotify関数を記述しましょう。変数をまず設定します。
string headers; char data[], result[];
headersの変数にコンテンツタイプを使用した単純なフォームを入れます。
- Authorization: Bearer
- Token
- \r\n Content-Type: application/x-www-form-urlencoded\r\n
をheadersに代入します。
headers="Authorization: Bearer "+Token+"\r\nContent-Type: application/x-www-form-urlencoded\r\n";
元のコードはこの「Content-Type:」の部分が抜けており、タブ文字が混ざった不正な形式になっていました。LINE NotifyのAPIはフォーム形式でのPOSTを期待しているため、ヘッダー名を正しく明記しておきます。
ArrayResize()は配列の次元のサイズを新しく設定します。
StringToCharArray()は文字列をchar型配列にコピーする関数です。
StringToCharArray()は以下の引数になっています。
int StringToCharArray( string text_string, // コピー元の文字列 uchar &array[], // コピー先配列 ※参照渡し int start = 0, // コピー開始する配列インデックス int count = -1, // コピーする文字数 uint codepage = CP_ACP // コードページ );
それらの情報を以下のように記載していきます。
ArrayResize(data,StringToCharArray("message="+Message,data,0,WHOLE_ARRAY,CP_UTF8)-1);
WebRequest()関数は指定したサーバーにHTTPリクエストを送ります。書式は以下のとおりです。
int WebRequest( string method, // HTTPメソッド string url, // 送信先URL string headers, // 送信するヘッダー int timeout, // タイムアウト(ミリ秒) char &data[], // 送信データ char &result[], // 受信結果の格納先 string &result_headers // 受信ヘッダーの格納先 );
元のコードは、6番目の引数(受信結果を格納するresult配列)に、誤って5番目の送信データと同じdata配列を渡していました。用意していたresult配列が使われておらず、送信データを上書きしてしまう不具合です。あわせて、4番目のタイムアウトが0(実質即タイムアウト)になっていたため、これも5000(5秒)に直します。
int res = WebRequest("POST", "https://notify-api.line.me/api/notify", headers, 5000, data, result, headers);
resの値が-1であれば、
if(res==-1)
エラーコードを出します。
Print("Error in WebRequest. Error code =",GetLastError());
MessageBox("Add the address 'https://notify-api.line.me' in the list of allowed URLs on tab 'Expert Advisors'","Error",MB_ICONINFORMATION);
これでLineNotify関数の記述は完了です。
void LineNotify(string Token,string Message)
{
string headers;//ヘッダー
char data[], result[];//送信データ、受信結果
headers="Authorization: Bearer "+Token+"\r\nContent-Type: application/x-www-form-urlencoded\r\n";
ArrayResize(data,StringToCharArray("message="+Message,data,0,WHOLE_ARRAY,CP_UTF8)-1);
int res = WebRequest("POST", "https://notify-api.line.me/api/notify", headers, 5000, data, result, headers);
if(res==-1) //エラーの場合
{
Print("Error in WebRequest. Error code =",GetLastError());
MessageBox("Add the address 'https://notify-api.line.me' in the list of allowed URLs on tab 'Expert Advisors'","Error",MB_ICONINFORMATION);
}
}
コンパイルする
コンパイルしてMT4で起動し、LINEに通知が飛んできたら完了です。

なお、WebRequest()を実際に動かす前に、MT4の「ツール」→「オプション」→「Expert Advisors」タブで、`https://notify-api.line.me` をWebRequestの許可URLリストに追加しておく必要があります(前述の「MT4の設定」の手順どおりです)。登録していないとres=-1が返り、通知が送信されません。
ソースコード全体
ここまでの修正をすべて反映した、実際にMetaEditorでコンパイルできる状態のコードです。
//+------------------------------------------------------------------+
//| LINE-EA.mq4 |
//| Copyright 2020, FX-EA System Project Creator |
//| https://creator.fx-ea-system-project.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, FX-EA System Project Creator"
#property link "https://creator.fx-ea-system-project.com/"
#property version "1.01"
#property strict
/*パラメーター*/
input string Line_token = "";//LINEのアクセストークン
input string Send_Message = "";//LINEに送りたいメッセージ
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//---
LineNotify(Line_token,Send_Message);//LineNotifyを呼び出し(起動時に1回だけ送信される)
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//---
}
//+------------------------------------------------------------------+
void LineNotify(string Token,string Message)
{
string headers;//ヘッダー
char data[], result[];//送信データ、受信結果
headers="Authorization: Bearer "+Token+"\r\nContent-Type: application/x-www-form-urlencoded\r\n";
ArrayResize(data,StringToCharArray("message="+Message,data,0,WHOLE_ARRAY,CP_UTF8)-1);
int res = WebRequest("POST", "https://notify-api.line.me/api/notify", headers, 5000, data, result, headers);
if(res==-1) //エラーの場合
{
Print("Error in WebRequest. Error code =",GetLastError());
MessageBox("Add the address 'https://notify-api.line.me' in the list of allowed URLs on tab 'Expert Advisors'","Error",MB_ICONINFORMATION);
}
}
まとめ
今回はOnInit()関数内で起動時に1回だけ送るようにしましたが、OnTick()関数内で決めた条件が成立したときにLineNotify()を呼び出せば、その条件のタイミングで通知が来るようになります。その際は、条件が成立している間ずっと毎ティック送られ続けないよう、通知済みフラグで最初の1回だけに絞る処理を忘れずに加えてください。これはぜひ自分でチャレンジしてみてくださいね。
コメント