クリプトHFTとか競プロとか

競技プログラミングや仮想通貨に関することを中心にブログを書いていきます.

【ファイナンス機械学習】ビットコインの価格の分数次差分を取ってみる【機械学習】

この記事では,分数次差分を取ったBTCの価格データを作成する方法について紹介しています.

モチベーション

ファイナンス機械学習第五章には,

価格の時系列データは低いシグナルノイズ比を示すことが知られている. 加えて,整数次差分のようにデータを定常時系列に変換するための標準的な手法は, メモリー(時系列の平均値を時間経過に従ってシフトさせる過去の水準の長期履歴)を取り除くことでさらにシグナルを弱めてしまうことになる. 一方,リターンのように整数次差分をとった時系列は,有限のサンプル期間外の履歴がすべて無視されているという意味で,メモリーが除去されている.

と書いてある.

BTCの値動きの差分を取ることで,データを定常時系列に変換することがあるが,それは原系列が持ってる長期的な記憶を消していることになっているそうだ.

さらに,

もし,特徴量が定常でなければ,新しい観測値をたくさんの既知の学習データへとマッピングすることができない. しかし,定常性は予測力を保証しない. 定常性は機械学習アルゴリズムが高いパフォーマンスをあげるための必要条件であるが,十分条件ではない. 問題は,定常性とメモリートレードオフが存在することである. 差分をとることで時系列をより定常にすることは常に可能であるが,その代償として メモリーを消してしまうことになり,予測という機械学習アルゴリズムの目的を損なうことになる.

とある.

そこで,筆者であるMarcos lopez de pradoは,1階差分や2階差分などではなく,分数次差分を取ることによってそのジレンマを解決することができるとしている.

なので,実際にBTCのデータについて分数次差分を取って,機械学習モデルに学習させることによって,学習能力が向上したかを検証したい.

今回の記事では実際に分数次差分をとった系列を作るとこまで行っていく.

使うデータ, ライブラリ

今回はbybitの5月中の約定データから作ったthreshold=50000としたドルバーを用いて,検証していく.

データの作り方については

kabukimining.hateblo.jp

を参照してほしい.

今回はmlfinlabとstatsmodelsを使っていく.*1

どちらも,下記コマンドを使ってpipからインストールすることができる.*2

pip install mlfinlab

pip install statsmodels

実際にやってみる

データを読み込む.

import pandas as pd
import numpy as np

# bybitでのthreshold=50000としてサンプリングした5月中のドルバーを読み込む
df = pd.read_csv("data/dollar_bybit-2020-05-01-2020-05-31.csv")
df = df.drop(columns=["date_time", "Unnamed: 0", "open", "high", "low", "tick_num", "buyTick_num", "sellTick_num", "volume",\
                     "buy_vol", "sell_vol", "vwap"])
print(df.head())

実際に"close"というcolumnを含んだDataFrameであれば,どんな形式でも問題はない.

    close
0  8628.5
1  8628.5
2  8625.5
3  8625.5
4  8625.5

現系列を表示してみる

%matplotlib inline

# 原系列を表示
prices = df.close
prices.plot()

表示した様子

分数次差分を取ってみる

mlfinlabにあるFractionalDifferentiationクラスを使うことで,簡単に分数次差分を計算することができる.

なお,計算コストが重いのか一回の計算に結構な時間がかかるので,CPUへの負荷も考慮した上で行ってほしい.

frac_diff_ffd関数の第2引数dは, 1.0にすることで,一階差分になり,dは小さければ小さいほど原系列の持つメモリーを保持し,定常性を失っていく.

from mlfinlab.features.fracdiff import FractionalDifferentiation

fdiff = FractionalDifferentiation()
# d = 1.0
d_10 = fdiff.frac_diff_ffd(df, 1)
d_10.close.plot()

d=1でのFFD系列

先程の通り,d=0.0001などに設定すると原系列の持つメモリーを多く保持する.

# d=0.0001 (ほとんど差分を取らない)
d_1 = fdiff.frac_diff_ffd(df, 0.0001)
d_1.close.plot()

d=0.0001でのFFD系列

最適なdを探す

ファイナンス機械学習にあるように,パラメータdには大きくすればするほど定常性を持つが,メモリーを失うというトレードオフの関係がある.

(定常性を保ちつつ,できるだけメモリーを大きくしたい.)

そこで,その時系列データが定常であるかどうかを調べることができるADF検定と二分法を用いて適切なdを探していく.*3*4

statsmodelsというパッケージのadfuler関数を使うことでADF検定をすることができ,今回は

有意水準: 0.01

帰無仮説:「FFD系列は非定常である」

対立仮説を「FFD系列は定常である」

としてADF検定を行った. *5

先述の通り,FFD系列を作るfrac_diff関数は結構なマシンパワーが必要なので,クラウドなどの従量課金制のプラットフォームを使ってる場合は注意してほしい.

# 定常性を保ち(ADF検定でP値 > 0.01),最大のメモリーを持つようなd(0.01 ~ 1) を二分法で探索する
# d は大きければ大きいほど定常性をもち,メモリーを失うので, 定常性を持つ範囲でメモリーをもたせたい(p > 0.05となるような範囲での最低のdを探す)
from statsmodels.tsa.stattools import adfuller

eps = 1e-3
ok = 1
ng = 0.0001
mid = (ok+ng)/2

while ok-ng > eps:
    mid = (ng+ok)/2
    d_ = fdiff.frac_diff(df, mid)
    d_ = d_.fillna(d_.close.mean())["close"].values
    result = adfuller(d_, maxlag=1, regression="c")
    p_ = result[1]
    
    # 帰無仮説を棄却できない. データは非定常
    if p_ > 0.01:
        ng = mid
        print(f"ng! mid:{mid} p_value:{p_}")
    # 帰無仮説を棄却できる. データは定常
    else:
        ok = mid
        print(f"ok! mid:{mid} p_value:{p_}")
    

print(f"{ng}, {ok}, {mid}, {result}")

dについてパラメータを探索した結果

今回の結果によると,d=0.05868789062499999でデータは定常になるらしい.

見つけたパラメータで作ったFFD系列を図示してみる
d_005 = fdiff.frac_diff(df, 0.05868789062499999)
d_005.plot()

d=0.05...でFFD系列を作り図示したところ,以下のようになった.

d=0.05でのFFD系列

私の目にはこのデータは非定常であるように見えるが,ADF検定によると有意に定常であるらしい.

終わりに

次の記事では,このデータを使って学習させて実際に1次差分をとった場合との比較もしていきたい.

参考文献

*1:別にmlfinlabライブラリを使わずとも、pandasなどで分数次差分は実装できる。これらのやり方はインターネット上にも記事があるし、ファイナンス機械学習でも具体的にかかれている。より具体的には、\alpha次差分を計算するには、バックシフトオペレータBについて、 (1-B)^{\alpha} =  \sum_{k=0}^{\infty}  \binom{n+k-1}{k} B^{k} を(適宜打ち切ったりしながら)原系列に作用させればよい。ただ、この記事を書いていた当時はその点を理解していなかったので、ライブラリに頼っている。

*2:mlfinlabについては,anaconda環境でのインストールが推奨されている

*3: ADF検定についてよく知りません. ごめんなさい. 詳しくはwikipedia参照

*4:検定をするのに,何度もパラメータを変えて有意水準を考えることは統計的にどうなのかという指摘については,わかりませんとしか答えられません. ごめんなさい

*5:参考: https://bellcurve.jp/statistics/course/9313.html, https://logics-of-blue.com/time-series-regression/