🙄

Binance永久先物の5秒足と1時間足で、移動平均とATRと戯れる

2022/10/31に公開

きっかけ

Binance永久先物の約定履歴と、いろんな時間間隔のタイムバーと戯れるで、色々な時間間隔のタイムバーを作って分析ができるようになったものの、どのタイムバーを選べばいいのかはまだよくわかっていません。

とりあえず、時間間隔が短い足と長い足で、移動平均線の振る舞いに違いがあるかを確認してみることにしました。

やること

5秒足と1時間足で7日移動平均と、1日移動平均の振る舞いに違いが見られるかを目視で確認する。

やったこと

以下のようなコードをJupyter Notebookで実行して確認しました。

(2019年10月から2022年10月までの5秒足のメモリ使用量がたったの1.5GBなのは助かります。約定履歴に比べてかなり扱いやすいです。)

コード
timebar_exercise.ipynb
import pandas as pd
import matplotlib.pyplot as plt
import japanize_matplotlib
import exercise_util

df_1h = exercise_util.concat_timebar_files('BTCUSDT', 3600)
df_5sec = exercise_util.concat_timebar_files('BTCUSDT', 5)

df_1h['close_7dayma'] = df_1h['close'].rolling(7 * 24).mean()
df_1h['close_1dayma'] = df_1h['close'].rolling(24).mean()
df_5sec['close_7dayma'] = df_5sec['close'].rolling(7 * 24 * 60 * 60 // 5).mean()
df_5sec['close_1dayma'] = df_5sec['close'].rolling(24 * 60 * 60 // 5).mean()

print(df_1h.info())
print(df_5sec.info())

plt.plot(df_1h.loc[df_1h.index > '2022-10-24', 'close_7dayma'], label = 'df_1h.7dayMA', linewidth = 0, marker = 'x', markersize = 1)
plt.plot(df_1h.loc[df_1h.index > '2022-10-24', 'close'], label = 'df_1h.close', linewidth = 1, alpha = 0.5, color = 'blue')
plt.plot(df_5sec.loc[df_5sec.index > '2022-10-24', 'close_7dayma'], label = 'df_5sec.7dayMA', linewidth = 0, marker = '.', markersize = 1)
plt.plot(df_5sec.loc[df_5sec.index > '2022-10-24', 'close'], label = 'df_5sec.close', linewidth = 1, alpha = 0.5)
plt.xticks(rotation = 45)
plt.legend()

出力はこんな感じになりました。

<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 27535 entries, 2019-09-08 17:00:00 to 2022-10-29 23:00:00
Freq: 3600S
Data columns (total 10 columns):
 #   Column             Non-Null Count  Dtype  
---  ------             --------------  -----  
 0   open               27535 non-null  float64
 1   high               27535 non-null  float64
 2   low                27535 non-null  float64
 3   close              27535 non-null  float64
 4   buy_trade_count    27535 non-null  int64  
 5   sell_trade_count   27535 non-null  int64  
 6   quote_buy_volume   27535 non-null  float64
 7   quote_sell_volume  27535 non-null  float64
 8   close_7dayma       27368 non-null  float64
 9   close_1dayma       27512 non-null  float64
dtypes: float64(8), int64(2)
memory usage: 2.3 MB
None
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 18869760 entries, 2019-09-13 00:00:00 to 2022-10-29 23:59:55
Data columns (total 10 columns):
 #   Column             Dtype  
---  ------             -----  
 0   open               float64
 1   high               float64
 2   low                float64
 3   close              float64
 4   buy_trade_count    int64  
 5   sell_trade_count   int64  
 6   quote_buy_volume   float64
 7   quote_sell_volume  float64
 8   close_7dayma       float64
 9   close_1dayma       float64
dtypes: float64(8), int64(2)
memory usage: 1.5 GB


7日移動平均のグラフ


1日移動平均のグラフ

なぜだか1時間足で作った移動平均のほうが、5秒足よりも先行しているようですが…あ、そうか。

タイムバーのインデックスは、そのタイムバーがオープンした瞬間なんでした。なので、1時間足のほうが5秒足よりもおおよそ1時間前にクローズ価格がプロットされてしまうんですね。なので、クローズ価格をshift(1)してからあれこれ計算する必要がありそうです。

気を取り直してプロットし直すと…。


7日移動平均のグラフ


1日移動平均のグラフ

タイムバーの時間間隔が違っても、移動平均はほぼ同じ値をとることがわかりました。

ATRはどうだろう

移動平均については、時間間隔の小さいタイムバーと時間間隔の大きいタイムバーはほぼ同じ値を取ることがわかりました。これなら、長い時間間隔のタイムバーで使ったロジックを短い時間間隔のタイムバーに移植できるかも?

ただ、タイムバーの間隔に大きな影響を受けるインジケーターもあります。例えばATRの計算方法は以下の3つの最大値を該当タイムバーのATRとし、n日 (一般的には14日) 分の平均値を取ってATRとします。

  • 該当タイムバーのHigh – 該当タイムバーのLow
  • 該当タイムバーのHigh – 直前タイムバーのClose
  • 該当タイムバーのLow – 直前タイムバーのClose

この場合、5秒足のATRは1時間足のATRよりもずっと小さなものになってしまいますので、以下のように安直に移動平均と同じように処理をすると、全く違うインジケータの値になってしまいます。

失敗例
df_1h['1dayatr'] = talib.ATR(df_1h['high'], df_1h['low'], df_1h['close'], timeperiod = 24)
df_5sec['1dayatr'] = talib.ATR(df_5sec['high'], df_5sec['low'], df_5sec['close'], timeperiod = 24 * 60 * 60 // 5)


タイムバーの間隔によって明らかに値の違うATRのグラフ

おわりに

ということで、同じロジックをいろいろな時間間隔のタイムバーで使いまわす場合は、執行ロジックがタイムバーの時間間隔に強く依存したインジケーターを使っていないかに注意する必要があることがわかりました。

Discussion