仮想通貨の自動取引入門 ~Pubnubによるマーケット・メイキング~
※bitbank.ccでは既にPubnubが廃止されているようです。WebSocketが代わりにサポートされています。
仮想通貨に限らず自動取引は取引時に人手を介さず、リスクをコントロールしつつコストの削減およびリターンを機械的に追求することであると考える。本稿ではPubnubとbitbank.cc リアルタイムAPIを活用して、Publisherから即時的に得たOHLC情報を活用したマーケット・メイキング・アルゴリズムを実施するプログラムを作成する。

- 自動取引とは(再掲)
- マーケット・メイキング
- PubnubによるリアルタイムAPI
- bitbank.cc サンプルの実装
自動取引とは
金融商品における自動取引とはシステムトレードの非裁量取引を、特にプログラムを用いて自動化しまた定例化することでリスクのコントロールもしくはリターンの追求を省力化した上で達成することであると考える。
システムトレードやアルゴリズム取引という語には使用する組織や文脈に応じて複数の意味があること、また意味の混ざることがあるためにここでは単純に自動取引という用語のみ使用する。[1]
アルゴリズムを使用しないものも含め、自動取引という用語の意味はより広義に捉えて頂いて問題ない。プログラムを介して、取引機会を発見し、それを自動化しているものを自動取引と呼びたいと思う。非裁量性や高頻度取引(High Frequency Trading)など様々な利点のある自動取引であるが、まず大分類としてコストの削減を目指すものとリターンの追求を実現するものの2種類がある。[2]
- コストの削減
・取引コストの削減(執行系アルゴリズム、VAMPなどのベンチマーク系アルゴリズム)
・マーケット・メイクやバスケット取引などの定型的な業務の自動化による内部コストの削減
・マーケット・インパクトによるコストの削減
・最適な市場の選択をすることによる手数料の削減 - リターンの追求
・収益機会の発見(裁定アルゴリズム、ディレクショナル・アルゴリズムなど)
・リベートの獲得(メイカー・テイカー手数料)
・スプレッド収益の発見(マーケット・メイキング・アルゴリズム)
個人投資家についてはよほど大口でない限り、リターン追求のために自動取引を導入すると考えられることから、ここでは収益機会の発見を達成する自動取引をプログラムでどのように記述していくかを考えていく。主に裁定アルゴリズム、ディレクショナルアルゴリズム、マーケット・メイキング・アルゴリズムを個人投資家は利用する。自動取引に限らないが以下のPDCAを回して行くことで(自動取引においては自動的に行うことが好ましい)、効率よく収益機会の発見および収益の発生を実現することが理想である。
1.情報の収集および整理
2.自動取引と約定の確認
3.バックテスト(ベンチマーク)
マーケット・メイキング
マーケット・メイキングとは売買主体が市場の仲値より自己に有利な価格で売り買い双方にメイク注文を行い市場に流動性を提供することで、主に売り買いでの相対によるスプレッド差を収益として獲得することである。
メイカー・テイカー手数料が導入されている取引所においては、メイクによるリベートも収益となる。この場合は、メイカー・リベートが確保されているため、スプレッドによる収益を犠牲にし、スプレッドを縮小することも行われている。bitbankではメイカー・テイカー手数料モデルが現時点で導入されているため、後者に該当する。
つまりスプレッド収益の発見とリベートの獲得を同時に行うか、もしくはスレッド収益を犠牲にしてメイカー・テイカー手数料によるリベートの獲得を目的とできる、となる(以下、前節のリターンの追求より)。
- スプレッド収益の発見(マーケット・メイキング・アルゴリズム)
- リベートの獲得(メイカー・テイカー手数料)
以下は実際にbitbankの板情報を目視で確認し、メイク注文を行った取引例である。メイカーとして手数料が-2.1893発生した。マイナスの手数料を課すことは流動性を提供したことに対する支払いを受け取ったことになる。手数料は以下に記載されている。

市場価格や情勢は刻一刻と変化していくため、マーケット・メイカーは、市場全体の価格や板の状況に合わせて、スプレッドの広さや数量を細かくコントロール必要がある。またこの場合、取引は高頻度かつ高速であれば有利であることは自明である。次節で取り扱うPubnubというライブラリによって板情報に変更のあった際、即座に取引所から板情報を受信し、価格変更に対応する形で取引を行うマーケット・メイキング・アルゴリズムを作成していく。
機械による自動取引では秒間に何度でも板情報を確認できるが、APIはコール数も限られていること、また変化の発生した時点で情報がパブリッシュされてくるため、等間隔の確認に対する価格変化の取りこぼしが発生し得ないことがマーケット・メイキングには有利だと考える。
「執行戦略と取引コストに関する研究の進展」(杉原 2012)によると、1970年代から金融取引に係る執行戦略の研究が盛んとなり、これらの研究は主に取引コスト・モデル、在庫モデル、情報モデルと大別することができるという。

売買主体に有利な仲値の値決めや数量の決定には在庫リスクの逐次評価が重要であり、この在庫リスクの評価および数理的理論は、古くからマーケット・メイキングの最適執行戦略として研究されてきた。PubnubとリアルタイムAPIを利用したマーケット・メイキングの実装は高頻度取引における在庫モデルで読み解けそうである。
『高頻度取引の執行戦略を巡る研究』の章におけるAvellaneda and Stoikov [2008]の研究を追っていく。まずある時刻tにおけるファンダメンタル価格がStである金融商品を売買することを考える。[3]

パラメータから指数効用関数を仮定して、期待効用を求める。γはリスク回避度を表すものとする。

そうすると高頻度取引におけるマーケット・メイキングの最適執行戦略は時刻tにおいて上記の期待効用を最大化する買い気配と売り気配を求めることに帰結する。
ここから各パラメータの変動をモデル化して動的計画法によって解くと以下のような近似的な関係式が得られるという。[4]

式はそれぞれファンダメンタル価格からのオフセット値(ファンダメンタル価格からどれ程の幅を取るか)と売り気配・買い気配のδの式を表している。特にオフセット式に注意したい。式はファンダメンタル価格に対する減算で第2項はリスク回避度と残存ボラティリティ、インベントリ(在庫)からなる関数である。インベントリが正である場合は、ファンダメンタル価格を下回るようにオフセットし、負となる場合は上回るように調整する。bitbankのようにメイカー・テイカー手数料によるリベート獲得ができる市場ではマーケット・メイキングにおいてスプレッドを縮小する傾向があるため、このオフセット値の調整による適切な在庫の管理が重要となる。
ただし次節でも触れるが、インベントリを管理している際に問題となるのがポジション損益である。仮に100BTCという在庫を指定してマーケット・メイキング・アルゴリズムで管理していた場合、相場の下落によってスプレッド収益やリベート収益が消滅する可能性が存在するが、今回作成するコードではポジション損益について考慮しない。
また数式からは残存ボラティリティが高いときにはスプレッドを広げるべき、オフセット量を大きくすべきであることが読み取れるが、ボラティリティが高いときなど相場が荒れているときは取引を停止することを考えるべきである。マーケット・メイキングはポジション損益が安定した相場で実行し、適切にスプレッド収益とリベート収益を計上することが最適解であると考える。他のテクニカル指標と連携して売買シグナルをコントロールすることが求められるだろう。
PubnubによるリアルタイムAPI
高頻度取引の損益は、スプレッド損益とポジション損益に分解できる。ここでのスプレッド損益は、執行価格とファンダメンタル価格の差か ら生じる損益であり、ポジション損益は、ファンダメンタル価格が変動することで ポジション(在庫)の価値が変化することによる損益である。
Menkveld [2011] によると高頻度取引のポジション損益が正値となるのは、ポジション保有時間が5秒未満の場合のみであり、それを超える保有時間ではポジション損益が損失となるほ か、1分以上の保有時間ではリスク量が無視できないほど拡大するという。[5]
したがって高頻度取引においては情報の取得から売買主体の仲値の算出、そして注文までに要する時間を限りなく少なくする努力が求められる。Pubnubを利用することで板情報をAPIによって取得するのではなく、取引所からリアルタイムに受信できる。価格が変更された段階の情報が即座に取得できるため、時間差による取引所との情報の非対称性が解消される。情報の取得についてはネットワークのレイテンシが主な問題となる。
※1点懸念があるとすると、取引所側からの受信を待つ形となるため、Publishがないと何も情報が得られない点である(ネットワークの接続エラーなども対象)。これはAPIなどによる能動的な情報取得と組み合わせる方法が考えられるが本稿では取り扱わない。
bitbankはリアルタイムAPIとしてPubnubを使用している。本節では、Pubnubを使用してbitbank.cc APIから情報を取得できるかテストする。[6]

Pubnubはリアルタイムのデータストリーミングやデバイスシグナリングのためのライブラリで、Publish/Subscribeモデルを採用している。永続的なソケットを確立して、データを1/4秒以下でエンドポイントへ配送することが可能である。
PubNub utilizes a Publish/Subscribe model for real-time data streaming and device signaling which lets you establish and maintain persistent socket connections to any device and push data to global audiences in less than ¼ of a second.
Messages
は Channels
から成り、実際に配送されるデータペイロードへ紐づけられる。bitbankはデータをPublishする際に Channels
を指定することで、Subscribeする側は指定した Channels
に対する Messages
のみを受信することができる。今回使用する Channels
は ticker_btc_jpy
である。
python3.6.0をローカルで使用していたが、ライブラリが動作しなかったためpyenvで3.7.3へ切り替えて操作した。pubnub 4.1.5をインストールしてPubnubのドキュメントに沿って実装していく。[7]
$ pyenv local 3.7.3
$ pip install pubnub
$ pip list | grep pubnub
pubnub 4.1.5
公式のコード例を参考に以下のコードを実装した。
Publishされたデータを受信すると message.channel
および message.message
を標準出力表示するプログラムである。
def message(self, pubnub, message):
# handle new message stored in message.message
print("channel: {0}\nmessage: {1}".format(message.channel, message.message))
実行結果は以下の通りで、 message.message
はJSON形式となっており、pid
と data
がキーとして渡されている。つまり message.message['data']
に取得対象となるOHLCVのデータが格納されている。
Successfully connected.
channel: ticker_btc_jpy
message: {'pid': 803264736, 'data': {'sell': '1057010', 'buy': '1056694', 'high': '1096465', 'low': '1040935', 'last': '1057015', 'vol': '1719.4730', 'timestamp': 1566447049801}}
channel: ticker_btc_jpy
message: {'pid': 803264748, 'data': {'sell': '1057009', 'buy': '1056695', 'high': '1096465', 'low': '1040935', 'last': '1057010', 'vol': '1719.4735', 'timestamp': 1566447050817}}
channel: ticker_btc_jpy
message: {'pid': 803264759, 'data': {'sell': '1057009', 'buy': '1056696', 'high': '1096465', 'low': '1040935', 'last': '1057010', 'vol': '1719.4735', 'timestamp': 1566447051757}}
channel: ticker_btc_jpy
message: {'pid': 803264773, 'data': {'sell': '1057008', 'buy': '1056695', 'high': '1096465', 'low': '1040935', 'last': '1056696', 'vol': '1719.5334', 'timestamp': 1566447052752}}
ほぼ毎秒にわたりデータを受信できることが確認できた。次節では、PubnubによるリアルタイムAPIの情報を基としてマーケット・メイキングを行うプログラムを作成する。
bitbank.cc サンプルの実装
bitbank.cc APIサイトにサンプルのマーケット・メイキングの紹介がされている。本節では先に確認したPubnubによる情報取得のコードを使用して、bitbankのサンプルコードをnodejsからpythonへ書き換える(bitbankのgithub上にあるコードはnodejsのライブラリを使用しているため)。[8]
まずREADMEで説明される取引ロジックや『動かし方』を確認する。このサンプルではインベントリ(在庫)リスクのコントロールが上昇方向にしか設定されていないため、売り方向の調整を追加してもよい。まずはコードの書き換えを行い、本ロジックが動作することを確認したい。

nodejsでの実装では売り価格と買い価格のキャッシングに lru_cache
を、ロジック部分の処理に async.waterfall
を使用している。
キャッシュ機能についてはライブラリを使用せず、PubnubのSubscriberCallbackクラスにインスタンス変数として ask
と bid
を定義した。コードは以下のリポジトリからクローンできる。[9]
pipenv install --dev
で必要なパッケージをインストールし、 lib/logic.py
にある API_KEY
および API_SECRET
を設定する。 pipenv run start
でプログラムの実行が可能となっている。取引ロジックの通り10秒ごとに前注文をキャンセルし、Pubnubから受信した最新情報を基にスプレッドの計算と仲値から売買価格を求めたうえで、売り買いの注文を行う。BTCが1.0以上となった場合には取引を行わない。
$ pipenv run start
Calling the trade logic, best_ask 1064199.0: best_bid 1064198.0--- prepare to trade ---
Successfully connected.
--- buy order ---
1063133.8015: 0.01
--- sell order ---
1065263.1985: 0.01
Calling the trade logic, best_ask 1064199.0: best_bid 1064198.0
--- prepare to trade ------ cancel all active orders ---
632656358
632656355
--- buy order ---
1063133.8015: 0.01
--- sell order ---
1065263.1985: 0.01
bitbankのトレード画面では自動取引が執行されていることが確認できた。

プロセスをバックグラウンドで実行しSIGHUPを送らないように指示することで常時動作させることができる。コマンドは以下の通りである。また標準出力が nohup.out
に保存されて行くためログとして利用できる。別途取引履歴をログやcsvに保存することも検討したい。
$ nohup pipenv run start &
$ disown %1
ここでは自己ポジションの参照や残存ボラティリティ、板の厚みや形によるスプレッドや数量の変更を行っていない。非常にシンプルなbotの作成ができた。マーケット・メイキング・アルゴリズムはバックテストが難しいと考えられるため実運用しつつコードチューニングを行っていく予定である。
以上、次回以降はバックテストや他の取引手法の自動化に取り組みたい。
Reference
- [1] システムトレード
- [2] NTTデータ・フィナンシャル・ソリューションズ — アルゴリズム取引の正体
- [3] 執行戦略と取引コストに関する研究の進展 — 杉原慶彦
- [4] マーケットメイク戦略の理論
- [5] Does Algorithmic Trading Improve Liquidity? — Terrence Hendershott, Charles M. Jones, Albert J. Menkveld
- [6] bitbank.cc APIドキュメント
- [7] PubNub Python V4 SDK 4.1.5
- [8] bitbankinc/sample-market-making-bot
- [9] yuyasugano/marketmaking-sample