入門者のEA自作のための作り方講座(MQL4/MT4)
目次/もくじ
プログラミング初心者の方が初めてでもEAを開発できるように解説したページです。このページの内容をマスターすれば、簡単な自動売買プログラムを自作できるようになります。
もともと、「EA開発講座」として複数ページにまたがっていたものを一覧で見られるようにしました。
すべてを1から読む必要はなく、必要に応じて適宜参照するという使い方もありだと思います。
EA開発環境を整える
メタエディタの準備
EAを開発するにはMT4に付属しているメタエディタというソフトを使います。MT4をインストールすればデフォルトでセットでついてくるので、新しくダウンロードする必要はありません。(Javaなどのように煩雑な環境設定はありません。)
まずMT4を立ち上げてください。MT4はどの会社のものでも構いません。
まだ、どの会社のMT4も持っていない場合は、
メタクォーツ社(https://www.metaquotes.net/en/metatrader4/trading_terminal)
からダウンロードしてください。
MT4を立ち上げると上部のタブに画像のようなアイコンがあります。
(バージョンにより!マークのアイコンの場合があります。)
これをクリックしてください。
すると、下記のようなウィンドウが開きます。これがメタエディタです。
EA開発におけるプログラミングはほぼこのソフト、この画面で行います。
メタエディタの表示の変更
メタエディタに特別な下準備は必要ありませんが、ソフトを見やすくするための設定を行います。(必要ないという方は飛ばしてかまいません。)
メタエディタの「Tool」から「Option」を選択します。
するとカラーやフォントが変更できる画面が表示されますので、
自分の好みのフォントにしてください。
カラーに関しても背景を黒、テキストを白にすると…なんとなくプログラマっぽくなります。
これでソフトの確認は終わりです。
新規作成
メタエディタから「New」(新規作成)をクリックしてください。
すると、これから何を作るのかを聞いてきますので、Experet Adviser(EA)を選択します。
「次へ」を押すと、次にファイル名を指示してきますので、
今回は「EA_make_1.00」とします。(名前は何でもOKです)
その他に開発者名とリンクの入力欄がありますが、これは特に必要なものではないので、デフォルトとのままで結構です。後で変更も可能です。
次に進むと、ハンドラの追加を求められます。
(ハンドラとは演算を開始するトリガーのようなものです。)
チェックはすべてなしで次に進みます。(チェックを入れても余計なコードが自動生成されるだけで別に問題はありません。)
次のテスターも無視して、そのまま完了を押します。
完了すると、メタエディタ画面に戻り下記のような画面になります。
(カラーリングやフォントの設定をしていない場合は、見た目の雰囲気が異なります。)
これでスタートラインに立てました。
プログラミングに挫折しないために
インデント、改行を適切に
階層を意識したインデント、改行を行いましょう。MQLでは半角スペースの有無は関係ありません。
エラーが発生した際やバグが発生した際の検査に
多大な時間を取られないようにするためには普段から見やすいコーディングを行いましょう。
こまめな保存、バックアップを
EAはメタエディタというソフトで開発しますが、エクセルやワードのように自動バックアップ機能などという便利な機能はついていません。
もし何らかの拍子でパソコンが強制シャットダウンしたり、
ブルースクリーン(フリーズ)したりしたら、
数時間の労力がパーになってしまいます。
また保存状況に応じて、ファイル名を少しずつ変更しましょう。
プログラミングではよくバージョン1.00などという表記がありますが、
EA開発でも、
”first_ea_1.01.mq4”
などのようにバージョンをつけましょう。
いらないと思って消した記述が後になってから、やっぱり必要になった場合の労力を減らすことができます。
また、PCのローカルな保存場所に一か所にデータを溜めておくと、
PCが壊れた際にデータが飛んでしまいます。
定期的に外部ドライブにデータをバックアップしましょう。
最近はクラウドに保存することもできますが、ハッキングや流出などの恐れもあるため、利用規約をよく読んで(特にGoogle Drive)、信頼できるドライブに保存しましょう。
なるべく名前は分かりやすいものに
MQLでは変数にさまざまな値や文字列を代入しますが、なるべく特定が簡単な名称にしましょう。
例えば時間を表す名称にtimeとしてしまうと、サーバー時刻なのかPC時刻なのか
トレードした時刻なのか、シグナルが発生した時刻なのか判断できません。
現在時刻にしても、
Current_Server_Timeなどとなるべく他と被る可能性が少ないものにしましょう。
※また数字ではなくアルファベットから始まる名前にしましょう。
一回で分からなくても諦めない
MQLを学ぼうとして、一回さらっと読んだだけでできるようになることを期待しないでください。頭が良い人でも一発でできるようになることはなかなかないあと思います。
何度も読んで、自分で書いて、実際に動かしてみて、初めて前進することができます。
最初は15分くらいで頭が一杯一杯になると思います。
そしたら時間をおいてまた最初から試してみてください。
次第に分かるようになります。
特にプログラミングはやっている人にとっては当たり前のことが、初めての人には理解できないセンスや感覚なことがあります。
分からない単語、用語はすぐに何度でも調べる
始めの内は知らない単語だらけです。
よくわからない単語をそのままにしておくと
「こういう意味だったら、こういう解釈になる」
という仮説が頭の中でどんどん分岐してしまいます。
知らない単語が3つ以上になったら、そのまま放置しないで、一度検索にかけてみましょう。
案外、当たり前なことにわざわざ用語がつけられているだけのことが多くあります。
逆に、一般的な解釈とは異なる意味合いを持つこともあります。(例: イコールなど)
プログラミングにおける暗黙の常識
プログラミングを始めるにあたって大切なことは”諦めないこと”、”飽きないこと”な訳ですが、正直言って適正があるのも事実です。
適性がなければさっさと諦めて自分が得意なことに専念した方が良いと思いますが、適性があるのに途中で諦めてしまう可能性があるのも事実です。
という訳で、そんな可能性を少しでも排除するために、プログラミングを始めるにあたってちょっとしたお約束のようなものを共有しておきます。
入力はすべて半角英数字
プログラミングはよほど特殊な言語でもない限り、半角英数字です。
日本語全角入力ができるVBAなどの言語もありますが、文字コードやエラーの原因になった際に原因特定に時間がかかるので、普通のプログラマなら半角を使いたがります。
またMT4は日本語入力が上手くいったり行かなかったりするので、できれば全部半角にしましょう。(特にパソコンが変わったタイミング、MT4がバージョンアップしたタイミング、VPSでコンパイルしたタイミングで文字化けが発生しやすいです。)
コメントに打った全角文字が
何故か//を通り抜けて本コードに影響を与えたりもします。
※プログラミングで言うコメントとは動作に影響を与えないメモのようなもののことを指します。
行の最後にはセミコロン;をつける
プログラミング(MQL)では改行は意味をもちません。セミコロンがあって初めて行の終わりだと認識してくれます。
このセミコロンがないとエラーが多発しますので、何かを記述したら最後にセミコロンをつけることを忘れずに行いましょう。
逆に一行がとても長くなる場合は一行にズラズラ書かずに改行してしまいましょう。
セミコロンがくるまでは同じ行だとして認識されるので、可読性が上がります。
新規作成後のデフォルトの記述の意味
新規作成で現れた記述に対する解説を行います。
// と /**/ と コメントアウト
プログラミングではその箇所がどのような記述を行っているのか頻繁にメモ書きをします。付箋のようなものです。
これはプログラムの動作に関係ないように記述しなければなりません。
MQLでは、//と/**/がコメントアウトの記述になります。
※MQLとは、EAを開発するプログラミング言語のことです。
1 2 3 4 5 6 |
//+------------------------------------------------------------------+ //| EA_make_1.00.mq4 | //| fxantenna | //| https://fxantenna.com | //+------------------------------------------------------------------+ |
上記はコメントアウトされた記述です。
//はその行のそれ以降の文字をコメントアウトし、
/**/は、 /* と */ で囲まれた範囲をコメントアウトします。
上記のように長い記述は
1 2 3 4 5 6 7 |
/*+------------------------------------------------------------------+ | EA_make_1.00.mq4 | | fxantenna | | https://fxantenna.com | +------------------------------------------------------------------+*/ |
のように書くこともできます。
プロパティ
下記の記述はプロパティと呼ばれる、
プログラミングの付属情報のようなものです。コピーライトやバージョン情報などを記載します。もちろん全部なくても問題ありません。
1 2 3 4 5 |
#property copyright "fxantenna" #property link "https://fxantenna.com" #property version "1.00" #property strict |
今回は、広くバージョン対応するため、#property strictは削除してください。
#property strictはコンパイルの精度が上がりますが、古いバージョンのMT4や古いバージョンで記述されたEAをコンパイルしようとするとこれが原因でエラーが発生します。
※コンパイルとは、ソースコードからEAファイル(機械語)を生成することを指します。
Init() OnInit()
OnInit(古いバージョンではInit)に記述された処理は、EA実行時の最初に一度だけ実行されます。
ここには口座認証や時刻修正や最小単位設定、既に所持しているポジションの識別などの初期設定をさせます。
1 2 3 4 5 6 7 8 9 10 11 |
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } |
Deinit() OnDeinit()
OnDeinit(古いバージョンはDeinit)はEAの終了時に一度だけ処理されます。
ここではチャート上に表示させた描写(ライン、シグナル、文字など)を消したりする際に使います。(あまり使いません。)
1 2 3 4 5 6 7 8 9 |
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } |
Start() OnTick()
OnTick(古いバージョンはStart())はEAのメインとなる記述場所です。
EAはMT4のチャートのティック更新毎に演算が行われます。
(レートが変化するたびに演算を行います。逆に言うと、レートが変化しないと演算されません。)
シグナルの認識、オーダーの送信、ポジションの決済、チャート描写などはすべてここに記述します。
1 2 3 4 5 6 7 8 9 10 |
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+ |
デフォルトでは、OnInit() DeInit() OnTick()の順番になっていますが、
それぞれの記述が膨らんだ場合には、
OnTick() OnInit() DeInit()の順番にコピー&ペーストで並び替えても問題なく稼働します。
ソースコード(.mq)からEA(.ex4)を生成するやり方
ここではソースコードからEAを生成します。
mqファイルのコンパイル
ソースコードファイルはEAファイルそのものではありません。
MT4にEAとして認識させ、稼働させるには
mqファイルからexファイル(EA)を生成する必要があります。
この作業がコンパイルです。
コンパイルにより、人の言葉で書かれたソースコードを
コンピュータが理解できるための実行ファイルに変換することができます。
まず、MT4の本アイコンをクリックしてください。これをクリックするとソースコードのエディタソフト(MetaEditor)が開きます。
「新規作成(New)」でエキスパートを選択してください。途中でいろいろなオプションが聞かれますが、後でコピペするので、何を選んでも構いません。
新規作成が終わったら、お持ちのソースコードを”上書きする形で”ここにコードを張り付けてください。(Ctrl+Aで全選択した状態でコピペしてください。)
コピペが完了したら、「コンパイル(Compile)」ボタンを押せばEA(ex4ファイル)が1秒ほどで生成されます。
メタエディタの上部のタブにある「Compile」(コンパイル)ボタンを押せば、
コンパイルすることができます。
ボタンを押して下にエラーが表示されなければ、コンパイル完了です。
バージョンによってはwarningが出ますが、errorでなければコンパイル成功です。
エラーとワーニング
ワーニングとエラーの違いですが、エラーはソースコードの記述に何か問題があり、コンパイルができないということを表します。
エラーが表示される横に該当の行が表示されますので、そこで確認しましょう。
ワーニングはコンパイルできないほどではないが、
何か問題を引き起こす可能性があることを表しています。
今回発生したエラーはOrderSendの返り値がないことを表し、
「OrderSendが正しく処理されたかどうかの確認の記述がありませんが、大丈夫ですか」
という内容です。
※2014年2月以降に追加されたエラーです。
ワーニングやエラーが出たからといって、
コンピュータが壊れるわけではないので、確認の意味も込めてコンパイルを行いましょう。
また、メタエディタではコンパイルすると上書き保存も同時に行われます。
コードが完成してからコンパイルすると、大量のワーニングが一度に出るので、
コードのチェックのためにもコンパイルをまめに行いましょう。
EXファイルの場所
問題のexファイルが生成された場所ですが、これはmqファイルと同じ場所に生成されます。
mqファイルの場所は「名前を付けてファイルを保存」のデフォルトの場所で確認できます。
また、
MT4から「ファイル」→「データフォルダを開く」→「MQL4」→「expert」で確認することもできます。
新規オーダーを送信するOrderSendの書き方
ここではMQLに特有の”新規ポジションの取得”を記述していきます。
OrderSend関数
MQLにはあらかじめ決められた関数という枠組みがあります。
必要な情報を与えてやれば、後はすべて勝手にやってくれるという優れものです。
通常はさまざまなトレード条件の記述を行い、それをクリアした後にオーダーを送信するのですが、
今回はまだ駆け出しなのでとにかくオーダーを出してみることを目標とします。
オーダー関数は、以下のような定義になっています。
1 |
OrderSend(Symbol(), CMD,Volume,Price,Slippage,SL,TP,Comment,Magic,Expiration,Color) |
これを日本語にすると、
1 |
OrderSend(オーダーを送信する通貨ペアは?,注文方式は?,ロットは?,レートは?,スリッページは?,損切り価格は?,利確価格は?,オーダーにメモをつける?,マジックナンバーは?,オーダーの有効期限は?,チャート上に何色で表示する?) |
という具合になります。
これらの情報を与えてやれば後はMT4がオーダーを送信してくれます。
ただし、
ポジションを新規取得する際には毎回すべての項目を満たしてやらなければ、
オーダーを送信してくれません。
ここではまず、それぞれの質問に適当に答えていきます。
1 |
OrderSend(オーダーを送信する通貨ペアはドル円, 注文方式は成行注文で買い, ロットは0.1 ,レートは現在値, スリッページは3, 損切り価格はなし,利確価格はなし, オーダーのメモはEA, マジックナンバーは1234, オーダーの有効期限はなし, チャート上に赤色で表示) |
しかし、
これではコンピュータが認識してくれないので、半角英数字で書き換えます。
1 |
OrderSend("USDJPY", OP_BUY, 0.1, Ask, 3, 0, 0, "EA", 1234, 0, Red) |
これで最後にセミコロン;をつければ完成です。
1 |
OrderSend("USDJPY", OP_BUY, 0.1, Ask, 3, 0, 0, "EA", 1234, 0, Red); |
ちなみにこのコードをEAとして稼働させると、
ティック(レート更新)ごとにポジションを取ろうとするため、
資金が不足するまでポジションを取り続けてしまいます。
間違ってもライブ口座で稼働させないでくださいね。
オーダーに条件をつけるやり方(IF文の書き方)
MQL4でオーダーを送信する前に条件を付ける方法について開設します。
MQL4での条件文の書き方
これまでに、「新規ポジションの送信」、「コンパイル」、「EAの生成」を行いました。ただ、このままでは、短時間に大量のポジションを持ってしまいまいます。
ここでは特定の条件が満たされたときのみに新規ポジションを取るようにします。
この条件というのにも様々な書き方がありますが、基本的にはif文を使います。
if文
IF文はif()でくくった()の中身が正しい場合に、{}で囲った処理を行います。
{}を省くこともできるのですが、後々面倒になるので{}で囲むようにしましょう。
簡単な例では、
1 2 3 |
if( 2 > 3 ) { OrderSend(略); } |
と書くと、
2>3が満たされないのでif文の中は処理されません。
また、
if文はif文の中に記述することもできます。(ネスト)
1 2 3 4 5 |
if( 3 > 2 ){ if( 2 > 1 ){ OrderSend(略); } } |
これはどちらも満たされるので、OrderSendが実行されます。
時刻の関数を使った条件文
MQLには時刻系の関数がたくさんあります。
その一部を紹介します。
1 2 3 4 5 6 |
DayOfWeek() : 現在の曜日(1:月曜日、2:火曜日....) Year(): 現在の年 Day():現在の日 Hour():現在の時 Minute():現在の分 Seconds():現在の秒 |
これを先ほどのif文と組み合わせて、
1 2 3 4 5 6 7 |
if( Day() == 10 ){ if( Hour() == 6 ){ if( Minute() == 30 ){ OrderSend(略); } } } |
のように書くと、
10日の6時30分にオーダーを送信するコードが出来上がります。
ここで注意したいのはDayではなく、Day()と必ず()をつけなければならないということと、
イコールを表すときには=ではなく、==であるということです。
Dayは変数ではなく関数なので、()をつけなければならないのですが、
そういうものだと覚えてしまいましょう。
次に、if文の中の条件を記述します。
== 等しいことを条件にする
A == B でAとBが等しいことを条件にできます。
これと現在の口座のポジション数を表すOrdersTotal()という関数を使うと
1 2 3 |
if( OrdersTotal() == 0 ){ OrderSend(略); } |
というように
ポジションを持っていない場合のみ
新規ポジションを取るように条件付けをすることができます。
数学でイコールは=ですが、プログラミングの世界では==です。
※OrdersTotal():口座のポジションの数。
&& かつ、しかも
A && Bで、AかつBという条件になります。これまでに使った関数を使うと、
1 2 3 |
if( OrdersTotal() == 0 && Hour() == 6 ){ OrderSend(略); } |
このようにすれば、
ポジション数が0で、かつ、6時のときにオーダーが送信されます。
これでも条件はまだまだ足りませんが、簡単な例では深夜のトレードや”朝スキャ”が自動でできるようになります。
|| または、もしくは
A || B で、AもしくはBという条件になります。先ほどのを書き換えると
1 2 3 |
if( OrdersTotal() == 0 || Hour() == 6 ){ OrderSend(略); } |
ポジションが0のとき、
もしくは、
時刻が6時のときにオーダーが送信されます。
この場合、
ポジションを1つ以上持っていたとしても6時になればオーダーが送信されます。
また、6時でなくてもポジションが0ならばオーダーが送信されます。
&& と || の連結
&& と || は一つのif文の中に何回でも記述することができます。
1 2 3 |
if( OrdersTotal() == 0 && ( Hour() == 5 || Hour() ==15) ){ OrderSend(略); } |
とすれば、
「ポジション数が0 かつ、 時刻が5時か15時のとき」
という条件文になります。
もしこのとき()が1つなかったら、
1 2 3 |
if( OrdersTotal() == 0 && Hour() == 5 || Hour() ==15 ){ OrderSend(略); } |
「ポジション数が0、 かつ、 時刻が5時か15時のとき」なのか
「ポジション数が0かつ時刻が5時、または15時のとき」なのかはっきりしません。
現在のMT4のバージョンでは
()の区切りを明確にしなくても||と&&の順序がおかしくなることはないですが、
なるべく()を使って論理の区切りをはっきりさせましょう。
1 < 2 < 3 とは書けない
もう一つ注意しなければならない点があります。
数学では不等号を用いて、1>2>3のように書くことができますが、
プログラミングではそれはできません。
if( 1>2>3 )
とするとエラーになります。
if( 1>2 && 2>3 )のようにそれぞれを分けて、&&で結んで記述しなければなりません。
ポジションを決済する記述
ここではポジションを決済するための記述を行います。
OrderClose
OrderSendで新規ポジションを持てたようにポジションを決済することができます。
OrderCloseは
1 |
OrderClose(ticket,lots,price,slippage,color); |
で記述します。
日本語にすると、
1 |
OrderClose(チケットナンバー、ロット、価格、スリッページ、チャート上の色) |
という意味になります。
チケットナンバーとは、オーダーごとの固有の識別番号のようなものです。
OrderSend関数が成功するとOrderSend関数はチケットナンバーを返します。
※○○を返す とはプログラミング用語でその値に変化することを指します。
1 |
int TicketNumber = OrderSend("USDJPY", OP_BUY, 0.1, Ask, 3, 0, 0, "EA", 1234, 0, Red); |
上記のように記述すると
TicketNumberという変数に今回のオーダーのチケットナンバーが格納されます。
※変数とは、数学でいうxのように特定の文字列に数値などを代入できるもののことです。
OrderSendで受け取ったTicketNumberをOrderCloseに利用して記述します。
1 |
OrderClose(TicketNumber,0.1,Bid,3,Yellow); |
これでポジションの決済を行います。
OrderSendとOrderClose、if文と関数を使ったサンプルコード
これまでの内容を元に新規ポジションと決済をまとめた記述を行います。
「ポジション数が0で、かつ、6時か18時のときに」新規ポジションを取り、
「ポジション数が1以上で、かつ7時か19時のときに」決済するEAを考えてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ static int TicketNumber; void OnTick() { //--- if( OrdersTotal() == 0 && ( Hour() == 6 || Hour() == 18 ) ){ TicketNumber = OrderSend("USDJPY",OP_BUY,0.1,Ask,3,0,0,"EA",1234,0,Red); } if( OrdersTotal() > 0 && ( Hour() == 7 || Hour() == 19)){ OrderClose(TicketNumber,0.1,Bid,3,Yellow); } } |
これでポジション数が0で、かつ、6時か18時に新規ポジションを取り、
ポジション数が1以上で、かつ7時か19時に決済するというEAができあがります。
OnTick()の上部に
static int TicketNumber;
という記述を忘れずに行ってください。
上記のコードをコンパイルして実行すると上記のように、短時間に大量にポジションを取るという処理が回避され、少しEAらしいEAになります。
変数の使い方
変数の役割
これまでにもあまり大した説明をせずに変数を扱ってきましたが、ここでは変数について重点的に行います。
変数には大きく分けて2種類の役割があります。
1.何度も使う数値や数式を引き出しやすくして、演算量を減らし、記述を楽にする
2.そのままだと消えてしまうデータを一時的に保存しておく
1だけ聞くと、
「演算量なんてどうでもいいし、楽な記述よりも慣れたやり方の方がいい」
という選択肢もあるのですが、
変数にはデータを一時的に保存することができるので、この機能を使うには避けて通ることはできません。
例えば、
テクニカル指標のシグナルが発生してすぐにエントリーではなく、
5分後にエントリーさせたい場合を考えます。
TimeCurrent()では現在の時刻しか取得できないため、
テクニカル指標のシグナルが発生した瞬間にその時間をどこかに記録しておく必要があります。
1 2 3 4 |
datetime RSI_Signal_Time; if( iRSI(Symbol(),0,14,PRICE_CLOSE,0) > 30 && iRSI(Symbol(),0,14,PRICE_CLOSE,1 ) <= 30 ){ RSI_Signal_Time = TimeCurrent(); } |
上記のようにすることによって
RSI_Signal_Timeという変数にシグナルが発生した時間が格納されます。
ここで注意しなければならないのは、冒頭の datetime RSI_Signal_Time; です。
変数は利用する前に、あらかじめどんなタイプの変数なのか、
どんな名前なのかを宣言しなければなりません。
タイプには、整数、少数、文字、時間、真偽などがあります。
1 2 3 4 5 |
整数:int 少数:double 文字:string 時間:datetime 真偽:bool |
それぞれのタイプに合わせて変数の宣言を行わなければなりません。
また、変数名は半角英数字で決めてください。
ただし、「数字から始まる文字」、「-(ハイフン)」などは利用できません。
後は分かりやすい名前にすることを強く推奨します。
=はイコールではなく代入
今までもチラチラと出現していましたが、
=はプログラミングではイコールではなく、代入を表します。
1 |
int Number = 5; |
これはNumberという変数に5を入れたということを意味します。
変数には一つの数字しか格納することができません。
1 2 |
int Number = 5; Number = 10; |
このように続けて同じ変数に値を入れようとすると、
古い数字は破棄され、新しい10がNumberに格納されます。
RSIのEAを作ってみる
これまでのことを元に、
14RSIが30ラインを下から上にクロスした後、
5分以内に10pips上がったらエントリーするというEAを作ってみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
static datetime RSI_Signal_Time; static double RSI_Signal_Rate; int OnTick() { //RSI Signal Check if( iRSI(Symbol(),0,14,PRICE_CLOSE,0 ) > 30 && iRSI(Symbol(),0,14,PRICE_CLOSE,1) <= 30 ) { RSI_Signal_Time = TimeCurrent(); RSI_Signal_Rate = Bid; } //Buy Entry if( OrdersTotal() == 0 && TimeCurrent() <= RSI_Signal_Time + 60*5 && Bid > RSI_Signal_Rate + 0.10) { OrderSend(Symbol(),OP_BUY,0.1,Ask,3,0,0,"Buy",1234,0,Red); } } |
冒頭にあるstaticというのは
ティック更新後にも変数をリセットしないでください、
という意味を持つ呪文です。
これがないと、ティック更新後に変数がリセットされてしまい、
RSI_Signal_TimeとRSI_Signal_Rateが0になってします。
シグナル発生後5分以内というのは、
言い換えると
「現在の時間 < シグナルが発生した時間 + 300秒」ということになります。 また、ドル円の場合10pipsは0.10なので、 10pips上がったらエントリーというのは「現在のレート > シグナル時のレート + 0.10」 ということになります。
Bid,TimeCurrent(),iOpen()などの使い方
MQLで使える数値を引き出す
MQLでは様々な値を簡単な記述で参照することができます。
例えば、
1 2 |
Bid : 現在のビッドレート Ask : 現在のアスクレート |
BidやAskはこのままで現在のレートを出してくれます。
Bidの後に()は必要ありません。また、小文字でbidと書いても認識されません。
また時間に関する参照は、
1 2 |
TimeCurrent() : 現在のサーバーの時刻 TimeLocal() : 現在のPCの時刻 |
で参照することができます。
こちらは()が必要です。TimeCurrentだけではエラーになります。
また、
この時刻関数は1970年1月1日からの秒数を返します。
つまり、現在時刻が2015年2月1日だとしても、
2015.02.01 00:00 等のような表記法ではなく、
1414953007などのような数値になっている訳です。
これは例えばエントリーしてからの経過時間などを決済条件に組み込むときに、
1 |
TimeCurrent() > エントリーした時刻 + 1時間分の秒数 |
などと書くことによって条件化できます。
※エントリーした時刻に、EntryTimeなどの変数名にTimeCurrent()の値を代入すればエントリーした時刻を保存できます。
これに前回までにやったロウソク足の関数iOpen(Symbol(),0,0)などが加わります。
値の参照には、
そのままのもの: Bid, Ask
()が必要なもの: TimeCurrent(), TimeLocal(),Day(), Hour()…
()と数値が必要なもの: iOpen(Symbol(),0,0), iRSI(Symbol(),0,14,0,0)…
があると覚えておきましょう。
配列とは?配列の使い方
配列が必要になる背景
配列が何であるかを説明する前に、配列が必要になりそうな例をちょっと出してみます。
例えば、MQLでティックチャートを使いたいとします。
MQLでは過去のティックチャートに関する予約済み変数・関数がないので、
直近のレートを変数に保存しておく必要があります。
1 2 3 4 5 6 7 |
double Tick0,Tick1,Tick2,Tick3,Tick4,Tick5; Tick5 = Tick4; Tick4 = Tick3; Tick3 = Tick2; Tick2 = Tick1; Tick1 = Tick0; Tick0 = Bid; |
上記のようにすると、
新しいレートはTick1に古いレートはTick2,Tick3と格納されていきます。
もしこれが100ティック分必要になったら変数の宣言だけで大変なことになります。
※エクセルになれている人はコピペで余裕とか思うかもしれませんが、何か変更が必要になったときに大変なのです。
配列とは
プログラミングでは配列という概念があります。説明するよりも実際に見てみた方が良いでしょう。
上記のコードは配列にすると、下記のようになります。
1 2 3 4 5 6 7 |
double Tick[6]; Tick[5] = Tick[4]; Tick[4] = Tick[3]; Tick[3] = Tick[2]; Tick[2] = Tick[1]; Tick[1] = Tick[0]; Tick[0] = Bid; |
変数の宣言部分がきれいになりました。
配列とは[]を使った簡略化法のようなものです。
変数名[整数]と宣言することで、その整数分の配列が使えるようになります。
Tick[6]と宣言すればTick[0]~Tick[5]までが使える訳です。
Tick[1]~Tick[6]ではないところがミソです。
ただし、配列で使える数も0からスタートとなるので、
double Tick[10];
と宣言しても
Tick[0],Tick[1],Tick[2],Tick[3],Tick[4],Tick[5],Tick[6],Tick[7],Tick[8],Tick[9]
までしか使えません。
Tick[10]は使えないことに注意してください。
また[]内は整数しか入れられません。
[1.4]や[“bid”](連想配列)のようなことはできません。
for文 ~配列はfor文と一緒に使う
プログラミングでは同じような処理を何回もしなければならないことがあります。
例えばさっきのような
1 2 3 4 5 6 7 |
double Tick[6]; Tick[5] = Tick[4]; Tick[4] = Tick[3]; Tick[3] = Tick[2]; Tick[2] = Tick[1]; Tick[1] = Tick[0]; Tick[0] = Bid; |
の処理が100や1000などになると記述も変更も大変です。
しかし、for文は上記のような処理をまとめることができます。
まずは上記の処理をfor文に書き換えてみます。
1 2 3 4 5 |
double Tick[6]; for(int i =1 ; i<6 ; i++ ){ Tick[i] = Tick[i-1]; } Tick[0] = Bid; |
上記のように書くと、
6の個所を10000とするだけで10000ティック分のデータを格納できます。
最初の
1 |
for(int i=1 ; |
は、
iという変数を宣言し、1を代入するという意味です。
次に、
1 |
for(int i=1 ; i<6 |
は、
iが6未満の場合は以下の処理を実行するという意味になり、
1 |
for(int i =1 ; i<6 ; i++ ){ |
これで、実行した場合はiを1増やすという意味になります。
つまりこのfor文の中でだけ使うiという変数を宣言し1を代入して、
6になるまで処理を繰り返せ、ということになります。
その下の、
1 |
Tick[i] = Tick[i-1]; |
は、iが1のときは
1 |
Tick[1] = Tick[1-1]; |
とり、
iが2のときは、
1 |
Tick[2] = Tick[2-1]; |
という風になります。
ポジションの情報を取得する
ポジションの情報を取得するには、
どのポジションに対して情報を要求するのか宣言する必要があります。
オーダーの識別はOrderSelectという関数で行います。
1 |
OrderSelect(ポジション番号、選択モード); |
ポジション番号とはチケットナンバーとは異なる識別番号で、
現在持っているポジション数-1が最大となる数のことです。
例えば、
ポジションを3つもっている場合はそれぞれのポジションに0,1,2という番号が割り振られています。
1からではなく0からなので注意してください。
※プログラミングでは数の始まりが1ではなく0からのことが多いので、
0スタートの方が当たり前という感覚にしましょう。
1 |
OrderSelect(0,SELECT_BY_POS); |
とすると
「現在持っているポジションのどれかをこれから参照しますよ」
という意味になります。
この宣言をすることで、
1 2 3 4 5 6 7 |
OrderOpenPrice() そのオーダーの約定価格 OrderOpenTime() そのオーダーの約定時間 OrderTicket() そのオーダーのチケット番号 OrderLots() そのオーダーのロット OrderSymbol() そのオーダーの通貨ペア OrderMagicNumber() そのオーダーのマジックナンバー OrderType() そのオーダーの取引種別 |
などが使えるようになります。
逆にこれらの関数はOrderSelectを使った後でないと使えません。
口座内の異なる通貨ペア、マジックナンバーのポジションを区別する
MT4では同時に異なる種類のポジションを持つことができます。
それゆえに決済の際にはどのポジションに対する条件なのかを明確にしないと、
ドル円でとったポジションをユーロドルの条件で決済してしまう、
なんてことになります。
これまでに使ったOrderSelect, for文を使ってポジションを区別します。
1 2 3 4 5 6 7 |
int PositionAmount = OrdersTotal(); for(int i = 0 ; i< PositionAmount ; i++ ){ OrderSelect(i,SELECT_BY_POS); if( OrderSymbol() == Symbol() && OrderMagicNumber == 1234 ){ 決済処理; } } |
上記は現在もっているポジションの通貨ペアとマジックナンバーでポジションを識別しています。
1 |
int PositionAmount = OrdersTotal(); |
一度PositionAmountという変数にポジション数を入れます。直接
1 |
for(int i=0 ;i< OrdersTotal(); i++){ |
と書いてしまうと、
for文で繰り返し処理をしている最中にOrdersTotal()の数が変わってしまうことがあり、バグの原因となります。
1 |
for(int i = 0 ; i< PositionAmount ; i++ ){ |
これはもっているポジションの数だけ下の判別処理をすることを表します。OrderSelectが0から始まるので、int iも0から宣言します。
1 2 |
OrderSelect(i,SELECT_BY_POS); if( OrderSymbol() == Symbol() && OrderMagicNumber == 1234 ){ |
オーダーを選択し、
それぞれの情報がマッチするかを調べます。
オーダーを送信する際にマジックナンバーを固定にせず変数に格納しておけば、
1234ではなくその変数名で条件に組み込むことができて便利です。
決算処理についてはこれでほぼ完成です。
決済条件にエントリー価格を使う
今までに作ったサンプルのEAはポジション数が1以上のときはエントリーしないフィルターがついていますので、ポジション番号は常に0になります。
ここでは、買いポジションを1つ持っている場合を想定して、
エントリーよりも10pips下がったら成行決済をさせます。
1 2 3 4 |
OrderSelect(0,SELECT_BY_POS); if( iClose(Symbol(),0,0) < OrderOpenPrice() - 10*pips ){ OrderClose(OrderTicket(),OrderLots(),Bid,3,Yellow); } |
これでエントリー価格をOrderOpenPrice()で取得し、決済条件に組み込むことができるようになりました。
決済条件は複数あってもいいので、現在までの決済条件に追加してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
static int TicketNumber; void OnTick() { //--- //Buy Position if( OrdersTotal() == 0 && JapanDayOfWeek != 1){ if( iRSI(Symbol(),0,24,PRICE_CLOSE,0) > 30 && iRSI(Symbol(),0,24,PRICE_CLOSE,1) <= 30 ){ TicketNumber = OrderSend(Symbol(),OP_BUY,0.1,Ask,3,0,0,"EA",1234,0,Red); OrderModify(TicketNumber,Ask,Ask-50*Point,Ask+50*Point,0,Red); } } //Close Position OrderSelect(0,SELECT_BY_POS); if( iClose(Symbol(),0,0) < OrderOpenPrice() - 100*Point ){ OrderClose(OrderTicket(),OrderLots(),Bid,3,Yellow); } //Close Position if(OrdersTotal() > 0 && OrderSelect(0,SELECT_BY_POS)){ if( iRSI(Symbol(),0,24,PRICE_CLOSE,0) >= 70 ){ OrderClose(TicketNumber,0.1,Bid,3,Yellow); } } } |
損切,利確注文SLTPの出し方
SL,TPをEAから送信する
ここでは、ポジション取得時に損切りと利確を同時に送信するMQLプログラミングを行います。
これまでのOrderSendの中に損切りと利確を入力する項目がありましたが、
あれば極力使わないようにしましょう。
FX業者の中には
OrderSendで送信されたSL,TP注文を受け付けてくれる業者と、受け付けない業者があります。
そのため、
「記述は何も間違っていないのにSL,TPを受け付けてくれない~(\^o^/)」
という事態になり、数時間も悩む羽目になりかねません。
ではどうすればいいのかというと、
OrderSendではSL,TPともに0で送信し、後から修正するという処理で解決します。
そのオーダーを後から修正する方法がOrderModfyです。(OrderSendより簡単です。)
1 |
OrderModify(修正するオーダーのチケットナンバー, オープンレート, 損切り値, 利確値, 有効期限, 色); |
OrderSendが成功すると
そのオーダーのチケットナンバーが返ってくるので、
それを変数に一時的に保存しておきます。
その変数をOrderModifyでチケットとして使います。
オープンレートは指値逆指値注文でエントリーしようとしたときに変更できます。
成行注文ですでにポジションを持っている場合に変更しようとするとエラーになります。
有効期限は指値逆指値時の有効期限なので今回は0と入力してください。
オーダーを送信してから修正するまでの一連の流れを確認します。
1 2 |
TicketNumber = OrderSend(Symbol(),OP_BUY,0.1,Ask,3,0,0,"EA",1234,0,Red); OrderModify(TicketNumber,Ask,Ask-5*pips,Ask+5*pips,0,Red); |
これでSL,TPが送信されます。(pipsはあらかじめ用意しておく必要があります。)
(MQL4)初めてのEA開発のための教科書~実用編~[EA自作]
EAが動かないときのその原因 一覧
関連記事
-
株価指数両建て裁定取引のやり方と検証
日経225やダウ、SP500のチャートには強い相関関係があります。かつては「日経はニューヨークの後追
-
短いコードでも右肩上がりのグラフにできることの証明(MT4EA)非実用
バックテスト結果 ソースコードはこちら 33行あるやんけ...
-
[MT4]EAに口座縛り/期間限定縛りをかける種類と方法(MT5)
良いEAができた場合、身内で配布したい場合があります。しかし、知らない間に自分のEAが出回っていた場
-
(MQL4)インジケーターをEA化するやり方[EA自作]
iCustomでZigZagをEA化する(初歩編) インジケーターとiCustom 今回はインジ
-
とある両建てロジックのEA検証(コードあり)
今回はちょっとしたシンプルな実験を行います。 お題は両建てです。 ポジションが0の場合SLTP付