今回は、MT4のEAサンプルソース(FX用)を公開します。
トレードに使うテクニカル分析はZigZagでマルチタイムフレーム化を行い、順張りエントリーをしています。
はじめに
以前からコメントやお問合せで、ZigZagEAのマルチタイムフレーム化が要望としてありました。今回はご要望にお応えして出来るだけシンプルにマルチタイムフレーム対応してみました。
EA自体は5分足で動かしており、上位足の15分足も高値安値をチェックするような感じです。
※ベースとなったZigZagサンプルEAの記事はこちら
実際の動き
修正を加えた事で、実際に5分足の高値安値と15分足の高値安値を見ている事を確認しました。
以下は5分足のチャートです。
次に、以下は15分足のチャートです。
5分足でも高値安値の切り上げ、15分足でも高値安値切り上げしている部分(青〇の部分)でロングエントリーできている事がわかります。
EAレポート結果
2012年~2021年の期間の5分足でバックテスト確認してみました。まずまずの結果になりました!
今回はプロパティの値等特に変えず、最適化もしていないのでこれよりいい結果になるプロパティ値もあると思います。
ダウンロード
上記のEAファイルになります。
“ZigZagマルチEA” をダウンロード 20_ZigMulti.ex4 – 3968 回のダウンロード – 21.17 KBソースコード
以下サンプルソースコードになります。
※丸々コピペでコンパイルでき
//+------------------------------------------------------------------+
//| 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 "2.00"
input int E_Depth = 12;
input int E_Deviation = 5;
input int E_Backstep = 3;
input int A_SPREAD = 10;
input double Lots = 0.01;
input int STOP_LOSS = 200;
input int TP = 200;
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;
double ZigTop_M15[5]; //ジグザグの山保存用(15分足用)
double ZigBottom_M15[5]; //ジグザグの谷保存用(15分足用)
int TopPoint_M15=0,BottomPoint_M15=0;
double ZigTop_M5[5]; //ジグザグの山保存用(5分足用)
double ZigBottom_M5[5]; //ジグザグの谷保存用(5分足用)
int TopPoint_M5=0,BottomPoint_M5=0;
//---
//新しい足ができた時だけやる
if(Time[0] != prevtime){
prevtime = Time[0];
}else{
return(0);
}
//***売買判断箇所***//
//ZigZagの高値安値を構造体に再セット
getZigZag(ZigTop_M15,ZigBottom_M15,TopPoint_M15,BottomPoint_M15,PERIOD_M15,1); //15分足のZigZag値を取得
getZigZag(ZigTop_M5,ZigBottom_M5,TopPoint_M5,BottomPoint_M5,PERIOD_M5,1); //5分足のZigZag値を取得
//高値切り上げ安値切り上げでロング(5分足)
if(ZigTop_M5[0] > ZigTop_M5[1] && ZigTop_M5[1] > ZigTop_M5[2] &&
ZigBottom_M5[0] > ZigBottom_M5[1] && ZigBottom_M5[1] > ZigBottom_M5[2] && BottomPoint_M5==1)
{
//高値切り上げ安値切り上げでロング(15分足)
if(ZigTop_M15[0] > ZigTop_M15[1] && ZigTop_M15[1] > ZigTop_M15[2] &&
ZigBottom_M15[0] > ZigBottom_M15[1])
{
orderPtn=1;
}
}
//高値切り下げ安値切り下げでショート(5分足)
else if(ZigTop_M5[0] < ZigTop_M5[1] && ZigTop_M5[1] < ZigTop_M5[2] &&
ZigBottom_M5[0] < ZigBottom_M5[1] && ZigBottom_M5[1] < ZigBottom_M5[2] && TopPoint_M5==1)
{
//高値切り下げ安値切り下げでショート(15分足)
if(ZigTop_M15[0] < ZigTop_M15[1] && ZigTop_M15[1] < ZigTop_M15[2] &&
ZigBottom_M15[0] < ZigBottom_M15[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);
}
//目視確認用コメント
Comment(
"│ ZigTOP0=", ZigTop_M15[0], "│ ZigBTM0=", ZigBottom_M15[0], "|\n",
"│ ZigTOP1=", ZigTop_M15[1], "│ ZigBTM1=", ZigBottom_M15[1], "|\n",
"│ ZigTOP2=", ZigTop_M15[2], "│ ZigBTM2=", ZigBottom_M15[2], "|\n",
"│ ZigTOP3=", ZigTop_M15[3], "│ ZigBTM3=", ZigBottom_M15[3], "|\n",
"│ TopP=", TopPoint_M15, "│ BottomP=", BottomPoint_M15, "|\n",
"│ ZigTOP0=", ZigTop_M5[0], "│ ZigBTM0=", ZigBottom_M5[0], "|\n",
"│ ZigTOP1=", ZigTop_M5[1], "│ ZigBTM1=", ZigBottom_M5[1], "|\n",
"│ ZigTOP2=", ZigTop_M5[2], "│ ZigBTM2=", ZigBottom_M5[2], "|\n",
"│ ZigTOP3=", ZigTop_M5[3], "│ ZigBTM3=", ZigBottom_M5[3], "|\n",
"│ TopP=", TopPoint_M5, "│ BottomP=", BottomPoint_M5, "|\n"
);
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;
}else if(ea_order_entry_Type == OP_SELL){
ea_order_entry_price = Bid; // 現在の売値でエントリー
order_Color = clrRed;
}
// 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 // オーダーアイコンカラー
);
}
if ( ea_ticket_res == -1) { // オーダーエラー
errorcode = GetLastError(); // エラーコード取得
if( errorcode != ERR_NO_ERROR) { // エラー発生
printf("エラー");
}
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;
}
}
return kekka;
}
//+------------------------------------------------------------------+
//|【関数】ジグザグの値をセット
//|【引数(参照)】ZigTop[],ZigBottom[],TopPoint,BottomPoint
//|【引数】 時間足(PERIOD_M15,PERIOD_M5,PERIOD_D1等)
//|【引数】 バーシフト
//|
//|【備考】なし
//+------------------------------------------------------------------+
bool getZigZag(double &bufT[5],double &bufB[5],int &bufTP,int &bufBP,int t_Period,int i)
{
int n=0,m=0,t=0;
bool kekka=false;
for(n=i;n<=i+200;n++)
{
//ZigZagの値を取得
double Zg=NormalizeDouble(iCustom(NULL,t_Period,"ZigZag",E_Depth,E_Deviation,E_Backstep,0,n),5);
//ZigZagの値と最高値が同じ場合、頂点なのでZigTopにセット
if(Zg!=0 && Zg==NormalizeDouble(iHigh(NULL,t_Period,n),5))
{
if(n==1){
bufTP=1;
}
bufT[m++]=Zg;
if(m>=4)break;
}
//ZigZagの値と最安値が同じ場合、底なのでZigBottomにセット
if(Zg!=0 && Zg==NormalizeDouble(iLow(NULL,t_Period,n),5))
{
if(n==1){
bufBP=1;
}
bufB[t++]=Zg;
if(t>=4)break;
}
}
kekka=true;
return(kekka);
}
関数の変更も含めた全体のサンプルソースコードです。
主な変更点
getZigZag()関数の変更
まず、ZigZagの値を複数の時間足(5分足と15分足)で取得したかったので、ZigZagの値を取得する関数『getZigZag()関数』を修正しました。ほぼ、この関数の修正だけで対応完了した感じです。
//+------------------------------------------------------------------+
//|【関数】ジグザグの値をセット
//|【引数(参照)】ZigTop[],ZigBottom[],TopPoint,BottomPoint
//|【引数】 時間足(PERIOD_M15,PERIOD_M5,PERIOD_D1等)
//|【引数】 バーシフト
//|
//|【備考】なし
//+------------------------------------------------------------------+
bool getZigZag(double &bufT[5],double &bufB[5],int &bufTP,int &bufBP,int t_Period,int i)
{
int n=0,m=0,t=0;
bool kekka=false;
for(n=i;n<=i+200;n++)
{
//ZigZagの値を取得
double Zg=NormalizeDouble(iCustom(NULL,t_Period,"ZigZag",E_Depth,E_Deviation,E_Backstep,0,n),5);
//ZigZagの値と最高値が同じ場合、頂点なのでZigTopにセット
if(Zg!=0 && Zg==NormalizeDouble(iHigh(NULL,t_Period,n),5))
{
if(n==1){
bufTP=1;
}
bufT[m++]=Zg;
if(m>=4)break;
}
//ZigZagの値と最安値が同じ場合、底なのでZigBottomにセット
if(Zg!=0 && Zg==NormalizeDouble(iLow(NULL,t_Period,n),5))
{
if(n==1){
bufBP=1;
}
bufB[t++]=Zg;
if(t>=4)break;
}
}
kekka=true;
return(kekka);
}
以前は決め打ちの15分足で配列にZigZagの高値と安値をセットしていただけでしたが、この関数の呼び出し元から時間足の種類(int t_Period)を渡すようにしたことで全ての時間足のZigZagの値を取得できるように修正しています。
あと、ZigZagの配列等も参照渡し(呼び出し元の引数に反映)して 『getZigZag()関数』 内で元の配列にセットしなおす等の処理を省いています。
メイン処理の変更
メイン処理の主な変更点です。
①配列を追加
まず、15分足用の配列の他に5分足の配列を追加しました。さらに1時間足も見たいってなったら同様に配列を追加してください。
あと、ベースとなったZigZagEAはメイン処理ではない先頭部でこの配列を宣言してグローバル配列みたいになってましたが、今回はローカル配列に落ち着きました。
②呼び出し部分を変更
まず、15分足用ZigZagの値を取得する用と、 5分足用ZigZagの値を取得するため2つ書いています 。 さらに、引数も前述していた通り追加された分を渡すようにしています。
③チェック処理を変更
5分足のZigZagの値で高値の切り上げ・切り下げ等をチェックしつつ、15分足のZigZagの値でも高値の切り上げ・切り下げ等をチェックするように変更しています。
変更点は以上です。
さいごに
以上、「ZigZagEAをマルチタイムフレーム化する」でした。
ZigZagの値配列に各時間足の情報を持たせるのは大変ですが、一度日足ぐらいまで見たものを作ってみたいと思います。
※ EAのサンプルソースを一覧表にまとめました
※オンラインレッスンやってます
コメント
いつも参考にさせていただいております。
ZigZagを複数の足において取得しようとすると必ずarray out of rangeとなってしまい、
エラーについて調べてもイマイチどこが悪いのか気づけません。具体的には
15分足と1H足について取得しようとしたさい、1H足はエラーとなり取得できない形です。
もし原因など思いつきましたらご教授おねがいできませんでしょうか?
返信が遅れてしまい申し訳ありません。
サンプルソースの通り5分足と15分足ならエラーにならないけど、15分足と1時間足だとエラーになるという事でしょうか?
考えられることとしては、1時間足のテストデータが足りない(テスト開始時より200本前にさかのぼれない)かな?と思いますがテスト期間を最近だけにしても同じような問題がおきますでしょうか?
お世話になっております。
グレープドラゴンです。
現在、こちらの記事を教材に一部コードを変えたり、追加したりして少しずつ学習を進めています。
質問ですが、zigzagの線の「向き」を判断することは可能でしょうか。
お世話になっております。
返信が遅れました。
ZigZagの線の「向き」は多分上向きか下向きだと思うのですが、判断は出来ます。
少し難しくはなりますが、ZigTop_M15[0]とZigBotoom_M15[0]に値をセットする際にどのバーシフトで値をセットしているかを見るようにして、ZigTop_M15[0]の方が0に近い場合は上向き、ZigBotoom_M15[0]の方が0に近い場合は下向きという感じになります。
//ZigZagの値と最高値が同じ場合、頂点なのでZigTopにセット
if(Zg!=0 && Zg==NormalizeDouble(iHigh(NULL,t_Period,n),5))
{
if(n==1){
bufTP=1;
}
ZigTopIdx[m]=n;
bufT[m++]=Zg;
if(m>=4)break;
}
//ZigZagの値と最安値が同じ場合、底なのでZigBottomにセット
if(Zg!=0 && Zg==NormalizeDouble(iLow(NULL,t_Period,n),5))
{
if(n==1){
bufBP=1;
}
ZigBottomIdx[t]=n;
bufB[t++]=Zg;
if(t>=4)break;
}
getZigZag()関数のところでこんな感じでバーシフトをセットするようにすると可能です。
ご丁寧に返信ありがとうございます。
初学者にとっては、少し難易度高めでした。。。(笑)
しかし、継続して学び続けます。
ありがとうございます。
お聞きしたいことがありまして、お問い合わせの欄からメッセージを送ってみたのですが送信ボタンから次に進めずにメッセージが届いているのか不安です。。。!
念のためXのDMでも連絡しようとしたところ、DMが解放されておらず断念しました。
もしよろしければ、他の連絡手段はございませんか?
すみません、お問合せの件全然気づいておりませんでした!
ブログを弄っていた際うまく送信できなくなってしまっていたようです。
送信できるようになったと思いますので、お手数ですが再度送信して頂いてもよろしいでしょうか。
XのDMも解放させて頂きました。(X初心者で申し訳ないです)
ご報告いただき有難うございます。
ご対応ありがとうございます!
お問い合わせの件、いまだに不具合で文字制限&送信ボタン押しても次に進まないの不具合があったのでXのDMにメッセージを送信しました!
お時間のある時に、ご確認いただけますと幸いです。