ディープじゃない機械学習で為替(FX)の予測をしてみる

機械学習というと深層学習（ディープラーニング）を連想されがちですが、それ以外にも沢山あります。また、ディープじゃ無いからダメかというとそうでもありません。
というわけで、[前回の記事](http://qiita.com/hayatoy/items/b9ddf4d6d8e026dc15e5)のデータを別の機械学習でやってみます。

**関連シリーズ**
- [第１回 TensorFlow (ディープラーニング)で為替(FX)の予測をしてみる](http://qiita.com/hayatoy/items/b9ddf4d6d8e026dc15e5)
- 第２回 ディープじゃない機械学習で為替(FX)の予測をしてみる
- [第３回 TensorFlow (ディープラーニング)で為替(FX)の予測をしてみる CNN編](http://qiita.com/hayatoy/items/0ec3f3da680ae192ddc7)

# TL;DR
![equity.png](https://qiita-image-store.s3.amazonaws.com/0/162369/79a7a5bf-0927-b681-afbf-021c67ebfcbc.png)
資産の増減をグラフにしました。半年ほどで<font color="#D81B60">12%の利益</font>になってますが、学習の条件に対して結果がロバストでは無いので今後もうまくいくとは限りません。

**GitHubにNotebookを置いてあります。**
Forkして遊んでみてください。
https://github.com/hayatoy/ml-forex-prediction

# Scikit-learn
Pythonで機械学習するならまずこれでしょう。インストールは`pip`なら

```bash
pip install -U scikit-learn
```
でいいと思います。

## Classifierの選択
全部試してもいいんですが、あまり線形なのはノイジーなデータに対していい結果が出ないかなと予想します。

![sphx_glr_plot_classifier_comparison_001.png](https://qiita-image-store.s3.amazonaws.com/0/162369/0fab5e94-7a71-3cdb-55c0-9cb1227123ec.png)
分類の例はこんな感じです[^1]。
[^1]: 出典：http://scikit-learn.org/stable/auto_examples/classification/plot_classifier_comparison.html

SVM(RBF), Naive Bayes辺りが良さそう。

## データの加工
同じShapeで`fit`してくれそうなのでそのまま使います。
…と思ったらクラスが1hot-vectorのままだと怒られちゃいました。（何かオプション有ったっけ？）
1hot-vectorからbinaryに変換ですが、今回は2クラスなので取ってくる場所を変えるだけで良さそうです。

```py
>> train_y
[[ 0.  1.]
 [ 0.  1.]
 [ 1.  0.]
 [ 0.  1.]
 [ 0.  1.]
 [ 0.  1.]
 [ 1.  0.]
 [ 0.  1.]
 [ 1.  0.]
 [ 0.  1.]]
>> train_y[:,1]
[ 1.  1.  0.  1.  1.  1.  0.  1.  0.  1.]
```
多クラスだとこの方法は使えないですね。何かカッコイイやり方無いかな。

# 学習させてみる
前回同様、前半90%をトレーニング、後半10%をテストに使います。`0.502118`を下回るとランダムに予測するより悪い事になります。

とりあえず公平にデフォルトパラメータで実施。

### SVM (RBF)

```py
from sklearn import svm
train_len = int(len(train_x)*0.9)
clf = svm.SVC(kernel='ref')
clf.fit(train_x[:train_len], train_y[:train_len,1])
clf.score(train_x[train_len:], train_y[train_len:,1])
```
<font color="#D81B60">結果：0.49694435509810231</font>

### Gradient Boosting

```py
from sklearn.ensemble import GradientBoostingClassifier
clf = GradientBoostingClassifier()
clf.fit(train_x[:train_len], train_y[:train_len,1])
clf.score(train_x[train_len:], train_y[train_len:,1])
```
<font color="#00897B">結果：0.52331939530395621</font>

### Random Forest

```py
from sklearn.ensemble import RandomForestClassifier
clf = RandomForestClassifier(random_state=0)
clf.fit(train_x[:train_len], train_y[:train_len,1])
clf.score(train_x[train_len:], train_y[train_len:,1])
```
<font color="#D81B60">結果：0.49726600192988096</font>

### Naive Bayes

```py
from sklearn.naive_bayes import GaussianNB
clf = GaussianNB()
clf.fit(train_x[:train_len], train_y[:train_len,1])
clf.score(train_x[train_len:], train_y[train_len:,1])
```
<font color="#D81B60">結果：0.50112576391122543</font>

### Nearest Neighbors

```py
from sklearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier()
clf.fit(train_x[:train_len], train_y[:train_len,1])
clf.score(train_x[train_len:], train_y[train_len:,1])
```
<font color="#D81B60">結果：0.49726600192988096</font>

### QDA

```py
from sklearn.discriminant_analysis import QuadraticDiscriminantAnalysis as QDA
clf = QDA()
clf.fit(train_x[:train_len], train_y[:train_len,1])
clf.score(train_x[train_len:], train_y[train_len:,1])
```
<font color="#00897B">結果：0.50981022836925061</font>

### まとめ
| 順位 | モデル | Accuracy |
|:-:| :-- |:-- |
| **1** | **BiRNN(LSTM)**  | **0.528883** |
| 2 | Gradient Boosting | 0.523319 |
| 3 | QDA | 0.509810 |
|   | 多いクラスの割合 | 0.502118 |
| 4 | Naive Bayes | 0.501126 |
| 5 | Random Forest | 0.497266 |
| 6 | Nearest Neighbors | 0.497266 |
| 7 | SVM (RBF) | 0.496944 |

前回のLSTMがやっぱり1位。2位はKaggleでも人気のGradient Boostingでした。
…って、あれ？パラメータ調整していないとはいえ、SVMが最下位？そんなはずは・・またの機会に`Grid Search`で最適なパラメータを探索してみます。

# PnL (Profit & Loss)を計算
正解率が悪いからといって、損益が悪くなるとは限りません。50%の正解率でも`利益 > 損失`なら良いわけですからね。

今回のデータは次の期間の終値が上がるか下がるかを予測しました。
なので
`(次の終値 - 現在の終値) * ロット - コミッション`
を損益として計算しました。
<font color="#888">(実際には現在の終値が確定した時点でPredictするので、終値でポジションは取れません。週の中日なら多少上下するだけですが週末を挟むと大きく値が動いている可能性があります)</font>

github版のEUR/USD日足データです。緑のラインは正解、赤のラインは不正解を意味します。
![equity.png](https://qiita-image-store.s3.amazonaws.com/0/162369/79a7a5bf-0927-b681-afbf-021c67ebfcbc.png)
初期資産は10000ドル、取引単位は10000通貨、コミッション（スプレッド）は0で、最終利益はプラス1197ドルとなりました。スプレッドを平均2pipsに設定してもまだプラス900ドル位ある計算です。

一見良さそうですが・・・
トレーニング期間を変えるとすぐにダメになってしまうので、モデルとデータ選択が良いというわけではなく偶々いい感じにフィットしただけのようです。

GitHubにNotebookを置いてありますので、お試しあれ。
https://github.com/hayatoy/ml-forex-prediction