【Brain公式】この記事を購入する
【仮想通貨】 高頻度BOT【Rust】
筆者が使っているピュアRust実装の高頻度BOTの実装です。
現在も利用していますがロジック部分は変更したため無料で紹介しています。
有料部分では解説とともに、すぐに実践投入できるbitflyer CFDとGMOコイン版のコードをダウンロードできます。
Profile: https://github.com/rinov
はじめに
想定する読者:
- 最速の高頻度BOTを目指したい方
- RustでのBOTの作り方に興味がある・勉強したい方
- すでにあるロジックを高速化したい方
Rustを使う理由:
Rustは安全性とパフォーマンスを最重要視して設計された言語で、C/C++にほぼ並んで最速の実行速度を得ることができます。また、高頻度BOTだけではなく、レイテンシーが重要となるオンチェーンBOTやAtomic Arbitrageにも応用ができます。よく採用されるのがPythonですが、高頻度BOTではリタイルタイムに演算をしながら数秒以内のトレードを繰り返すため、処理速度が10 ~ 100倍ほど速いRustを利用することで速度が遅いことが原因ではないか、という疑問を持たずに開発に集中できるメリットもあると思います。
今となってわかりますが、高頻度BOTのロジックをPythonで実装するのはウサインボルトにハイヒールを履かせて走らせるくらいもったいないです。高頻度においては短期での相関をもとにそのエッジを回収できるかが課題になりますが、タイムスケールが短くなるほど1人だけが思いつく秀逸なアイデアなどはなくなってくるため、同じことを思いついている第三者を想定し、そことの優位性の違いが強みになります。
2018年からシンプルなロジックにもかかわらず淘汰されずに安定した成績を出せているのは"速度"これ自体がエッジであるからだと認識しています。
あると読みやすい知識 (なくても調べながらで大丈夫です)
- 排他制御
- ベイズ統計・期待値
- 並列・並行処理
- 高階関数
- 非同期プログラミング (async / await)
- アルゴリズムとデータ構造 (B-Tree, HashMap, Queue etc..)
運用中のBOTはWebSocketからのデータをメインとした確率・数理計算を結構いれていますが計算から注文までの時間は基本1ms以内で完了できるようにしています。しかし、多くの場合通信などのI/O処理などが処理時間の大半を占めるため、BOTをデプロイする場所も重要になりますが本記事ではインフラに関することは割愛させていただきます。
免責
当記事に掲載された内容によって生じた損害等の一切の責任を負わないものとします。ご購入に際しては、購入者様のご判断により自己責任でお願いいたします。
ロジック付きのものをそのままダウンロードできる状態となっていますが、これはそのまま稼働して儲かるということではありません。あくまでも参考にしていただく形にし、購入者様の方で工夫していくことによって最短で高頻度BOTを運用していくためのものとお考えください。
参考
額は伏せますが、年毎の成績です。確か途中まではPythonでの実装でした。
2018年より前のデータは見つからなかったのでありませんが、より良い成績でした。悪い日も当然ありますがそういう日はスクショを残してなかったので、全てがこういった成績ではなく、あくまで参考までにお願いします。
2019/5
2020/8
2021/9
2022/2
2022/8
2022/12
開発環境
Rust (rustc 1.65.0)
Visual Studio Code
セットアップなどは以下の記事などが参考になります。
Rust コーディング環境構築@VSCode zenn.dev
以下の拡張は必須で導入するレベルで推奨です。
- rust-analyzer
- rustfmt
- clippy
下記で取り上げるソースコードの取引所はbitFlyerを想定しています。
ソースコード全体はファイル分割されていますが合計すると1300行ほどあります。
ロジック
直近N秒の中値(S)から収益期待値が最大となるBestOffset(B, S)を算出する。
期待値は以下の式で算出します。
Bp: 買指値が約定する確率
Sp: 売指値が約定する確率
V: ボラティリティ
α: リスク回避度 (0≦α)
Eprofit = Bp × Sp × (SellPrice - BuyPrice)
Eloss = (1 - (1 - Bp) × (1 - Sp) - Bp × Sp) × Vα
E = Eprofit - Eloss
Eprofitではマーケットメイクにおける差益に対して両方が約定する確率を掛けることで期待収益を計算します。
Elossではどちらかの指値だけが約定する確率にボラティリティ*リスク回避度αをかけることで期待損失を計算しています。
BpとSpが独立事象ではない前提では、条件付き確率で算出できますがこれらは同様の結果になります。
E=(Bp × (1 - Sp) + Sp × (1 - Bp)) × Va
つまり、マーケットメイクによる売りと買いの指値が両方約定し利益がでる期待値から、どちらかだけが約定した場合の逆選択リスクを引いた値を期待値としてこのE(max)となる買いと売りの指値のペアを算出します。
ただし、市場は非定常的であるため同じ確率分布を使い続けるのは得策ではありません。そこで直近N秒のみの情報を保持し、そこから確率分布を仮定します。
そこで中値(S)から任意の離散化したステップ価格Boffset i…nを用意し、その価格が注文間隔秒後に約定しているか、していないかを約定配信からリアルタイムに算出します。
イメージ:
…
Soffset3 (+300)
Soffset2 (+200)
Soffset1 (+100)
S (2,000,000)
Boffset1 (-100)
Boffset2 (-200)
Boffset3 (-300)
…
すると任意の期間内での指値価格S + Soffset(i..n), S - Boffset(i..n)の約定回数が求められます。
ここからベイズ統計を用いて事後確率を更新(ベイズ更新)していき、確率の平均を算出します。今回は約定するかしないかの2種類のみなのでベルヌーイ分布からデータを取得しますが、この場合事後分布はベータ分布として扱うことができます。ちなみにベイズ統計を用いずに、そのまま約定した回数を試行回数で割るだけで確率は出せますが、これは点推定と呼ばれ、いわば頻度のようなものです。試行回数が多く得られる高頻度BOTにおいては大数の法則によって確率分布が真の平均に近づいていくと仮定すると、ベイズ統計を用いることでこの確率自体の確度を算出できることに価値が出てきます。
ライブラリ(クレート)
url = "2.5.0"hyper = "1.3.1"reqwest = { version = "0.12.4", features = ["json"] }futures = { version = "0.3.30", default-features = true }tokio = { version = "1.37.0", features = ["full"] }tokio-tungstenite = {version = "0.21.0", features = ["native-tls"]}serde = { version = "1.0.200", features = ["derive"] }serde_json = "1.0.116"ring = "0.17.8"hex = "0.4.3"rand = "0.8.5"rayon = "1.10.0"parking_lot = "0.12.2"chrono = { version = "0.4.38", features = ["serde"] }serde_yaml = "0.9.34"polars = "0.39.2"
非同期ランタイムはtokioを利用します。
rayonはデータ並列処理ライブラリで、軽量かつ逐次計算を簡単に並列計算に変換することができます。
parking_lotは標準の排他制御機能より効率的かつ高速な実装を取り入れているクレートです。