(MQL4)初めてのEA自作のための教科書~実用編~[EA自作]
目次/もくじ
実用に向けたEAのコーディングについて説明します。本当に1からEAの開発について知りたい方はこちら。
ロウソク足パターンのEAを作る~3回連続で陽線になったら買い~
ロウソク足の記述方法
ロウソク足がどうなっているかを知るためにはロウソク足のデータを取得する関数・変数を使う必要があります。MQL4にはロウソク足の4本値が標準搭載されています。
それぞれ、
1 2 3 4 |
始値:iOpen(通貨ペア、時間軸、ロウソク足の位置) 終値:iClose(通貨ペア、時間軸、ロウソク足の位置) 高値:iHigh(通貨ペア、時間軸、ロウソク足の位置) 安値:iLow(通貨ペア、時間軸、ロウソク足の位置) |
という記述で表現できます。
ロウソク足の位置は現在のロウソク足を0として、
1つ前のロウソク足は1、
10本前のロウソク足は10というように指定します。
そのため同じ記述でも時間が進むにつれて値が変化します。
また、通貨ぺアは今までは”USDJPY”のように記述していましたが、
これではどの通貨ペアチャートにセットしてもドル円を強制的にトレードしてしまうため、これからはSymbol()と記述しましょう。
Symbol()は現在チャートで選択されている通貨ペアを表します。
Symbol()と記述することによって、EAをセットした通貨ペアチャートに応じて、ドル円でもユーロドルでも利用できるEAを開発することができます。
また、時間軸(タイムフレーム)は
1 2 3 4 5 6 7 8 |
1分足:PERIOD_M1 5分足:PERIOD_M5 15分足:PERIOD_M15 30分足:PERIOD_M30 1時間足:PERIOD_H1 4時間足:PERIOD_H4 日足:PERIOD_D1 現在選択されている時間足:0 |
で指定できます。これは、指定することによってEAをセットしたチャートの時間枠を無視して演算することになります。
時間軸についても「0」としておけば、
チャートによって5分足でも1時間足でも利用できるEAになるので、
汎用性を持たせるために「0」で入力しましょう。
ロウソク足が3回連続で陽線になったら
それでは、
「ロウソク足が3回連続で陽線になったら買いポジションを取る」というEA
を作ってみたいと思います。
まず最新の現在構築中のロウソク足については陽線陰線が確定していないので、1つ前のロウソク足から考えます。
1つ前のロウソク足が陽線であるというのは、1つ前の始値よりも1つ前の終値が大きいということなので、
1 |
iOpen(Symbol(),0,1) < iClose(Symbol(),0,1) |
となります。
※恐らくこの辺りで一度?となると思います。その場合、先に進まず一度振り返りましょう。
2つ前のロウソク足が陽線であるというのは、
1 |
iOpen(Symbol(),0,2) < iClose(Symbol(),0,2) |
です。
3つ前のロウソク足も同様で、それを三つの条件として連結させると、
1 2 3 |
iOpen(Symbol(),0,1) < iClose(Symbol(),0,1) && iOpen(Symbol(),0,2) < iClose(Symbol(),0,2) && iOpen(Symbol(),0,3) < iClose(Symbol(),0,3) |
となります。
IF文の中にこれを記述すると
1 2 3 4 5 6 7 8 9 10 |
if( iOpen(Symbol(),0,1) < iClose(Symbol(),0,1) && iOpen(Symbol(),0,2) < iClose(Symbol(),0,2) && iOpen(Symbol(),0,3) < iClose(Symbol(),0,3) ){ TicketNumber = OrderSend(Symbol(),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); } |
このようになります。
※決済条件は前回のまま
「ポジションが1つ以上で7時か19時になったら」
という条件です。
これでどの通貨ペアでもどの時間足でも使える、3回連続で陽線になったら買いエントリーというEAが出来上がりました。
※これはあくまでロウソク足の条件を学習するためのコードで、実用にはまだまだです。具体的にはポジションの決済に関する記述がかなり甘いので、そちらは他の記事で学習してください。
全体のコードは下記になります。
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
//+------------------------------------------------------------------+ //| EA_make_1.00.mq4 | //| FXantenna | //| https://fxantenna.com | //+------------------------------------------------------------------+ #property copyright "fxantenna" #property link "https://fxantenna.com" #property version "1.00" #property strict //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ static int TicketNumber; void OnTick() { //--- if( iOpen(Symbol(),0,1) < iClose(Symbol(),0,1) && iOpen(Symbol(),0,2) < iClose(Symbol(),0,2) && iOpen(Symbol(),0,3) < iClose(Symbol(),0,3) ){ TicketNumber = OrderSend(Symbol(),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); } } //+------------------------------------------------------------------+ |
テクニカル指標(RSI)を使ったEAの作り方
今回はテクニカル指標によるEAの一つとして、RSIのEAを作ります。
※実際に稼働させるEAには他にも様々な設定や記述や安全装置が必要です。
RSIを条件に入れたEAを作る
MQLにはデフォルトで様々なテクニカル指標が搭載されていますが、
その中でも有名で、かつ、指定する情報が少ないRSIを今回は取り上げます。
RSIって何?という方は以下の画像をご覧ください。
下記のサブウィンドウの表示のインジケーターがRSIです。
オシレーター系のインジケーターで逆張りによく利用されます。
RSIの関数は、
1 |
double iRSI(Symbol, TimeFrame, Period, Applied_Price, Shift) |
です。
意味合いは、
少数表示 iRSI(通貨ペア、時間足、期間、適応価格、RSIの場所)
です。
期間はRSIの計算期間を指し、適応価格は四本値(始値、終値、高値、安値)の内どれで計算するかを指します。「何のこと?」と思った方はとりあえず、適応価格は終値にしましょう。
例えば、
1 |
iRSI(Symbol(),0,24,PRICE_CLOSE,0) |
と書くと、
現在セットされている通貨ペアで、
現在セットされているチャートの時間軸で、
終値べースでロウソク足24本分計算された、
現在のRSIの値が取得できます。
「RSIが30上抜け」は 「RSI >30」ではない
エントリー条件として、RSIが30を下から上に抜けた場合を考えます。
一般的な感覚では、
1 |
iRSI(Symbol(),0,24,PRICE_CLOSE,0) > 30 |
とすれば良さそうですが、
この条件ではRSIが30以上のときは常にエントリー条件を満たしてしまいます。
上抜ける場合は、
1つ前のロウソク足の位置でRSIが30よりも下にあるという条件が必要です。
1 2 |
iRSI(Symbol(),0,24,PRICE_CLOSE,0) > 30 && iRSI(Symbol(),0,24,PRICE_CLOSE,1) <= 30 |
これでRSIが30を上抜けということを条件にできます。
※<=は以下を表します。 <は未満です。 =<と書いてもエラーになるので注意しましょう。
RSIが30上抜けで買い、70到達で決済するEA
下記がコードになります。これからはポジションを持つ際には、
現在のポジションを条件に入れるようにしましょう。
また、こういった必ず使う条件は”フィルター”として分けておくのも良いでしょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
static int TicketNumber; void OnTick() { //--- //Buy Order if( OrdersTotal() == 0 && 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); } //Close Position if(OrdersTotal() > 0 && iRSI(Symbol(),0,24,PRICE_CLOSE,0) >= 70 ){ OrderClose(TicketNumber,0.1,Bid,3,Yellow); } } |
フィルター化した場合のコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
static int TicketNumber; void OnTick() { //--- //Filter if( OrdersTotal() == 0 ){ //Buy Position 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); } } //Close Position if(OrdersTotal() > 0 ){ if( iRSI(Symbol(),0,24,PRICE_CLOSE,0) >= 70 ){ OrderClose(TicketNumber,0.1,Bid,3,Yellow); } } } |
EAをテストしてみる
上記のコードをコンパイルしてEAにしてテストしてみます。
下記がテスト結果のサンプル(2014/1/1~2014/12/31 USDJPY 5M)です。
一見良さそうに見えますが、
今回のコードには損切りの記述がないので一回の損失に制限がありません。
EAに指値注文、逆指値注文でエントリーさせる
これまでには決済で指値注文、逆指値注文をしてきましたが、今回はエントリーにおいて指値注文、逆指値注文を行います。これによりエントリータイミングをずらしたり、トラリピ、マーチンゲール、ドテン、ピラミッティングなどのロジックが可能になります。
予約注文の方法は、これまでにOrderSend関数で書いてきたOrderTypeをOP_BUYからOP_BUYSTOP、OP_BULIMITにするだけです。(OP_SELLの場合は、OP_SELLSTOP、OP_SELLLIMIT)
また、価格は現在値から離して注文しなければならないので、その点にも注意しましょう。
現在のレートから10pips上に逆指値の買い注文を入れる
これまでの下記OrderSned関数を元にエントリーを成行注文から逆指値注文に変更します。
1 2 |
TicketNumber = OrderSend(Symbol(),OP_BUY,Lot,Ask,Slippage,0,0,"EA",MagicNumber,0,Red); OrderModify(TicketNumber,Ask,Ask-10*pips,Ask+10*pips,0,Red); |
オーダータイプとSL,TPを適当な値に変更します。
1 2 |
TicketNumber = OrderSend(Symbol(),OP_BUYSTOP,Lot,Ask+10*pips,Slippage,0,0,"EA",MagicNumber,0,Red); OrderModify(TicketNumber,Ask+10*pips,Ask,Ask+20*pips,0,Red); |
SLとTPがエントリー想定価格からの幅になっていることに注意してください。予約注文の場合、現在のレートから近すぎる値で予約注文を出すとエラーになることがあります。(この幅は業者によって変わります。)
予約注文が可能な現在からのレート幅を求める
予約注文が可能な最小の現在からのレート幅を”ストップレベル”と言います。業者によってはこのストップレベルが0のこともありますが、大きい業者では5pips程度のこともあります。
EAにこのストップレベルに関する条件を組み込んで行いと、業者によって使える場合と使えない場合がでてきます。ストップレベルの参照は、
1 |
MarketInfo(Symboo(), MODE_STOPLEVEL) |
で参照可能です。
ストップレベルを予約送信前に組み込むには、2つの数値の内、大きい方の数値を返すMathMaxを使います。
1 |
MathMax(数A, 数B) |
数Aと数Bの内、大きい方を返す。
1 |
MathMax(10*pips,MarketInfo(Symbol(),MODE_STOPLEVEL)) |
とすれば、ストップレベルが10pipsよりも小さい場合には10pipsが返り、ストップレベルが10pipsよりも大きい場合にはストップレベルが返ります。
先ほどはただ単に、
1 2 |
TicketNumber = OrderSend(Symbol(),OP_BUYSTOP,Lot, Ask+10*pips,Slippage,0,0,"EA",MagicNumber,0,Red); |
としましたが、
1 2 |
TicketNumber = OrderSend(Symbol(),OP_BUYSTOP,Lot, Ask+MathMax(10*pips,MarketInfo(Symbol(),MODE_STOPLEVEL)),Slippage,0,0,"EA",MagicNumber,0,Red); |
とすることで、ストップレベルが大きい場合でもエラーが出ずにオーダーが送信されます。
さまざまな値をフィルターに使う
MarketInfoという情報屋
MQLではスプレッド、口座通貨、ストップレベルなどを取得する際に
MarketInfo
という関数を使います。
1 |
MarketInfo(Symbol(),MODE_SPREAD) |
このように書くとその通貨ペアのスプレッドを返してくれます。
(値を出力してくれます。)
単位は最小単位を整数とした値なので、
ドル円0.5pipsのスプレッドの場合は5と表示され、1pipsの場合は10となります。
ご存じのようにスプレッドはほとんどの業者で固定ではなく、変動しています。
せっかくいいエントリータイミングだと思っても
スプレッドが大きすぎて手数料で負けてしまう等ということもあります。
この関数を使うことでそういう際のフィルターを作ることができます。
スプレッドが2pip以上のときはエントリーしない
それではスプレッドフィルターを作ってみましょう。
現在のスプレッドはMarketInfoで取得できますが、単位に注意しましょう。
スプレッドが2pips(20point)未満のときにエントリーさせる条件を考えます。
1 2 3 4 5 |
int MaxSpread = 20; if( MarketInfo(Symbol(),MODE_SPREAD) < MaxSpread ) { エントリー処理 } |
これでスプレッドが広がったときにはエントリーを見送る条件文が完了です。
安全装置を付ける
短時間に大量のポジションを取らないようにする
EAプログラミングでは思わぬバグがつきものです。
よくあることとしては短時間の間に大量にポジションをとってしまうことなどが挙げられます。
今回は短い時間に大量のエントリーと決済を繰り返させない方法の一つとして、
「前のエントリーから次のエントリーまでの時間」
を制限します。
ポジションの時間間隔を制御する
本来は最後の決済時間から次のエントリーまでの時間を制御した方が良いのですが、
成行決済ではなく予約注文で決済した場合にはその決済した瞬間を認識させる記述が別途必要になります。
今回は簡略化のため制御しやすいエントリーのタイミングを記録します。
これまではエントリーの条件が整ったらエントリーして終わりでしたが、エントリー後に下記のようにあらかじめ宣言しておいたLastOrderOpenTimeという変数にエントリー時刻を格納します。
1 2 3 |
TicketNumber = OrderSend(Symbol(),OP_BUY,Lot,Ask,Slippage,0,0,"EA",MagicNumber,0,Red); OrderModify(TicketNumber,Ask,Ask-5*pips,Ask+5*pips,0,Red); LastOrderOpenTime = TimeCurrent(); |
これでオーダーが送信され新規ポジションを取得すると、LastOrderOpenTimeにその時刻が格納されます。
後はこれをフィルター化してエントリー条件に再度組み込みます。
書き方はいろいろありますが、
ここでは
「最後のエントリーから5本分のロウソク足の経過をしないとエントリーを許可しない」
という条件にします。
1 |
TimeCurrent() > LastOrderOpenTime + Period()*60*5 |
上記のフィルターをエントリー条件前に記述すると
下記のように少しエントリーが落ち着きます。
エントリー間の時間制御はあくまでトレードルールに穴があった場合の安全装置です。
短時間の大量にポジションを取ると
たとえすぐに決済してもスプレッド分の手数料がどんどん口座から減ってしまいます。
時間制御をしてもエントリー頻度が高い場合にはトレードルールそのものに条件不足の可能性があります。
また、このフィルターをつけることにより、逆に「エントリー条件を満たしているのにエントリーしない」というクレームに繋がることもあるので、つける場合はちゃんと説明しておいた方が良いです。
EAに汎用性を持たせる~”USDJPY”よりSymbol()~
MT4で設定を変更ができるような記述をする
これまでに様々なコードを扱いましたが、これまでの書き方では毎回設定や数値を変更するたびにソースコードを編集しなければなりません。
なるべくコードを編集せずに、MT4側で設定を変更できるようにします。
例えば、
通貨ペアを指定する場面で”USDJPY”と指定してしまうと、
“USDJPYmajor”、”USDJPY‗”等のようになっている特殊なMT4FX業者の場合、正しく処理されずバグに繋がります。
1 |
OrderSend("USDJPY",OP_BUY,0.1,Ask,3,0,0,"Buy",1234,0,Red); |
↓
1 |
OrderSend(Symbol(),OP_BUY,0.1,Ask,3,0,0,"Buy",1234,0,Red); |
上記のようにSymbol()を使うことにより、MT4側の変更がEAに反映されるようになります。
また、
時間足についてもPERIOD_M5などを使うと、たとえチャートが1時間足になっていたとしてもEAでは5分足で計算してしまいます。
1 |
iRSI(Symbol(),PERIOD_M5,14,PRICE_CLOSE,0); |
↓
1 |
iRSI(Symbol(),Period(),14,PRICE_CLOSE,0); |
のように「Period()」もしくは「0」と入力することにより汎用性が増します。
1 2 |
Period() : 現在セットされている時間足 Symbol() : 現在セットされている通貨ペア |
パラメーター設定できるようにする
パラメータ変数の作り方
EAはMT4側で各種項目を設定できるようにした方がユーザーフレンドリーです。
また、MT4で数値を変更できるようにすることで数値最適化を行うことができるようになります。
これまでに宣言した変数をパラメータ化し、MT4で編集できるようにします。
パラメータ化は非常に簡単で、変数を宣言するときに前にexternかinputをつけるだけです。
1 |
extern 型 変数名 |
でできます。
ここで代入した値はパラメータ設定のデフォルト値になります。
パラメータ化すると下記のようにMT4で設定できます。
EAからチャート上にデータを表示させる(Comment())
開発者の場合はソースコードをいじれるので何が起きているかをデバッグすることができますが、トレーダー側はEAで何が起きているのかを判断する術がありません。
Comment()という関数を使うとビジュアルモードのバックテストの際や、ライブトレードの際にチャート上にコメントを表示させることができます。
1 |
Comment(表示させる文字); |
例えば、
1 |
Comment("EA working..."); |
と書けば、
のように左上にテキストが表示されます。
これを使えば簡単なデバッグ(プログラムが正しく動いているか確認)することができます。
※MT4には他にもPrint()というエキスパートタブ、操作履歴にテキストを残す関数もありますが、こちらはログがすぐに消えてしまったり、処理が遅かったりすのであまり推奨しません。
テキスト同士は+でつなぐことができ、また、+”\n”+を入れることで改行することができます。
1 |
Comment("EA working..."+"\n"+"Kaigyo"); |
とすれば、
のように改行されます。
チャート上に口座情報を表示させる
口座の情報を表示する関数とComment関数を使えば、下記のように口座情報を表示させられます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Comment(WindowExpertName( )+"\n"+ "MagicNumber: "+MagicNumber+"\n"+ "\n"+ "PC : "+TimeToStr(TimeLocal(),TIME_DATE|TIME_MINUTES)+"\n"+ "Server: "+TimeToStr(TimeCurrent(),TIME_DATE|TIME_MINUTES)+"\n"+ "\n"+ "Trade Company : "+AccountCompany()+"\n"+ "Server : "+AccountServer()+"\n"+ "\n"+ "Spread : "+MarketInfo(Symbol(),MODE_SPREAD)+"\n"+ "Stop Level : "+MarketInfo(Symbol(),MODE_STOPLEVEL)+"\n"+ "\n"+ "Swap Long : "+MarketInfo(Symbol(),MODE_SWAPLONG)+"\n"+ "Swap Short : "+MarketInfo(Symbol(),MODE_SWAPSHORT)+"\n"+ "\n"+ "Max Leverage : "+AccountLeverage()+"\n"+ "Currency : "+AccountCurrency()+"\n"+ "Equity : "+AccountEquity()+"\n"+ "\n"); |
先に知っておくと時間を節約できるMQL4の仕様
StringToTimeの仕様
StringToTimeは0や0000.00.00 00 00を変換すると2001年1月1日のタイムスタンプになる
0を変換したら0(1970/1/1)のタイムスタンプになると思いきや2001年に飛ばされるんですね。
マニュアルにも一切注意書きがないので意外と引っかかりました。
OrderClosePriceは決済前では現在レートを返す
MT4のオーダー情報もそうなってるので当たり前と言っちゃ当たり前ですが、
よく考えると(いや、良く考えなくても)この仕様っておかしいですよね。
決済してないのに決済レートに値が入っているってどういうことよ、と。
この2つが何に影響するかというと、決済されているかどうかを調べるときにOrderCloseTime>0 とするかOrderClosePrice>0とするかに響いてきます。
前述のようにOrderClosePrice>0ではこの条件を上手く表してくれません。
EA内でOrderCloseTimeを判定するときには良いのですが、外部から時刻を文字列で引っ張て来てそれを変換して比較しようとすると、上の仕様にぶち当たります。
OrdersHistoryTotalとOrderSelectのMODE_HISOTRYのオーダーの検索順序
履歴のオーダーを調べたい場合、履歴の情報が膨大になるとメモリに負担がかかるので、たとえば最新10件のオーダーだけを調べたいことなどがあります。
そのときに重要になってくるのが、OrderSelectでSELECT_BY_POSしたときの順序です。
1 |
for(i=0;i<MathMin(OrdersHistoryTotal(),10);i++) |
とすると、古い10件が取り出せます。
新しい10件を取り出したい場合には、
1 |
for(i=OrdersHistoryTotal()-1;i>=MathMax(OrdersHistoryTotal()-10,0);i--) |
とすればOKです。
ちなみに順序はチケットナンバー順です。OrderOpenTimeではないので注意が必要です。(MT4のヒストリーの表示状態に依存する可能性があります。)
ストップレベル=0はストップレベルがないことではない
MT4/MT5では予約注文を行う際にストップレベルというものがあります。
ストップレベルというのは、現在レートに近すぎるレートに予約注文(指値、逆指値)は出せませんよ~、という目安の数字です。
ストップレベルが10の場合は、最低でも現在レートよりも10point離れたレートでないと予約注文を出すことができません。(それ以内の場合は注文拒否にになります。)
さて、表題の件ですが、MarketInfoなどでストップレベルを取得した際に0になっているにもかかわらず、ストップレベルが原因で注文が弾かれることがあります。
これはストップレベル=0が「ストップレベルによる制限がないことを表す」訳ではないことを意味します。
一般的には=0の場合、制限がないことを表すことが多いですが、ストップレベルに関しては不定を表します。
そのため、予約注文前にフェイルセイフでストップレベルを検査して0が検出された場合には、一般的なストップレベルの目安であるスプレッドx2を設定します。(これが保証されている訳ではありません。)
ロウソク足が確定したタイミング(始値)で演算する
EAやインジケーターの演算はティック更新毎に行われますが、演算内容によってはロウソク足が確定するタイミングだけ演算すればOKということもあります。
ロウソク足が確定するタイミングの条件(強制始値のみ)
まだ、駆け出しのころは
1 |
if( iClose(Symbol(),Period(),0) == iOpen(Symbol(),Period(),0) ) |
なんて記述を愚かにもしていたこともあったのですが、 なぜこれがダメなのかわかりますか。
これだと、
始値のタイミングと
現在のロウソク足の中でたまたまレートが始値と一緒になったときと、
区別がつかないので、必要十分条件ではありません。
Barsを使う
現在のチャートに表示されているロウソク足の本数を返す予約済み変数Barsを使います。ロウソク足が更新されると、これが増加するので、
1 2 3 4 5 6 7 8 9 10 11 |
static int Bar[2]; Bar[1] = Bar[0]; Bar[0] = Bars; if( Bar[1] != Bar[0] ){ //足の更新のタイミングの記述 } |
これでOKです。もちろんチャート操作で表示するロウソク足の本数を変えられたりすると、そのタイミングでおかしなことになる可能性があります。(まず、ないですけどね)
iBars()を使う
Barsの場合、現在のEA・インジケーターがセットされているチャートのロウソク足の本数を返すので、
もしユーザーが意図しない時間枠にEA・インジケーターをセットした場合は、演算のタイミングが狂ってしまいます。
例えば、15分ごとに何かの演算をしたいとした場合、15Mチャートにセットしなければなりませんが、トレードそのものは5分足で行いたい場合にミスマッチになってしまいます。
そこで、そんなときにはiBars()を使います。
1 2 3 4 |
static int Bar[2]; Bar[1] = Bar[0]; Bar[0] = iBars(Symbol(),PERIOD_M15,0); if( Bar[1] != Bar[0] ){ //足の更新のタイミングの記述 } |
PERIOD_M15で時間枠を指定・固定しているのでこれで15分ごとに演算を行います。
ただし、ティック更新が極端に少ない場合、たとえば15分以上ティックが来なかったりすると、またそれはそれでバグの要因になるので、
指定時間に絶対演算させたい場合はWhileかfor文でループさせましょう。
これに関する記述方法はがっちりマンデー手法のソースコードにあるので、そちらを参照してください。
EA内で時間をずらす
EAの演算時刻をずらす方法
MT4の時刻は多くの場合日本時間ではなく、サーバーが置いてある場所の時刻になっています。
そのため、例えば雇用統計の時間をEAに認識させるためには時刻の修正を行わなければいけません。
MT4自体には時刻を変更する機能がないので、
EAのプログラミングで時差分を修正する必要があります。
ここでは
MT4のサーバー時刻の時間関数
Month(), DayOfWeek(), Day(), Hour(), Minute()に代わるJapanMonth,JapanDayOfWeek,JapanDay,JapanHour,JapanMinute
という変数を作ります。
※元の時間は関数ですが、これから作るのは変数なので()は不要になります。
まず変数を宣言します。EA全体で使うのでstaticで冒頭に宣言してください。
1 2 |
static int JapanDay,JapanDayOfWeek,; static int JapanMonth,JapanHour,JapanMinute; |
次にプラスする分の時間を宣言し、プラス分の時間を入れます。
サーバー時刻が標準時刻の場合日本時間は+7時間が多いので、この場合AddJapanHourという変数に7を代入します。
1 2 |
static int AddJapanHour = 7; static int AddJapanMinute = 0; |
まずは、それぞれの変数にサーバー時刻の値を入れ、その後修正をかけます。
1 2 3 4 5 |
JapanHour = Hour() + AddJapanHour; JapanMinute = Minute() + AddJapanMinute; JapanDayOfWeek = DayOfWeek(); JapanDay = Day(); JapanMonth = Month(); |
分が59を超えた場合は60を引き、時間を+1します。
1 2 3 4 |
if(JapanMinute > 59){ JapanMinute -= 60; JapanHour++; } |
時間が23を超えた場合、24を引きます。
1 |
if(JapanHour > 23 ) JapanHour -= 24 ; |
足した分の時間と分が日付変更を超えた場合、その日付に合わせて日付と曜日を加算します。
それぞれの月でMAXの日付が異なるので、月の最大の日付を超えた場合には日付を1にし、月を+1します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
if( 60*(Hour() + AddJapanHour)+ Minute() + AddJapanMinute > 24*60 ){ JapanDay++; JapanDayOfWeek++; if( (Month() ==1 && JapanDay == 32) || (Month() ==2 && JapanDay == 29 && MathMod(Year(),4) != 0) || (Month() ==2 && JapanDay == 30 && MathMod(Year(),4) == 0) || (Month() ==3 && JapanDay == 32) || (Month() ==4 && JapanDay == 31) || (Month() ==5 && JapanDay == 32) || (Month() ==6 && JapanDay == 31) || (Month() ==7 && JapanDay == 32) || (Month() ==8 && JapanDay == 32) || (Month() ==9 && JapanDay == 31) || (Month() ==10&& JapanDay == 32) || (Month() ==11&& JapanDay == 31) || (Month() ==12&& JapanDay == 32) ){ JapanDay = 1; JapanMonth = Month() + 1; } if(JapanDayOfWeek >= 7) JapanDayOfWeek -= 7; } |
これらをまとめて処理を行う1つの関数※にします。
※関数の作成についてはまだ行っていないので、それほど気にしないでください。
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
static int JapanDay,JapanDayOfWeek; static int JapanMonth,JapanHour,JapanMinute; static int AddJapanHour = 7 static int AddJapanMinute = 0; void JapanTimeFunction(){ JapanHour = Hour() + AddJapanHour; JapanMinute = Minute() + AddJapanMinute; JapanDayOfWeek = DayOfWeek(); JapanDay = Day(); JapanMonth = Month(); if(JapanMinute > 59){ JapanMinute -= 60; JapanHour++; } if(JapanHour > 23 ) JapanHour -= 24 ; if( 60*(Hour() + AddJapanHour)+ Minute() + AddJapanMinute > 24*60 ){ JapanDay++; JapanDayOfWeek++; if( (Month() ==1 && JapanDay == 32) || (Month() ==2 && JapanDay == 29 && MathMod(Year(),4) != 0) || (Month() ==2 && JapanDay == 30 && MathMod(Year(),4) == 0) || (Month() ==3 && JapanDay == 32) || (Month() ==4 && JapanDay == 31) || (Month() ==5 && JapanDay == 32) || (Month() ==6 && JapanDay == 31) || (Month() ==7 && JapanDay == 32) || (Month() ==8 && JapanDay == 32) || (Month() ==9 && JapanDay == 31) || (Month() ==10&& JapanDay == 32) || (Month() ==11&& JapanDay == 31) || (Month() ==12&& JapanDay == 32) ){ JapanDay = 1; JapanMonth = Month() + 1; } if(JapanDayOfWeek >= 7) JapanDayOfWeek -= 7; } } |
後はこの関数JapanTimeFunction()をOnTick,Start内に置けば完成です。
【追記】
その後新しい関数が追加されたことにより、より簡単で汎用性がある記述が可能になりました。
新しく追加された関数TimeGMT()
<追記>—————————————————————-
現在はTimeGMT()というグリニッジ標準時の関数が追加されたので、それをもとに作成すると、簡単に作れます。
1 2 3 4 5 |
datetime TimeJapan; string TimeJapanStr; TimeJapan = TimeGMT() + 9*(60*60); TimeJapanStr = TimeToString(TimeJapan,TIME_SECONDS); |
このTimeJapanが日本時間のUnixタイムになるので、
ここから日付や時間や分を取得するときには、
1 2 3 |
TimeDay(TimeJapan);//日付 TimeHour(TimeJapan);//時 TimeMinute(TimeJapan);//分 |
を使います。
———————————————————————–
TimeGMT()が新しい関数です。GMTのdatetimeの時刻を返します。日本はGMT+9時間なので、ここから起算すれば簡単に日本時刻を作ることができます。
1 |
datetime TimeJapan = TimeGMT() + 9*60*60; |
GMTの時刻に9時間分の秒数を足します。
あとは、datetime型からそれぞれの時間に変換する関数(TimeDay()とかTImeMonth()とか)を使います。
1 2 3 4 5 6 7 8 |
TimeJapan = TimeGMT() + 9*60*60; JapanMonth=TimeMonth(TimeJapan); JapanDay=TimeDay(TimeJapan); JapanDayOfYear=TimeDayOfYear(TimeJapan); JapanDayofWeek=TimeDayOfWeek(TimeJapan); JapanHour=TimeHour(TimeJapan); JapanMinute=TimeMinute(TimeJapan); JapanSeconds=Seconds(); |
あとはstaticでそれぞれの変数を宣言して、全体を関数化すればOKです。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
static datetime TimeJapan; static int JapanDayOfYear,JapanDayofWeek,JapanMonth,JapanDay,JapanHour,JapanMinute,JapanSeconds; void JapanTimeFunction() { TimeJapan = TimeGMT() + 9*60*60; JapanMonth=TimeMonth(TimeJapan); JapanDay=TimeDay(TimeJapan); JapanDayOfYear=TimeDayOfYear(TimeJapan); JapanDayofWeek=TimeDayOfWeek(TimeJapan); JapanHour=TimeHour(TimeJapan); JapanMinute=TimeMinute(TimeJapan); JapanSeconds=Seconds(); } |
関連記事
-
(MQL4)インジケーターをEA化するやり方[EA自作]
iCustomでZigZagをEA化する(初歩編) インジケーターとiCustom 今回はインジ
-
[MT4]EAに口座縛り/期間限定縛りをかける種類と方法(MT5)
良いEAができた場合、身内で配布したい場合があります。しかし、知らない間に自分のEAが出回っていた場
-
入門者のEA自作のための作り方講座(MQL4/MT4)
プログラミング初心者の方が初めてでもEAを開発できるように解説したページです。このページの内容をマス
-
MT4 チャート上にボタンを作る、シダの葉を作る、動画を作る
(MQL4)MT4のチャート上にボタンを作るやり方 現在のMT4は比較的いろいろな機能が追加されて
-
とある両建てロジックのEA検証(コードあり)
今回はちょっとしたシンプルな実験を行います。 お題は両建てです。 ポジションが0の場合SLTP付