📚
約定履歴と戯れて、タイムバーに情報を追加してみる
きっかけ
以前からタイムバーを約定履歴から作るなら、タイムバーの中にあるトレードの統計的特性を約定履歴から作って、タイムバーの内部のより細かい情報として追加したいと思っていたので、今回はそれをやってみました。
やること
- OHLCVの情報に加えて、全約定価格の重み付き平均、重み付き分散、重み付き歪度、重み付き尖度を計算して、タイムバーの情報として追加します
本当は重み付き分位点の情報を追加したいのですが、まだ求め方を今ひとつ理解できていないので保留しています。ここにかなり丁寧な説明があるので、後で読んで理解したいものです。ベータ関数ってなんぞ?
計算のしかた
stackoverflowに書いてあったものを使うことにしました。
kurt()
の流儀に合わせて3を引いた値を返すようにします。
Wikipediaより引用
尖度には、4次の標準化モーメントとも呼ばれる
から3を引いて正規分布の尖度を 0 とする定義と、4次の標準化モーメントをそのまま用いて正規分布の尖度を 3 とする定義があることに注意。 \mu_4 / \sigma^4
コード
いくつか自分で理解しづらかったポイントを抜き出しておきます。
重み付きのn次モーメントを計算する関数
def calc_weighted_moment(values, weights, n, sum_weights = None, weighted_mean = None, weighted_var = None):
assert n > 0
assert values.shape == weights.shape
if sum_weights is not None:
_sum_weights = sum_weights
else:
_sum_weights = np.sum(weights)
if _sum_weights == 0:
return np.nan
if n == 1:
return np.sum(weights * values) / _sum_weights
if weighted_mean is not None:
_weighted_mean = weighted_mean
else:
_weighted_mean = np.sum(weights * values) / _sum_weights
if n == 2:
return np.sum(weights * (values - _weighted_mean) ** 2) / _sum_weights
if weighted_var is not None:
_weighted_var = weighted_var
else:
_weighted_var = np.sum(weights * (values - _weighted_mean) ** 2) / _sum_weights
_weighted_std = np.sqrt(_weighted_var)
if n == 4:
return np.sum(weights * ((values - _weighted_mean) / _weighted_std) ** n) / _sum_weights - 3
return np.sum(weights * ((values - _weighted_mean) / _weighted_std) ** n) / _sum_weights
上の関数を使って重み付きn次モーメントのDataFrameを生成して、タイムバーのDataFrameと結合する部分
_interval_str = f'{interval}S'
...(中略)...
def custom_resampler(x):
_total_quote_qty = x['quote_qty'].sum()
_buy_trade_count = len(x[x['is_buyer_maker'] == False].index)
_sell_trade_count = len(x) - _buy_trade_count
_buy_quote_qty = x.loc[x['is_buyer_maker'] == False, 'quote_qty'].sum().astype(float)
_sell_quote_qty = _total_quote_qty - _buy_quote_qty
_weighted_price_mean = calc_weighted_moment(x['price'], x['quote_qty'], 1, sum_weights = _total_quote_qty)
_weighted_price_var = calc_weighted_moment(x['price'], x['quote_qty'], 2, sum_weights = _total_quote_qty, weighted_mean = _weighted_price_mean)
_weighted_price_std = np.sqrt(_weighted_price_var)
_weighted_price_skew = calc_weighted_moment(x['price'], x['quote_qty'], 3, sum_weights = _total_quote_qty, weighted_mean = _weighted_price_mean, weighted_var = _weighted_price_var)
_weighted_price_kurt = calc_weighted_moment(x['price'], x['quote_qty'], 4, sum_weights = _total_quote_qty, weighted_mean = _weighted_price_mean, weighted_var = _weighted_price_var)
return pd.Series([_buy_trade_count, _sell_trade_count, _buy_quote_qty, _sell_quote_qty, _weighted_price_mean, _weighted_price_var, _weighted_price_skew, _weighted_price_kurt, _weighted_price_std], ['buy_trade_count', 'sell_trade_count', 'buy_quote_qty', 'sell_quote_qty', 'vw_price_mean', 'vw_price_var', 'vw_price_skew', 'vw_price_kurt', 'vw_price_std'])
_df_statistics = _df.groupby(pd.Grouper(freq = _interval_str)).apply(custom_resampler)
_df_timebar = pd.concat([_df_timebar, _df_statistics], axis = 1)
コード全体はこちらです。
結果
とりあえずそれっぽいデータがタイムバーのデータファイルに入るようになりました。よかったよかった。
それっぽいデータ
おまけ
せっかくなので、ちょっと散布図を書いてみました。
- 対象期間は2019-09-08から2022-11-04です
- 対象銘柄はBTCUSDT永久先物です
- 対象の時間足は1時間足です
結果を見た感じ、ひと目でめっちゃつよい! というわけではなさそうですね。将来役に立つといいんですが。
実験用のコード
import pandas as pd
import numpy as np
import datetime
import matplotlib.pyplot as plt
import japanize_matplotlib
from tqdm.auto import tqdm
import exercise_util
df_timebar_1h = exercise_util.concat_timebar_files('BTCUSDT', 3600)
df_timebar_1h['lr'] = np.log(df_timebar_1h['close']) - np.log(df_timebar_1h['close'].shift(1))
df_timebar_1h['lr_future'] = df_timebar_1h['lr'].shift(-1)
df_timebar_1h['vw_price_std_future'] = df_timebar_1h['vw_price_std'].shift(-1)
df_timebar_1h.replace([np.inf, -np.inf], np.nan, inplace = True)
df_timebar_1h = df_timebar_1h.dropna()
exercise_util.show_correlation(np.log(df_timebar_1h['close']) - np.log(df_timebar_1h['vw_price_mean']), df_timebar_1h['lr_future'], xaxis_label = 'クローズ価格 - 重み付き平均価格', yaxis_label = '未来の対数リターン', legend_loc = 'upper right')
exercise_util.show_correlation(df_timebar_1h['vw_price_skew'], df_timebar_1h['lr_future'], xaxis_label = '重み付き歪度', yaxis_label = '未来の対数リターン', legend_loc = 'upper right')
exercise_util.show_correlation(df_timebar_1h['vw_price_std'], df_timebar_1h['vw_price_std_future'], xaxis_label = '重み付き標準偏差', yaxis_label = '未来の重み付き標準偏差', legend_loc = 'upper right')
あるバーのクローズ価格と重み付き平均価格の変化率 vs 1本後のバーの対数リターン
あるバーの重み付き歪度 vs 1本後のバーの対数リターン
あるバーの重み付き標準偏差 vs 1本後のバーの重み付き標準偏差
Discussion