どうも潤奈です( ・Д・)
ではオリジナル関数の解説続きです!頑張りましょう!
では参考コードEAを元に解説して行きたいと思います!
え?それどこにあるの?という方は次の記事から読み進めて下さい。
潤奈流EA講座①(参考コードと構成説明)
それ以降の記事はこちら。
潤奈流EA講座②(プロパティとパラメーター)
潤奈流EA講座③(OnInit,OnTick,OnDeinit関数)
潤奈流EA講座④(オリジナル関数とは,AdjustPoint関数,AdjustSlippage関数)
Position関数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//ポジション数を取得 int Position(int PositionDirection){ int res=0; for(int i=0;i<OrdersTotal();i++){ if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==true){ if(OrderSymbol()==_Symbol && OrderMagicNumber()==MagicNumber){ if(PositionDirection==1){ if(OrderType()==OP_BUY)res++; }else if(PositionDirection==-1){ if(OrderType()==OP_SELL)res++; } } } } return(res); } |
これは現在保持しているポジション数を買いと売りで分けてカウントする関数になります。
それでは分解して解説して行きます。
1 2 3 4 |
//ポジション数を取得 int Position(int PositionDirection){ } |
まずint型(整数)のPosition関数とPosition(関数内で使用する型と引数(変数))を宣言します。
1と-1を入れる箱として、int型のPositionDirection変数を用意しました。
1 |
int res=0; |
次に計算したポジション数を入れる箱として、int型のres変数に0を代入してリセットしておきます。
1 2 3 |
for(int i=0;i<OrdersTotal();i++){ } |
for文とは、ある条件の回数分だけ繰り返し処理をします。
基本構成は、for(変数宣言と初期値;繰り返す処理数の範囲;処理カウント){ }になります。
今回で言うと、
【変数宣言と初期値】int型のi変数に0を代入します。(初期値)
【繰り返す処理数の範囲】ここに登場するOrdersTotal()は現在の口座で保持している総ポジション数を返すMT4に備わっている関数になります。処理内容は、i変数の数より総ポジション数が大きい場合。ですね。
【処理カウント】i++とはi変数に1を足す。という意味になります。
以上を踏まえて訳すと、i変数が0からスタートして、総ポジション数より小さい場合は1を足して繰り返し処理をする。となります。
という事は、例えば総ポジション数が10ある場合0からスタートして9までの10回{ }内を処理する事になります。
1 2 3 4 5 6 7 8 |
1回目処理 i=0; 2回目処理 i=1; 3回目処理 i=2; ・ ・ ・ 10回目処理 i=9; 終了 |
1 2 3 |
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==true){ } |
その{ }中の処理ですが、まずそのポジションの情報を取得する為にOrderSelect関数を使用します。
それぞれのポジションには0から数字が割り当てられています。
・i は呼び出すポジションの番号になります。
・SELECT_BY_POS は番号でポジションを呼び出す為の選択方式です。
・MODE_TRADES はエントリーや保留中の注文の中から呼び出す為の選択方式です。
・==true は上記条件で正しく情報が取得出来たかを確認しています。
まとめると、エントリーや保留中の注文の中から指定した番号のポジション情報を取得出来たら、{ }内の処理に移って下さい。という意味になります。
1 2 3 |
if(OrderSymbol()==_Symbol && OrderMagicNumber()==MagicNumber){ } |
更に{ }内に条件があります。先ほどOrdersTotal()は現在の口座で保持している総ポジション数と説明しました。
ですので他のEAや裁量でエントリーしていると、このEA以外のポジション数も含まれてしまいますので、その中からこのEAのポジションがあるかを確認します。
if文の中には2つ条件式が入っています。
①OrderSymbol()==_Symbol
OrderSymbol関数はそのポジションの通貨ペアを取得します。
_Symbol変数は表示チャートの通貨ペアが格納されています。
よって、その取得したポジションが同じ通貨ペアであるかを確認しています。
②OrderMagicNumber()==MagicNumber
OrderMagicNumber関数はそのポジションのマジックナンバーを取得します。
MagicNumber変数はマジックナンバーが格納されていますので、その取得したポジションが同じマジックナンバーでエントリーされた物かを確認しています。
この2つの条件を && で繋げているので、2つの条件が合っていれば次の{ }内の処理へと移ります。
1 2 3 4 5 6 |
if(PositionDirection==1){ if(OrderType()==OP_BUY)res++; }else if(PositionDirection==-1){ if(OrderType()==OP_SELL)res++; } |
ここまで来たらもうこのEAのポジションで間違いないでしょう。
まず、このPosition関数を使用する時に1か-1を引数でPositionDirectionに渡していますので、前提としてどちらのポジション数が知りたいかを決めています。
if(PositionDirection==1)で買いポジションが知りたいのか、else if(PositionDirection==-1)それとも売りポジションが知りたいのかという条件式があります。
例)買いの場合は最後にOrderType()==OP_BUYという条件があります。
OrderType関数は取得したポジションの注文タイプを取得します。
OP_BUYとは買いポジションのタイプです。
取得したポジション情報が買いポジションの場合は、res変数に1を足す。という流れになります。
ちなみに res++ とは res=res+1 と同じ意味になります。
1 |
return(res); |
これを総ポジション数分を繰り返してこのEAでエントリーしたポジションが現在どれだけ残っているかを確認して最後にreturn(res)でカウントした数を返します。
OpenOrder関数
1 2 3 4 5 6 7 8 9 10 11 12 |
//ポジションエントリー関数 void OpenOrder(int EntryPosition){ int res=0; double TP=TakeProfit*Pips; double SL=StopLoss*Pips; if(EntryPosition==1){ res=OrderSend(_Symbol,OP_BUY,Lots,Ask,SLP,Ask-SL,Ask+TP,NULL,MagicNumber,0,clrBlue); }else if(EntryPosition==-1){ res=OrderSend(_Symbol,OP_SELL,Lots,Bid,SLP,Bid+SL,Bid-TP,NULL,MagicNumber,0,clrRed); } } |
これは買いと売りの注文を出す関数になります。
1 2 3 4 |
//ポジションエントリー関数 void OpenOrder(int EntryPosition){ } |
まずvoid型のOpenOrder関数とOpenOrder(関数内で使用する型と引数(変数))を宣言します。
1と-1を入れる箱として、int型のEntryPosition変数を用意しました。
このvoid型とは戻り値を返さない関数に使用します。
1 |
int res=0; |
OrderSend関数の戻り値を入れる箱を用意します。
今回は特に使用する訳ではありませんので簡単に説明だけすると、OrderSend関数で注文を出すとチケット番号が返って来ます。あと何かの原因でエラーが発生した場合は-1が返って来ます。その番号を入れる箱としてres変数を用意していて、もし-1が返って来たらそのエラーコードを取得するなどの使用方法があります。
1 2 |
double TP=TakeProfit*Pips; double SL=StopLoss*Pips; |
まずdouble(小数点数)のTP変数とSL変数を宣言していて、その中に利確幅価格と損切幅価格を代入しています。
TakeProfitとStopLossはパラメーターで設定した値で、単位はpipsです。
PipsはAdjustPoint関数から取得した値を格納している変数です。
この2つを掛ける事で、Pipsを価格の単位に変換する事が出来ます。
(例)TakeProfit=50(pips)の場合
USDJPY:104.550の50pips上は104.600になります。つまり50pips=0.5という事です。
変換する為にはPips変数=0.01を掛ける事で 50pips×0.01=0.5 となります。
EURUSD:1.18422の50pips上は1.18922になります。つまり50pips=0.005という事です。
変換する為にはPips変数=0.0001を掛ける事で50pips×0.0001=0.005となります。
1 2 3 4 5 6 |
if(EntryPosition==1){ res=OrderSend(_Symbol,OP_BUY,Lots,Ask,SLP,Ask-SL,Ask+TP,NULL,MagicNumber,0,clrBlue); }else if(EntryPosition==-1){ res=OrderSend(_Symbol,OP_SELL,Lots,Bid,SLP,Bid+SL,Bid-TP,NULL,MagicNumber,0,clrRed); } |
このOpenOrder関数を使用する時に1か-1を引数でEntryPositionに渡していますので、前提としてどちらのポジション注文を出すか決めています。
if(EntryPosition==1)で買い注文か、else if(EntryPosition==-1)それとも売り注文を出すのかという条件式になります。
OrderSend関数の引数ですが、
OrderSend(通貨ペア名、注文タイプ、ロット数、注文価格、スリッページ、損切価格、利確価格、コメント、マジックナンバー、有効期限、色)を指定してあげます。
詳細はボリュームが大きいので今回は省きますので、こういう書き方だと覚えて下さい。
注文が通ればresへチケット番号を代入し、エラーであれば-1を代入します。
CloseOrder関数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
//ポジションクローズ関数 void CloseOrder(int ClosePosition){ int res=0; for(int i=OrdersTotal()-1;i>=0;i--){ if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==true){ if(OrderSymbol()==_Symbol && OrderMagicNumber()==MagicNumber){ if(OrderType()==OP_BUY && ClosePosition==1){ res=OrderClose(OrderTicket(),OrderLots(),OrderClosePrice(),SLP,clrMagenta); }else if(OrderType()==OP_SELL && ClosePosition==-1){ res=OrderClose(OrderTicket(),OrderLots(),OrderClosePrice(),SLP,clrMagenta); } } } } } |
これは買いと売りの注文を手仕舞いする関数になります。
1 2 3 4 |
//ポジションエントリー関数 void CloseOrder(int ClosePosition){ } |
まずvoid型のCloseOrder関数とCloseOrder(関数内で使用する型と引数(変数))を宣言します。
1と-1を入れる箱として、int型のClosePosition変数を用意しました。
1 |
int res=0; |
CloseOrder関数の戻り値を入れる箱を用意します。
こちらも今回は特に使用する訳ではありませんので簡単に説明だけですが、CloseOrder関数の型は実はbool型です。
bool型とはtrue(真)かfalse(偽)の論理値のみ使用する型です。数字表現ではtrue(1)かfalse(0)で表現が出来ますので、CloseOrder関数でres変数に入る戻り値は、決済が成功した場合は1でエラーの場合は0が戻り値として入ります。
1 2 3 |
for(int i=OrdersTotal()-1;i>=0;i--){ } |
今までの流れで分かると思いますが、for文を使いOrdersTotal関数で取得する注文数分を繰り返し確認して行きます。
今回は、例えばOrdersTotal()が10ポジションあるとすると、i変数に10-1=9が代入されて、0になるまで-1引いて繰り返すプログラムになっています。
まずこの式についての解説ですが、
【変数宣言と初期値】OrdersTotal()-1となっていて最初に1を引いて9にしているのは何故か分かりますか?
それは、OrdersTotal関数はポジションの数を数えるので10になるのですが、各ポジションに割り当てられている番号は0からなので、最初に1を引いて0~9の10ポジションを確認出来るようにしています。
【繰り返す処理数の範囲】これはi変数の数字が0より大きい&同じになるまでの範囲です。
【処理カウント】i–とはi変数に-1を引く。という意味になります。
ところでPosition関数で紹介した式と違う事にお気付きですか?
Position関数ではこうでした
1 |
for(int i=0;i<OrdersTotal();i++) |
そう、0から始まって1,2,3,4~と数を増やしながら繰り返すようなプログラムでした。
では何故CloseOrder関数では逆なのでしょうか?
それは、繰り返し処理している最中に決済されるとポジションの番号がズレるからです!
どういう事かと言うと、仮に10ポジションあり各ポジションには番号が割り当てられると以下の様になります。
これで例えばfor文が0からスタートしてCloseOrder関数で割当番号3を手仕舞いしたとします。
0~2は確認をして違うEAのポジションだったため手仕舞いをせずにそのままです。(処理済:黄色)
3はこのEAのポジションだったため手仕舞いします。(処理済:赤色)
(上表が手仕舞い時 下表が手仕舞い後)
すると手仕舞い後には割当番号が詰められて元々4番だったポジションが3番になってしまいます。(下表の現在の割当番号と元番号をよく見て下さい)
for文では次に処理するのは4番(緑色)ですが、そのポジションは元5番のポジションであって元4番は飛ばされてしまいます。もし多数ポジションのEAで元4番も対象だった場合、決済ロジック動いてるのに何故か残っているポジションがある?何故?となってしまいます。
これを逆に9から処理(黄色)していくと3番を手仕舞い(赤色)しても次の3番は元4番で確認済。次は2番を確認しに行くので問題がありません。
すみません。かなり脱線してしまいましたw( ・Д・)
1 2 3 |
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==true){ } |
ここはPosition関数と同じ内容ですので簡単に。
OrderSelect関数で割当番号のポジション情報を取得します。
1 |
if(OrderSymbol()==_Symbol && OrderMagicNumber()==MagicNumber){ |
ここもPosition関数と同じ内容ですので簡単に。
OrderSymbol関数で通貨ペアが同じか、OrderMagicNumber関数でマジックナンバーが同じかを確認しています。
1 2 3 4 5 6 |
if(OrderType()==OP_BUY && ClosePosition==1){ res=OrderClose(OrderTicket(),OrderLots(),OrderClosePrice(),SLP,clrMagenta); }else if(OrderType()==OP_SELL && ClosePosition==-1){ res=OrderClose(OrderTicket(),OrderLots(),OrderClosePrice(),SLP,clrMagenta); } |
まずOrderType関数で買いポジションであるかの確認と、買いポジション側の手仕舞い指示で引数1が来ているかの確認をします。
どちらも条件に当てはまった場合、OrderClose関数で手仕舞いを行います。
OrderClose関数の引数は、OrderClose(チケット番号、ロット数、決済価格、スリッページ、決済色)を指定してあげます。
買い手仕舞いの条件に当てはまらない場合、売り手仕舞い条件の確認に進みます。
今回はかなりボリュームが大きくなってしまいました。
今日はこの辺りで。
では( ・Д・)
次の記事「潤奈流EA講座⑥(TrailingStop関数,PerfectOrder関数,PerfectClose関数,まとめ)」
コメント