MT4でも仮想通貨自動売買はできます。
重要なのが、スワップポイントです。
Table of Contents
FXGT・Exnessスワップポイント比較
FXGTのスワップポイント
$20000なら、約2$分が徴収されます。
4時間に一回なので、6回/日です。1日12ドル分。
FXGTはスキャルピングやデイトレード向きです。
Exnessのスワップポイント
Exnessでも仮想通貨の取り扱いがあります。
Exnessはスワップポイントが0なので中長期の戦略が使える。
今回のロジックは中長期のタイプなのでExnessを使いたいと思います。
MT4で自動売買EA(エキスパートアドバイザー)を自作する
毎度ですが先にゴールを決めてから作りはじめます。
今回のゴールは、「MAゴールデンクロスの自動売買ツールを完成させる」。
MT4のEA作成手順
自動化させるルール探します。
今回は元祖ドテン君と同じでドンチアンブレイクアウトを実装します。
期間Nの高値をブレイクアウトでロング、期間Nの安値をブレイクアウトでショート。
つまり永遠にポジションを保有します。
・エントリー
・決済
の2個のルールが必要
ネットでググるとほとんど情報がでてくるので、きっと作れます。
自動化させるロジックを確認
先にTradingViewでロジックを確認します。
1時間足のBTCUSDを表示させています。
チャネルブレイクアウトという名前でTradingView公式のストラテジーにあります。
これの時間軸:日、期間:5が強かったので、1時間足の期間120にして使っていきたいと思います。
ドンチアンブレイクアウト by hosono_p on TradingView.com
コードは以下のリンクから確認できます。
ブログの存続のため、TradingViewの有料アカウントを申し込むときは以下の紹介リンクから申し込みをお願いします。
MT4のインストール
仮想通貨の自動売買をしたいのでMT4はエクスネスのデモ口座を利用します。
エクスネスのMT4デモ口座の申請方法
お疲れ様でした。これでMT4の準備完了です。次はプログラミング用のエディタを表示させます。
MT4のプログラミングエディタ(メタエディタ)を起動
MQLのプログラムのコードはここに書いていきます。
エキスパートアドバイザーの新規作成
新規でEAを作ります。
テンプレコードの説明
いちおうテンプレの説明しておきます。飛ばしてもOKです。
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が実行させているので返却しなくてもいい。
期間Nの高値・安値を取得する
MT4に標準搭載されていました。
簡単に期間Nの高値安値とれました。
マジックナンバーを付与することでEAでのポジションを識別する
マジックナンバーをつけておきます。
1 |
int magic_number = 111; |
関数の外側を外部スコープと呼びます。
関数に属していないのをグローバルスコープと呼びます。
int magic_number = 111;
は関数に属していない、グローバルスコープの変数です。
グローバルスコープにおいてあると処理で変更をしない限りデータは変更されません。
保存しておきたい数字はグローバルスコープへ置くと保存されます。
また、関数に属している変数のことをローカルスコープの変数と呼びます。
ローカルスコープの変数はOnTickが実行されるとデータはリセットされます。
条件分岐をつくる
次はポジション保有状態で条件分岐させます。
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 |
void OnTick() { //期間120の高値を取得する int takane_bar = iHighest(NULL,PERIOD_H1,MODE_HIGH,120,1); double takane=iHigh(NULL,PERIOD_H1,takane_bar); //期間120の安値を取得する int yasune_bar = iLowest(NULL,PERIOD_H1,MODE_LOW,120,1); double yasune=iLow(NULL,PERIOD_H1,yasune_bar); //ノーポジ if(buy_posi_count()==0 && sell_posi_count()==0) { if(takane<=iHigh(NULL,PERIOD_H1,1)) { } if(yasune>=iLow(NULL,PERIOD_H1,1)) { } } //買いポジションを持っている if(buy_posi_count()>0) { if(yasune>=iLow(NULL,PERIOD_H1,1)) { } } //売りポジションを持っている if(sell_posi_count()>0) { if(takane<=iHigh(NULL,PERIOD_H1,1)) { } } } } int buy_posi_count() { int count = 0; for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderType() == OP_BUY) { if(OrderSymbol()==Symbol()) { if(OrderMagicNumber()==magic_number) { count++; } } } } } return count; } int sell_posi_count() { int count = 0; for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderType() == OP_SELL) { if(OrderSymbol()==Symbol()) { if(OrderMagicNumber()==magic_number) { count++; } } } } } return count; } |
期間Nの高値・安値を変数にいれる
1 2 3 4 5 6 |
//期間120の高値を取得する int takane_bar = iHighest(NULL,PERIOD_H1,MODE_HIGH,120,1); double takane=iHigh(NULL,PERIOD_H1,takane_bar); //期間120の安値を取得する int yasune_bar = iLowest(NULL,PERIOD_H1,MODE_LOW,120,1); double yasune=iLow(NULL,PERIOD_H1,yasune_bar); |
再利用しやすいようにdouble takane,double yasuneに計算結果を代入しています。
double は小数点の変数宣言です。
グローバルスコープに状態(フラグ)を保存する
buy,sellのフラグを保存します。
ローソク足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 |
//ノーポジ if(buy_posi_count()==0 && sell_posi_count()==0) { if(takane<=iHigh(NULL,PERIOD_H1,1) || buy_on) { OrderSend(NULL,OP_BUY,0.01,Ask,0,0,0,"買い",magic_number,0,clrGreen); buy_on = false; } if(yasune>=iLow(NULL,PERIOD_H1,1) || sell_on) { OrderSend(NULL,OP_SELL,0.01,Bid,0,0,0,"売り",magic_number,0,clrRed); sell_on = false; } } //買いポジションを持っている if(buy_posi_count()>0) { if(yasune>=iLow(NULL,PERIOD_H1,1)) { position_close("buy_close"); sell_on = true; } } //売りポジションを持っている if(sell_posi_count()>0) { if(takane<=iHigh(NULL,PERIOD_H1,1)) { position_close("sell_close"); buy_on=true; } } |
引き渡すデータを引数と呼びます。
(double stop) が引数です。
今回は引数を1個渡しています。
引数は呼ぶときに呼び出し元で記述します。
set_stop(価格); なら保有ポジションにストップ指値を付与します。
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 |
#property strict int magic_number = 111; bool buy_on=false; bool sell_on=false; void OnTick() { static datetime prev_time = 0; if(prev_time!=iTime(NULL,0,0)) { prev_time=iTime(NULL,0,0); //期間120の高値を取得する int takane_bar = iHighest(NULL,PERIOD_H1,MODE_HIGH,120,1); double takane=iHigh(NULL,PERIOD_H1,takane_bar); //期間120の安値を取得する int yasune_bar = iLowest(NULL,PERIOD_H1,MODE_LOW,120,1); double yasune=iLow(NULL,PERIOD_H1,yasune_bar); //ノーポジ if(buy_posi_count()==0 && sell_posi_count()==0) { if(takane<=iHigh(NULL,PERIOD_H1,1) || buy_on) { OrderSend(NULL,OP_BUY,0.01,Ask,0,0,0,"買い",magic_number,0,clrGreen); buy_on = false; } if(yasune>=iLow(NULL,PERIOD_H1,1) || sell_on) { OrderSend(NULL,OP_SELL,0.01,Bid,0,0,0,"売り",magic_number,0,clrRed); sell_on = false; } } //買いポジションを持っている if(buy_posi_count()>0) { if(yasune>=iLow(NULL,PERIOD_H1,1)) { position_close("buy_close"); sell_on = true; } } //売りポジションを持っている if(sell_posi_count()>0) { if(takane<=iHigh(NULL,PERIOD_H1,1)) { position_close("sell_close"); buy_on=true; } } } } int buy_posi_count() { int count = 0; for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderType() == OP_BUY) { if(OrderSymbol()==Symbol()) { if(OrderMagicNumber()==magic_number) { count++; } } } } } return count; } int sell_posi_count() { int count = 0; for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderType() == OP_SELL) { if(OrderSymbol()==Symbol()) { if(OrderMagicNumber()==magic_number) { count++; } } } } } return count; } void position_close(string side) { if(side=="buy_close") { for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderType() == OP_BUY) { if(OrderSymbol()==Symbol()) { if(OrderMagicNumber()==magic_number) { bool cl = OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 10, clrBlue); } } } } } } if(side=="sell_close") { for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderType() == OP_SELL) { if(OrderSymbol()==Symbol()) { if(OrderMagicNumber()==magic_number) { bool cl = OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 10, clrBlue); } } } } } } } |
あとは
この表示がでる。
これで完成です。
バックテストする
MT4の画面を開くとバックテストができます。
これで
レポートで右クリックすると
レポートの保存ができます。
レポートは以下のように表示されます。
考察
ドローダウンがでけえ。
純益が142ドルで最大DDが198ドルなので瞬間最大負け額が大きいです。
この手法はトレンド発生時はドカンと勝てる。マグロの一本釣りのような手法。
1時間足の期間120なのでマイナスポジションを保有している時間はかなり長い。
精神的に苦しいです。
エントリールールだけでイグジットもするため、
エントリールールに優位性があるのかも知れない。
イグジットを改良していこう。
書籍の手法だと1年ぐらいポジション放置することがあるのでBOT化する必要性あるのだろうか?
実際に稼働させる
これでおわりです。
MT4のEAが完成!
お疲れ様でした。
全部がおわりです。
内容でわからないことはコメントに書き込みしてください。
まとめとおさらい
エントリーと決済ルールが必要。
・無料
・デモ口座が使える
・情報が豊富にある
・MT4の操作に癖があるので慣れるまでちょっとめんどくさい
このブログは広告で運営費を捻出しています。
なので広告収入がないとそのうち消滅します。
なので紹介リンクから口座開設の協力をお願いいたします。
あとAmazonで何か買い物をしてください。
夏はビールがうまい!
FXのEAを作ってみるのもありかも。
BybitがGoogleのIPアドレス規制をしているためです。国内のVPSなら使…
自分のbotで使ってるAPIキーを使用しているんですが、 You have br…
pybit 最新版にコードを変更しました。コードとrequirements.tx…
お返事ありがとうございます。はい。pybit==2.3.0になっております。
コードはあっていると思います。rewuirements.txtは「pybit==…