【AI投資システム構築】4.学習データの生成（時系列データの扱いについて）

# 入力データと出力データの生成

前回の投稿で為替レート予測のために、どのデータを使用するべきかを記載しました。

https://qiita.com/marcy_st_tech/items/52265d4e162b5752d0b6

次に選択した時系列データを機械学習の教師データにするため、時系列データを一定の長さに分割して入力データにし、一意の出力データを決める必要があります。
その方法と注意点について、それぞれ記載します。

# 入力データの生成

「時系列データを一定の長さに分割する」とは、つまり「予測のために見る窓枠のサイズを決める」という意味です。

図で書くと下記のようになり、この窓枠の横幅を決めます。

![チャート予測.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992567/fc2529dd-5638-438e-ba42-9ff61a7d142e.png)

これは長すぎても、短すぎてもダメです。

長すぎる場合、入力データのパターン数が少なくなるため、汎用的な学習が行えず、過学習が起きてしまいます（学習した期間は得意だが、未学習の期間は不得意という学習モデルができあがります）

逆に短すぎる場合、特徴量が少なすぎるため、予測精度は良くなりません。
これは「細い窓枠から、先の行動を予測する」ことと同じなので想像しやすいのではないでしょうか。

正直、どの幅が正解か、私も断言できません。
幅を変えて、何度も学習と検証を繰り返して、最も汎用的な予測精度となる幅を見つける必要があります。
経験上、通貨によっても、使用している特徴量によっても異なります。
ただ、為替レートは日次や週次のサイクル（市場サイクル）がありますので、
1時間足をベースとした時系列データの場合は、幅を12や24にした方がいい気もします。

幅を決めたら、下記の図のように、時系列を1つずつずらしながら幅固定の入力パターンを生成していきます。
2重ループでピックアップしていくだけで、プログラムは簡単なので割愛します。

<div align="center">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992567/4209764d-4d8c-41f1-beaf-422dee89c4f8.png" width="80%">
</div>

::: note info
前々回の投稿「2. データの前処理方法」で、"欠損データがある場合、その時刻のレコードを削除する"と記載しました。そのため厳密にいえば、その時系列を分断することになるため、上記入力パターン生成でも分断したところは除外する必要があるのだと思います。ですが何度も記載してる通り、個人で集められる市場データには限りがありますので、「除外するのはもったいない」と思い、私はあえて「ノイズ」の一つとして気にせず使用しています。
:::

https://qiita.com/marcy_st_tech/items/9428fecff0d6628e5170


## 【注意】Time(0)における Close(0), High(0), Low(0) は未来です

::: note alert
気をつけて下さい。私はこれに気づかず数か月（数年？）を無駄にしました。
:::

MQL4,MQL5で現在時刻の為替レートを取得する方法として、
下記のようなメソッドがあります。
このメソッドを用いて、MT4,MT5で保持している時系列データから、銘柄（USDJPYなど）と期間（H4,D1,など）を指定し抽出します。
~~~c#:MQL4, MQL5
double  iClose(
  const string        symbol,         // 銘柄
  ENUM_TIMEFRAMES     timeframe,      // 期間
  int                shift           // シフト
  );

double rate = iClose(Symbol(), PERIOD_H4, 0); // 現期間の終値を取得
~~~

このメソッドのシフトについて、時系列データを保持する配列のインデックス（以降は _i_ と記載します）を意味しますが、MQL4,MQL5特有で、_i_ が増加する度に過去の時刻になります。そしてこの配列はキュー構造をしているため、一定の時間間隔（上記メソッドの"期間"）で、_i_ = 0 を表す時刻が切り替わります。

![インデックス説明.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992567/48d6f583-c31a-4b31-a066-562f474a09c6.png)

余談ですが、一目均衡表の先行スパンのように、未来の時刻の指標値を取得する場合、下記メソッドのシフトには、マイナスの値を設定します。

~~~c#:MQL4
double  iIchimoku(
   string       symbol,         // 通貨ペア
   int          timeframe,      // 時間軸
   int          tenkan_sen,     // 転換線期間
   int          kijun_sen,      // 基準線期間
   int          senkou_span_b,  // 先行スパン期間
   int          mode,           // ラインインデックス
   int          shift           // シフト
   );
~~~

メソッドはClose（終値）だけではなく、Open（始値）、High（高値）、Low（安値）のメソッドや、時間期間の開始時刻を取得する _iTime_ も用意されています。図で表すと下記の通りです。

![OHLC.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992567/7b8601b5-bdff-44af-84e9-67bb3145e91d.png)


::: note alert
さて、ここから要注意です
:::

この _i_ = 0 の _Close_, _High_, _Low_ について、
リアルタイムに取得する場合は、下記図のようになります。

![OHLC_リアルタイム.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992567/441c4e11-2690-4d05-ae44-bb15ec28f1e0.png)


次の時間間隔となり期間更新されるまで、
 _Close(0)_, _High(0)_, _Low(0)_ は更新され続けることになります。

気をつけないといけないのは、過去の時系列データから _Close(0)_, _High(0)_, _Low(0)_ を取得した時です。
下記図をご覧下さい。

![OHLC_過去チャート.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992567/3043b3f8-8695-49bc-a85a-5f79d7e3d46b.png)

同じ _i_ = 0ですが、過去の時系列データなので、
_i_ = 0 を表す時刻（ _Time(0)_ ）より未来時間の範囲（ _-1 < i ≦ 0_ ）の値が取得できてしまうのです。つまり _Close(0)_, _High(0)_, _Low(0)_ の値は未来の値を含んでしまうということです。

この値、時間間隔が狭い場合はあまり問題となりませんが、4時間足や1日足では未来の情報が多くなるため、予測結果に大きな影響がでてしまいます。

例えば、活用している時系列データの間隔が4時間足で、8時間後の為替レートを予測することにしましょう。
学習の時は過去の時系列データなので、4時間未満の未来情報を含んで学習することになりますが、リアルタイムトレードの場合、未来情報を含まない状態で予測することになります。
これにより、シミュレーションと実際のトレードで、予測結果に大きな差が生じてしまいます。
私の場合、シミュレーションでは勝率8割、実際のトレードでは勝率5割という結果となり、かなり悩みました。

::: note info
:coffee: **Coffee Break**
まぁ結局は、私が _Time(0)_ の時刻を勘違いしていたという失敗談です。皆様は私と同じ過ちをしないように気を付けてください。
:::

# 出力データの生成

つぎに予測対象となる出力データを生成します。
ここで考えられる予測対象ですが、単純に思いつくのは4通りぐらいあるかと思います。
1. 任意の損切（StopLoss : SL）と利確（TakeProfit : TP）でトレードした場合の勝敗率
1. 任意の時間後、上がるか下がるかの確率
1. 任意の時間後の変動値
1. 任意の時間後の変動値と最大値と最小値

### 1. 任意の損切（SL）と利確（TP）でトレードした場合の勝敗率

<div align="center">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992567/c9bf6458-3976-4664-a0d0-a4ff6b0a08ff.png" width="80%">
</div>

機械学習でいうところの、クラス分類問題（二値分類）となります。
まず、1.はやめた方がいいです。
個人が取得した時系列データについて、同じ時刻に _High(i)_ と _Low(i)_ がありますが、
厳密にどちらが先のデータなのか、わからないからです。
値動きが激しい場合、または _High(i)_ と _Low(i)_ が近い場合は、本当にわかりません。
1分足のデータを用いて、どちらが先か判断することもできますが、
個人で集められる1分足のデータは数か月分が限界だと思います。

仮に「どちらかわからない場合は損切（SL）を優先」とした場合、
負け率ばかり高くなり、教師データに偏りができてしまいます。
「どちらかわからない場合は、そのパターンは使わない」とするのも、
有限のデータを減らすことになるのでナンセンスです。

### 2. 任意の時間後、上がるか下がるかの確率

<div align="center">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992567/0e994eb2-b0cb-44c0-b3c7-785bf4c34132.png" width="80%">
</div>


こちらも機械学習でいうところの、クラス分類問題（二値分類）となります。
実は、2.もやめた方がいいです。

理由は、上がるか下がるかのクラス分類では、必ず偏りがあるからです。
特に昨今の円安傾向により、10年分のデータだけでは、どうしても上昇クラスの方が多くなってしまいます。
下記はここ数年分のUSDJPYの為替レートの推移です。右肩上がりですね。

![チャートサンプル_上がり続けている.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992567/646af942-943e-4359-b22b-0bdae4ab1f2d.png)

またUSDJPYについて、4、8、12時間後、それぞれの時間経過後の変動値をヒストグラムにしたものが下記図です。
一目瞭然、上昇の割合の方が多いことが分かります。

<div align="center">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992567/f1cb65c4-533a-45fb-84e4-dec719ede01d.png" width="80%">
</div>

#### *クラス分類問題で、クラスの偏りがあると、何が悪いのか

これから生成しようとしている学習モデルは、
正答率が50～60%程度、人によるファンダメンタルズ分析と合わせて、やっと70%を超える程度のシビアなモデルです。
仮に、クラス割合が上昇クラス：下降クラス = 60:40 であるとしましょう。
このクラスに対して、学習を行ったらどうなると思いますか？
学習者（AIです）も賢いもので、
「入力パターンで学習を進めても、正答率がなかなか上がらない…そうだ！すべて上昇クラスにしてしまおう！そしたら正答率は60%だ！」
となってしまうのです。

AIの学習経過を、損失値（loss）や正解率（accuracy）でプロットすると、
ある段階で急に跳ね上がって、その後一定になります。
「あ、ここでAIもあきらめたか」とわかるので面白いですよ。

それゆえクラス分類問題では、可能な限りクラスの偏りはなくした方がいいといえます。

### 3. 任意の時間後の変動値

<div align="center">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992567/2295402a-e8e1-4a4a-bb4c-6cdcc40659f6.png" width="80%">
</div>

機械学習でいうところの、回帰問題となります。
例えば、上記ヒストグラムのように、現在値から、4時間後や8,12時間後の変動値を予測する学習モデルです。
クラス分類問題と違い、変動値そのものを算出するため、多少の偏りがあっても大丈夫です。
私はこれを目標値としました。
目標値についても、過去の投稿「2.データの前処理方法」で標準化を行った値となります。
そのため、その予測結果を用いてトレードを行う際は、標準化を逆に戻す必要があります。

https://qiita.com/marcy_st_tech/items/9428fecff0d6628e5170


### 4. 任意の時間後の変動値と最大値と最小値

<div align="center">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992567/6f8d686d-78fa-411f-a9d7-7832487a1b36.png" width="80%">
</div>

機械学習でいうところの、回帰問題（重回帰分析）となります。
任意の時間後の「変動値」と「最大変動値」と「最小変動値」など、複数種を出力データとして、学習することになります。
実はこの目標値について。何度も検証しましたが、変動値と最大値と最小値が反転するなど、全く上手く行きませんでした。
おそらく前提条件付きの学習モデルにするなど、通常の回帰問題とは違う過程が必要なのだと思いますが、
私は解決できませんでした。もしご存じの方がいらっしゃいましたら教えて頂きたいです。

# 偏りをなくすための逆順データの生成

上記「3. 任意の時間後の変動値」で多少の偏りがあっても問題ないと記載しましたが、
それでも、「上昇」の教師データが多いと、「上昇」に偏った予測をする学習モデルとなってしまいがちです。
そこで逆順データを導入しました。下記図をご覧ください。

![左右反転.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992567/d8aaf5b3-2c07-424b-83b3-10e1aefd6d7f.png)

為替チャートを左右反転、つまり時間軸を逆にした図です。
逆順でも同じように、テクニカルチャート分析の考えが使えると思いませんか？

さらに具体的な問題を出してみます。
例えば、つぎの「？」に来る数字を予測する問題があるとします。
- 2, 3, 4, 5, ?
- 5, 4, 3, 2, ?

小学生の算数に出てきそうな問題ですが、上記は２つとも同じ理論で解けると思いませんか？

私はこの考えを、為替レートの学習データの生成でも使うことにしました。
もしかしたらノイズを増やしただけかもしれませんが、
結果、学習モデルが偏った予測をすることは是正されたと思います。

# まとめ
学習データの生成方法（入力データと出力データ）について記載しました。
また【注意】でも記載しましたが、入力データに"未来"の情報を含めないよう、また、同じレコードには同じ時刻データが入るよう、十分注意してデータを生成することをオススメします。

ここで私が記載した内容はすべて正解ではありません。
学習データの生成方法について、少しでも参考にしていただけますと幸いです。

次は、「学習モデルの作り方」について、投稿する予定です。
よろしくお願いいたします。