1 この記事は何?
「ファイナンス機械学習第2.3.2.2節 ドルインバランスバー」の考え方、実際の使い方を学習したので、学習メモを残します。
「ファイナンス機械学習第2.3.1.4節 ドルバー」には、証券の売買代金があらかじめ決めたスレッシュを上回った時点でOHLCVをサンプリングするという考えが紹介されていますが、あらかじめ決めたスレッシュよりも動的に設定するスレッシュを採用する方が、サンプリングされたデータ群がより統計学的に扱いやすいデータになることが記載されています。この意味を「ファイナンス機械学習 練習問題2.2」を学習することで考えてみたいと思います。
学習できたこと
・ドルインバランスバーの生成アルゴリズム
・mlfinlabライブラリを使い、ドルバーとドルインバランスバーを算出する方法
・ドルバーよりもドルインバランスバーの方が、強い自己相関係数がある。
2 内容
「ファイナンス機械学習 練習問題2.2」
(1)E-mini S&P 500先物データに対して、ドルバーとドルインバランスバーを計算せよ。
(2)どちらのバーの系列相関(自己相関)が大きいか。
(3)それはなぜか?
2-1 理論の説明
期間$T$の不均衡を定義します。期間Tは、それぞれのOHLCVバーがサンプリングされるまでのティック数のことです。
Θ_{T}=\sum_{t=1}^{T} b_{t}v_{t}
ただし、$v_{t}$はティックtでの売買代金を示し、$b_{t}$は下記で定義される値をとります。
b_{t} = \left\{
\begin{array}{ll}
1 & (p_{t}>p_{t-1}) \\
-1 & (p_{t}<p_{t-1})
\end{array}
\right.
ここで、$Θ_{T}$の期待値を計算する。
E[Θ_{T}]=E[T] (P[b_{t}=1] E[v_{t}|b_{t}=1] - P[b_{t}=-1]E[v_{t}|b_{t}=-1]) \tag{1}
ここで、
E[v_{t}]=P[b_{t}=1]E[v_{t}|b_{t}=1] + P[b_{t}=-1]E[v_{t}|b_{t}=-1]
ということなので、
式(1)は、下記のとおり書き換えることができる。
よって、
Θ_{T}>E[Θ_{T}] \tag{2}
となった事象が起きればOHVLCバーを新たに生成するという流れになる。
2-2 データの準備
S&P500 miniのデータを下記サイトから取得します。
https://s3-us-west-2.amazonaws.com/tick-data-s3/downloads/ES_Sample.zip
ダウンロードしたデータをそのまま使用するとデータ量も多く計算に時間を要するため、先頭から1/5までのデータのみを取り出します。
#【STEP0】★E-mini S&P 500先物データを準備する。
PATH='./ES_Sample/ES_Trades.csv'
tmp=pd.read_csv(PATH) #E-mini S&P 500先物データを読み出す。
df=tmp.iloc[0:math.floor(len(tmp['Symbol'])/5)] #E-mini S&P 500先物データの先頭1/5のみを抽出する。
df.to_csv('./ES_Sample/ES_Trades1_5.csv' ) #csvファイルにデータを書き出す。
2-3 データ成形を行います。
上記で用意したS&P500 miniのデータから['date', 'price', 'volume']の行のみを取り出す。次のステップで使用するライブラリ関数に適用したデータ形式にするためです。
#指定されたWebページからダウンロードしたE-mini S&P 500先物データ
PATH0='./ES_Sample/ES_Trades1_5.csv'
#ライブラリ関数に入力できるように、「ES_Trades1_5.csv」の中で「date」と「price」と「volume」のみを取り出したcsvファイルを格納するディレクトリ。
PATH1='./ES_Sample/raw_tick_data1_5.csv'
data = pd.read_csv(PATH0)
data
#(1)ライブラリ関数に入力できるように、「ES_Trades1_5.csv」の中で「date」と「price」と「volume」のみを取り出したデータテーブルを作成する。
date_time = data['Date'] + ' ' + data['Time'] # Dont convert to datetime here, it will take forever to convert.
new_data = pd.concat([date_time, data['Price'], data['Volume']], axis=1)
new_data.columns = ['date', 'price', 'volume']
print(new_data.head())
print('\n')
print('Rows:', new_data.shape[0])
#(2) 上記(1)で作成したデータをcsvファイルに書き出す。以降ティックデータと呼ぶ。
new_data.to_csv(PATH1, index=False)
2-4 ドルバーを作ります。
ドルバーは、mlfinlabライブラリのget_dollar_bars関数を使えば生成ができます。
threshold=4E5,8E5,16E5の3通りに設定し、それぞれについてドルバーを作成しました。
import mlfinlab as ml
#【STEP2】STEP1で作成したティックデータからドルバーを生成する。生成したドルバーをcsvファイルに書き出す。
PATH1='./ES_Sample/raw_tick_data1_5.csv'
dollar4E5 = ml.data_structures.get_dollar_bars(PATH1, threshold=40000, batch_size=1000000, verbose=False, to_csv=True,output_path='./ES_Sample/dollar_var_4E5.csv')
dollar8E5 = ml.data_structures.get_dollar_bars(PATH1, threshold=80000, batch_size=1000000, verbose=False, to_csv=True,output_path='./ES_Sample/dollar_var_8E5.csv')
dollar16E5 = ml.data_structures.get_dollar_bars(PATH1, threshold=160000, batch_size=1000000, verbose=False, to_csv=True,output_path='./ES_Sample/dollar_var_16E5.csv')
2-5 ドルインバランスバーを作成します。
ドルインバランスバーは、mlfinlabライブラリのget_ema_dollar_imbalance_bars関数を使えば生成ができます。get_ema_dollar_imbalance_bars関数の引数のうち、ticks_constraintsの与え方次第で、出力結果が異なるので、ticks_constraints=[100, 10000],[100, 5000],[100, 7000]の3通りに設定しドルインバランスバーを作成しました。ticks_constraintsの設定値は結果の収束性に関わっているとマニュアルに記載がありましたが、実際にコードを見ることができないのでどのような作用をしているのかがわかりません。
関数:get_ema_dollar_imbalance_bars
引数の説明
• num_prev_bars – (int) Window size for E[T]s (number of previous bars to use for expected number of ticks estimation)
• expected_imbalance_window – (int) EMA window used to estimate expected imbalance
• exp_num_ticks_init – (int) Initial expected number of ticks per bar
• exp_num_ticks_constraints – (list) Minimum and maximum possible number of expected ticks. Used to control bars sampling convergence
• batch_size – (int) The number of rows per batch. Less RAM = smaller batch size.
• verbose – (bool) Print out batch numbers (True or False)
• to_csv – (bool) Save bars to csv after every batch run (True or False)
• analyse_thresholds – (bool) Flag to save and return thresholds used to sample imbalance
bars
• output_path – (str) Path to csv file, if to_csv is True
Returns (pd.DataFrame) DataFrame of dollar imbalance bars and DataFrame of thresholds, if
to_csv=True returns None
import mlfinlab as ml
#【STEP3】STEP1で作成したティックデータからドルインバランスバーを生成する。
PATH0='./ES_Sample/raw_tick_data1_5.csv' #ティックデータcsvファイルのpath
#(パターン1)ticks_constraints=[100, 10000],expected_imbalance_window=10000と設定した場合
PATH1_1E4='./ES_Sample/impanace_para1e4.csv' #ドルインバランスバー作成にあたり、関連するパラメータ値を記録するcsvファイルのpath
PATH2_1E4='./ES_Sample/imbalance_var1e4.csv' #ドルインバランスバーcsvファイルのpath
dollar_imbalance_ema_1E4 = ml.data_structures.get_ema_dollar_imbalance_bars(PATH0, num_prev_bars=3, exp_num_ticks_init=50000,exp_num_ticks_constraints=[100, 10000], expected_imbalance_window=10000,analyse_thresholds=True)
dollar_imbalance_ema_1E4[0].to_csv(PATH2_1E4) #ドルインバランスバー作成にあたり、関連するパラメータ値の算出結果をcsvファイルに書き出す。
dollar_imbalance_ema_1E4[1].to_csv(PATH1_1E4) #ドルインバランスバーの値をcsvファイルに書き出す。
#(パターン2)ticks_constraints=[100, 5000],expected_imbalance_window=10000
PATH1_5E3='./ES_Sample/impanace_para5e3.csv' #ドルインバランスバー作成にあたり、関連するパラメータ値を記録するcsvファイルのpath
PATH2_5E3='./ES_Sample/imbalance_var5e3.csv' #ドルインバランスバーcsvファイルのpath
dollar_imbalance_ema_5E3 = ml.data_structures.get_ema_dollar_imbalance_bars(PATH0, num_prev_bars=3, exp_num_ticks_init=50000,exp_num_ticks_constraints=[100, 5000], expected_imbalance_window=10000,analyse_thresholds=True)
dollar_imbalance_ema_5E3[0].to_csv(PATH2_5E3) #ドルインバランスバー作成にあたり、関連するパラメータ値の算出結果をcsvファイルに書き出す。
dollar_imbalance_ema_5E3[1].to_csv(PATH1_5E3) #ドルインバランスバーの値をcsvファイルに書き出す。
#(パターン3)ticks_constraints=[100, 7000],expected_imbalance_window=10000
PATH1_7E3='./ES_Sample/impanace_para7e3.csv' #ドルインバランスバー作成にあたり、関連するパラメータ値を記録するcsvファイルのpath
PATH2_7E3='./ES_Sample/imbalance_var7e3.csv' #ドルインバランスバーcsvファイルのpath
dollar_imbalance_ema_1E3 = ml.data_structures.get_ema_dollar_imbalance_bars(PATH0, num_prev_bars=3, exp_num_ticks_init=50000,exp_num_ticks_constraints=[100, 7000], expected_imbalance_window=10000,analyse_thresholds=True)
dollar_imbalance_ema_1E3[0].to_csv(PATH2_7E3) #ドルインバランスバー作成にあたり、関連するパラメータ値の算出結果をcsvファイルに書き出す。
dollar_imbalance_ema_1E3[1].to_csv(PATH1_7E3) #ドルインバランスバーの値をcsvファイルに書き出す
ドルインバランスバーの計算に関係あるパラメータが関数get_ema_dollar_imbalance_barsの返り値として得られます。(2)式における、ΘTはcum_theta(関数get_ema_dollar_imbalance_barsの返り値)が該当します。かつ、
E[ΘT]は、expected_imbalance × exp_num_ticks(双方とも関数get_ema_dollar_imbalance_barsの返り値) が該当します。ここで、|ΘT|と|E[ΘT]|の時系列グラフを記載してみました。(パターン1)ticks_constraints=[100, 5000],(パターン2)ticks_constraints=[100, 7000],(パターン3)ticks_constraints=[100, 10000]の3通りでグラフを描写してみました。
|Θ_{T}|>|E[Θ_{T}]| \tag{3}
が満たされなくなればサンプリングがされ、$|Θ{T}|$が0に戻り再び$|Θ{T}|$が増加(途中で減少することもある)に転じていることがわかります。ticks_constraintsの与え方により、関数get_ema_dollar_imbalance_barsが生成するドルインバランスバーのティック数が変化することがわかります。
節2-3で生成したティックデータをticks_constraintsパラメータの値によりドルインバランスバーを3パターン生成しました。設定したticks_contraintsパラメータ値では、節2-3で生成したティックデータは、ドルインバランスバーに変換することによりティックが11%以下に圧縮されました。
パターン | ticks_contraints | 変換前ティック行数 | 変換後ティック行数 | 圧縮率 |
---|---|---|---|---|
パターン1 | [100, 5000] | 1.09E+6 | 1.2E+05 | 11% |
パターン2 | [100, 7000] | 1.09E+6 | 6.9E+04 | 6.3% |
パターン3 | [100, 10000] | 1.09E+6 | 28 | 0.01%以下 |
2-6 ドルバーの自己相関係数の検証
節2-3で生成したティックデータより生成したドルバー(節2.4)の自己相関係数を計算してみました。
import statsmodels.api as sm
#ドルバーとドルインバランスバーについてclose値の前日,当日の変化率のlogを計算する。
dollar_s4e5['log_ret'] = np.log(dollar_s4e5['close']).diff().fillna(0)
dollar_s8e5['log_ret'] = np.log(dollar_s8e5['close']).diff().fillna(0)
dollar_s16e5['log_ret'] = np.log(dollar_s16e5['close']).diff().fillna(0)
#ドルバーとドルインバランスバーについて自己相関係数を算出する
plot_acf(dollar_s4e5.log_ret, lags=10, zero=False,title='dollar_s4e5')
plot_acf(dollar_s8e5.log_ret, lags=10, zero=False,title='dollar_s8e5')
plot_acf(dollar_s16e5.log_ret, lags=10, zero=False,title='dollar_s16e5')
下記グラフがドルバー生成時(節2.4)に設定したthreshold=4E+04,8E+04,16E+04において、生成された各ドルバーの自己相関係数の算出結果です。下記グラフを見る限り、lag=1,2までは有意な相関が観測されていることが分かります。
つまり、本日-昨日, 本日-一昨日までは、有意な相関があるということになると思います。
2-7 ドルインバランスバーの自己相関係数の検証
節2-5で生成したドルインバランスバーの自己相関係数を算出してみました。
import statsmodels.api as sm
#(パターン1)ticks_constraints=[100, 10000]
dollar_imbalance_var1e4=pd.read_csv('./ES_Sample/imbalance_var1e4.csv')
#(パターン2)ticks_constraints=[100, 5000]
dollar_imbalance_var5e3=pd.read_csv('./ES_Sample/imbalance_var5e3.csv')
#(パターン3)ticks_constraints=[100,7000]
dollar_imbalance_var7e3=pd.read_csv('./ES_Sample/imbalance_var7e3.csv')
#ドルバーとドルインバランスバーについてclose値の前日-当日の変化率logを計算する。
dollar_imbalance_var1e4['log_ret'] = np.log(dollar_imbalance_var1e4['close']).diff().fillna(0)
dollar_imbalance_var5e3['log_ret'] = np.log(dollar_imbalance_var5e3['close']).diff().fillna(0)
dollar_imbalance_var7e3['log_ret'] = np.log(dollar_imbalance_var7e3['close']).diff().fillna(0)
#ドルバーとドルインバランスバーについて自己相関係数を算出する
plot_acf(dollar_imbalance_var1e4.log_ret, lags=10, zero=False,title='dollar_s1e4')
plot_acf(dollar_imbalance_var5e3.log_ret, lags=10, zero=False,title='dollar_s5e3')
plot_acf(dollar_imbalance_var7e3.log_ret, lags=10, zero=False,title='dollar_s7e3')
下記グラフがドルバー生成時(節2.5)に設定したticks_constraints=[100,10000],[100,5000],[100,7000]において、生成された各ドルバーの自己相関係数の算出結果です。下記グラフを見る限り、ticks_constraints=[100,10000]の場合は、生成されたティック数があまりにも少なく(28ティック)有意な自己相関が確認できませんでした。一方ticks_constraints=[100,7000]の場合は、生成されたティック数が6.9E+04行存在し、lag=10に至っても有意な自己相関が確認できます。他方、ticks_constraints=[100,5000]の場合は、生成されたティック数は1.2E+05行存在し。lag=3までは自己相関係数が確認できます。つまり、ドルインバランスバーの出力は適度にティック行が存在したほうが自己相関が得られやすいということと思います。かつ、ドルバーの場合生成されたデータは、高々lag=2までしか有意な自己相関が得られませんでしたが、ドルインバランスバーの場合は、ドルバーよりもより大きなlagに至っても有意な自己相関が見られることが分かりました。この点がドルバーよりもドルインバランスバーを使う方が統計学的に好ましいという意味だと思います。