TFlearnでテクニカル指標を学習させる準備

# はじめに
相場分析で使うテクニカル指標には、たくさんの種類がありますが、基本的には過去の４本値を入力データとして、それに何らかの計算をして出力します。

では、入力データと出力データがわかっているときに、どういう計算をしているかを推定することはできるでしょうか？計算モデルがわかっていて、そのなかに出てくるパラメータを決めるだけなら、システム同定とか、最適化アルゴリズムとか求める手法は色々とあります。

ただ、計算モデルまで推定するということだとちょっと難しいでしょうが、今流行りの人工知能、ディープラーニングが「万能」なら、それくらいできるでしょう。いや、それくらいできなければ人工知能と名乗るべきではないでしょう。

それはさておき、今回、予めわかっているテクニカル指標が学習できるかどうかTFlearnで試してみました。

#学習させるテクニカル指標
テクニカル指標で最も基本的なものは移動平均です。これは単純な線形処理なので、ニューラルネットワークを使うまでもありませんが、単純すぎて困ることはありません。ほんとに簡単なものから試してみます。

入力１個じゃ平均も取れないので、とりあえず２個使います。ただし、重みは1/3と2/3と変えておきます。

$$y(n)=\frac{1}{3}x(n-1)+\frac{2}{3}x(n)$$

これは、いわゆる期間２の線形加重移動平均（LWMA）で、MT5だと、

````
iMA(_Symbol, 0, 2, 0, MODE_LWMA, PRICE_CLOSE);
````
の関数を使えば算出できます。[MT5のプログラム](https://github.com/toyolab/MT5IndicatorsPy/blob/master/OutputRates.mq5)で、USDJPYの日足データと一緒に出力させたファイルが以下です。

````USDJPY.f16408.txt
Time,Open,High,Low,Close,Ind0
2015.01.02 00:00,120.416,120.74,119.805,120.458,120.257999999996
2015.01.05 00:00,120.531,120.646,119.371,119.631,119.906666666662
2015.01.06 00:00,119.285,119.504,118.05,118.363,118.785666666662
2015.01.07 00:00,118.587,119.647,118.504,119.249,118.953666666662
2015.01.08 00:00,119.314,119.961,119.157,119.652,119.517666666662
2015.01.09 00:00,119.727,119.876,118.415,118.509,118.889999999995
2015.01.12 00:00,118.315,119.315,118.098,118.346,118.400333333328
2015.01.13 00:00,118.363,118.849,117.534,117.925,118.065333333328
2015.01.14 00:00,117.89,117.937,116.067,117.335,117.531666666662
2015.01.15 00:00,117.257,117.941,116.151,116.165,116.554999999995
2015.01.16 00:00,116.183,117.764,115.849,117.541,117.082333333328
2015.01.19 00:00,117.426,117.78,116.919,117.557,117.551666666661
2015.01.20 00:00,117.654,118.866,117.64,118.766,118.362999999995
2015.01.21 00:00,118.67,118.759,117.179,117.956,118.225999999994
2015.01.22 00:00,117.917,118.665,117.245,118.469,118.297999999994
2015.01.23 00:00,118.633,118.813,117.534,117.754,117.992333333328
2015.01.26 00:00,117.634,118.497,117.263,118.447,118.215999999994
2015.01.27 00:00,118.413,118.657,117.334,117.861,118.056333333327
2015.01.28 00:00,117.746,118.262,117.249,117.536,117.644333333327
2015.01.29 00:00,117.489,118.486,117.385,118.257,118.016666666661
2015.01.30 00:00,118.336,118.459,117.296,117.542,117.780333333327
  :
````

# 教師データの作成
上のファイルを読み込んで、教師データを作成します。ファイルには４本値と指標値が入っていますが、ここで使うのは、終値`Close`と指標値`Ind0`だけです。

`X`に入力データ（今回は２個）、`Y`に指標出力値をそれぞれ200サンプルほど作ります。

````py
import numpy as np
import pandas as pd
import tensorflow as tf
import tflearn

file = 'USDJPY.f16408.txt'
ohlc = pd.read_csv(file, index_col='Time', parse_dates=True)
close = ohlc.Close.values
ind0 = ohlc.Ind0.values

N = 2
X = np.empty((0,N))
Y = np.empty((0,1))
for i in range(200):
    X = np.vstack((X, close[i:i+N]))
    Y = np.vstack((Y, ind0[i+N-1:i+N]))
````

# グラフの定義
TFlearnで使うグラフの定義ですが、今回は線形モデルだとわかっているものとして、単に入力層から線形結合させたものを定義します。あと、バイアスも使わないので、`bias=False`としておきます。

ただ、`regression`については、デフォルトのままだとあまりよい結果がでなかったので、SGDメソッドを使って学習率を徐々に下げていくよう調整を行いました。

````py
# Graph definition
layer_in = tflearn.input_data(shape=[None, N])
layer1 = tflearn.fully_connected(layer_in, 1, activation='linear', bias=False)
sgd = tflearn.optimizers.SGD(learning_rate=0.01, lr_decay=0.95, decay_step=100)
regression = tflearn.regression(layer1, optimizer=sgd, loss='mean_square')
````

# 学習
TFlearnを使うと学習の記述は簡単です。10000世代まで学習させます。

````py
# Model training
m = tflearn.DNN(regression)
m.fit(X, Y, n_epoch=10000, snapshot_epoch=False, run_id='MAlearn')
````

# 結果
学習結果として重み係数を出力させます。

````py
# Weights
print('\nweights')
for i in range(N):
    print('W['+str(i)+'] =' ,m.get_weights(layer1.W)[i])
````
以下が学習結果の出力です。

````
---------------------------------
Run id: MAlearn
Log directory: /tmp/tflearn_logs/
---------------------------------
Training samples: 200
Validation samples: 0
--
Training Step: 40000  | total loss: 0.61673
| SGD | epoch: 10000 | loss: 0.61673 -- iter: 200/200
--

weights
W[0] = [ 0.43885291]
W[1] = [ 0.56114811]
````
正確な重みは`W[0]=0.3333`, `W[1]=0.6777`なので、ちゃんと学習できているようには見えません。

# 教師データの処理
今回の例は、２入力の線形和を求めるだけのモデルだったのですが、二つの入力データの差が小さいと誤差が小さくなってしまい、学習がうまくできていませんでした。そこで、こんどは、二つのデータの差が１以上のみを教師データとしてみました。

````py
N = 2
X = np.empty((0,N))
Y = np.empty((0,1))
for i in range(200):
    if abs(close[i]-close[i+1]) >= 1.0:
        X = np.vstack((X, close[i:i+N]))
        Y = np.vstack((Y, ind0[i+N-1:i+N]))
````

結果です。

````
---------------------------------
Run id: MAlearn
Log directory: /tmp/tflearn_logs/
---------------------------------
Training samples: 22
Validation samples: 0
--
Training Step: 10000  | total loss: 1.94699
| SGD | epoch: 10000 | loss: 1.94699 -- iter: 22/22
--

weights
W[0] = [ 0.33961287]
W[1] = [ 0.66053367]
````

サンプル数が22個と減ってしまいましたが、前よりは重みが真値に近くなりました。

# ノイズ入りの教師データ
そもそもニューラルネットワークは、正確な数値予測のためではなく、結構アバウトな予測を行うためのものです。

そこで、こんどは指標値に平均0、標準偏差0.1のガウスノイズを付加したものを教師データとしてみました。

````py
N = 2
X = np.empty((0,N))
Y = np.empty((0,1))
for i in range(200):
    if abs(close[i]-close[i+1]) >= 1.0:
        X = np.vstack((X, close[i:i+N]))
        noise = np.random.normal(0,0.1)
        Y = np.vstack((Y, ind0[i+N-1:i+N]+noise))
````
結果です。

````
---------------------------------
Run id: MAlearn
Log directory: /tmp/tflearn_logs/
---------------------------------
Training samples: 22
Validation samples: 0
--
Training Step: 10000  | total loss: 3.79990
| SGD | epoch: 10000 | loss: 3.79990 -- iter: 22/22
--

weights
W[0] = [ 0.32918188]
W[1] = [ 0.67114329]
````
結果は毎回微妙に変わりますが、だいたい学習できていることがわかります。ノイズが入ってもなんとなく予測できるというのがニューラルネットワークのメリットでしょう。

#まとめ
コード中の`N`を変えれば３個以上のデータにも対応可能ですが、実際、３入力以上だと重み係数はあまりいい精度では求まりませんでした。結果は省略します。

ということで、任意のテクニカル指標の学習させるには、まだまだ道は遠そうです。とりあえず、準備という程度の記事でした。

まあ、アイデアがいいかどうかを短期間で検証できるということが、TFlearnを使うメリットということでしょう。