【Tips】約定履歴からリアルタイムt秒足tickデータを自作する+α (Cryptowatchの利用可能性)
こんにちは、minaulです。
最近、高頻度botばかり作っては、その参入障壁の高さに毎回打ちのめされてるドMです。
次回は高頻度に関するまとめ記事でも書こうかと思います。
今回はその前段階として、高頻度取引には必須の秒足データを作成するTips記事を簡単に執筆します。
t秒足のtickデータを自作するコード
正直この手の記事って既にかなり飽和してると思いますが、意外と任意のt秒だとか値を返すだけのシンプルなやつとか、そんな感じの記事が少なそうなので手短に書いてみます。
ちなみに取引所APIはBitFlyerのRealtime APIではなく、HTTP APIを使っています。
理由はシンプルにするためです。
…いや、筆者自身がWebsocket周りをよく分かってない、てところもありますね。そんなんで高頻度云々を語るなと。全くその通りです、すいません。
厳密なリアルタイム性を要求する場合は、WebsocketでRealtimeAPIを使うことを推奨します。
最初に必要なライブラリをインポートします。
# ライブラリのインポート
import datetime
import time
import numpy as np
import ccxt
bitflyer = ccxt.bitflyer() # 約定履歴はパブリックAPIなので、APIキーなどは不要
次に本題の関数を書きます。
コード自体はかなりシンプルな作りで事足ります。
# 約定履歴から秒足tickデータを作成する関数
def get_tick(t):
exp_list = [] # 約定価格の配列
exv_list = [] # 約定サイズの配列
ext_list = [] # 約定時間の配列
try:
execution = bitflyer.public_get_getexecutions(params = {"product_code" : "FX_BTC_JPY", "count" : 500}) # countは任意、指定しなければ100
except Exception as e:
#logger.info(e) # ログ設定してるならコメントアウト
time.sleep(1)
execution.reverse() # 約定の順番を逆順にする
now_time = datetime.datetime.now() # 現在時刻(リアルタイム)の参照
for i in range(len(execution)):
try:
exe_time = datetime.datetime.strptime(execution[i]["exec_date"], '%Y-%m-%dT%H:%M:%S.%f') # 文字列を時間に変換
except Exception as e:
exe_time = datetime.datetime.strptime(execution[i]["exec_date"], '%Y-%m-%dT%H:%M:%S') # 稀にミリ秒以下が記述されない例外が存在
exe_time = exe_time + datetime.timedelta(hours=9) # 約定時間を日本時間に合わせる
if exe_time + datetime.timedelta(seconds=t) > now_time: # 現在時刻から過去t秒間の約定履歴を参照
exp_list.append(float(execution[i]['price']))
exv_list.append(float(execution[i]['size']))
ext_list.append(exe_time)
# 約定データをOHLCVデータに集計して返す
return {'open':exp_list[0], 'high':np.max(exp_list), 'low':np.min(exp_list), 'close':exp_list[-1], 'volume':np.sum(exv_list)}
基本的にはコメントを挟んでいるので、それを元に読み解いてください。
大まかには、現在時間との差分をt秒で定義して、その間の約定情報を受信して秒足OHLCVデータに加工している、というのが上記コードの流れになります。
ポイントを1つだけ挙げると、受け取った約定履歴の時間情報には基本的にミリ秒(ms)が含まれていますが、まれにms以下が含まれない場合もあり、この対処は必須になります。
実際に先ほどの関数を使いたい場合は、以下のように書けば、例えば10秒足tickデータを10秒間隔で出力し続けます。
t = 10 # 例えばこれだと10秒足を作成できます
# t(=10)秒間隔でループを回す
while True:
tick = get_tick(t)
print(tick)
time.sleep(t)
出力結果はこんな感じです。
必要に応じて色々と使い分けてみてください。
{'open': 2708032.0, 'high': 2708287.0, 'low': 2707821.0, 'close': 2708287.0, 'volume': 0.035}
{'open': 2708297.0, 'high': 2708297.0, 'low': 2708193.0, 'close': 2708193.0, 'volume': 0.03}
{'open': 2707933.0, 'high': 2708012.0, 'low': 2707854.0, 'close': 2708012.0, 'volume': 0.0915}
{'open': 2708210.0, 'high': 2708529.0, 'low': 2707977.0, 'close': 2708529.0, 'volume': 0.2581}
{'open': 2708476.0, 'high': 2708529.0, 'low': 2706828.0, 'close': 2706828.0, 'volume': 0.35301588}
{'open': 2706932.0, 'high': 2706932.0, 'low': 2706270.0, 'close': 2706335.0, 'volume': 0.5246875000000001}
{'open': 2706350.0, 'high': 2706764.0, 'low': 2706205.0, 'close': 2706764.0, 'volume': 0.599592}
{'open': 2706164.0, 'high': 2706164.0, 'low': 2705917.0, 'close': 2705917.0, 'volume': 0.36000000000000004}
{'open': 2706004.0, 'high': 2706774.0, 'low': 2706004.0, 'close': 2706687.0, 'volume': 0.32887743}
{'open': 2706576.0, 'high': 2706576.0, 'low': 2706413.0, 'close': 2706413.0, 'volume': 0.10600215}
Cryptowatchなどの外部データを使うべきか
ティックデータに関連して、一つ小話をしてみたいと思います。
みなさんはCryptowatchやCryptCompareなどの外部サービスを使ったことはあるでしょうか。
かくいう私もbot作成の入り口として、このような外部サービスの利用を紹介しているブログを参考にしていたため、最近までこれらを一部組み合わせて動かしていました。
実際にこういったサービスを利用してみて分かったことがあります。
確かにこれらのサービスは無料でありながら、チャート価格に近いティックデータを返してくれる良サービスだと思います。
ただし、大幅な価格変動時には反映が数分単位で乖離(≒遅延)する、という事象が幾度も確認されました。
大きな短期価格変動のことをジャンプと呼んだりしますが、仮想通貨のような高ボラティリティな金融商品ではこのジャンプがかなり頻発します。
高頻度取引では、このジャンプ現象にかなり頭を悩ませることになるのですが、そんな時に数分単位での乖離(実質的には遅延)というのは、致命的な逆選択リスクに晒されることを意味します。
低頻度ではともかく高頻度の場合は、Cryptowatch等の外部サービスの利用は大きな遅延コストを抱えることになるので、避けた方が賢明でしょう。
その点、約定履歴や板情報などはリアルタイム性の高いデータとなっており、これらの情報を織り込んだティックデータの活用が高頻度においては必須になります。
例えば、純粋なmmbotなどはわざわざOHLCVの形式に整える必要はないと思いますが、ルールベースの場合は今回のようなティックデータを自炊する、というのが今更ですが当たり前なのだろうなと思います。
といった感じで、締めたいと思います。(急に雑)
もし、高頻度なんてやったことないよ、とかティック系は外部サービスに頼ってたよ、という方が見てくださっていたら、参考にして頂ければ幸いです。