相場の価格変動をCSV出力するEA(曜日毎に集計)

CSV分析

どうも潤奈です( ・Д・)

前回波のゆくさきArmadaさんがnoteに書かれているゴトー日を研究してEAを作ろう!の記事の途中過程にある、価格変動を日本時間基準でCSV出力させるコードを参考にしたCSV出力用EAを書いて行きました。

今回はそこから応用して各曜日毎に集計してCSV出力するコードを書いて行きたいと思います( ・Д・)

CSV出力イメージ

※右のグラフは表計算ソフトで表示したグラフになります。

スポンサーリンク

ソースコード

ではさっそくコードを書き込んで行きます。

パラメーター作成

#property~の4行は作成した段階で挿入されている定型文なので、その下から入力して行きます。

1文づつの構成は、「修飾子 型 固有名=値; //コメント」となります。

まずMT4のサーバー時間は日本時間とタイムゾーン(GMT)が違うのでその差を修正する為に、GMT_Adjust変数でその時間差を宣言します。(証券会社によっては日本時間表示のMT4もあるのでチャートを開いて確認して下さい。)

【GMT(グリニッジ標準時間)とは】
イギリスのグリニッジ天文台(経度0度)を基準とした、世界各国の標準時でMT4のサーバー時間はGMT+2/+3を採用しているFX会社が多く、日本時間はGMT+9となります。

次に基準となる時間からの価格変動を取得したいので、基準となる「sHour=時」と「sMinutes=分」をそれぞれ宣言し、その基準からどこまで(eHour=時)取得するのか?の変数を宣言します。

全体に関わる変数/配列を宣言

ここで宣言する理由としては、{ }で閉じた中で宣言すると、別の{ }内で値を引き継いで利用が出来ない為(方法はありますが)、{ }の外であるこの段階で宣言します。

それぞれの変数/配列の説明はコメントで加えていますので詳細は割愛しますが、今回は3次元配列を使っているので簡単にその説明だけします。

3次元配列とは

今回はrate配列とCount配列は3次元配列で宣言しています。
rate[601]の1次元配列だと使用出来る箱が601個なのに対し、rate[601][31][5]の3次元配列にすると601×31×5=93,155個の箱を用意出来ます。
※[601]という数はこの後ArrayResize関数で指定します。
※[31]の数字は31日分という意味で、[5]は月~金の5曜日分の数になります。

下に表で表していますが、rate[縦(行)][横(列)][奥(シート)]となります。例としてrate[2][4][0]は-26.26の値が入っています。
今回の項目に置き換えると、rate[分][日付][曜日]です。
※この縦・横・奥という概念は今回のデータを取る上での私の解釈です。

今回のこのイメージ図は出力したいCSVデータの形とは異なりますが、前回の「日付毎の集計」から考え方を引き継いでいますのでこの様な形で説明させて頂きました。

OnInit()内のコード

OnInit()はチャートに適用した時や、チャートの時間軸を変更した時に最初に呼び出される関数です。
なので、1回だけ計算すれば良い物などはここに書いてしまいます。

point→pipsに変換用の値を求める

_Digits変数で適用したチャートの通貨ペアの価格の小数点以下の桁数を取得します。
その取得した桁数をswitch処理で一致するcase(条件)に進み処理を行い、break処理でswitch処理から抜けます。

小数点以下の桁数が2桁と3桁の業者があったり、決済通貨(右側)が円かドルなどでも桁数が違うのでこういった調整が必要になります。

配列数の準備

各配列には各分毎×時の値を入れる為の箱数が必要なので、eHour=遡る時間に60分を掛けた値をpNum変数に入れておきます。

次にArrayResize関数でそれぞれの配列サイズ(数)を指定します。
+1にしているのは、基準となる時間も1枠必要なので〇〇時間+1分の配列サイズが必要になります。

例)10:00を含む10分後までの必要な配列サイズは、10分+1分必要になります。

計測開始年月日を取得

最初に処理される所なので、計測開始年月日としてここで現在年月日を取得し、「+」で連結させてsTime変数に入れておきます。

Year()の頭に(string)が付いているのは、datetime型であるYear()をstring型に変換する。という意味になります。

IntegerToString関数は、指定した桁数に足らない場合、指定した文字で埋める関数です。
例えばMonth()=1の場合、1桁しかないので’0’で文字埋めして2桁にするようにしているので01となります。

OnTick()内のコード

OnTick()はティックを受信する度に処理される関数です。

サマータイム切り替え日を求める

アメリカ基準時刻の業者では夏時間:3月第2日曜日で切替わり、冬時間:11月第1日曜日で切替わります。
MT4には第〇〇曜日が〇日です。という事を求める関数がないので計算しないといけません。

夏時間と冬時間の求め方は同じで、StrToTime関数で現在年と”.03.14″(冬は”.11.07″)を連結させた文字をdatetime型の時間に変換して、SummerTime(WinterTime)変数に入れます。
そしてTimeDayOfWeek関数でその年月日の曜日を取得(日曜日:0~土曜日:6)して86400を掛けて秒数に変換します。
その値をSummerTime変数の年月日から減算(-=)します。

つまり、その年の3月14日が日曜日であれば0なので値に変更なしで、水曜日であれば3×86400=3日分の秒数を引いた3月11日が日曜日という事になります。

サマータイム判定

TimeCurrent()関数でサーバー時間を取得してCurrentTime変数に入れます。
Summer変数には0を代入しておきます。
そしてif()関数で先ほど求めた夏時間と冬時間の切り替え日の間に、現在のサーバー時間が該当すれば、Summer変数に1を入れます。

日本時間を求める

GMT+2(夏時間GMT+3)のブローカーの場合、日本時間はGMT+9なので9-2=7をGMT_Adjust変数にパラメーターで設定します。
よってサーバー時間にGMT差7×3600(7時間分の秒数)を足した日本時間をjTime変数に入れます。

サマータイムの場合、MT4のサーバー時間はGMT+3になる為、GMT差は6になります。

次にTimeDay()関数とTimeHour()関数とTimeMinute()関数とTimeDayOfWeek()関数を使って、日本時間の日と時と分と曜日を取得してjD変数に日、jH変数に時、jM変数に分、dW変数に曜日を入れます。
※曜日は0~6の数字で取得され、0=日、1=月、2=火、3=水、4=木、5=金、6=土となります。

計測開始時分を確認して処理{ }

if()文でパラメーターで設定した時と分が現在の日本時間と同じか確認します。

次に基準となる現在の始値Open[0]と現在の時間Time[0]をそれぞれの変数に入れます。
Blank変数には0を、First変数にはstatic変数をint型の前に書いて初回だけ0が入るようにしておきます。

最後に計測するループ処理後にFirst変数に1を入れます。

計測する分数のループ処理

for()文で現在値0から遡る分数の回数ループ処理させます。
ここでのi変数に入る数は基準時間からの〇分前の数になります。

まずif()文で計測開始時間cTimeの時間(分)とこれから取得する時間(分)が同じかを確認します。
同じ場合はrate[i][日付-1][曜日-1]配列に、取得した始値から基準価格の差(point)の値を、Pips変数で割ってpoint→Pipsに単位を変換して配列に加算(+=)します。
Count[i][日付-1][曜日-1]配列には、加算した回数として1を足して行きます。

}else{ は、もし時間が違う場合は、ヒストリカルデータの欠損が考えられるので、Blank変数にその回数を足して行きます。

rate[i][日付-1][曜日-1]で日付と曜日から1引いている理由
日付の配列数は31で宣言していますが0から始まるので0~30になるからです。
曜日の配列も同様で0から始まるので、月~金(1~5)で取得されるので1を引きます。
もし引かずにいると存在しない31の箱と6の箱に入れようとしてarray out of rangeというエラーが発生します。

ところで、Time[i-Blank]という書き方をしているのは何故かについて詳しく説明します。

まずi変数は〇分前という数で使用しますので以下の表のようになります。

しかし、例えば9:54のヒストリカルデータが欠損していたとしたら次の表のように、欠損した所から1分ズレてしまいます。(これが結構あるんです…)
理想の挙動としては欠損していた場合、もう一度同じi変数の値から取得して行かないといけません。

なので取得したい時間と違った場合、Blank変数に1を足してTime[i-Blank]とする事で欠損していた場合は値を取得せずに、次の処理でもう一度同じ値を取得するという処理になるようにしています。

1列目の時間情報を取得して格納

出力させるイメージはA列のようなデータです。
これは変動するデータではないので1回だけ処理出来れば良いです。

if()文で初回のみ処理させます。
まず、日本時間jTimeからループで回ってくるi=〇分に60を掛けた秒数を引いて、その時間(時)をtHour変数に入れます。
次にcTime変数の時間(分)をtMinute変数に入れます。

そして、rateTime[]配列に取得した時間(時、分)を文字列として繋げて入れます。

if()文を抜けた所で、cTime変数の時間を手動で60秒減算(-=)しておきます。

更に、for()文を抜けた所でFirst変数に1を入れます。そうする事で最初のfor()文のループ処理だけrateTime[]配列にデータを入れる処理をする事が出来ます。

OnTester()内のコード

OnTester()関数はバックテストをした際、最後に処理される関数です。
ここにCSV出力のコードを書いて行きます。

計測終了年月日を取得

コード構成は開始年月日を取得時と同じです。
この最後に処理されるタイミングの年月日を取得して繋げたデータをeTime変数に入れます。

31日分のファイル作成用のループ処理

各日付毎にCSVファイルを分けたいので、CSVファイル作成~終了までの式をfor()文のループ処理で31日分繰り返させます。

CSVファイル準備

ファイルデータの名前として必要な情報を連結してfName変数に入れます。
CSVファイルなので最後に”.csv”を付け忘れないように注意して下さい。

今回は各日付毎にファイルを分けるので、ファイル名”CSV_Analysis_AllDay_Weekdays_”の後に日付の数字を追加しています。
d変数には配列の関係で0から始まるループ処理をさせていますので、日付に直す為に1を足しています。
※これを追加しないとファイル名が全て同じになってしまい、上書き(置き換える)する事になって1つしかファイルが残りません。

【ファイル名に使用出来ない文字】
MT4上ではエラーは出ませんが正しく出力されない可能性があります。
/ : * ? ” < > |

FileDelete()関数でfName変数の名前のデータがあれば削除させます。

FileOpen()変数でcsvファイルを作成します。
各引数はCSVファイルを作成するのに必要な引数として覚えて頂いたら問題ないです。

【バックテスト後の注意】
実際にバックテストした後にファイルを開くと思います。そのファイルを開いたまま再度バックテストするとMT4ではそのファイルを操作出来ないので必ずCSVファイルを閉じて下さい。

見出し作成して書き込む

まず見出しとなる件名を入れるsubject変数と曜日名を入れるdWeek変数を文字列型で宣言します。

switch処理でdWeek変数に各曜日を入れて、break処理でswitch処理から抜けます。

次にfor文で集計日付と曜日を「,」で区切って連結させて行きます。

if(i=0){ から始めているのは、最初の1回だけでよい i==0 の処理より回数の多い処理を上に先に持って来ているからです。

FileOpen関数で開いているファイルfOpenにsubject変数のデータを書き込みます。それぞれの要素は「,」で区切ってあるのでセルが分かれます。

FileSeek関数で行末(ファイルの最後)に移動します。

各分毎のデータを書き込む

説明ポイントが多いので、コードを分割させながら説明して行きます。

まず1行毎のデータを入れるdata変数を宣言します。
for()文でi変数には取得した分数を入れて、減算させながらループ処理させて行きます。これが縦軸のループ処理になります。

続いてその中にfor()文でk変数に0を入れて、加算させながら5回ループ処理させて行きます。これが横軸のループ処理になります。
その中で価格変動幅を入れるpRange変数を宣言します。

価格変動幅の平均値を出すのに取得合計した価格差を取得回数で割るのですが、一度も取得していない時は0となり、0で割るとエラーが出てしまいます。
なのでif()文で、その[時間][日付][曜日]の値を取得した回数で0以外の時に計算するように制限を加えます。

取得していれば、pRange変数に取得合計したrate[時間][日付][曜日]配列を取得回数Count[時間][日付][曜日]配列で割って平均値を入れます。

見出し作成時と同じ様に、最初は時間と「,」と平均値を入れて、その後は「,」と平均値を連結させてdata変数に加えて行きます。

FileWrite関数で指定ファイルfOpenにdata変数の値を書き込みます。
※for(int k~)文の外に書いて下さい。
FileWrite関数はファイルの書き込み後に改行文字が追加されるので自動で改行されます。

すべて書き込み後、FileClose関数で指定ファイルfOpenを閉じます。

31日分のループ処理の}で閉じた後、return(true);でOnTester関数の処理は終了です。

コンパイル

コードをすべて書き込んだ後に「コンパイル」を実行します。

コンパイルを実行して、下にエラーが出なければこれでこのコードが使用する事が出来ます。

【エラーが出た場合は解消しないと動かす事は出来ません】
ちなみに以下の原因は23行目のeTimeの後ろに「;」がない事によるエラーです。
この一つの入力忘れでもこれだけのエラーが出てしまいますので、頑張って下さい( ゚Д゚)w

バックテスト時の設定

このコードをバックテストする際は以下の設定でお願いします。
期間(時間足):M1(1分足)
モデル:始値のみ(全ティックを選択しないでください)

このコードは1分毎に価格変動のデータを集計しますので、1分足の設定をして下さい。
そして1分毎の時間はコード内で確認していますが、1分の間でのティック処理までの制御は行っていません
従って全ティックでバックテストを行ってしまうと1分×ティック数のデータを集計してしまう事になりますので、値が変化してしまいます。
※より正確になるという事ではありません。

まとめ

前回は各日付毎に列を分けて集計しましたが、今回は日付をファイル毎に分けて曜日毎にデータを集計してみましたので、更に細かい分析が出来るようになります。

バックテストなどで勝てるロジック(結果を見て)探すのも良いですが、そのベースになるローソク足(価格の動き)を分析する事はロジックを考える上でもかなり重要だと思います。

そういった点で波のゆくさきArmadaさんのnoteにはその考え方が書かれておりとても勉強になります( ・Д・)

ちなみにこのように1回で31日分のデータファイルが出力されるので、いろんな期間を連続でバックテストすると後の整理が大変なので注意して下さいw

【免責事項】

※当ソースコードにおける如何なる損失もご自身の自己責任となります。投資資産における運用の結果生じた損害の全部若しくは一部について一切の責任及び負担を負わないものとします。
※当ソースコードにつきましても、バグや不具合がないことを保証するものではありません。

※当ソースコードについてはサポート等については行っておりません。
※このブログで掲載されている情報は、投資等の勧誘又は推奨を目的としたものではありません。
※掲載されている内容は予告なしに変更することがあります。

EAの全ソースコード

以下の記事をご購入頂きましたら今回のEAの全ソースコードをまとめて閲覧する事が出来ますので、コピーして作成すればすぐにバックテストで試す事が出来ます( ・Д・)

尚、この無料記事にすべて記述していますので、模写して行けば同じ物が出来ます。

時短を考える方、この記事を評価して頂ける方だけご購入ご検討下さい( ・Д・)

コメント

タイトルとURLをコピーしました