MQL5超入門(8)/時系列配列とは

今日は、名古屋の知り合いに連れられて、あんかけスパゲティを食べてきました。
3回食べるとやみつきになる(らしい?)B級グルメですが、1度目でもそれなりに美味しかったです。
(名古屋でパスタというと...マウンテンの恐怖を思い出すのですが、今回はまともで良かった....謎)


さて、今日の超入門は、時系列配列 ( TimeSeries Array もしくは、Series Array ) について説明します。
時系列配列は、MQL4 でも当たり前のように使われているのですが、逆に常識すぎて誰も解説していない.....
解説されていないから、初心者には正しく理解されていない......気がしています。
時系列配列と、そうでない配列の違いをご存知の人は読み飛ばしてよいお話です...orz。



時系列配列の導入


まず、自分で配列 MyClose を作成して、8/3 からの終値を、毎日、順番に追加していくシーンを想像してください。

MyClose[0] = 8/3 の終値
MyClose[1] = 8/4 の終値
MyClose[2] = 8/5 の終値

MyClose[17] = 8/25の終値

シンプルに毎日データを1つずつ追加してゆく場合、このようになりますよね。
MyClose[最大値] が最新の終値です。
(実世界で、漫画の第一巻が一番古く、最新刊の巻番号が一番大きいのと同じイメージです..)


実は、MQL4/MQL5 で用意される価格データの配列も、物理的なメモリ上では、同じ構造(順序)になっています。
メモリ上では、MyClose[0] が一番古いデータになるという枠組みは理解してもらえると思うのですが、人間は、現在を基準に1日前、2日前、3日前…と思考する生き物なので、これではデータの取り扱いが少し不便です。
そこで、時系列配列という概念を導入して、「時系列配列では、データを後ろから数える」と定義してしまったのです。


MyClose を時系列配列として扱いたい場合は、

ArraySetAsSeries(MyClose,true);

と宣言します。
これ以後は、後ろから数え上げられるので、

MyClose[17] = 8/3 の終値
MyClose[16] = 8/4 の終値
MyClose[15] = 8/5 の終値

MyClose[0] = 8/25の終値 // [0]が最新のデータになった!!

となります。
ちなみに、MyClose が時系列配列として設定されているかどうかは、ArrayGetAsSeries で調べることが出来ます。

if(ArrayGetAsSeries(MyClose) ) Alert("時系列配列です");

時系列配列がもたらす混乱


MQL4 時代も XXXX[0] が最新のデータだったし、それがMQL5 でもサポートされるのなら何も変わらないのね!めでたしめでたし。
…と終われたらよかったのですが、実は、この時系列配列の存在がヤヤコシイ事態を引き起こすことになります。


MQL5 のプログラム中の配列には、メモリ上の後ろから数え上げる時系列配列と、前から数える普通の配列が混在しています
時系列配列のデータを CopyBuffer, CopyTime, ...といった、物理メモリ上の並びをそのまま配列にコピーする関数を使って、
うっかり、普通の配列にコピーしてしまったら、データの順番が逆になってしまうのです。
(実際には、物理メモリ上の順番は同じなのですが、数え上げる方向が違う..)


以下は、ヘルプ内にあった実証コードです。

datetime TimeAsSeries;
ArraySetAsSeries(TimeAsSeries,true); // TimeAsSeries 配列を時系列配列に設定

int copied = CopyTime(NULL,0,0,10,TimeAsSeries); // MQL4 のTime相当のデータをコピー

for(int i=0;i<10;i++)
{
  Print("TimeAsSeries["+i+"] =",TimeAsSeries[i]);
}

datetime ArrayNotSeries;
ArraySetAsSeries(ArrayNotSeries,false); // ArrayNotSeries を非時系列配列に設定

copied = CopyTime(NULL,0,0,10,ArrayNotSeries); // MQL4 のTime相当のデータを同じ方法でコピー

for(int i=10-1;i>=0;i--)
{
  Print("ArrayNotSeries["+i+"] =",ArrayNotSeries[i]);
}

その実行結果が、以下。

TimeAsSeries[0] = 2009.06.11 14:00:00 // [0]が最新
TimeAsSeries[1] = 2009.06.11 13:00:00
TimeAsSeries[2] = 2009.06.11 12:00:00
TimeAsSeries[3] = 2009.06.11 11:00:00
TimeAsSeries[4] = 2009.06.11 10:00:00
TimeAsSeries[5] = 2009.06.11 09:00:00
TimeAsSeries[6] = 2009.06.11 08:00:00
TimeAsSeries[7] = 2009.06.11 07:00:00
TimeAsSeries[8] = 2009.06.11 06:00:00
TimeAsSeries[9] = 2009.06.11 05:00:00


ArrayNotSeries[9] = 2009.06.11 14:00:00 // 最新は[0]ではない...(@@;)
ArrayNotSeries[8] = 2009.06.11 13:00:00
ArrayNotSeries[7] = 2009.06.11 12:00:00
ArrayNotSeries[6] = 2009.06.11 11:00:00
ArrayNotSeries[5] = 2009.06.11 10:00:00
ArrayNotSeries[4] = 2009.06.11 09:00:00
ArrayNotSeries[3] = 2009.06.11 08:00:00
ArrayNotSeries[2] = 2009.06.11 07:00:00
ArrayNotSeries[1] = 2009.06.11 06:00:00
ArrayNotSeries[0] = 2009.06.11 05:00:00 // [0]が最古!!!

同じデータをCopyTime したのに、コピー先が時系列配列かどうかで順番が変わるのは、慣れていないと本当に気持ち悪いものです。


現状の資料では、MQL4 時代のように配列コピー時に自動的に時系列配列フラグを設定してくれるかどうか不明なのと、どの配列を時系列配列として扱うべきなのか不明なので、今は、単に逆順に数えられてしまう可能性がある…程度に記憶してもらえれば良いと思います。