EA Sample_NoTrade_ReadFile.mq4の解説
今回のテーマは、
で紹介したサンプルEAファイルの説明です。
→Sample_NoTrade_ReadFile.mq4とNoTradeTime.csvをダウンロード
Sample_NoTrade_ReadFile.mq4の解説
まずは、『NoTradeTime.csv』ファイルを見てみましょう。
2010.02.18 01:30
2010.02.18 03:00
2010.02.18 04:30
:
:
というように、『yyyy.mm.dd hh.mm』の日付フォーマットで、
トレードしない時間が入力してあります。
『Sample_NoTrade_ReadFile』は、
この時間データを読み取り、その前後の何分間は、
No Trade Time Zone
とチャート上に表示させるようになっています。
早速、メタエディタでSample_NoTrade_ReadFile.mq4の中身を見てみましょう^^
パラメータ
extern int NoTradeBeforeMin = 20; extern int NoTradeAfterMin = 30; extern string NoTradeFileName = "NoTradeTime.csv";
NoTradeBeforeMin ・・・ 指定時間の何分前からかを指定 NoTradeAfterMin ・・・ 指定時間の何分後までかを指定 NoTradeFileName ・・・ データのファイル名を指定
です。
そして、記憶用変数(グローバル変数)で
int BeforSecond; int AfterSecond;
パラメータ『NoTradeBeforeMin』と『NoTradeAfterMin』の
『分指定』を『秒指定』に変換したものを格納しておく変数です。
その下に、どの関数からも共通で使える変数(グローバル変数)で
datetime LastNoTradeData; datetime NextNoTradeData;
『LastNoTradeData』は、
csvファイルで指定した時間データのうち、
過去のもので現在の時間に一番近い時間データを格納しておく変数です。
『NextNoTradeData』は、
csvファイルで指定した時間データのうち、
未来のもので現在の時間に一番近い時間データを格納しておく変数です。
その下に、
bool NoData; datetime Now;
『NoData』は、
もうcsvファイルの指定時間をチェックする必要がなくなったら、
NoDataをtrueにして、ファイルを開かないようにするための変数です。
『Now』は、
現在の時間データを代入する変数です。
次は、EA開始時に1度だけ実行されるinit()関数です。
int init()
{
BeforSecond = NoTradeBeforeMin * 60;
AfterSecond = NoTradeAfterMin * 60;
LastNoTradeData = -1;
NextNoTradeData = -1;
NoData = false;
return(0);
}
『BeforSecond』『AfterSecond』に、
パラメータ『NoTradeBeforeMin』『NoTradeAfterMin』の、
秒に換算したものを代入します。
今後、時間の比較を、秒単位で進行する時間データで行うためです。
パラメータ『NoTradeBeforeMin』『NoTradeAfterMin』の役割は、
ここで終了し、もう使いません^^;
『LastNoTradeData』『NextNoTradeData』に、マイナス値を入します。
これらの変数にマイナス値が入っている場合は、
それに対応するデータが空っぽというルールにするためです。
『NoData』にfalseを代入しておきます。
ここで、グローバル変数の宣言部分に初期値を代入しておくのと、
init()関数内で値を代入するのでは、若干動作が違います。
グローバル変数は、記憶力を持った変数で、
EAをチャートから外したときに、はじめて格納されている値がなくなります。
もし、仮に『NoData』を、
グローバル変数宣言
bool NoData = false;
として、init()関数ではfalseを代入しなかった場合、
EA稼動中、NoDataがtrueになったあと、
csvファイルの時間データを更新して、
再読込させようと、EAのパラメータ等を入力するプロパティウィンドウを開き、
OKボタンで再スタートさせたとします。
しかし、EAは前回稼動していたグローバル変数の値が残っていますので、
NoDataがtrueのままで、csvファイルを読まないようになってしまいます。
そうならないためにも、init()関数の中で
稼動開始時にグローバル変数に入っていて欲しい値を代入しておきます。
次に、ティック毎に実行されるstart()関数です。
int start()
{
Now = TimeCurrent();
一番最初に、『Now』に現在の時間データ(時刻1秒ごとに付けられている整数)を代入しておきます。
次に、
if(Now >= NextNoTradeData && !NoData) SetNoTradeData();
として、
もし現在の時間が、前回記憶していた『未来の直近の時間データ』に到達し、 かつ、まだファイルをチェックする必要がある場合は、 SetNoTradeData()関数を実行する。
という意味です。
SetNoTradeData()関数では、時間指定のcsvファイルを開いて、
『LastNoTradeData』『NextNoTradeData』『NoData』に
それぞれの値を代入する関数として、start()関数の下に作ってあります。
後で、見てみましょう。
次に、
static datetime PrintData;
if(NextNoTradeData > 0 && NextNoTradeData != PrintData)
{
Print("No Trade: ", TimeToStr(NextNoTradeData - BeforSecond,
TIME_DATE | TIME_MINUTES), " - ",
TimeToStr(NextNoTradeData,
TIME_DATE | TIME_MINUTES), " - ",
TimeToStr(NextNoTradeData+AfterSecond,
TIME_DATE | TIME_MINUTES));
PrintData = NextNoTradeData;
}
として、
記憶用変数『PrintData』を宣言。 もし、『NextNoTradeData』に指定時間が入っていて、その値が『PrintData』と違う場合、 『No Trade: 取引停止開始時刻 - 取引停止指定時刻 - 取引停止終了時刻』を出力。 『PrintData』に『NextNoTradeData』の値を代入。
という意味です。
新しく『未来の直近の時間データ』になった場合は、
1度、操作履歴にその時刻と停止開始・終了時刻を出力するということです。
次に、
if(IsNoTradeTime()) return(0);
IsNoTradeTime()関数がtrueを返したら、ここで実行終了。
という意味です。
IsNoTradeTime()関数は、それぞれの時間データから
現在取引停止の時間かどうかを判断し、停止の時間の場合、
チャート上に『No Trade Time Zone』と表示させ、trueを返します。
そうでない場合は、falseを返します。後で、見てみましょう。
そして、return(0)で実行終了とならなかった場合は、次の
Comment("Trade Time Zone");
チャート上に『Trade Time Zone』と表示させます。
これで、start()関数は終了ですね。
つぎは、SetNoTradeData()関数とIsNoTradeTime()を見ていきます^0^
つづく^^
タグ
2010年10月 1日|
カテゴリー:EAの作成方法
EAの休止時間をcsvファイルで指定する方法
今回のテーマは、csvファイル等で指定した時間の前後は
取引をしないようにする方法です。
サンプルファイルはこちら→Sample_NoTradeTime.zip
2つのmq4ファイルと、2つのcsvファイルがありますが、
2つの対になっていて、それぞれやり方が違っています^^
どのように違うかは、
・・・^^;
また、次の機会に書きますm(_ _;)m
通常稼動で使う場合は、csvファイルをMT4→experts→filesフォルダに保存してください。
バックテストで使う場合は、MT4→tester→filesフォルダに保存してください。
でわ、このへんで^^
タグ
複数のシグナル・複数のフィルタを搭載し選択できるEA
今回のテーマは、EAの中で複数のシグナル・複数のフィルタを搭載し、
それを使うかどうかパラメータで選択できるようにする方法です。
サンプルファイルはこちら→Sample_SwitchSignal.zip
(↑'10.01.22 午前 一部修正)
Start()関数の中身は上から、
- シグナル計算2つ
- フィルタ計算3つ
- エントリシグナル計算
- エグジットシグナル計算
- ポジション確認
- エグジット処理
- エントリ処理
という感じです。
シグナルが2種類、フィルタが3種類あり、
それぞれどれを使うか[Use~]というパラメータで選択できるようにしています。
シグナル計算やフィルタ計算はそれぞれ[~sign]や[~filter]といった
個別の変数に計算結果を代入します。
そして、サンプルファイル132行目~140行目を見てください。
int sign;
if((!UseMaCrossSignal || MaCrossSign==1) &&
(!UseMacdCrossSignal || MacdSign==1) &&
(!UseMaTrendFilter || MaFilter==1) &&
(!UseAdxFilter || AdxFilter) &&
(!UseAtrFilter || AtrFilter)) sign=1;
if((!UseMaCrossSignal || MaCrossSign==-1) &&
(!UseMacdCrossSignal || MacdSign==-1) &&
(!UseMaTrendFilter || MaFilter==-1) &&
(!UseAdxFilter || AdxFilter) &&
(!UseAtrFilter || AtrFilter)) sign=-1;
エントリ用のシグナルを代入する整数変数 sign を宣言。
もし、
UseMaCrossSignal が false または、MaCrossSign が1で
UseMacdCrossSignal が false または、MacdSign が1で
UseMaTrendFilter が false または、MaFilter が1で
UseAdxFilter が false または、AdxFilter が true で
UseAtrFilter が false または、AtrFilter が true の場合、
signに1を代入
もし、
UseMaCrossSignal が false ・・・(省略)
・・・の場合、
signに-1を代入
といった感じにします。
これで、選択されたシグナルやフィルタのみを使ったシグナルが
sign に入ります^^
フィルタのシグナル用の変数でtrue/falseのbool型の場合は、
外でif文で書いてもいいですね^^
その下のエグジット用のシグナルの条件では
サンプルではフィルタ用の条件は含めてませんが、
書き方としては、同じ感じですね^^
そのほかで、ちょっと分かりづらいところがありますが^^;
コメントで質問してくださいね^^;
でわ、このへんで^^
タグ
VQをEA内で計算する方法
のやり方でいきますと、
extern bool Crash = false; extern int TimeFrame = 0; extern int Length = 5; extern int Method = 3; extern int Smoothing = 1; extern int Filter = 5; extern int SignShift = 1;
として、VQからコピペ&SignShiftというパラメータを追加。
そして、Start()関数内で
int sign; sign = iCustom(NULL, TimeFrame, "VQ", Crash, 0, Length, Method, Smoothing, Filter, 6, SignShift );
とすれば、買い状態の時は、sign が1、売り状態の時は、sign が-1となり、あとは条件をいろいろと加えるだけですね^^
でも、VQ自体にいろいろな機能が付いているため、EA内で計算したほうが稼動がスムーズです。
VQを内部で計算したEAのコード
extern string Entry_Setting = "---------- Entry Setting";
extern double Lots = 0.1;
extern int StopLoss = 50;
extern int TakeProfit = 100;
extern int Slippage = 3;
extern int MagicNumber = 123456;
extern string Indicator_Setting = "---------- Indicator Setting";
extern int TimeFrame = 0;
extern int Length = 5;
extern int Method = 3;
extern int Smoothing = 1;
extern int Filter = 5;
extern int EntryShift = 1;
extern int ExitShift = 0;
//記憶用共通変数
int VQsign[2]; //VQのシグナル
int VQsignbar[2]; //VQのシグナルが出た場所(足の番号)
int bar; //エントリした時の足の数(足の番号)
int init()
{
//----
ArrayInitialize(VQsign,0);
ArrayInitialize(VQsignbar,0);
bar=0;
//----
return(0);
}
int start()
{
//----
//VQ sign計算************
FuncVQ(0);
FuncVQ(1);
//オーダーに関する部分******
//現在のポジションの情報を取得
int pos=-1;
for(int i=OrdersTotal()-1;i>=0;i--)
{
OrderSelect(i,SELECT_BY_POS);
if(OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber) pos=i;
}
/*
この時点でポジションなしの場合 pos は -1
ポジションありの場合 pos は、そのポジションのインデックスの値
となります。
*/
//ポジション有りの場合
if(pos>=0)
{
//そのポジションを選択し情報を取得できるようにする
OrderSelect(pos,SELECT_BY_POS);
if((OrderType()==OP_BUY && VQsign[ExitShift]==-1) || (OrderType()==OP_SELL && VQsign[ExitShift]==1))
OrderClose(OrderTicket(),OrderLots(),OrderClosePrice(),Slippage,Yellow);
}
int ticket=0; //注文番号を代入する変数
double sl,tp; //ストップとリミットを代入する変数
//まだエントリしていない足で ポジションなしの場合
if(bar!=iBars(NULL,TimeFrame) && pos<0)
{
//エントリ条件を計算
int sign;
if(VQsignbar[EntryShift]==iBars(NULL,TimeFrame)-EntryShift)
{
if(VQsign[EntryShift]==1) sign=1;
if(VQsign[EntryShift]==-1) sign=-1;
}
//sign が 1 の場合
if(sign==1)
{
sl=Ask-StopLoss*Point;
if(StopLoss<=0) sl=0;
tp=Ask+TakeProfit*Point;
if(TakeProfit<=0) tp=0;
ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,Slippage,sl,tp,NULL,MagicNumber,0,Blue);
}
//sign が -1 の場合
if(sign==-1)
{
sl=Bid+StopLoss*Point;
if(StopLoss<=0) sl=0;
tp=Bid-TakeProfit*Point;
if(TakeProfit<=0) tp=0;
ticket=OrderSend(Symbol(),OP_SELL,Lots,Bid,Slippage,sl,tp,NULL,MagicNumber,0,Red);
}
//エントリした時の足の数を代入
if(ticket>0) bar=iBars(NULL,TimeFrame);
}
//----
return(0);
}
//+------------------------------------------------------------------+
//VQ keisan
void FuncVQ(int VQshift)
{
double MH1,ML1,MO1,MC1,MCS1,VQ1,SumVQ1,SumVQ2;
HideTestIndicators(true);
if(MathAbs(VQsign[VQshift])!=1)
{
for(int j=0;j 0) if (MathAbs(SumVQ1 - SumVQ2) < Filter * Point) SumVQ1 = SumVQ2;
if(SumVQ1 > SumVQ2)
{
if(VQsign[VQshift]==-1) break;
VQsign[VQshift]=1;
VQsignbar[VQshift]=iBars(NULL,TimeFrame)-VQshift-j;
}
if(SumVQ1 < SumVQ2)
{
if(VQsign[VQshift]==1) break;
VQsign[VQshift]=-1;
VQsignbar[VQshift]=iBars(NULL,TimeFrame)-VQshift-j;
}
}
}
MH1 = iMA(NULL,TimeFrame,Length,0,Method,PRICE_HIGH,VQshift);
ML1 = iMA(NULL,TimeFrame,Length,0,Method,PRICE_LOW,VQshift);
MO1 = iMA(NULL,TimeFrame,Length,0,Method,PRICE_OPEN,VQshift);
MC1 = iMA(NULL,TimeFrame,Length,0,Method,PRICE_CLOSE,VQshift);
MCS1 = iMA(NULL,TimeFrame,Length,0,Method,PRICE_CLOSE,VQshift + Smoothing);
if(MathMax(MH1 - ML1,MathMax(MH1 - MCS1,MCS1 - ML1))==0 ||(MH1 - ML1)==0) return(0);
VQ1 = MathAbs(((MC1 - MCS1) / MathMax(MH1 - ML1,MathMax(MH1 - MCS1,MCS1 - ML1)) + (MC1 - MO1) / (MH1 - ML1)) * 0.5) * ((MC1 - MCS1 + (MC1 - MO1)) * 0.5);
SumVQ2 = iClose(NULL,TimeFrame,2);
SumVQ1 = SumVQ2 + VQ1;
if (Filter > 0) if (MathAbs(SumVQ1 - SumVQ2) < Filter * Point) SumVQ1 = SumVQ2;
if(SumVQ1 > SumVQ2 && VQsign[VQshift]==-1)
{
VQsign[VQshift]=1;
VQsignbar[VQshift]=iBars(NULL,TimeFrame)-VQshift;
}
if(SumVQ1 < SumVQ2 && VQsign[VQshift]==1)
{
VQsign[VQshift]=-1;
VQsignbar[VQshift]=iBars(NULL,TimeFrame)-VQshift;
}
if(VQshift==0 && SumVQ1 == SumVQ2)
{
VQsign[0]=VQsign[1];
VQsignbar[0]=VQsignbar[1];
}
HideTestIndicators(false);
}
今回は詳しい説明は、省略です^^;
ソースファイルもアップしますので、いろいろと改良してみてくださいね!
でわ、今回はこのへんでm(..)m
VQの関連記事
カスタムインジケータをEAにする方法の関連記事
タグ
メール機能を追加する方法
今回は、メール機能を追加する方法の基礎の部分です^^
その前に、MT4のツール→オプション→E-メールで
有効にするにチェックし、各項目に必要事項を入力してください。
メールを送信するには、
SendMail( 題名, 本文) ;
というようにSendMail関数を書いてあげましょう。
今回は、練習として
新たな足がでたら、メールする。
メールは3回まで。
題名を【Test】
本文を【通貨ペア 足の番号】
というようにしましょうか。
でわ、早速
extern int MailCount=3;
int bar,cnt;
として、
メール発信する回数をパラメータ用整数変数 MailCount として
初期値を3としてあげましょう。
そして、記憶用整数変数 bar と cnt として、
現在の足の番号、メール送信した回数を代入するため宣言します。
次に start() 関数の{}内に
if(Bars>bar && MailCount>cnt)
{
SendMail("Test",Symbol()+" "+Bars);
bar=Bars;
cnt++;
}
としてあげましょう。
これは、
もし、足の番号がbarより大きくて、cntがMailCount未満の場合
{
題名 Test
本文 通貨ペア名 足の番号
のメールを送信。
barに現在の足の番号を代入。
cntに1を足す。
}
といった感じの意味です。
これで、足が変わるたびにメールを送信し、MailCountで設定した回数に達したらメール送信は終わります。
こんな感じで、条件が揃ったときだけメールを送るというコードを
インディケータなどに組み込むこともできますね^^
いろいろ試してみてくださいね!
でわこのへんで^^
カスタムインディケータをEAにする方法3
下記の前回の記事と前々回の記事・・・
の続きです!
まずは、ファイルダウンロード
→BBands_Stops_EA.zip(mq4ファイルのダウンロード)
- EAの全体構成を決める
- EAのパラメータを作る
- 条件分けをする
といった感じにやっていきましょう!
1.EAの全体構成を決める
まず、ドテンを実現するためにはシグナルでクローズするというコードが必要ですね。
ドテンするかしないかの選択ができるように、そのパラメータもつくりましょう。
ドテンする場合で、シグナルが出た瞬間の取引の場合、コストが大きくなってしまいますので、
「1度エントリしたら次の足まで待って、シグナルがダマシだった場合は
クローズして次のシグナルまで待つ」
といった取引をするようにしましょうか。
2.EAのパラメータを作る
EAを使うときに、選択できるようにするものをパラメータ化していきましょう!
エントリの時のパラメータとして
extern double Lots = 0.1; extern int StopLoss = 0; extern int TakeProfit = 0; extern int Slippage = 1;
ロット数、ストップ値、リミット値、スリッページ
インディケータの使い方として
extern int SignShift = 1; extern bool ClosebySign = true;
何本前の足のシグナルか、シグナルでクローズするか
EAのマジックナンバーもパラメータにしましょうか^^;
extern int MagicNumber = 883102;
EAでエントリしたときのコメントをパラメータにしたいときは、
string EA_Comment = "EA";
の前にexternをつけてください。
また、他のインディケータを使うときに改造しやすいように
//Other Set string IndicatorName ="BBands_Stops_mtf"; int IndicatorUpIndex = 2; int IndicatorDnIndex = 3; int NoSignValue = -1;
として、インディケータ名、シグナルのインデックス、シグナルが出ていないときの値を
変数に代えときましょうか。
シグナル部分は
double upsign=iCustom(NULL,TimeFrame,IndicatorName,0,Length,Deviation, IndicatorUpIndex,SignShift); double dnsign=iCustom(NULL,TimeFrame,IndicatorName,0,Length,Deviation, IndicatorDnIndex,SignShift); int sign; if(upsign!=NoSignValue) sign=1; if(dnsign!=NoSignValue) sign=-1;
といった形になるかと思います。
3.条件分けをする
条件分けは、
- ポジションがあるかないか
- シグナルがどういう状態か
- エントリした足から足が新たにできているか
などなど・・・大まかな条件から、枝分かれするイメージで作っていきましょう。
まず、ポジションがあるか無いかを調べるために、
int pos=-1;
for(int i=OrdersTotal()-1;i>=0;i--)
{
OrderSelect(i,SELECT_BY_POS);
if(OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber) pos=i;
}
として、
ポジションが無かったら変数posは-1
ポジションがあれば、posはそのポジションのインデックスの値
とします。
そして、
if(pos>=0)
{
}
のカッコ内にポジションがあるときに実行してもらうコード
if(pos<0)
{
}
のカッコ内にポジションが無いときに実行してもらうコード
を書いていきましょう。
まず、ポジションが無いときに実行してもらうコードを書いていきましょうか。
//MM double lots=Lots;
として、後でマネーマネジメントの機能追加をしやすいように書いておきましょう^^
if(bar!=iBars(NULL,TimeFrame))
{
として、エントリした足(TimeFrameで指定したチャートのバー番号)では、実行しないようにします。
ここで出てくる bar は、エントリしたときのバー番号を記憶してもらう整数変数ですので、
パラメータの下あたりに、
int bar;
として、記憶用整数変数 bar を宣言しておきましょう!
そして、
if(sign==1)
{
if(TakeProfit>0) double tp=Ask+TakeProfit*Point;
else tp=0;
if(StopLoss>0) double sl=Ask-StopLoss*Point;
else sl=0;
int ticket=OrderSend(Symbol(),OP_BUY,lots,Ask,Slippage,sl,tp,EA_Comment,MagicNumber,
0,Blue);
if(ticket>0) bar=iBars(NULL,TimeFrame);
}
として、sign が1のときに実行してもらう内容を書いてあげましょう。
内容は、
もし、TakeProfitが0より大きい場合、小数変数tpは買値+TakeProfitピプス。
そうでない場合、tpは0。
もし、StopLossが0より大きい場合、小数変数slは買値-StopLossピプス。
そうでない場合、slは0。
指定した内容で買い注文をし、整数変数ticketに注文番号を代入。
もし、ticketが0より大きい場合、 bar にTimeFrameで指定したチャートのバー番号を代入。
という感じです。
ここで、TakeProfitやStopLossが0の場合、ストップやリミットを設定しないで注文をするといったことが出来るようになりました。
最近では、ストップやリミットが小さいとエントリを受け付けないブローカーが増えてきましたので、
MarketInfo(Symbol(),MODE_STOPLEVEL)と
MarketInfo(Symbol(),MODE_SPREAD)
ブローカーのストップ・リミット制限と
スプレッド
を使って条件わけをするとよいかと思います。
今回は省略します^^;
signが-1のときも同様に書いてあげましょう。
ここでの注意は、変数 tp,sl,ticket はすでに宣言してありますので、
変数宣言の必要がありません。
これでポジションなしの時のコードが完成しました!
次に、ポジションがあるときに実行してもらうコードを書いていきましょう!
OrderSelect(pos,SELECT_BY_POS);
として、ポジションの情報を取得できるようにポジションの選択をしておきます。
if(ClosebySign==true && bar!=iBars(NULL,TimeFrame))
{
として、ClosebySignがtrueでバー番号がエントリした足のバー番号ではないときだけ、実行してもらうようにします。
この条件は、ポジションの分岐とあわせて、
if(pos>=0 && ClosebySign==true && bar!=iBars(NULL,TimeFrame))
{
次に
if( (OrderType()==OP_BUY && sign==-1) || (OrderType()==OP_SELL && sign==1) )
{
OrderClose(OrderTicket(),OrderLots(),OrderClosePrice(),Slippage,Yellow);
pos=-1;
}
として、買いポジションでsignが-1、または、売りポジションでsignが1のときクローズ
posに-1を代入とします。
ここで、posに-1を代入してあげると、下に書いたポジション無しの場合のコードも実行してくれるといった感じです。
そして、最後に大きな課題
「1度エントリしたら次の足まで待って、シグナルがダマシだった場合はクローズして、次のシグナルまで待つ」
を書きましょう。
if(SignShift==0 && bar==iBars(NULL,TimeFrame)-1)
{
double upsign1=iCustom(NULL,TimeFrame,IndicatorName,0,Length,Deviation,
IndicatorUpIndex,1);
double dnsign1=iCustom(NULL,TimeFrame,IndicatorName,0,Length,Deviation,
IndicatorDnIndex,1);
if( (OrderType()==OP_BUY && upsign1==NoSignValue) || (OrderType()==
OP_SELL && dnsign1==NoSignValue) )
{
OrderClose(OrderTicket(),OrderLots(),OrderClosePrice(),Slippage,Yellow);
return(0);
}
}
とします。
SignShiftが0で、前の足でエントリしたときにのみ実行してもらうコードです。
また、iCustom関数で1つ前の足のシグナルをしらべます。
(signを決定するブースで記憶変数を使って調べる方法もありますが、今回はiCustom関数を使ってみましょう)
結局シグナルが出ていなかった場合、クローズする
という条件のコードを書いてあげましょう。
これで、一旦クローズしておいて、次のシグナルを待つという形になりました。
今回は、これで完成です。
あとは、ストップ・リミットが0でシグナルでもクローズしないとなるといつまででもポジションをもっていることになりますので、お好みでその条件も加えると良いかと思います。
でわ、今回はこのへんでm(..)m
カスタムインジケータをEAにする方法の関連記事
カスタムインディケータをEAにする方法
今回は、
BBands_Stops_mtf
というインディケータを使って
・途転売買を実行する/しない、の切り替え
・シグナルが出た瞬間の売買/シグナルが出た次のバーの初値で
売買の切り替え
の機能がついたEAの作り方を知りたいといったお便りを頂きましたので、
書いていこうと思います!
まずはインディケータダウンロード
→BBands_Stops_mtf.zip(mq4ファイルのダウンロード)
今回のように、インディケータにマルチタイムフレーム機能(違う時間足のものを表示させる機能)がついたものは、
バックテストのビジュアルモードでちゃんと表示できなかったり、ちゃんとバックテストできなかったりしますので、気をつけなければなりません。
バックテストでもちゃんと表示させるようにするにはインディケータを修正する必要がありますので省略させていただきますね^^;
では、早速。
手順としましては、
①インディケータのインデックスを大体把握する。
②iCustom関数で必要なインデックスの値を取得する。
③シグナルに換える。
といった具合です。
①インディケータのインデックスを大体把握する。
通常のインディケータは、特別な配列(インディケータ用配列)をつかって計算します。
普通の配列とは違い、新しい足ができる度に同じように配列の先頭に1つ記憶スペースができるといった機能があります。
そのインディケータ用配列には、
SetIndexBuffer(インデックス番号,インディケータ用配列);
という形でインデックス(番号)が割り当てられています。
BBand_Stops_mtfの場合も、
init()関数の中に
SetIndexBuffer(0,UpTrendBuffer);
SetIndexBuffer(1,DownTrendBuffer);
SetIndexBuffer(2,UpTrendSignal);
SetIndexBuffer(3,DownTrendSignal);
SetIndexBuffer(4,UpTrendLine);
SetIndexBuffer(5,DownTrendLine);
といった具合に、0~5のインデックスがそれぞれ割り当てられています。
この中のどれかを使うわけですが、幸いこのインディケータの場合、
・シグナル系だということ
・インディケータ用配列の名前が分かりやすいということ
で、インディケータの表示と照らし合わせても
買いシグナルにはインデックス2番
売りシグナルにはインデックス3番
を使えばよさそうですね!
②iCustom関数で必要なインデックスの値を取得する。
では早速、Newボタンで新規EA作成しましょう!
ファイル名は、何でもよいですが、BBands_Stops_EAとしましょうか。
iCustom関数の()内の書き方は、
iCustom(通貨ペア,時間足,インディケータ名,,...,インデックス,バーシフト);
です。
インディケータ名は、文字列で入力してあげましょう。
その後ろの【,,...,】が気になりますが、ここにはインディケータのパラメータ(インディケータのプロパティで表示されるもの)が入ります。
省略することもできますが、正確には、『以下省略』という具合に、後ろ側を省略するという感じです。
なので、「1つ目のパラメータを省略して2つ目を入力する」ということができません。
インディケータのパラメータの最後のものをEAで変更して使いたいというときは、
インディケータのパラメータを全部並べてあげる必要があるわけですね^^;
インデックスはインディケータ用配列に割り振られたインデックスです。
バーシフトは、何本前の足の値を使うかという値です。
現在変動中の足での値を取得する場合は、0と入力してあげます。
以上のことを考えると、インディケータで使っているパラメータを
EAでもパラメータ化してあげる必要がありますね!
インディケータのコードの
//---- input parameters
extern int TimeFrame = 0;
extern int Length=13; // Bollinger Bands Period
extern double Deviation=1.62; // Deviation was 2
の部分をコピーして、EAのコードに貼り付けてしまいましょう!
場所は、#property ... の下あたりにしましょう。
そして、start関数の中でiCustom関数を使って、買いシグナルの値を取得してみましょう!
int start()
{
//----
double upsign=iCustom(NULL,0,"BBands_Stops_mtf",TimeFrame,Length,Deviation,2,0);
...
といった感じです。
その後、
Comment(upsign);
として、バックテストのビジュアルモードで確認してみましょう!
んが(T□T;)!!このインディケータ、バックテストではTimeFrameの機能がちゃんと作動しないようです。
こんなときは、インディケータのTimeFrameの機能をつかわず、
iCustom関数のTimeFrameの機能を使いましょう!
int start()
{
//----
double upsign=iCustom(NULL,TimeFrame,"BBands_Stops_mtf",0,Length,Deviation,2,0);
...
という具合です。
これで、TimeFrameで設定した時間足のシグナルが取得できるようになりました。
おまけに、シグナルが出ているときはシグナルの高さ(レート)の値になり、
そうでないときは、-1という値になることがわかりました(^0^)/
このComment関数を使った確認方法はいろんなケースで使えますので、
ぜひ覚えておいてくださいね!
まだ、途中ですが続きは次回とさせていただきますm(..)m
でわでわ。
VQの計算方法
今回は、VQの中身のお話です^^
VQって何?ってゆう方は
http://samuraifx.seesaa.net/article/102860052.html
↑の記事を読んでみてください^^;
VQの値は、次の計算式で割り出しています。
(説明のため形を変えてます。このままではうごきません^^;)
MH = iMA(NULL,0,Length,0,Method,PRICE_HIGH,0);
ML = iMA(NULL,0,Length,0,Method,PRICE_LOW,0);
MO = iMA(NULL,0,Length,0,Method,PRICE_OPEN,0);
MC = iMA(NULL,0,Length,0,Method,PRICE_CLOSE,0);
MCS = iMA(NULL,0,Length,0,Method,PRICE_CLOSE,Smoothing);
VQ0 = MathAbs(((MC - MCS) / MathMax(MH - ML,MathMax(MH - MCS,MCS - ML)) + (MC - MO) / (MH - ML)) * 0.5) * ((MC - MCS + (MC - MO)) * 0.5);
SumVQ= Close[1] + VQ0;
if (Filter > 0 && MathAbs(VQ0) < Filter * Point)
SumVQ = SumVQ1;
↑この【SumVQ】というのが、VQの値です。
(注意:チャートの通貨ペア・時間足、現在変動中のバーの値)
では上の方から見ていきましょうか!
MH = iMA(NULL,0,Length,0,Method,PRICE_HIGH,0);
ML = iMA(NULL,0,Length,0,Method,PRICE_LOW,0);
MO = iMA(NULL,0,Length,0,Method,PRICE_OPEN,0);
MC = iMA(NULL,0,Length,0,Method,PRICE_CLOSE,0);
MCS = iMA(NULL,0,Length,0,Method,PRICE_CLOSE,Smoothing);
VQのパラメータである、Length,Method,Smoothingはここで使われます。
それぞれ、高値、安値、始値、終値、Smoothing本前までの終値
の移動平均ですね。
ちなみに、
Lengthは平均する期間
Methodは平均の方法(4種類)
MODE_SMA 0 単純移動平均
MODE_EMA 1 指数平滑平均
MODE_SMMA 2 平滑平均
MODE_LWMA 3 加重平均
です。
VQ0 = MathAbs(((MC - MCS) / MathMax(MH - ML,MathMax(MH - MCS,MCS - ML)) + (MC - MO) / (MH - ML)) * 0.5) * ((MC - MCS + (MC - MO)) * 0.5);
さっき計算した移動平均をつかって計算します。
ここで出てくる
MathAbs はカッコ内の絶対値
MathMax は、コンマの左右でおっきい方
といった意味です。
ごちゃごちゃしてるので、紙に書いて確認してみてください^^;
MH - ML
MH - MCS
MCS - ML
の内、最大のもので
MC - MCS
を割ったものと、
MH - ML
で
MC - MO
を割ったものを
足して0.5をかけたものと、
MC - MCS
と
MC - MO
を足して0.5をかけたものを
かけた値
となるでしょうか^^;
最後に
SumVQ= Close[1] + VQ0;
if (Filter > 0 && MathAbs(VQ0) < Filter * Point)
SumVQ = SumVQ1;
VQのパラメータである、Filterはここで使われます。
一旦、SumVQ に前のバーの終値に VQ0 を足したものを代入して、
もし、Filterが0より大きくて、VQ0の絶対値がFilterピプスよりちいさかったら、
SumVQ は SumVQ1(前のバーのSumVQ)を代入。
といった感じです。
SumVQ= Close[1] + VQ0
となって、値に変化があったときにシグナルを出すのですが、
今までと同じ方向への変化はシグナルを出さないようにしてありますので、売り買い交互のシグナルになる感じです。
基本部分はこんな感じですね^^;
でわ、このへんで。
タグ
記録ファイルを作ってみよう!
今回は、弱気なブルートさんのコメントにありました一定時間毎のドローダウンを記録するファイルを作るEAを作ってみましょう!
サンプルとして、ランダムエントリーでATRの数倍のトレイリングストップするシステムとして
紹介した記事「利益を出すランダムエントリーシステム!?」
を使って、作ってみました。
→FileWriteSample.zip(mq4ファイルをダウンロード)
内容は、ポジションが有る時、4時間足が新しくできるたびに
月、日、時間、残高、有効証拠金、現在のポジションのドローダウン、残高ピーク時からのドローダウン
を記録するという感じです。
バックテストで実行した場合は、
testerフォルダ→filesフォルダ内にファイルが作成されます。
リアルタイムで実行した場合は、
expertsフォルダ→filesフォルダ内にファイルが作成されます。
でわ、さっそくコードを見ていきましょうか(^-^)/
extern int ATRperiod=14;
extern double stoplevel=4.5;
extern double lots=0.1;
extern int Magic=216326;
extern string FileName="writesample";
extern string FileType="txt";
パラメータ用の変数です。
FileNameには、作成するファイル名を入力します。
FileTypeには、作成するファイルの拡張子を入力します。
『txt』や『log』などと入力すると、テキストファイルとなります。
『xls』と入力すると、エクセルファイルになります。
int bar;
int writebar;
double HighAB;
string filename;
記憶用の変数です。
それぞれ
整数 現在のバーの値
整数 ファイルに書き込んだ時の4時間足のバーの値
小数 過去最高の残高の値
文字列 ファイル名
です。
次に出てきますが、filenameには、パラメータで設定したファイル名と拡張子をつなげたものを記憶してもらっています。
int init()
{
//----
filename=FileName + "." + FileType;
int handle;
handle=FileOpen(filename,FILE_WRITE,"\t");
if(handle>0)
{
FileWrite(handle ,"Month","Day","Hour","Balance", "Equity", "PosDD","DD");
FileClose(handle);
}
//----
return(0);
}
int init()の{}内にスタート前にしてもらうことを書きます。
まず、
filename=FileName + "." + FileType;
で、filename にFileName と FileType をピリオドでつなげたものを代入します。
(パラメータ初期値の場合、『writesample.txt』といった具合になります。)
次に、
int handle;
handle=FileOpen(filename,FILE_WRITE,"\t");
で、handleという整数変数を宣言。
filename というファイルを書き込みモードでタブ区切り形式で開きます。
(ファイルがない場合は作成されます。)
そして、その時のファイルハンドル(ファイル認識番号みたいな感じ)を変数 handle に代入します。
FileOpen関数を使うと、指定したファイルを開くと同時に『現在開いているファイルで何番目か』という番号が割り当てられます。
ポジション確認の時のインデックス番号に似ていますね!ファイルハンドルの場合は、1から始まっているので間違えないように気をつけましょう。
そして、
if(handle>0)
{
FileWrite(handle ,"Month","Day","Hour","Balance", "Equity", "PosDD","DD");
FileClose(handle);
}
handleに正常に番号が割り当てられたら、
Month,Day,Hour,Balance,Equity,PosDD,DDという文字を書き込みます。
そして、ファイルを閉じます。
FileWrite関数のカッコ内の最初やFileClose関数のカッコ内は、ファイル名ではなく、ファイルハンドルで指定しますので気をつけましょう!
続いて、int start()のカッコ内を見ていきましょう!
//残高の最高額更新
if(AccountBalance()>HighAB) HighAB=AccountBalance();
現在の残高が HighAB に記憶しておいた値より大きい場合は、
HighAB に現在の残高を代入します。
ARTを使ったストップ用の数値の計算
ポジションの確認
ポジション無しの時
は、省略です^^;
//ポジション有りの時
else
{
(中略)
//ファイル書き込み
if(iBars(NULL,240)!=writebar)
{
ポジション有りの時で、
4時間足のバーの値が writebar に記憶しておいた値と違う場合は、
といった感じです。
次にファイルに記録したいものを変数として宣言し、値を代入していきます。
int month=TimeMonth(TimeCurrent());
int day=TimeDay(TimeCurrent());
int hour=TimeHour(TimeCurrent());
現在の月、日、時間をmonth,day,hourとし、値を代入します。
double AB=AccountBalance() ;
double AE=AccountEquity() ;
残高、有効証拠金も同様にします。
double posDD=(1-AE/AB)*100;
if(posDD<=0) string posDDstr="-";
else posDDstr=DoubleToStr(posDD,2)+"%";
小数変数 posDD を宣言し、現在の残高からのドローダウンを代入します。
そして、posDD が0以下(ドローダウンなし)の時は、文字列変数 posDDstr に『-』を代入。
それ以外(含み損が有る)時は、 posDDstr に posDD の値に『%』を付け加えたものを代入します。
double HighDD=(1-AE/HighAB)*100;
if(HighDD<=0) string HighDDstr="-";
else HighDDstr=DoubleToStr(HighDD,2)+"%";
残高ピーク時からのドローダウンも HighAB の値を使って計算し、同様にします。
int handle;
handle=FileOpen(filename,FILE_READ|FILE_WRITE,"\t");
先ほどと同じ様な感じで、読み込んダリ、書き込んダリ、...モードで^^;
タブ区切り形式で、 filename に記憶されているファイルを開きます。
この時、FILE_READモードも加えてあげないと、ファイルには上書きを繰り返して最後の書き込みしか残らなくなってしまいますので、気をつけましょう!
if(handle>0)
{
正常にファイルが開いたら、
FileSeek(handle, 0, SEEK_END);
FileWrite(handle ,month,day,hour,DoubleToStr(AB,2), DoubleToStr(AE,2), posDDstr,HighDDstr);
FileClose(handle);
writebar=iBars(NULL,240);
}
}
}//ポジション有りの時のカッコ
ファイルの最後の段にポインタを移動させ、
それぞれの値をファイルに書き込みます。
ABやAEを文字列にしているのは、テキストファイルの表示桁数をそろえるためです。文字列に変換しなくても、問題はないです^^;
そして、ファイルを閉じます。
最後に、現在の4時間足のバーの値を writebar に記憶しておいてもらいます。
こんな感じですね。
書き込みのタイミングや、記録する内容など改造したりして試してみてくださいね。
でわ、このへんで(^0^)/
PipSHakerのアルゴリズム2
今回は、前回の記事「PipSHakerのアルゴリズム1」のつづきです。
でわ、さっそく
オーダー部分です。
TrendSignが1の時
ロットの大きさを決めて、
買いロット合計がMaxTotalLots以内なら
買いオーダー送信。約定されたらtrade1をtrueに。
TrendSignが-1の時
同様に売りのオーダーです。
続いてクローズの部分です。
double WinTotal=BuyWinTotal+SellWinTotal;
double HighPoint = MathMax(HighestBuy, HighestSell);
double LowPoint = MathMin(LowestBuy, LowestSell);
double MidPoint = (HighPoint + LowPoint) / 2;
と変数の宣言をします。
WinTotal 利益が出ているポジションの利益合計
HighPoint 保持ポジションの中の最高値
LowPoint 保持ポジションの中の最安値
MidPoint HighPointとLowPointの中間値
といった感じです。
いよいよ、ここからがPipShakerのシンズイです!
①買いポジションを2つ保持、売りポジション無しで、
最安値の方の買いポジションの利益が ProfitTarget 以上のとき、
それをクローズ。
②売りポジション無し、かつ、買いポジションが1つ以外で
買いポジションで利益が出ているものの利益合計と、一番高く買ったポジションの損益との和が ProfitTarget 以上のとき、
一番高く買ったポジションをクローズ。
利益が出ているもの全部をクローズ。(←CloseWin()という関数を作って実行しています)
③買いポジション無し、売りポジション2つ保持で、
最高値の方の売りポジションの利益が ProfitTarget 以上のとき、
それをクローズ。
④買いポジション無し、かつ、売りポジションが1つ以外で
売りポジションで利益が出ているものの利益合計と、一番安く売ったポジションの損益との和が ProfitTarget 以上のとき、
一番安く売ったポジションをクローズ。
利益が出ているもの全部をクローズ。
⑤合計ポジション数が1より大きい(2以上の)とき
買値がMidPoint(保持ポジションの中間値)より上の場合で、
利益が出ているものの利益合計と、一番安く売ったポジションの損益との和が ProfitTarget 以上のとき、
一番安く売ったポジションをクローズ。
利益が出ているもの全部をクローズ。
売値がMidPoint(保持ポジションの中間値)より下の場合で、
利益が出ているものの利益合計と、一番高く買ったポジションの損益との和が ProfitTarget 以上のとき、
一番高く買ったポジションをクローズ。
利益が出ているもの全部をクローズ。
といった感じです。
①~④はディテールでより効率よく取引するためのもの。
⑤はPipMakerに似てますが、利益が出ているポジション全体で一番離れているポジションを相殺するようにしてあります。
次に、オブジェクト部分、ストップロス、トレイリングストップ
と続きます。
これらは、特に変わったことが無いので省略します。
トレイリングストップの部分で
if(OrdersTotal()==1 && trailingpips!=0)
を
if(BuyOrders + SellOrders==1 && trailingpips!=0)
に変えたほうがいいですね^^;
スタート関数を閉じた後に、
void CloseWin()
として、CloseWin()関数を作っています。
これは、利益が出ているポジション全てをクローズするといった関数になっています。
これで、おしまいですね。
サラッとだけでしたので、分からないところとかは、コメントでよろしくお願いします^^;
でわ、このへんで。


