今回は、コメントでご質問があった件について回答していきたいと思います。
ZigZagのソースコードを理解するのにも良い題材なので是非ご拝読頂ければと思います。
最終的に動くMT4のEAサンプルソース(FX用)も公開しています。
はじめに
今回も、以下の記事でコメントを頂いた内容について解決していきたいと思います。
内容はこんな感じです。
損切を直近のZigZagの底に設定し、リスクリワードを1:1にする手法で利確されたとします。
すぐに利確され、まだZigZagのエントリー条件が成立しているままだと再度エントリーされてしまうのを防ぎたいです。
条件が「はじめて」揃った時だけ、エントリーしたいですがどうしたらよいでしょうか?
※利確後○○分間はエントリーしないという方法以外で!
上記の『すぐに利確され、まだZigZagのエントリー条件が成立しているままだと再度エントリーされてしまう』現象を、実際のエントリーで表してみます。
こんな感じで1つのZigZagの下げに対して5回もエントリーしちゃいますね。
※わかりやすいように5pips利確にしてみました
ではこれを、『条件が「はじめて」揃った時だけ、エントリーしたい』とした場合、
こうなります。
簡単に思いつく修正方法は、『損切り・利確後、数分間エントリーしない』といった方法になりますが、この場合数分間という内容も曖昧ですし、上の例でいえば最後の方はエントリーしちゃうかもしれないので正しい修正ではないですよね
今回は、時間の縛り以外で要件を満たしていく方法を解説していきます。
それでは作っていきましょう!
冒頭にもありましたが、ベースとなるソースは以下の記事にあるソースで解説していきたいと思います。
修正のポイント
今回肝となるのは、『損切り・利確後、数分間エントリーしない』という制限を設けずに、『ZigZagの条件が初めて揃った時だけエントリーする』といった形です。
一体どう修正すればいいのでしょうか?
これは意外と簡単です。
①ZigZagの状況を覚える
要件を満たすために、まずこのZigZagの状況下でエントリーしたかどうかを判別させます。ざっくり表すと、
この赤丸部分(エントリー直前)のZigZagの状況を覚えておき、このZigZag状況下では複数回エントリーしなければOKという形です。
では、このZigZag状況を覚えておくにはどうしたらいいでしょうか。
それは価格を覚えておきます。イメージはこんな感じ。
イメージ左側のZigZagの山と谷の頂点に赤線を引いています。これがZigZagの状況(価格帯)です。頂点の価格帯を保持する感じですね。ZigZagの状況が山と谷の価格帯が2連続pipsレベルで完全一致する事はまずないのでこうする事でZigZag状況を正確に判定できます。
なのでエントリーする際に、このZigZagの価格帯を別変数等に保持して覚えておきます。
こんな感じです。
total=OrdersTotal();
if(total ==0 && orderPtn > 0)
{
if(orderPtn == 1)
{
ea_order_stop_price = Ask - STOP_LOSS * Point;
ea_order_good_price = Ask + TP * Point;
}
else if(orderPtn == 2)
{
ea_order_stop_price = Bid + STOP_LOSS * Point;
ea_order_good_price = Bid - TP * Point;
}
//新規注文
OrderKekka = funcOrder_Send(orderPtn - 1,ea_order_stop_price,ea_order_good_price,0,0);
ZigTopBak[1]=ZigTop[1];
ZigTopBak[2]=ZigTop[2];
ZigBottomBak[1]=ZigBottom[1];
ZigBottomBak[2]=ZigBottom[2];
}
ZigTopBak[]とZigBottomBak[]をグローバル変数として新たにつくり、そこにZigTop[]とZigBottom[]の値をセットするようにしました。
※グローバル変数って何?という方へ、詳細はこちら↓
とりあえずこれでOKです。
②ZigZagの値を判定する
では次に、ZigZagの状況が変わったかどうかを判定する方法についてです。
先ほど、エントリー時のZigZagの値を覚えおくという修正をしました。この覚えておいた値と現在のZigZagの値を比較します。
例えば、この部分を見てみましょう。
直前にエントリーしていますので、この青丸のどこでエントリーしていいか?をチェックしてもZigTopBak[1]の値はZigTop[1]の値と同じとなり、エントリーしないようにします。
では次エントリーできる時はいつでしょうか?
それは、時間が進み次の山と谷が出来ると
こんな感じで先ほどのZigTop[0]の値段がZigTop[1]へ、ZigTop[1]の値段がZigTop[2]となりますよね。
こうなれば、ZigTopBak[1]の値はZigTop[1]の値と違うので別のZigZagの山が形成されたことになります。こうなればまたエントリーしてもOKという感じにしてあげましょう。
//ZigZagの高値安値を構造体に再セット
getZigZag(1);
//高値切り上げ安値切り上げでロング
if(ZigTop[0] > ZigTop[1] && ZigTop[1] > ZigTop[2] &&
ZigBottom[0] > ZigBottom[1] && ZigBottom[1] > ZigBottom[2] && BottomPoint==1 && ZigTopBak[1]!=ZigTop[1])
{
orderPtn=1;
}
//高値切り下げ安値切り下げでショート
else if(ZigTop[0] < ZigTop[1] && ZigTop[1] < ZigTop[2] &&
ZigBottom[0] < ZigBottom[1] && ZigBottom[1] < ZigBottom[2] && TopPoint==1 && ZigBottomBak[1]!=ZigBottom[1])
{
orderPtn=2;
}
else
{
orderPtn=0;
}
買い側も売り側もif文の最後に条件を1つ追加しています。ZigTop[0]とZigTop[1]が完全一致するとそもそもエントリー条件にならないので今回は簡潔に1つの山と谷だけで見ています。
こうする事で、ZigZagの状況を見極め『条件が初めて揃った時だけエントリーする』という事が実現できるわけですね。
ソースコード
では実際に修正したサンプルソースコードになります。濃い色になっている箇所が主な修正ポイントです。
※丸々コピペでコンパイルできます
//+------------------------------------------------------------------+
//| ZigZag_Sample.mq4 |
//| Copyright 2020, mef Software. |
//| https://fx-prog.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, mef Software."
#property link "https://fx-prog.com/"
#property version "1.00"
input int E_Depth = 12;
input int E_Deviation = 5;
input int E_Backstep = 3;
input int A_SPREAD = 120;
input double Lots = 0.01;
input int STOP_LOSS = 200;
input int TP = 200;
double ZigTop[5]; //ジグザグの山保存用
double ZigBottom[5]; //ジグザグの谷保存用
int TopPoint;
int BottomPoint;
double ZigTopBak[5]; //ジグザグの山保存用(backup)
double ZigBottomBak[5]; //ジグザグの谷保存用(backup)
datetime prevtime;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int init()
{
//---
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
int start()
{
int orderPtn=0; //0:何もしない 1:買い 2:売り
int total=0;
double ea_order_stop_price=0,ea_order_good_price=0; //ストップロスレート,利確レート,エントリーレート
bool OrderKekka;
//---
//新しい足ができた時だけやる
if(Time[0] != prevtime){
prevtime = Time[0];
}else{
return(0);
}
//***売買判断箇所***//
//チェック用変数の初期化
TopPoint=0;
BottomPoint=0;
//ZigZagの高値安値を構造体に再セット
getZigZag(1);
//高値切り上げ安値切り上げでロング
if(ZigTop[0] > ZigTop[1] && ZigTop[1] > ZigTop[2] &&
ZigBottom[0] > ZigBottom[1] && ZigBottom[1] > ZigBottom[2] && BottomPoint==1 && ZigTopBak[1]!=ZigTop[1])
{
orderPtn=1;
}
//高値切り下げ安値切り下げでショート
else if(ZigTop[0] < ZigTop[1] && ZigTop[1] < ZigTop[2] &&
ZigBottom[0] < ZigBottom[1] && ZigBottom[1] < ZigBottom[2] && TopPoint==1 && ZigBottomBak[1]!=ZigBottom[1])
{
orderPtn=2;
}
else
{
orderPtn=0;
}
//***売買判断箇所***//
//***決済判断箇所***//
total=OrdersTotal();
if(total ==0 && orderPtn > 0)
{
if(orderPtn == 1)
{
ea_order_stop_price = Ask - STOP_LOSS * Point;
ea_order_good_price = Ask + TP * Point;
}
else if(orderPtn == 2)
{
ea_order_stop_price = Bid + STOP_LOSS * Point;
ea_order_good_price = Bid - TP * Point;
}
//新規注文
OrderKekka = funcOrder_Send(orderPtn - 1,ea_order_stop_price,ea_order_good_price,0,0);
ZigTopBak[1]=ZigTop[1];
ZigTopBak[2]=ZigTop[2];
ZigBottomBak[1]=ZigBottom[1];
ZigBottomBak[2]=ZigBottom[2];
}
return(0);
}
//+------------------------------------------------------------------+
//|【関数】新規注文関数 |
//| |
//|【引数】 ea_order_entry_Type:売買(0:買 1:売) |
//|【引数】 ea_order_stop_price:損切値 ea_order_good_price:利確値 |
//|【引数】 orderComment:オーダーコメント ea_order_MagicNo:マジックNo |
//| |
//|【戻値】True:成功 |
//| |
//|
bool funcOrder_Send(int ea_order_entry_Type, double ea_order_stop_price, double ea_order_good_price,int orderComment,int ea_order_MagicNo)
{
int order_resend_num; // エントリー試行回数
int ea_ticket_res; // チケットNo
int errorcode; // エラーコード
double ea_order_entry_price; // エントリーレート
color order_Color;
bool kekka;
for( order_resend_num = 0; order_resend_num < 10; order_resend_num++ ) { // エントリー試行回数上限:10回
if(MarketInfo(NULL,MODE_SPREAD) < A_SPREAD ){
if(ea_order_entry_Type == OP_BUY){
ea_order_entry_price = Ask; // 現在の買値でエントリー
order_Color = clrBlue;
Print(Ask+"でロングするよ!");
}else if(ea_order_entry_Type == OP_SELL){
ea_order_entry_price = Bid; // 現在の売値でエントリー
order_Color = clrRed;
Print(Bid+"でショートするよ!");
}
// FXCMでは新規エントリー時にストップ/リミットを設定出来ない。
ea_ticket_res = OrderSend( // 新規エントリー注文
NULL, // 通貨ペア
ea_order_entry_Type, // オーダータイプ[OP_BUY / OP_SELL]
Lots , // ロット[0.01単位](FXTFは1=10Lot)
ea_order_entry_price, // オーダープライスレート
20, // スリップ上限 (int)[分解能 0.1pips]
ea_order_stop_price, // ストップレート
ea_order_good_price, // リミットレート
orderComment, // オーダーコメント
ea_order_MagicNo, // マジックナンバー(識別用)
0, // オーダーリミット時間
order_Color // オーダーアイコンカラー
);
Print("結果:"+ea_ticket_res);
if ( ea_ticket_res == -1) { // オーダーエラー
errorcode = GetLastError(); // エラーコード取得
if( errorcode != ERR_NO_ERROR) { // エラー発生
printf("エラー:"+ IntegerToString(errorcode));
}
Sleep(2000); // 1000msec待ち
RefreshRates(); // レート更新
printf("再エントリー要求回数:%d, 更新エントリーレート:%g",order_resend_num+1 ,ea_order_entry_price);
} else { // 注文約定
Print("新規注文約定。 チケットNo=",ea_ticket_res," レート:",ea_order_entry_price);
Sleep(300); // 300msec待ち(オーダー要求頻度が多過ぎるとエラーになる為)
break;
}
}else{
Print("スプレッドNG 許容スプレッド:"+A_SPREAD+"pips未満 環境スプレッド:"+MarketInfo(NULL,MODE_SPREAD)+"pips");
Sleep(2000); // 2000msec待ち
RefreshRates();
}
}
return kekka;
}
//+------------------------------------------------------------------+
//|【関数】ジグザグの値をセット
//|
//|
//|【備考】なし
//+------------------------------------------------------------------+
bool getZigZag(int i)
{
int n,m,t;
bool kekka;
m=0;
t=0;
kekka=false;
for(n=i;n<=i+200;n++)
{
//ZigZagの値を取得
double Zg=NormalizeDouble(iCustom(NULL,0,"ZigZag",E_Depth,E_Deviation,E_Backstep,0,n),5);
//ZigZagの値と最高値が同じ場合、頂点なのでZigTopにセット
if(Zg!=0 && Zg==NormalizeDouble(High[n],5))
{
if(n==1){
TopPoint=1;
}
ZigTop[m++]=Zg;
if(m>=4)break;
}
//ZigZagの値と最安値が同じ場合、底なのでZigBottomにセット
if(Zg!=0 && Zg==NormalizeDouble(Low[n],5))
{
if(n==1){
BottomPoint=1;
}
ZigBottom[t++]=Zg;
if(t>=4)break;
}
}
kekka=true;
//目視確認用コメント
Comment(
"│ ZigTOP0=", ZigTop[0], "│ ZigBTM0=", ZigBottom[0], "|\n",
"│ ZigTOP1=", ZigTop[1], "│ ZigBTM1=", ZigBottom[1], "|\n",
"│ ZigTOP2=", ZigTop[2], "│ ZigBTM2=", ZigBottom[2], "|\n",
"│ ZigTOP3=", ZigTop[3], "│ ZigBTM3=", ZigBottom[3], "|\n",
"│ TopP=", TopPoint, "│ BottomP=", BottomPoint, "|\n"
);
return(kekka);
}
関数内で宣言するローカル変数と違い、一連の処理が終わっても変数の値を保持し続けます。
ですので、必ず定義外の26行目、27行目等に変数を追加してあげましょう。
さいごに
以上、『ZigZagのEAで条件が初めて揃った時だけエントリーする方法』でした。
ソースコードを見ると特段難しいことをやっていませんが、この修正に至るためにはソースコードの内容をある程度理解できている必要があります。
プログラミング初心者やまだまだプログラミング勉強中の方には少し難しいかもしれませんが、ZigZagの理解を深めることで自然とこういった修正案が思い浮かぶようになってきますので是非頑張ってください。
※ EAのサンプルソースを一覧表にまとめました
※オンラインレッスンやってます
コメント
リクエストに応えていただきまして、誠にありがとうございます。
過去の値を保持するという考え方、今後も応用していきたいです。
ありがとうございました(^^)/
いろいろ、試行錯誤していたら疑問点があったので詳細をTwitterで質問させていただきます(__)m