どうも潤奈です( ゚Д゚)
今回は過去のボラティリティを各年毎に集計してCSV出力するEAを書いて行きたいと思います( ゚Д゚)
ボラティリティを取得する為にATR(アベレージトゥルーレンジ)のインジケーターを使用します!
CSV出力されるデータはこちらです。
コード作成の様子はYoutubeの作業配信でも行っていますので、どのように作成するのか参考にしたくて時間に余裕がある方はご覧ください( ゚Д゚)
ソースコード
さっそくですが、全ソースコードは以下になります( ゚Д゚)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
//+------------------------------------------------------------------+ //| AtrKeisoku_01.mq4 | //| Copyright 2023, 潤奈FX | //| https://zyunafx.com/ | //+------------------------------------------------------------------+ #property copyright "Copyright 2023, 潤奈FX" #property link "https://zyunafx.com/" #property version "1.00" #property strict //---パラメーター設定 input ENUM_TIMEFRAMES AtrTimeFrame=PERIOD_M5; //ATR時間軸 input int AtrPeriod=144; //ATR期間 //---全体に関わる変数/配列 double Pips; //価格をpipsに置き換える為の値を入れる変数 string sDate,eDate; //計測開始と終りの時間を入れる変数 int ArrayYear[]; //集計年を入れる配列 double AtrMaxYear[]; //各年で最大値を入れる配列 double AtrMinYear[]; //各年で最小値を入れる配列 double AtrTotalYear[]; //各年で取得した合計値を入れる配列 double Count[]; //各年で取得した回数を入れる配列 double AtrAveYear[]; //各年で平均値を入れる配列 int y=0; //配列番号用の変数 //+-------------------------------------------------------------------------------------------------------------------------------------------------------------+ int OnInit() { //価格をpipsに置き換える為の値を入れる Pips=AdjustPoint(_Symbol); //配列のサイズを指定 ArrayResize(ArrayYear,1); ArrayResize(AtrMaxYear,1); ArrayResize(AtrMinYear,1); ArrayResize(AtrTotalYear,1); ArrayResize(Count,1); //配列の値を初期化 ArrayInitialize(AtrMinYear,1000); //計測開始年を代入 ArrayYear[0]=TimeYear(TimeCurrent()); //計測開始年月日として取得 sDate=StringConcatenate(Year(),IntegerToString(Month(),2,'0'),IntegerToString(Day(),2,'0')); return(INIT_SUCCEEDED); } //+-------------------------------------------------------------------------------------------------------------------------------------------------------------+ void OnDeinit(const int reason) { } //+-------------------------------------------------------------------------------------------------------------------------------------------------------------+ void OnTick() { //バー始値制御 static datetime nTime=0; if(nTime!=iTime(_Symbol,AtrTimeFrame,0)){ //ATR値を取得してpipsに変換 double ATR=iATR(_Symbol,AtrTimeFrame,AtrPeriod,1)/Pips; //年が変わった場合 if(ArrayYear[y]!=TimeYear(TimeCurrent())){ //配列番号を更新 y++; //配列のサイズを変更 ArrayResize(ArrayYear,y+1); ArrayResize(AtrMaxYear,y+1); ArrayResize(AtrMinYear,y+1); ArrayResize(AtrTotalYear,y+1); ArrayResize(Count,y+1); //追加した配列に値を代入 AtrMinYear[y]=1000; ArrayYear[y]=TimeYear(TimeCurrent()); } //ATR値の最大値、最小値を更新 AtrMaxYear[y]=MathMax(AtrMaxYear[y],ATR); AtrMinYear[y]=MathMin(AtrMinYear[y],ATR); //ATR値を合計して回数をカウント AtrTotalYear[y]+=ATR; Count[y]++; //現在の時間を代入 nTime=iTime(_Symbol,AtrTimeFrame,0); } } //+-------------------------------------------------------------------------------------------------------------------------------------------------------------+ double OnTester() { //計測終了年月日として取得 eDate=StringConcatenate(Year(),IntegerToString(Month(),2,'0'),IntegerToString(Day(),2,'0')); //ファイル名に(_開始年月日_終了年月日_通貨ペア.csv)を追加 string fName="AtrKeisoku"; fName+=("_"+sDate+"_"+eDate+"_"+_Symbol+".csv"); //該当ファイル名があれば削除し、作成して開く int fDelete=FileDelete(fName,0); int fOpen=FileOpen(fName,FILE_CSV | FILE_READ | FILE_WRITE,","); //見出しを書き込んでファイルの最後に移動する string subject="計測年,AtrMax,AtrMin,AtrAve,Count"+",時間軸="+(string)AtrTimeFrame+",期間="+(string)AtrPeriod; FileWrite(fOpen,subject); FileSeek(fOpen,0,SEEK_END); //計測した年数分のループ処理をして,(カンマ)で繋げて1行毎に書き込む string data; for(int i=0;i<=y;i++){ data=(string)ArrayYear[i]; data+=","+(string)NormalizeDouble(AtrMaxYear[i],_Digits); data+=","+(string)NormalizeDouble(AtrMinYear[i],_Digits); data+=","+(string)NormalizeDouble((AtrTotalYear[i]/Count[i]),_Digits); data+=","+(string)Count[i]; FileWrite(fOpen,data); //書き込み } //ファイルを閉じる FileClose(fOpen); return(true); } //+-------------------------------------------------------------------------------------------------------------------------------------------------------------+ //ポイント調整 double AdjustPoint(string Currency){ int SymbolDigits=(int)MarketInfo(Currency,MODE_DIGITS); switch(SymbolDigits){ case 2: return(0.01); break; case 3: return(0.01); break; case 4: return(0.0001); break; case 5: return(0.0001); break; } return(0.0); } |
ソースコード説明
ではソースコードの説明に行きたいと思います( ゚Д゚)
パラメーター作成
1 2 3 4 5 6 7 8 |
#property copyright "Copyright 2023, 潤奈FX" #property link "https://zyunafx.com/" #property version "1.00" #property strict //---パラメーター設定 input ENUM_TIMEFRAMES AtrTimeFrame=PERIOD_M5; //ATR時間軸 input int AtrPeriod=144; //ATR期間 |
パラメーターはシンプルに、ATRインジケーターに適用する時間軸と期間の2つを用意します。
全体に関わる変数/配列を宣言
1 2 3 4 5 6 7 8 9 10 |
//---全体に関わる変数/配列 double Pips; //価格をpipsに置き換える為の値を入れる変数 string sDate,eDate; //計測開始と終りの時間を入れる変数 int ArrayYear[]; //集計年を入れる配列 double AtrMaxYear[]; //各年で最大値を入れる配列 double AtrMinYear[]; //各年で最小値を入れる配列 double AtrTotalYear[]; //各年で取得した合計値を入れる配列 double Count[]; //各年で取得した回数を入れる配列 double AtrAveYear[]; //各年で平均値を入れる配列 int y=0; //配列番号用の変数 |
それぞれの変数/配列の説明はコメントで加えていますので詳細は割愛します。
int OnInit()内のコード
OnInit()はチャートに適用した時や、チャートの時間軸を変更した時に最初に呼び出される関数です。
なので、1回だけ計算すれば良い物などはここに書いてしまいます。
point→pipsに変換する為の値を入れておく
1 2 |
//価格をpipsに置き換える為の値を入れる Pips=AdjustPoint(_Symbol); |
全体のソースコードの一番下に配置しているAdjustPoint()関数を呼び出して、Pips変数に値を入れておきます。
この値は通貨ペアが変わる事がない限り変更しないので、ここで入れておきます。
どの様に使うかと言うと、例えばATRインジケーターの値がドル円チャートで0.0644だった場合、それにAdjustPoint()で取得した値(ドル円の場合は0.01)を÷ことでpips単位に計算出来ます。
0.0644÷0.01=6.44pips
配列のサイズを指定する
1 2 3 4 5 6 |
//配列のサイズを指定 ArrayResize(ArrayYear,1); ArrayResize(AtrMaxYear,1); ArrayResize(AtrMinYear,1); ArrayResize(AtrTotalYear,1); ArrayResize(Count,1); |
先ほど宣言した各配列のサイズ(個数)を宣言しておきます。
とりあえず各サイズを1個で宣言しておいて、あとから増やして行きたいと思います。
配列の値を初期化する
1 2 |
//配列の値を初期化 ArrayInitialize(AtrMinYear,1000); |
次に配列の値を初期化(初期値)を入れておきます。
AtrMinYear配列は最小値を上書きしていくので、まず最初は適当に大きい数字で初期化しておきます。
計測開始年、年月日を入れておく
1 2 3 4 5 |
//計測開始年を代入 ArrayYear[0]=TimeYear(TimeCurrent()); //計測開始年月日として取得 sDate=StringConcatenate(Year(),IntegerToString(Month(),2,'0'),IntegerToString(Day(),2,'0')); |
ArrayYear[0]の最初の配列にはこの時点の年を取得して入れておきます。
sDate変数も同様に計測開始年月日として取得し、StringConcatenate関数を使って連結させます。
IntegerToString関数は指定した桁数に足らない場合、指定した文字で埋める関数です。
例えばMonth()=1の場合、1桁しかないので’0’で文字埋めして2桁にするようにしているので、01となります。
void OnTick()内のコード
OnTick()はティックを受信する度に処理される関数です。
バー始値制御で囲む
1 2 3 4 5 6 7 8 9 |
//バー始値制御 static datetime nTime=0; if(nTime!=iTime(_Symbol,AtrTimeFrame,0)){ //この中にコードを記述していく //現在の時間を代入 nTime=iTime(_Symbol,AtrTimeFrame,0); } |
ティック毎に処理する必要はなく、ATRで取得したい時間軸毎に処理が動くように制御を入れておきます。
最初はnTime変数には0が入っているので、iTime関数で取得した値とは違いますのでif文内の処理に入ります。
最後に現在の時間を入れておくと、指定した時間足が切り替わらないとif文内の処理に入れなくなります。
ATR値を取得してpipsに変換
1 2 |
//ATR値を取得してpipsに変換 double ATR=iATR(_Symbol,AtrTimeFrame,AtrPeriod,1)/Pips; |
iATR関数を使用して、ATR値を取得します。その値をPips変数の値で割る事でpipsに変換して、ATR変数に入れます。
年が変わった時の処理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//年が変わった場合 if(ArrayYear[y]!=TimeYear(TimeCurrent())){ //配列番号を更新 y++; //配列のサイズを変更 ArrayResize(ArrayYear,y+1); ArrayResize(AtrMaxYear,y+1); ArrayResize(AtrMinYear,y+1); ArrayResize(AtrTotalYear,y+1); ArrayResize(Count,y+1); //追加した配列に値を代入 AtrMinYear[y]=1000; ArrayYear[y]=TimeYear(TimeCurrent()); } |
最初のif文の処理は、バー始値制御と同じ理屈で年が切り替わったタイミングのみ処理されるようにしています。
初年は各配列の個数は1で、配列番号は0となっています。
なので配列番号として使用するy変数に1を足して、配列のサイズ(個数)を1つ足して増やす処理を行います。
次に追加した配列に初期値として値を入れておきます。
AtrMinYear配列は前回同様で、初期値として大きい数字を入れておきます。
ArrayYear配列は新しい年を入れておきます。
ATR値の最大値、最小値を更新する
1 2 3 |
//ATR値の最大値、最小値を更新 AtrMaxYear[y]=MathMax(AtrMaxYear[y],ATR); AtrMinYear[y]=MathMin(AtrMinYear[y],ATR); |
最大値はMathMax関数を使って、AtrMaxYear配列に入っている値と取得したATR値を比べて大きい方をAtrMaxYear配列に入れるようにします。
最小値はMathMin関数を使って、同様の処理で小さい方をAtrMinYear配列に入れるようにします。
ATR値を合計して回数をカウントする
1 2 3 |
//ATR値を合計して回数をカウント AtrTotalYear[y]+=ATR; Count[y]++; |
AtrTotalYear配列にはATR値を「+=」を使って足していきます。
Count配列には「++」を使って1ずつ足していきます。
後でこの合計を回数で割る事で平均値を出す為に必要です。
double OnTester()のコード
OnTester()関数はバックテストした際に、最後に処理される関数です。
ここにCSV出力のコードを書いていきます。
計測終了年月日を取得する
1 2 |
//計測終了年月日として取得 eDate=StringConcatenate(Year(),IntegerToString(Month(),2,'0'),IntegerToString(Day(),2,'0')); |
計測開始年月日と同様で、このバックテスト終了時の年月日を取得しれ入れておきます。
CSVファイルを準備する
1 2 3 4 5 6 7 |
//ファイル名に(_開始年月日_終了年月日_通貨ペア.csv)を追加 string fName="AtrKeisoku"; fName+=("_"+sDate+"_"+eDate+"_"+_Symbol+".csv"); //該当ファイル名があれば削除し、作成して開く int fDelete=FileDelete(fName,0); int fOpen=FileOpen(fName,FILE_CSV | FILE_READ | FILE_WRITE,","); |
ファイルデータの名前として必要な情報を連結してfName変数に入れます。
CSVファイルなので最後に”.csv”を付け忘れないように注意して下さい。
FileDelete関数でfName変数の名前のデータがあれば削除させます。
FileOpen変数でCSVファイルを作成します。
()内の引数はファイル名とCSVファイルを作成するのに必要な値として覚えて下さい。
見出しを書き込む
1 2 3 4 |
//見出しを書き込んでファイルの最後に移動する string subject="計測年,AtrMax,AtrMin,AtrAve,Count"+",時間軸="+(string)AtrTimeFrame+",期間="+(string)AtrPeriod; FileWrite(fOpen,subject); FileSeek(fOpen,0,SEEK_END); |
見出しに必要な情報をsubject変数に「,」カンマで区切って入れておきます。
FileWrite関数でその見出しをfOpenのファイルに書き込みます。それぞれの要素を「,」で区切る事でセルが分かれます。
FileSeek関数で行末(ファイルの最後)に移動します。
計測した年数分のデータを書き込む
1 2 3 4 5 6 7 8 9 10 11 |
//計測した年数分のループ処理をして,(カンマ)で繋げて1行毎に書き込む string data; for(int i=0;i<=y;i++){ data=(string)ArrayYear[i]; data+=","+(string)NormalizeDouble(AtrMaxYear[i],_Digits); data+=","+(string)NormalizeDouble(AtrMinYear[i],_Digits); data+=","+(string)NormalizeDouble((AtrTotalYear[i]/Count[i]),_Digits); data+=","+(string)Count[i]; FileWrite(fOpen,data); //書き込み } |
y変数は計測した年数の値が入っているので、これを使ってfor文でループ処理を行います。
data変数に見出しと同じ順番のデータを「,」を間に入れながら「+=」で連結させていきます。
上からそれぞれ、計測年、最大値、最小値、平均値(合計÷回数)、計測回数となります。
NormalizeDouble関数を使って、不要な小数点以下の値を丸めてデータをスッキリさせます。
そして1行ずつFileWrite関数でファイルに書き込みを行います。
CSVファイルを閉じます
1 2 |
//ファイルを閉じる FileClose(fOpen); |
最後にFileClose関数で指定ファイルを閉じます。
オリジナル関数(AdjustPoint関数)
1 2 3 4 5 6 7 8 9 10 |
//ポイント調整 double AdjustPoint(string Currency){ int SymbolDigits=(int)MarketInfo(Currency,MODE_DIGITS); switch(SymbolDigits){ case 2: return(0.01); break; case 3: return(0.01); break; case 4: return(0.0001); break; case 5: return(0.0001); break; } return(0.0); |
こちらの関数は以下の記事で説明していますので参考にして下さい。
コード説明は以上となります( ゚Д゚)
まとめ
今回のコードは年単位でのボラティリティを計測できるように作成しました。
途中の年が変わった時の処理のコードを、月単位に変更するなど応用してみて下さい。
では( ゚Д゚)
コメント