人気の「ゴールドのナンピンマーチンEA」をつくってみます。
初心者むけに解説しています。
わからないことはコメントに書いてね。
ちなみに俺はナンピンマーチン否定派です。
Udemyで動画作りました!
Table of Contents
MT4で自動売買EA(エキスパートアドバイザー)を自作する
毎度ですが先にゴールを決めてから作りはじめます。
今回のゴールは、「RSI逆張りEAを完成」です。
MT4のEAの作り方は、
エントリールール
- RSI70到達でショート
- RSI30到達でロング
- ダブルモメンタムフィルター
ナンピンマーチンルール
- エントリー価格から逆行【ユーザー入力】PIPSで追加
決済ルール
- 【ユーザー入力】獲得PIPSで利食い
・エントリー
・決済
の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()==777999) { 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 13 14 15 |
void position_entry(int side) { double qty = 0.01; if(side==0) { bool res= OrderSend(NULL,side,qty,Ask,0,0,0,NULL,777999,0,clrGreen); } if(side==1) { bool res= OrderSend(NULL,side,qty,Bid,0,0,0,NULL,777999,0,clrRed); } } |
注文数量です。
ポジション決済の関数をつくる
ググったらでる。
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には定数と呼ばれる,
あらかじめ用意されている変数があります。
ポジションの平均取得単価を計算する機能をつくる
平均取得単価を計算します。
ナンピンとマーチンゲール法を組み合わせて絶対に損を出さない必勝法(資金が無尽蔵なら)
例えば、ドル円140.00円で1枚ロングしたら、次は139.90でロング、139.80でロング・・・・・とポジションが損を抱えていたらさらにポジションを建てて無限に平均取得単価を下げていく。
平均取得単価、例えばこんな感じでポジションを1枚づつ建てていくとしたなら
140.00 1枚
139.90 1枚
139.80 1枚
139.70 1枚
139.60 1枚
ポジション平均取得単価 139.8 5枚
計算式 (A + B + C + D + E)/ 5
※ ポジションABCDE、計5ポジを持つとき
平均取得単価、例えばこんな感じでポジションを2倍で建てていくとしたなら
140.00 1枚
139.90 2枚
139.80 4枚
139.70 8枚
139.60 16枚
ポジション平均取得単価 139.68 31枚
計算式 (A*1 + B*2 + C*4 + D*8 + E*16)/ 31
※ ポジションABCDE、計5ポジを持つとき
※ 31はポジションの総枚数
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 |
double position_average_price(int side) { double lots_sum = 0; double price_sum = 0; double average_price = 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()==777999) { lots_sum += OrderLots(); price_sum += OrderOpenPrice() * OrderLots(); } } } } } if(lots_sum && price_sum) { average_price = price_sum/lots_sum; } return average_price; } |
平均取得単価の計算が完成。
条件を満たしたら決済する機能をつくる
以下の条件でロングポジションが決済される
- 買いポジション数が0を超えている
- 平均取得単価から250ポイント獲得
以下の条件でショートポジションが決済される
- 売りポジション数が0を超えている
- 平均取得単価から250ポイント獲得
エントリーは入れ子なしで&&とつなげて書いたけど入れ子(ネスト)でも書ける。
処理速度的にはネストの方が速いらしいです。
どっちでもOKです。
初回ポジションのエントリー価格を取得する関数をつくる
ポジションを一列に並べて価格を比較して一番大きい価格と一番小さい価格を取得します。
配列と変数を追加。50は適当な数字。
変数の複数系です。
配列のインデックス番号は数字の0から かぞえます。
例)
doulbe entry_prices[2]; 空の配列インデックス数は2はインデックス番号は[0,1](ダブル型)の宣言。
entry_prices[0] = 140.0; 配列の0番に140.0を追加。
entry_prices[1] = 140.0; 配列の1番に140.0を追加。
entry_prices[2] = 140.0; エラー。インデックス番号が存在しない。
配列の初期化のコマンド。配列を0.0でリセットする。
ショートポジションの場合、エントリー価格の最小値を取得する。
そのため、配列をでかい数字で初期化リセットする。
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 |
double position_entry_price(int side) { double entry_prices[50]; double entry_price=0; ArrayInitialize(entry_prices,0.0); if(side ==1){ArrayInitialize(entry_prices,9999999.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()==777999) { entry_prices[i] = OrderOpenPrice(); } } } } } if(side==0) { entry_price = entry_prices[ArrayMaximum(entry_prices,WHOLE_ARRAY,0)]; } if(side==1) { entry_price = entry_prices[ArrayMinimum(entry_prices,WHOLE_ARRAY,0)]; } return entry_price; } |
初回エントリー価格を取得する関数が完成。
別の例)最大ロット数からマーチンゲールのロットサイズを指定する方法
よく考えてみたらこっちの方法が簡単かも。
現在のポジションを一列に並べて、最大ロット数を取得、それにマーチンゲールの倍率を掛けて、小数点を2ケタに丸めた関数。
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 |
double calc_martin_lots(int side) { double bairitu = 2.0; double position_lots_list[50]; double martin_lots=0; ArrayInitialize(position_lots_list,0.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()==magic_number) { position_lots_list[i] = OrderLots(); } } } } } double position_max_lots = position_lots_list[ArrayMaximum(position_lots_list,WHOLE_ARRAY,0)]; martin_lots = NormalizeDouble(position_max_lots * bairitu, 2); return martin_lots; } |
ナンピンポジションエントリーの関数をつくる
エントリーに倍率を指定する。
引数を2個にします。
売買方向と、倍率
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void position_entry_nanpin(int side,double bairitu) { double qty = 0.01; if(side==0) { bool res= OrderSend(NULL,side,qty * bairitu,Ask,0,0,0,NULL,777999,0,clrGreen); } if(side==1) { bool res= OrderSend(NULL,side,qty * bairitu,Bid,0,0,0,NULL,777999,0,clrRed); } } |
条件を満たしたらナンピンマーチンする機能をつくる
空の関数を追加
ロングナンピンマーチンの条件
- ポジション数が0を超えている
- エントリープライスを取得する
- ナンピンマーチンのトリガー価格を計算する
- ロット数の計算をする
- トリガー価格になった場合、エントリーする
ショートナンピンマーチンの条件
- ポジション数が0を超えている
- エントリープライスを取得する
- ナンピンマーチンのトリガー価格を計算する
- ロット数の計算をする
- トリガー価格になった場合、エントリーする
BUYポジションの場合
ナンピンマーチントリガー価格 = エントリー価格 ー(190ポイント × ポジション数)
SELLポジションの場合
ナンピンマーチントリガー価格 = エントリー価格 +(190ポイント × ポジション数)
lots_bairitu = MathPow(2,position_num);
2の、 ポジション数 乗
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 |
void nanpin_martin_judge() { if(posi_count(OP_BUY) > 0) { int position_num = posi_count(OP_BUY); double entry_price = position_entry_price(OP_BUY); double nanpin_price = entry_price - (190 * _Point) * position_num; double lots_bairitu = MathPow(2,position_num); if(nanpin_price > Ask) { position_entry_nanpin(OP_BUY,lots_bairitu); } } if(posi_count(OP_SELL) > 0) { int position_num = posi_count(OP_SELL); double entry_price = position_entry_price(OP_SELL); double nanpin_price = entry_price + (190 * _Point) * position_num; double lots_bairitu = MathPow(2,position_num); if(nanpin_price < Bid) { position_entry_nanpin(OP_SELL,lots_bairitu); } } } |
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 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
#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); } //決済処理 if(posi_count(OP_BUY) > 0) { if(position_average_price(0) + 250 * _Point < Bid) { position_close(0); } } if(posi_count(OP_SELL) > 0) { if(position_average_price(1) - 250 * _Point > Ask) { position_close(1); } } //ナンピンマーチン nanpin_martin_judge(); } 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()==777999) { count++; } } } } } return count; } void position_entry(int side) { double qty = 0.01; if(side==0) { bool res= OrderSend(NULL,side,qty,Ask,0,0,0,NULL,777999,0,clrGreen); } if(side==1) { bool res= OrderSend(NULL,side,qty,Bid,0,0,0,NULL,777999,0,clrRed); } } void position_entry_nanpin(int side,double bairitu) { double qty = 0.01; if(side==0) { bool res= OrderSend(NULL,side,qty * bairitu,Ask,0,0,0,NULL,777999,0,clrGreen); } if(side==1) { bool res= OrderSend(NULL,side,qty * bairitu,Bid,0,0,0,NULL,777999,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()==777999) { bool res= OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 0, clrBlue); } } } } } } double position_average_price(int side) { double lots_sum = 0; double price_sum = 0; double average_price = 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()==777999) { lots_sum += OrderLots(); price_sum += OrderOpenPrice() * OrderLots(); } } } } } if(lots_sum && price_sum) { average_price = price_sum/lots_sum; } return average_price; } double position_entry_price(int side) { double entry_prices[50]; double entry_price=0; ArrayInitialize(entry_prices,0.0); if(side ==1){ArrayInitialize(entry_prices,9999999.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()==777999) { entry_prices[i] = OrderOpenPrice(); } } } } } if(side==0) { entry_price = entry_prices[ArrayMaximum(entry_prices,WHOLE_ARRAY,0)]; } if(side==1) { entry_price = entry_prices[ArrayMinimum(entry_prices,WHOLE_ARRAY,0)]; } return entry_price; } void nanpin_martin_judge() { if(posi_count(OP_BUY) > 0) { int position_num = posi_count(OP_BUY); double entry_price = position_entry_price(OP_BUY); double nanpin_price = entry_price - (190 * _Point) * position_num; double lots_bairitu = MathPow(2,position_num); if(nanpin_price > Ask) { position_entry_nanpin(OP_BUY,lots_bairitu); } } if(posi_count(OP_SELL) > 0) { int position_num = posi_count(OP_SELL); double entry_price = position_entry_price(OP_SELL); double nanpin_price = entry_price + (190 * _Point) * position_num; double lots_bairitu = MathPow(2,position_num); if(nanpin_price < Bid) { position_entry_nanpin(OP_SELL,lots_bairitu); } } } |
あとは
この表示がでる。
これで完成です。
バックテスト
クライマックス13段
実際に稼働させる
これでおわりです。
MT4のEAが完成!
こんなんでいいのかな!?
結果的によく分からないものが出来たw
これを最適化させたり、フィルター機能を加えていけばよくなるんですかね?
お疲れ様でした。
全部がおわりです。
こっからどう改良していいかいまいちよく分からないです。
ナンピンマーチンについてよくわからないのでおすすめの設定や試して欲しいことコメントで教えてください。
まとめとおさらい
エントリーと決済ルールが必要。
・無料
・デモ口座が使える
・情報が豊富にある
・MT4の操作に癖があるので慣れるまでちょっとめんどくさい
このブログはアフィリエイトで運営費を捻出しています。
なのでアフィリエイト収入がないとそのうちブログが消滅します。
Exnessの口座を持ってない方はこちらのリンクから申し込みをしてもらえると管理人にアフィリエイト収入が入ります。
以下のリンクからAmazonで何かしらを購入してもらえると管理人にアフィリエイト収入が入ります。
稼いだ方はお祝いどうぞ
マーチンゲール
MQL勉強にいいです
外国為替
外国為替2
MQLの本が出来たみたいですよ😀
BybitがGoogleのIPアドレス規制をしているためです。国内のVPSなら使…
自分のbotで使ってるAPIキーを使用しているんですが、 You have br…
pybit 最新版にコードを変更しました。コードとrequirements.tx…
お返事ありがとうございます。はい。pybit==2.3.0になっております。
コードはあっていると思います。rewuirements.txtは「pybit==…