教師あり機械学習を使ってBTC価格を予想してみる①
こんにちは、alumiです。久しぶりの投稿です。
前に書いた自動取引noteの紹介noteがTwitterで少し拡散されて、それをきっかけにnoteのフォロワーが急に増えてビクビクしています。
最近の自動取引界隈はmmbotの競争がますます激しくなっているという印象です。私は中長期スイング系のbotを動かしているので遠くから眺めています。(ちなみにそんなに利益出てなくて困ってます笑)
何か新しい戦略はないものかな、と考えた時に教師あり機械学習を使った価格予測をとふと思いついたので自分の復習も兼ねていろいろ試してみることにしました。
実験記録的なnoteです。現時点で私も結果の予測がつきません。
※注意 機械学習については自分も最近学び始めた初学者なので間違ったこと言ってるかもしれません。そのときはそっと教えていただけると嬉しいです。
簡単な説明
今回試していくのは教師あり機械学習というもので、トレーニングデータを学習しそれをもとに境界線を引いてテストデータの出力を予測します。
本noteではひとまず「過去10本分のロウソク足のOHLCVデータを使って、次のロウソク足が陽線になるか陰線になるかの予測」をしていきます。
データの前処理
早速やっていきましょう。pythonの機械学習ライブラリscikit-learnを使っていきます。
まずはOHLCVデータを扱いやすい形にしていきます。扱いやすいのでCryptowatchのAPIを利用しましょう。
import requests
# APIを利用してresponseにOHLCVデータを代入
period = 1800 # 時間足(秒単位)
response = requests.get("https://api.cryptowat.ch/markets/bitflyer/btcfxjpy/ohlc",params = { "periods" : period ,"after" : 1})
response = response.json()
さてこのときresponseの中身を見てみるとこんな風になっています。
{'result': {'1800': [[1525528800, 1178158, 1182650, 1175005, 1175005, 2438.5933, 2874265900], [1525530600, 1175044, 1180909, 1174783, 1178000, 1714.3374, 2019921400], [1525532400, 1178161, 1185204, 1177308, 1183100, 1945.2648, 2299375400],...
6000件返ってくるので途中から省略してます。'result'の中の'1800'というキーのvalueに2次元配列が入っていることがわかりますね。この一つ一つの値の意味は[closetime(終値時のunixtime),open(始値),high(高値),low(安値),close(終値),volume(出来高),謎の数値]となってます。最後のやつはなぜかAPIドキュメントに書いていなかったので正体不明です。今回は無視していきましょう。
さて、これを扱いやすい形にしましょう。少し考えたのですが、今回は何の工夫もなく過去5本分のOHLCVデータを順番に並べた25次元のデータにしましょう。(普段からデータ分析をされているような方から呆れられそうな雑なやり方ですが)
numpyのarrayの形にしてxに代入していきます。それと同時に正解ラベルを「陽線なら1、陰線なら0」と決めてyに代入していきます。
import numpy as np
x_data = []
y_data = []
for i in range(5994):
# xの要素
arr = np.array(response['result'][str(period)][i:i+5])
x_data.append(arr[:,1:6].ravel())
# yの要素
if response['result'][str(period)][i+6][4] - response['result'][str(period)][i+6][1] > 0:
target = 1
else:
target = 0
y_data.append(target)
x = np.array(x_data)
y = np.array(y_data)
これでxには50次元のarrayが5994個入った2次元array、yには0または1の数値が5994個入った1次元arrayが代入されたことになります。
テスト(1回目)
まずはこの状態で試してみましょう。ロジスティック回帰を識別器に用いてトレーニングデータを時系列に沿って古い方から5割、テストデータを残り5割としてやってみます。
# 識別器の選択
from sklearn import linear_model
clf = linear_model.LogisticRegression()
# トレーニングとテストのデータ数を決める
n_samples = x.shape[0]
n_train = n_samples//2
n_test = n_samples-n_train
# 扱いやすいようにindexを作る
train_index = range(0,n_train)
test_index = range(n_train,n_samples)
# データをセットする
x_train,x_test = x[train_index],x[test_index]
y_train,y_test = y[train_index],y[test_index]
# 学習とテスト
clf.fit(x_train,y_train)
print(clf.score(x_train,y_train))
print(clf.score(x_test,y_test))
これを実行するとトレーニングデータとテストデータに対する正解率が順に出ます。さあ、ついに結果が出るわけですね!わくわく。
結果と考察
出力結果
0.5348682015348682
0.4894894894894895
ど゛う゛し゛て゛な゛ん゛だ゛よ゛お゛お゛ぉ゛お゛!゛!゛!゛ん゛あ゛あ゛あ゛あ゛あ゛ぁ゛ぁ゛あ゛あ゛!゛!゛!゛!゛
養゛分゛は゛永゛遠゛に゛養゛分゛な゛の゛か゛よ゛お゛お゛お゛お゛お゛!゛!゛!゛
おっと失礼、取り乱してしまいました。
陽線か陰線の2択予想なのでコイントスで予測するのと変わらない結果なわけです。そもそもトレーニングデータに対してすら50%ちょっとの正解率しか出せていないので、テストデータに通用するはずもありません。予測(運任せでないとは言ってない)です。
さあ、それでは何か改善できる点はあるのでしょうか。
自分でやっておいてあれなんですが、このやり方には問題しかありません。冷静に考えてみて無茶苦茶なテストです。ぱっと思いつく問題点を列挙していきます。
扱うデータそのものについて
・価格の変動率などではなく単にOHLCVの値を使っている
・過去5本分しか考慮していない
・扱う時間足を30分足に限定している
・次の足の結果だけを予測している
・時系列データであることを活かせていない
データの処理について
・シャッフルや層別サンプリングを行っていない
・正規化、標準化などスケーリングを行っていない
学習方法について
・識別器をロジスティック回帰に限定している
・パラメータ調整を行っていない
機械学習が通用しない可能性
・そもそも値動きは完全にランダムで予想できるものではない?
最後のやつは元も子もないですね・・・。効率的市場仮説でしたっけ?裁量で利益出せている人がいる以上、何かしらの法則や確率の偏りはあると信じたいものです。ということでそれ以外について考えてみましょう。
個人的には「扱うデータそのものについて」の問題点が一番大きいのかなと思っています。ここをまず改善していきましょう。データ処理関連の専門的な言葉についてはそれにとりかかるときにまた説明したいと思います。
というわけで
いったん今回はここで切ろうかと思います。実験記録なので私もこのシリーズがどのような着地点になるのか想像がつきません。(一瞬「何をやっても予測できませんでした!残念!」というオチが浮かんでしまいましたが気にしないことにしましょう)
機械学習を学びながら同時並行で進めていく感じなので更新頻度は高くないですし、実用的な結果が得られる可能性はけっこう低いですが「なんかやってんな〜」ぐらいに見守っていただけると幸いです。
次回は扱うデータについてまず見直していきたいと思います。それではまた!
第2回はこちら