EA開発 Part5 ストップロスとforループ
前回はパラメーターを作って最適化してみましたが、ストップロス(SL)とテイクプロフィット(TP)は未設定のままでした。
今回は前回未設定だったストップロスをパラメーターで指定していきます。
ストップロスとテイクプロフィットをパラメーターで指定
input int Magic = 123465;
input double Lots = 0.1;
input int Slippage = 2;
input string OrdersComment = "ZeroCreate";
input int StopLoss = 200;
input int TakeProfit = 400;
input int StartHour = 9;
input int StopHour = 18;
5行目と6行目にStopLossとTakeProfitを追加しました。
なお、指定はPointというMT4の最小単位で指定することにします。(小数点以下が3桁・5桁の場合にはPipsを10倍するだけです。上記の設定ですとStopLossに20pips、TakeProfitに40pipsを指定したことになります。
次にOrderSendの際にストップロスを指定するわけですが、このPointという数値をそのまま入れてもうまくいきません。
OrderSendは「価格」を指定する必要があるからです。指定されたPointを使ってストップロスとテイクプロフィットの価格を計算してOrderSendに渡します。
double stopLoss = Ask - StopLoss * Point;
double takeProfit = Bid + TakeProfit * Point;
OrderSend(Symbol(),OP_BUY,Lots,Ask,Slippage,stopLoss,takeProfit,OrdersComment,Magic,0,clrBlue);
stopLossという変数を定義しました。変数は値が入る「箱」のようなもので、doubleは小数の型です。
PointというのはMT4で定義されている定数で「その通貨ペアの最小値」です。EURUSDで小数点が5桁であれば0.00001、USDJPYで小数点が3桁なら0.001という値が入ってきます。
ですから、StopLossに200を指定すると 200 × 0.00001 = 0.00200という計算になります。
これを買値(Ask)から減算すれば「200point(20pips)下にストップロスを置く」という意味になるわけです。
テイクプロフィットも同様です。クローズする時は売値(Bid)がベースになるためBidに加算しています。
これでストップロスとテイクプロフィットの計算は完成ですが、このままだと問題が起こります。
このシステムは「開始時刻にポジションを持っていなければオープンする」という条件になっているので、開始時刻にオープンした後、1時間経過する前に「ストップロスまたはテイクプロフィットに到達」してクローズしてしまうと、
オープンの条件を再度満たしてしまうため、もう一度オープンしてしまいます!
これを防ぐ方法は幾つかあると思いますが、今回は「フラグ」というプログラムでよく使う方法で対処していきたいと思います。
フラグ
フラグは「一度処理を実行したよ」ということがわかるように立てておいたり、フラグの状況で「どういう状況か」を把握したりするのに使います。
まずはパラメーターの下あたり(OnInit関数の上)に行を空けて以下の行を追記してみてください。
input int Magic = 123465;
input double Lots = 0.1;
input int Slippage = 2;
input string OrdersComment = "ZeroCreate";
input int StopLoss = 200;
input int TakeProfit = 400;
input int StartHour = 9;
input int StopHour = 18;
bool IsOpened = false;
bool型というのが新しくでてきました。これはtrue(真)かfalse(偽)のみを指定できる型でフラグによく使われます。
初期値はfalse(オープンしていない)を指定しておきます。
次に「オープンしたらフラグを立てる」という処理を入れます。
if(Hour() == StartHour && OrdersTotal() == 0 && IsOpened == false)
{
double stopLoss = Ask - StopLoss * Point;
double takeProfit = Bid + TakeProfit * Point;
OrderSend(Symbol(),OP_BUY,Lots,Ask,Slippage,stopLoss,takeProfit,OrdersComment,Magic,0,clrBlue);
IsOpened = true;
}
OrderSendの下にIsOpenedにtrueを入れて「オープンしましたよ」というフラグを立てておきます。
そして、オープン条件(if文)に「IsOpendがfalseだったら」と入れておきます。
次に「立てたフラグを戻す」処理が必要になります。
OrderCloseでフラグを戻したいところですがストップロスでクローズされることもあるので、それでは不十分です。
クローズ処理をちょっと変更して「クローズ時間になったらポジションを持っているかどうかに関わらずフラグを戻す」ようにしました。
if(Hour() == StopHour)
{
if(OrdersTotal() > 0)
{
for(int i=0; i<=OrdersTotal()-1; i++)
{
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES) == false) break;
OrderClose(OrderTicket(),OrderLots(),Bid,2,clrBlue);
}
}
IsOpened = false;
}
if文を2つに分けて、「ポジションを持っている時はクローズ処理をする」ようにしています。
これでクローズ時間であれば「ポジションを持っていなくてもIsOpenedはfalse」になります。
というわけで今回の「修正後のソースコード」は以下のようになりました。
//+------------------------------------------------------------------+
//| ZeroCreatePart2.mq4 |
//| |
//| |
//+------------------------------------------------------------------+
#property copyright ""
#property link ""
#property version "1.00"
#property strict
input int Magic = 123465;
input double Lots = 0.1;
input int Slippage = 2;
input string OrdersComment = "ZeroCreate";
input int StopLoss = 200;
input int TakeProfit = 400;
input int StartHour = 9;
input int StopHour = 18;
bool IsOpened = false;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//---
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
if(Hour() == StartHour && OrdersTotal() == 0 && IsOpened == false)
{
double stopLoss = Ask - StopLoss * Point;
double takeProfit = Bid + TakeProfit * Point;
OrderSend(Symbol(),OP_BUY,Lots,Ask,Slippage,stopLoss,takeProfit,OrdersComment,Magic,0,clrBlue);
IsOpened = true;
}
if(Hour() == StopHour)
{
if(OrdersTotal() > 0)
{
for(int i=0; i<=OrdersTotal()-1; i++)
{
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES) == false) break;
OrderClose(OrderTicket(),OrderLots(),Bid,2,clrBlue);
}
}
IsOpened = false;
}
}
//+------------------------------------------------------------------+
それではコンパイル後、バックテストしてみましょう。最適化も実行します。
最適化実行

前回結果が良かった10時開始、23時終了というトレード条件にしておきます。
最適化するのはStopLossとTakeProfitで、100Point~500Pointの範囲を100Pointずつ加算します。
TakeProfitの方は200Point~1000Pointの範囲を100Pointずつ加算とします。(設定はお好きな値に変更して構いません)
それでは実行してみましょう。

PFは上がりましたが損益は減ったような・・・。
今回の改修は以上です。
次回はいよいよ「インジケーターの呼び出し」をします!
For文について
これまで「書いてあったけど説明していなかった」for文について、簡単に説明します。
for(int i=0; i<=OrdersTotal()-1; i++)
{
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES) == false) break;
OrderClose(OrderTicket(),OrderLots(),Bid,2,clrBlue);
}
forの後に()で値・式を3つ入力します。
1つ目が使用する「変数」で上記はint(整数型)でiという変数を定義、初期値を0としています。
(iという変数はfor文でよく使われる変数名です。)
2つ目に指定しているのがループの条件。
iという変数がOrderTotal(ポジション数)から1引いた値の間はループするという意味。
3つ目はインクリメント条件で1度ループする度にiに1を加算しています。
※「i++」は「i=i+1」の省略形です。
ポジション数が10あった場合、10回ループして{}の中に書かれている処理を実行します(10回実行します)
まとめ
ストップロスを入れるとフラグ処理まで入れなくてはいけなくなってしまったので、無駄に長くなってしまいました。
おかげでfor文の方が短くなってしまったのですが…大丈夫…ですかね?
「意味がわからん!」という方がいらっしゃいましたら、コメント欄にご質問ください。
さて、次回はいよいよインジケーターを組み込んでいきます。
今までは「ただ買うだけ」だったのですが、インジケーターを条件に「買い」または「売り」を入れるEAに変更します!
以上、初心者向けEA講座「自動売買システムを作ろう!ゼロから始めるEA開発Part5 ストップロスとforループ」でした。
→ ゼロから始めるEA開発Part6 移動平均の使い方
コメント