ついに作るときがきた!
初心者むけに解説しています。
わからないことはコメントに書いてね。
Table of Contents
MT5で自動売買EA(エキスパートアドバイザー)を自作する
毎度ですが先にゴールを決めてから作りはじめます。
今回のゴールは、「ヘーキン様」です。
MT5のEAの作り方は、
後述します。
・エントリー
・決済
の2個のルールが必要です。
ネットでググるとほとんど情報がでてくるので、きっと作れます。
自動化させるロジックを確認
エントリールール
BTCUSD 日足平均足を使う
- A (平均足終値[1] – 平均足始値[1])
Aが プラス で BUY
Aが マイナス で SELL
ドテン仕様です。
MT5のインストール
仮想通貨の自動売買をしたいのでMT5はエクスネスのデモ口座を利用します。
エクスネスのMT4デモ口座の申請方法
お疲れ様でした。これでMT5の準備完了です。次はプログラミング用のエディタを表示させます。
MT5のプログラミングエディタ(メタエディタ)を起動
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 |
//+------------------------------------------------------------------+ //| gotobi.mq5 | //| Copyright 2021, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+ |
上から8行目まではEAの説明文など
1 2 3 4 5 6 7 8 |
//+------------------------------------------------------------------+ //| gotobi.mq5 | //| Copyright 2021, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" |
最後の 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はMT5が実行させているので返却しなくてもいい。
売買シグナルの関数をつくる
初心者向けに関数の説明からするので知ってる人は次項まで飛ばしてください。
ロング条件とショート条件をつくる。
関数is_buy()、is_sell()の空っぽの関数をつくっておきます。
呼び出したら、内容に応じてデータを返却します。
これだけの説明だと意味不明なので
まあ、使ってみますか。
関数の動作確認します。
1 2 3 4 5 6 7 8 9 10 |
void OnTick(){ Print(is_buy()); } bool is_buy(){ return false; } bool is_sell(){ return false; } |
この状態でコンパイルを押すと、
ツールボックスに詳細が表示される。
Print()
Print()でエキスパートのメッセージにPrintする文字を出力させることができます。
現在is_buy関数の返却されたデータを表示させています。
今は、
だけなので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行)ごとです。
初心者はセミコロン;をつける、つけないはわからないと思います。慣れるしかないです。
平均足を取得する
平均足はMT5のインディケーターが付属してあります。
指標のExamplesの
Heiken_Ashi ね。
チャートにつけると平均足がでます。
このデータを取得するには、
データウインドウをだします。
データウインドウには平均足のデータがのっています。
こういうふうにデータウインドウに表示される場合はmqlでデータを取得することができます。
1 2 3 4 5 6 7 8 9 |
void OnTick() { int handle = iCustom(NULL,PERIOD_CURRENT,"Examples/Heiken_Ashi",0,0); double heikin_open[]; double heikin_close[]; CopyBuffer(handle,0,1,1,heikin_open); CopyBuffer(handle,3,1,1,heikin_close); Print("1個前のopen:",heikin_open[0]," ","1個前のclsoe:",heikin_close[0]); } |
CopyBufferの仕様は初心者には難しすぎるため説明はしません。気になる人は二才さんの動画観てね。
なぜなのか?配列のインデックス番号が逆になる仕様
なので欲しいデータがとれるまで
Print()で力技でとりました。
値がとれればヨシ!
うごけばヨシ!
あとは売買条件を再確認します。
- A (平均足終値[1] – 平均足始値[1])
Aが プラス で BUY
Aが マイナス で SELL
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 |
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool is_buy() { int handle = iCustom(NULL,PERIOD_CURRENT,"Examples/Heiken_Ashi",0,0); double heikin_open[]; double heikin_close[]; CopyBuffer(handle,0,1,1,heikin_open); CopyBuffer(handle,3,1,1,heikin_close); double open1 = heikin_open[0]; double close1 = heikin_close[0]; bool A = close1 - open1 > 0; if(A) { return true; } return false; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool is_sell() { int handle = iCustom(NULL,PERIOD_CURRENT,"Examples/Heiken_Ashi",0,0); double heikin_open[]; double heikin_close[]; CopyBuffer(handle,0,1,1,heikin_open); CopyBuffer(handle,3,1,1,heikin_close); double open1 = heikin_open[0]; double close1 = heikin_close[0]; bool A = close1 - open1 < 0; if(A) { return true; } return false; } |
売買シグナルが完成。
heikin_open[0]の結果をdouble open1 変数に代入しています。
is_buy()が呼び出されるときに
double open1 = heikin_open[0];
が実行されます。
算数の=とプログラミングの=は意味が違います。
算数的な = はプログラミングでは、 == イコールイコールと書きます。
なので値動き毎に計算結果が 変数 に保存される
データの保存メモリー
プログラムは0と1の数字の世界なのでデータは数字、変形する数字なので変数(へんすう)
英語ではvariable(ヴァリアブル)と呼びます。この言葉はスプラトゥーンでしか聞かないのではないでしょうか?
計算結果は浮動小数点数型(ふどうしょうすうてんすうがた)なので
double (ダブル) を つける。
doubleは小数点型とかダブル型と呼びます。
if、スペース、カッコ、波カッコの順番で書きます。
if(){}
if:
スペース:
カッコ:()
波カッコ:{}
カッコ()には条件式
波カッコ{}の中には、条件式を満たしたときの処理を書きます。
if(rsi < 30) { return true; }
なのでrsiの数値が「30を下回る場合」は「return true;」されます。
関数の内側をローカルスコープと呼びます。
関数に外側を外部スコープと呼びます。
double high0 は関数に属している、ローカルスコープの変数です。
関数の中でつかわれる変数は独立して存在していますので名前がダブってしまってもOK。
returnで 処理を完了させ、呼び出し元に戻る。
今回はBUYのシグナルが発生したらtrue、発生しなければfalseを返却する。
確認したければ、コンパイル→実行→エキスパートのメッセージで確認しよう。
MagicNumber(マジックナンバー)をつけてEAでのポジションを識別させる
EAのポジションとほかでエントリーしたポジションを判別できるようにマジックナンバーをつけます。
定数(変数)でmagic_numberを宣言しておきます。
関数の外側を外部スコープと呼びます。
関数に属していないのをグローバルスコープと呼びます。
int magic_number = 222;
は関数に属していない、グローバルスコープの変数です。
グローバルスコープにおいてあると処理で変更をしない限りデータは変更されません。
保存しておきたい数字はグローバルスコープへ置くと保存されます。
また、関数に属している変数のことをローカルスコープの変数と呼びます。
ローカルスコープの変数はOnTickが実行されるとデータはリセットされます。
ポジション数を調べる機能をつくる
ポジション数が0の時にのみトレードを行いたいのでポジション数を調べる機能をつくります。
ググったらでてきます。
- すべてのオーダーを確認
- オーダータイプ(ロングorショート)
- オーダーシンボル(通貨ペア)
- マジックナンバー(EA識別番号)
上記すべてを満たすポジションが存在するならcountの数字をプラス1する。
マジックナンバーはあとでエントリー時に付与するのですが、magic_numberに指定します。マジックナンバーなら、このEAでのエントリーになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
int position_count(int side) { int count =0; for(int i=PositionsTotal()-1; i>=0; i--) { if("" != PositionGetSymbol(i)) { if(PositionGetInteger(POSITION_TYPE)==side) { if(Symbol()==PositionGetString(POSITION_SYMBOL)) { if(PositionGetInteger(POSITION_MAGIC)==magic_number) { count++; } } } } } return count ; } |
カッコの中は引数です。
引き渡すデータを引数と呼びます。
(int side)が引数です。
今回は引数を1個渡しています。
引数は呼ぶときに呼び出し元で記述します。
関数仕様の例)
position_count(0); ならBUYポジションをカウントします。
position_count(1); ならSELLポジションをカウントします。
countは初期値0の整数です。
int count = 0;
整数型は++をつけるとカウントアップします。
これをインクリメントと呼びます。
ちなみに–をつけるとカウントダウンします。これはデクリメントと呼びます。
注文が残っている場合は”何もしない”をする
MT5では
- 注文
- 約定
- ポジション
という流れでポジションを取得します。注文が残っている状態では新規で注文をしないように注文数を確認する機能をつくります。
- 総オーダー数を確認
- 総オーダーに現在のチャートの通貨ペアが含まれるか確認
- 総オーダーに現在のEAに設定しているマジックナンバーが含まれるか確認
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
int order_count() { int count =0; for(int i=OrdersTotal()-1; i>=0; i--) { if("" != OrderGetString(ORDER_SYMBOL)) { if(Symbol()==OrderGetString(ORDER_SYMBOL)) { if(OrderGetInteger(ORDER_MAGIC)==magic_number) { count++; } } } } return count ; } |
値動きごとに注文が残っている場合を確認し、注文が残っている場合は何もしないをします。
OnTick()の直下に以下のコマンドを貼り付けときます。return;でOnTick()が終了します。(注文が残っている状態で再度新規注文させないようにする)
1 |
if(order_count() > 0 ){return;} |
MT5は成行注文→約定→ポジション保有状態(注文失敗でも注文が残る)
MT4は成行注文→約定すればポジション保有状態(注文失敗で注文が無効になる)
MT5は注文から約定までの間の情報を取得可能になりました。
だから注文の価格取得をして約定の価格取得をするとスリッページを取得できたりする。
まあぶっちゃけこの仕様めんどくさいです。
MT5は約定の履歴、注文の履歴とバラバラで管理されるため開発者を混乱させる一因になっている。
いつものセット置いとくよ
MT4からMT5に移行するときの”いつものやつ”をコピペでどこかに置いときます。詳しく説明する長くなるので省略します。
詳しく知りたいなら公式サイトを一読ください。(ググってもでてこない)
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 |
#include <Trade\SymbolInfo.mqh> CSymbolInfo iSymbol; double Bid,Ask,High,Low,Spread; void InitSymbol() { iSymbol.Name(Symbol()); } void SetSymbol() { iSymbol.RefreshRates(); Bid = iSymbol.Bid(); Ask = iSymbol.Ask(); High = iHigh(NULL,PERIOD_CURRENT,0); Low = iLow(NULL,PERIOD_CURRENT,0); Spread = iSymbol.Spread(); } #include <Trade\Trade.mqh> CTrade iTrade; void InitTrade() { iTrade.SetDeviationInPoints(0); iTrade.SetTypeFillingBySymbol(Symbol()); } void SetTrade(ulong magic_num) { iTrade.SetExpertMagicNumber(magic_num); } int OnInit() { InitSymbol(); InitTrade(); SetTrade(magic_number); return(INIT_SUCCEEDED); } |
ポジションエントリーの関数をつくる
公式サイトのドキュメントに書いてあります。(ググってもでてこない)
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= iTrade.Buy(qty,NULL,0,0,0,"Buy"); } if(side==1) { bool res= iTrade.Sell(qty,NULL,0,0,0,"Sell"); } } |
注文数量です。
ポジション決済の関数をつくる
公式サイトを熟読するとでてきます。(ググってもでてこない)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void position_close(int side) { for(int i=PositionsTotal()-1; i>=0; i--) { if("" != PositionGetSymbol(i)) { if(PositionGetInteger(POSITION_TYPE)==side) { if(Symbol()==PositionGetString(POSITION_SYMBOL)) { if(PositionGetInteger(POSITION_MAGIC)==magic_number) { iTrade.PositionClose(PositionGetTicket(i)); } } } } } } |
エントリー待機中なら何もしないようにOnTick関数を書き換える
注文数量が0ではない場合は成り行き注文が残っている状態なので何もしないようにします。
1 2 3 4 5 6 7 |
void OnTick() { if(order_count() > 0 ){return;} } |
現在のローソク足の高値安値を計算するようにOnTick関数を書き換える
double Bid,Ask,High,Low,Spread;をOnTickごとに再計算します。
1 2 3 4 5 6 7 |
void OnTick() { if(order_count() > 0 ){return;} SetSymbol(); } |
条件を満たしたらエントリーする機能をつくる
細かい関数はつくりおわったのでOnTick()を書き換えていきます。
- is_buy()がtrue
- 売りポジション数、買いポジション数が0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void OnTick() { if(order_count() > 0) { return; } SetSymbol(); //エントリー if(is_buy() && position_count(POSITION_TYPE_BUY)==0 && position_count(POSITION_TYPE_SELL)==0) { position_entry(POSITION_TYPE_BUY); } if(is_sell() && position_count(POSITION_TYPE_BUY)==0 && position_count(POSITION_TYPE_SELL)==0) { position_entry(POSITION_TYPE_SELL); } } |
MQLには定数と呼ばれる,
あらかじめ用意されている変数があります。
定数は列挙型と言われる整数型の変数です。
上から順番に0,1,2と数値が取得できます。
POSITION_TYPE_BUY は ENUM_POSITION_TYPE の 1個目なので 「0」 です。
POSITION_TYPE_SELL は ENUM_POSITION_TYPE の 2個目なので 「1」 です。
POSITION_TYPE_BUY が 0
POSITION_TYPE_SELL が 1
条件を満たしたら決済する機能をつくる
以下の条件でショートポジションが決済される
- is_buy()がtrue
- 売りポジション数が0を超えている
以下の条件でロングポジションが決済される
- is_sell()がtrue
- 売りポジション数が0を超えている
エントリーは入れ子なしで&&とつなげて書いたけど入れ子(ネスト)でも書ける。
処理速度的にはネストの方が速いらしいです。
どっちでもOKです。
inputをつけてユーザー入力を可能にする
ポジションエントリーの関数からqtyを削除する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void position_entry(int side) { if(side==0) { bool res= iTrade.Buy(qty,NULL,0,0,0,"Buy"); } if(side==1) { bool res= iTrade.Sell(qty,NULL,0,0,0,"Sell"); } } |
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 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
//+------------------------------------------------------------------+ //| ProjectName | //| Copyright 2020, CompanyName | //| http://www.companyname.net | //+------------------------------------------------------------------+ #property description "ビットコインの日足で動かしてください。" input int magic_number = 222;//マジックナンバー input double qty = 0.01;//注文数量 //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void OnTick() { if(order_count() > 0) { return; } SetSymbol(); //エントリー if(is_buy() && position_count(POSITION_TYPE_BUY)==0 && position_count(POSITION_TYPE_SELL)==0) { position_entry(POSITION_TYPE_BUY); } if(is_sell() && position_count(POSITION_TYPE_BUY)==0 && position_count(POSITION_TYPE_SELL)==0) { position_entry(POSITION_TYPE_SELL); } //決済 if(is_buy() && position_count(POSITION_TYPE_SELL)>0) { position_close(POSITION_TYPE_SELL); } if(is_sell() && position_count(POSITION_TYPE_BUY)>0) { position_close(POSITION_TYPE_BUY); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool is_buy() { int handle = iCustom(NULL,PERIOD_CURRENT,"Examples/Heiken_Ashi",0,0); double heikin_open[]; double heikin_close[]; CopyBuffer(handle,0,1,1,heikin_open); CopyBuffer(handle,3,1,1,heikin_close); double open1 = heikin_open[0]; double close1 = heikin_close[0]; bool A = close1 - open1 > 0; if(A) { return true; } return false; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool is_sell() { int handle = iCustom(NULL,PERIOD_CURRENT,"Examples/Heiken_Ashi",0,0); double heikin_open[]; double heikin_close[]; CopyBuffer(handle,0,1,1,heikin_open); CopyBuffer(handle,3,1,1,heikin_close); double open1 = heikin_open[0]; double close1 = heikin_close[0]; bool A = close1 - open1 < 0; if(A) { return true; } return false; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int position_count(int side) { int count =0; for(int i=PositionsTotal()-1; i>=0; i--) { if("" != PositionGetSymbol(i)) { if(PositionGetInteger(POSITION_TYPE)==side) { if(Symbol()==PositionGetString(POSITION_SYMBOL)) { if(PositionGetInteger(POSITION_MAGIC)==magic_number) { count++; } } } } } return count ; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int order_count() { int count =0; for(int i=OrdersTotal()-1; i>=0; i--) { if("" != OrderGetString(ORDER_SYMBOL)) { if(Symbol()==OrderGetString(ORDER_SYMBOL)) { if(OrderGetInteger(ORDER_MAGIC)==magic_number) { count++; } } } } return count ; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void position_entry(int side) { if(side==0) { bool res= iTrade.Buy(qty,NULL,0,0,0,"Buy"); } if(side==1) { bool res= iTrade.Sell(qty,NULL,0,0,0,"Sell"); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void position_close(int side) { for(int i=PositionsTotal()-1; i>=0; i--) { if("" != PositionGetSymbol(i)) { if(PositionGetInteger(POSITION_TYPE)==side) { if(Symbol()==PositionGetString(POSITION_SYMBOL)) { if(PositionGetInteger(POSITION_MAGIC)==magic_number) { iTrade.PositionClose(PositionGetTicket(i)); } } } } } } #include <Trade\SymbolInfo.mqh> CSymbolInfo iSymbol; double Bid,Ask,High,Low,Spread; void InitSymbol() { iSymbol.Name(Symbol()); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void SetSymbol() { iSymbol.RefreshRates(); Bid = iSymbol.Bid(); Ask = iSymbol.Ask(); High = iHigh(NULL,PERIOD_CURRENT,0); Low = iLow(NULL,PERIOD_CURRENT,0); Spread = iSymbol.Spread(); } #include <Trade\Trade.mqh> CTrade iTrade; void InitTrade() { iTrade.SetDeviationInPoints(0); iTrade.SetTypeFillingBySymbol(Symbol()); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void SetTrade(ulong magic_num) { iTrade.SetExpertMagicNumber(magic_num); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int OnInit() { if(ChartPeriod(0)!=PERIOD_D1){ Alert("Not Period Please select a day/日足を選択してください"); return 1;} InitSymbol(); InitTrade(); SetTrade(magic_number); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ |
handleの処理が重たすぎたためOnInitへ避難しました。ソースコード全文です。
Handleを毎回呼び出すと毎回インジケーターへアクセスしてるようでクソ重たくてダメでした。
HandleはOnInitで呼び出して使ってください。
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 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
//+------------------------------------------------------------------+ //| ProjectName | //| Copyright 2020, CompanyName | //| http://www.companyname.net | //+------------------------------------------------------------------+ #property description "ビットコインの日足で動かしてください。" input int magic_number = 222;//マジックナンバー input double qty = 0.01;//注文数量 //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void OnTick() { if(order_count() > 0) { return; } SetSymbol(); //エントリー if(is_buy() && position_count(POSITION_TYPE_BUY)==0 && position_count(POSITION_TYPE_SELL)==0) { position_entry(POSITION_TYPE_BUY); } if(is_sell() && position_count(POSITION_TYPE_BUY)==0 && position_count(POSITION_TYPE_SELL)==0) { position_entry(POSITION_TYPE_SELL); } //決済 if(is_buy() && position_count(POSITION_TYPE_SELL)>0) { position_close(POSITION_TYPE_SELL); } if(is_sell() && position_count(POSITION_TYPE_BUY)>0) { position_close(POSITION_TYPE_BUY); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool is_buy() { double heikin_open[]; double heikin_close[]; CopyBuffer(handle,0,1,1,heikin_open); CopyBuffer(handle,3,1,1,heikin_close); double open1 = heikin_open[0]; double close1 = heikin_close[0]; bool A = close1 - open1 > 0; if(A) { return true; } return false; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool is_sell() { double heikin_open[]; double heikin_close[]; CopyBuffer(handle,0,1,1,heikin_open); CopyBuffer(handle,3,1,1,heikin_close); double open1 = heikin_open[0]; double close1 = heikin_close[0]; bool A = close1 - open1 < 0; if(A) { return true; } return false; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int position_count(int side) { int count =0; for(int i=PositionsTotal()-1; i>=0; i--) { if("" != PositionGetSymbol(i)) { if(PositionGetInteger(POSITION_TYPE)==side) { if(Symbol()==PositionGetString(POSITION_SYMBOL)) { if(PositionGetInteger(POSITION_MAGIC)==magic_number) { count++; } } } } } return count ; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int order_count() { int count =0; for(int i=OrdersTotal()-1; i>=0; i--) { if("" != OrderGetString(ORDER_SYMBOL)) { if(Symbol()==OrderGetString(ORDER_SYMBOL)) { if(OrderGetInteger(ORDER_MAGIC)==magic_number) { count++; } } } } return count ; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void position_entry(int side) { if(side==0) { bool res= iTrade.Buy(qty,NULL,0,0,0,"Buy"); } if(side==1) { bool res= iTrade.Sell(qty,NULL,0,0,0,"Sell"); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void position_close(int side) { for(int i=PositionsTotal()-1; i>=0; i--) { if("" != PositionGetSymbol(i)) { if(PositionGetInteger(POSITION_TYPE)==side) { if(Symbol()==PositionGetString(POSITION_SYMBOL)) { if(PositionGetInteger(POSITION_MAGIC)==magic_number) { iTrade.PositionClose(PositionGetTicket(i)); } } } } } } #include <Trade\SymbolInfo.mqh> CSymbolInfo iSymbol; double Bid,Ask,High,Low,Spread; void InitSymbol() { iSymbol.Name(Symbol()); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void SetSymbol() { iSymbol.RefreshRates(); Bid = iSymbol.Bid(); Ask = iSymbol.Ask(); High = iHigh(NULL,PERIOD_CURRENT,0); Low = iLow(NULL,PERIOD_CURRENT,0); Spread = iSymbol.Spread(); } #include <Trade\Trade.mqh> CTrade iTrade; void InitTrade() { iTrade.SetDeviationInPoints(0); iTrade.SetTypeFillingBySymbol(Symbol()); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void SetTrade(ulong magic_num) { iTrade.SetExpertMagicNumber(magic_num); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int handle; int OnInit() { if(ChartPeriod(0)!=PERIOD_D1){ Alert("Not Period Please select a day/日足を選択してください"); return 1;} handle = iCustom(NULL,PERIOD_CURRENT,"Examples/Heiken_Ashi",0,0); InitSymbol(); InitTrade(); SetTrade(magic_number); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ |
この表示がでる。
これで完成です。
バックテスト
ボラティリティがないと弱いんだな・・・。
ビジュアルモードでは平均足でドテンを繰り返している
実際に稼働させる
これでおわりです。
MT5のEAが完成!
お疲れ様でした。
全部がおわりです。
ボラティリティが激しく、トレンドが強くなると結果もよくなります。
内容でわからないことはコメントに書き込みしてください。
まとめとおさらい
エントリーと決済ルールが必要。
・無料
・デモ口座が使える
・UIが綺麗
・新しい
・MT5の操作に癖があるので慣れるまでちょっとめんどくさい
・MT4よりもプログラミング難易度が高い
BybitがGoogleのIPアドレス規制をしているためです。国内のVPSなら使…
自分のbotで使ってるAPIキーを使用しているんですが、 You have br…
pybit 最新版にコードを変更しました。コードとrequirements.tx…
お返事ありがとうございます。はい。pybit==2.3.0になっております。
コードはあっていると思います。rewuirements.txtは「pybit==…