RSIを検証したら強かったので一刻も早く運用を始めたくてEAを作ってみました。
初心者むけに解説しています。
わからないことはコメントに書いてね。
Table of Contents
MT4で自動売買EA(エキスパートアドバイザー)を自作する
毎度ですが先にゴールを決めてから作りはじめます。
今回のゴールは、「RSI逆張りEAを完成」です。
MT4のEAの作り方は、
エントリールール
- RSI70到達でショート
- RSI30到達でロング
- ダブルモメンタムフィルター
決済ルール
- RSI60到達で決済
- RSI40到達で決済
・エントリー
・決済
の2個のルールが必要です。
ネットでググるとほとんど情報がでてくるので、きっと作れます。
自動化させるロジックを確認
先にTradingViewでロジックを確認します。
- RSIが70なったらショートする
- RSIが30なったらロングする
- ダブルモメンタムフィルター
フィルター機能とはトレードを厳選するための手法。主に上位足のトレンド方向へ逆らわないようにする。
単純に100個前のローソク足と現在のローソク足を比較して価格が下がっていたら下降トレンドとする。上昇トレンドはその逆。
モメンタム100とモメンタム200を使用。複数になると、より強いトレンドを判断できる。
つまりフィルター機能をつければ長期では順張り、短期では逆張りの瞬間を狙えるようになる。
RSI Strategyの改変 by hosono_p on TradingView.com
ブログの存続のため、TradingViewの有料アカウントを申し込むときは以下の紹介リンクから申し込みをお願いします。
MT4のインストール
仮想通貨の自動売買をしたいのでMT4はエクスネスのデモ口座を利用します。
エクスネスのMT4デモ口座の申請方法
お疲れ様でした。これでMT4の準備完了です。次はプログラミング用のエディタを表示させます。
MT4のプログラミングエディタ(メタエディタ)を起動
MQLのプログラムのコードはここに書いていきます。
エキスパートアドバイザーの新規作成
新規でEAを作ります。
テンプレコードの説明
いちおうテンプレの説明しておきます。飛ばしてもOKです。※MAゴールデンクロスの転用です。
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 |
//+------------------------------------------------------------------+ //| MAゴールデンクロス.mq4 | //| Copyright 2022, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2022, MetaQuotes Software Corp." #property link "https://www.mql5.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 | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+ |
上から9行目まではEAの説明文など
1 2 3 4 5 6 7 8 9 |
//+------------------------------------------------------------------+ //| MAゴールデンクロス.mq4 | //| Copyright 2022, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2022, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property strict |
最後の property strict で日本語化されます。
他はEAの説明文に転載されます。
// ダブルスラッシュから始まるのはコメントアウトです。
int OnInit()はEA起動時に呼ばれる機能です。
1 2 3 4 5 6 7 8 9 10 |
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } |
int OnDeinit()はEA削除時に呼ばれる機能です。
1 2 3 4 5 6 7 8 |
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } |
void OnTick()は値動きがあった時に呼ばれる機能です。
1 2 3 4 5 6 7 8 9 |
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+ |
テンプレには3つの機能がデフォルト搭載されている
・EAを開いたとき
・EAを終了させたとき
・値動き時
今回は値動き時のときだけプログラムのコードを実行させるので値動きだけでOK
OnTick関数は値動きがあったら呼ばれる機能です。
例えばBTCUSDの最小単位0.01が動けば実行されます。
20000.00→20000.01
ちなみにMT4では最小単位をPointと呼びます。
void 何も返さない
実行したときに呼び出し元に何も返さない。
対義としては何かを返す関数。
voidの他に、何かを返す関数はint,double,boolが使われる。
何も返さない場合は void を関数の先頭につける。
OnTickはMT4が実行させているので返却しなくてもいい。
売買シグナルの関数をつくる
初心者向けに関数の説明からするので知ってる人は次項まで飛ばしてください。
ロング条件とショート条件をつくる。
関数is_buy()、is_sell()の空っぽの関数をつくっておきます。
呼び出したら、内容に応じてデータを返却します。
これだけの説明だと意味不明なので
まあ、使ってみますか。
関数の動作確認します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#property strict void OnTick() { Print(is_buy()); } bool is_buy() { return false; } bool is_sell() { return false; } |
この状態でコンパイルを押すと、
ツールボックスに詳細が表示される。
Print()
Print()でエキスパートのログに出力させることができます。
関数の返却されたデータを表示させています。
今は、
だけなのでfalseが表示されています。
一般的にほぼ馴染みがないfalseはプログラムしてるとたびたび遭遇します。
〇か×かで
〇 は true;
× は false;
true(トゥルー) 又は false(フォルス) を真偽値(しんぎち)と呼びます。
真偽値はBoolean型、bool型、ブール型と呼ばれる。
一般的にほぼ馴染みがない「型(がた)」はプログラムしてるとたびたび遭遇します。
文字は文字列型、数字は数字型、真偽値はブール型とデータよって型が決まります。
例)
”1” → 文字列型の1(文字列は””、’’で囲む)
1 → 整数型の1
true → ブール型のtrue
型で使える機能が違います。
・整数型の例)
例えば整数型の1と整数型の2を足すと、算数になる
1 + 2
= 3
・文字列型の例)
例えば文字列型の”1”と文字列型の”2”を足すと、文字の連結になる
“1 “+ “2”
= “12”
”1” → 文字列型の1(文字列は””、’’で囲む)
1 → 整数型の1
true → ブール型のtrue
関数は、
型、スペース、カッコ、波カッコの順番で書きます。
bool is_buy(){}
型:bool
スペース:
カッコ:()
波カッコ:{}
カッコ()には引き渡すデータを書きます。(今回は引き渡しのデータなしなので空白)
波カッコ{}の中には、処理と返却値を書きます。
bool is_buy(){return false;}
現在の関数の返却値はfalseです。
OnTick関数は値動きがあったらOnTick内部の処理が実行されます。
例えばBTCUSDの最小単位0.01が動けば実行されます。
20000.00→20000.01
呼び出してPrint()で表示させています。
なので、以下のプログラムは
値動き毎に、is_buy()の中身をエキスパートのメッセージに出力しています。
void OnTick() { Print(is_buy()); }
なので、今は値動き毎にfalseが表示されています。
void OnTick() { Print(is_buy()); }
セミコロン;を付け忘れるとerrorが表示される。
セミコロン;をつけるタイミングはステップ(1行)ごとです。
初心者はセミコロン;をつける、つけないはわからないと思います。慣れるしかないです。
RSIを計算する
MQLは基本的なテクニカル指標は機能一発で呼び出せます。
RSIもあります。
わからないところはググってコピペしてきましょう。
is_buy()に書き足していきます。
まずはRSI期間14の計算式はこれ
1 |
double rsi = iRSI(NULL,0,14,0,1); |
計算結果をdouble rsi に代入している
iRSI(NULL,0,14,0,1)の結果を double rsi 変数に代入しています。
is_buy()が呼び出されるときに
double rsi = iRSI(NULL,0,14,0,1);
が実行されます。
算数の=とプログラミングの=は意味が違います。
算数的な = はプログラミングでは、 == イコールイコールと書きます。
なので値動き毎にRSIの計算結果が 変数 rsi に保存される
データの保存メモリー
プログラムは0と1の数字の世界なのでデータは数字、変形する数字なので変数(へんすう)
英語ではvariable(ヴァリアブル)と呼びます。この言葉はスプラトゥーンでしか聞かないのではないでしょうか?
RSIの計算結果は浮動小数点数型(ふどうしょうすうてんすうがた)なので
double (ダブル) を つける。
doubleは小数点型とかダブル型と呼びます。
そしてRSIの計算結果をもとに条件分岐させる
1 2 3 |
if(rsi < 30) { return true; } |
if、スペース、カッコ、波カッコの順番で書きます。
if(){}
if:
スペース:
カッコ:()
波カッコ:{}
カッコ()には条件式
波カッコ{}の中には、条件式を満たしたときの処理を書きます。
if(rsi < 30) { return true; }
なのでrsiの数値が「30を下回る場合」は「return true;」されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
bool is_buy() { double rsi = iRSI(NULL,0,14,0,1); if(rsi < 30) { return true; } return false; } bool is_sell() { double rsi = iRSI(NULL,0,14,0,1); if(rsi > 70) { return true; } return false; } |
売買シグナルの関数が完成。
これでRSIの計算結果で売買シグナルでだせます。
関数の内側をローカルスコープと呼びます。
関数に外側を外部スコープと呼びます。
double rsi は関数に属している、ローカルスコープの変数です。
関数の中でつかわれる変数は独立して存在していますので名前がダブってしまってもOK。
returnで 処理を完了させ、呼び出し元に戻る。
今回はRSIのシグナルが発生したらtrue、発生しなければfalseを返却する。
確認したければ、コンパイル→実行→エキスパートのメッセージで確認しよう。
フィルター機能をつくる
上昇トレンドと下降トレンドのみエントリーさせるためにフィルター機能の関数をつくります。
今回はダブルモメンタムフィルターです。
フィルター機能とはトレードを厳選するための手法。主に上位足のトレンド方向へ逆らわないようにする。
単純に100個前のローソク足と現在のローソク足を比較して価格が下がっていたら下降トレンドとする。上昇トレンドはその逆。
モメンタム100とモメンタム200を使用。複数になると、より強いトレンドを判断できる。
つまりフィルター機能をつければ長期では順張り、短期では逆張りの瞬間を狙えるようになる。
ローソク足の終値を拾います。
終値を計算する機能もMQLに搭載されてるので、ググって調べます。
コメントアウトはメモ書きでプログラムに影響されない。メモっておくときはダブルスラッシュをつける。
if(0 < mom100 && 0 < mom200 )でダブルモメンタムフィルター。&&は両方の条件をクリアした時に処理が実行される。
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 |
bool is_up_trend() { double close = iClose(NULL,0,0); double close100 = iClose(NULL,0,100); double close200 = iClose(NULL,0,200); double mom100 = close - close100; double mom200 = close - close200; //「momが0超え」なら上昇トレンド if(0 < mom100 && 0 < mom200 ){ return true; } return false; } bool is_down_trend() { double close = iClose(NULL,0,0); double close100 = iClose(NULL,0,100); double close200 = iClose(NULL,0,200); double mom100 = close - close100; double mom200 = close - close200; //「momが0未満」なら下降トレンド if(0 > mom100 && 0 > mom200 ){ return true; } return false; } |
フィルター機能が完成。
今回はダブルモメンタムフィルターのみだけど、フィルター機能を強化すると収益は改善傾向になる場合とあまり効果なく取引数が減ってしまう場合があるので一長一短がある。
フィルター機能を知りたいならこの本読んでみてください。
ポジション数を調べる機能をつくる
ポジション数が0の時にのみトレードを行いたいのでポジション数を調べる機能をつくります。
ググったらでてきます。
- すべてのオーダーを確認
- オーダータイプ(ロングorショート)
- オーダーシンボル(通貨ペア)
- マジックナンバー(EA識別番号)
上記すべてを満たすポジションが存在するならcountの数字をプラス1する。
マジックナンバーはあとでエントリー時に付与するのですが、「777」に指定します。777のマジックナンバーなら、このEAでのエントリーになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
int posi_count(int side) { int count = 0; for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderType() == side) { if(OrderSymbol()==Symbol()) { if(OrderMagicNumber()==777) { count++; } } } } } return count; } |
カッコの中は引数です。
引き渡すデータを引数と呼びます。
(int side)が引数です。
今回は引数を1個渡しています。
引数は呼ぶときに呼び出し元で記述します。
関数仕様の例)
posi_count(0); ならBUYポジションをカウントします。
posi_count(1); ならSELLポジションをカウントします。
countは初期値0の整数です。
int count = 0;
整数型は++をつけるとカウントアップします。
これをインクリメントと呼びます。
ちなみに–をつけるとカウントダウンします。これはデクリメントと呼びます。
ポジションエントリーの関数をつくる
ググったらでてきます。
1 2 3 4 5 6 7 8 9 10 11 12 |
void position_entry(int side) { double qty = 0.1; if(side==0) { bool res= OrderSend(NULL,side,qty,Ask,0,Ask - 1000 * _Point,0,NULL,777,0,clrGreen); } if(side==1) { bool res= OrderSend(NULL,side,qty,Bid,0,Bid + 1000 * _Point,0,NULL,777,0,clrRed); } } |
注文数量です。
損切をつけました。
_Point には現在チャートの通貨ペアの最小ティック値。
Ask から 1000ポイントなので 100Pipsです。
ポジション決済の関数をつくる
ググったらでる。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void position_close(int side) { for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderType() == side) { if(OrderSymbol()==Symbol()) { if(OrderMagicNumber()==777) { bool res= OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 0, clrBlue); } } } } } } |
条件を満たしたらエントリーする機能をつくる
細かい関数はつくりおわったのでOnTick()を書き換えていきます。
- is_buy()がtrue
- is_up_trend()がtrue
- 買いポジション数が0
- 売りポジション数が0
1 2 3 4 |
void OnTick() { if(is_buy() && is_up_trend() && posi_count(OP_BUY) == 0 && posi_count(OP_SELL) == 0){position_entry(OP_BUY);} if(is_sell() && is_down_trend() && posi_count(OP_BUY) == 0 && posi_count(OP_SELL) == 0){position_entry(OP_SELL);} } |
MQLには定数と呼ばれる,
あらかじめ用意されている変数があります。
条件を満たしたら決済する機能をつくる
以下の条件でロングポジションが決済される
- 買いポジション数が0を超えている
- RSIが60を超えた
以下の条件でショートポジションが決済される
- 売りポジション数が0を超えている
- RSIが40を下回った
エントリーは入れ子なしで&&とつなげて書いたけど入れ子(ネスト)でも書ける。
処理速度的にはネストの方が速いらしいです。
どっちでもOKです。
以下の条件でロングポジションが決済される
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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
#property strict void OnTick() { //エントリー処理 if(is_buy() && is_up_trend() && posi_count(OP_BUY) == 0 && posi_count(OP_SELL) == 0) { position_entry(OP_BUY); } if(is_sell() && is_down_trend() && posi_count(OP_BUY) == 0 && posi_count(OP_SELL) == 0) { position_entry(OP_SELL); } //決済処理 double rsi = iRSI(NULL,0,14,0,1); if(posi_count(OP_BUY) > 0) { if(rsi > 60) { position_close(OP_BUY); } } if(posi_count(OP_SELL) > 0) { if(rsi < 40) { position_close(OP_SELL); } } } bool is_buy() { double rsi = iRSI(NULL,0,14,0,1); if(rsi < 30) { return true; } return false; } bool is_sell() { double rsi = iRSI(NULL,0,14,0,1); if(rsi > 70) { return true; } return false; } bool is_up_trend() { double close = iClose(NULL,0,0); double close100 = iClose(NULL,0,100); double close200 = iClose(NULL,0,200); double mom100 = close - close100; double mom200 = close - close200; //「momが0超え」なら上昇トレンド if(0 < mom100 && 0 < mom200 ) { return true; } return false; } bool is_down_trend() { double close = iClose(NULL,0,0); double close100 = iClose(NULL,0,100); double close200 = iClose(NULL,0,200); double mom100 = close - close100; double mom200 = close - close200; //「momが0未満」なら下降トレンド if(0 > mom100 && 0 > mom200 ) { return true; } return false; } int posi_count(int side) { int count = 0; for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderType() == side) { if(OrderSymbol()==Symbol()) { if(OrderMagicNumber()==777) { count++; } } } } } return count; } void position_entry(int side) { double qty = 0.1; if(side==0) { bool res= OrderSend(NULL,side,qty,Ask,0,Ask - 1000 * _Point,0,NULL,777,0,clrGreen); } if(side==1) { bool res= OrderSend(NULL,side,qty,Bid,0,Bid + 1000 * _Point,0,NULL,777,0,clrRed); } } void position_close(int side) { for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderType() == side) { if(OrderSymbol()==Symbol()) { if(OrderMagicNumber()==777) { bool res= OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 0, clrBlue); } } } } } } |
あとは
この表示がでる。
これで完成です。
バックテスト
実際に稼働させる
これでおわりです。
MT4のEAが完成!
お疲れ様でした。
全部がおわりです。
テクニカル指標だけで勝てるEAがつくれるのはちょっと意外😆
これをさらにエントリールール、決済ルールを磨いていけばさらに強いEAができそうです。
また、わからないことあったらコメントどうぞ。
まとめとおさらい
エントリーと決済ルールが必要。
・無料
・デモ口座が使える
・情報が豊富にある
・MT4の操作に癖があるので慣れるまでちょっとめんどくさい
このブログはアフィリエイトで運営費を捻出しています。
なのでアフィリエイト収入がないとそのうちブログが消滅します。
Exnessの口座を持ってない方はこちらのリンクから申し込みをしてもらえると管理人にアフィリエイト収入が入ります。
以下のリンクからAmazonで何かしらを購入してもらえると管理人にアフィリエイト収入が入ります。
稼いだ方はお祝いどうぞ
パソコン
MQLの本が出来たみたいですよ😀
MQL勉強にいいです
BybitがGoogleのIPアドレス規制をしているためです。国内のVPSなら使…
自分のbotで使ってるAPIキーを使用しているんですが、 You have br…
pybit 最新版にコードを変更しました。コードとrequirements.tx…
お返事ありがとうございます。はい。pybit==2.3.0になっております。
コードはあっていると思います。rewuirements.txtは「pybit==…