仮想通貨botter Advent Calendar 2024 裏 23日目の裏記事です。
@rarirureluis といいます。
みんなのレベルが高すぎる件について
他の botter を見ていると、mmbot で DEX で裁定パスを算出して利鞘を得ていたり、Atomic Arbitrage をやっていたり、機械学習を使って色々やっていたり…
そんな難しいことできないよぉ…ということでもっと簡単に稼げないかなと考えた結果、良いロジックを思いつきました。
ワイの Bot の生い立ち
Bot ととの出会いは、インターネットで見かけた Bitflyer の CB Bot でした。
これは他の方が無料で公開していて、Python で書かれていました。
こんなかんたんなコードで、サーキットブレーカーが動けばお金になるかあと思い Go でリファクタリングしたのがきっかけです。
その後 TradingView のアラート(Webhook)に JSON を埋め込み、 CEX に注文を行う tvbit-bot というものを OSS で公開しました。
TradingView のアラートに Webhook を設定し、注文情報を書けば Bybit に出せるものです。
{
"name": "alert name, description or something",
"symbol": "BTCUSDT",
"type": "Market",
"price": "0",
"side": "Buy",
"qty": "0.014",
"tp": "0",
"sl": "{{high}}"
}
現在では Bybit 公式でこのような機能があるので、需要は無くなりつつあるも、まだ star がちょびっとずつ増えているのでもしかしたら利用している人はいるのかもしれません。
この Bot (というよりツール)は TP, SL を % での記載もできるのでかゆいところに手が届くツールだったと思います。
クリーンアーキテクチャの勉強を兼ねて作ったもので、今はこれを横に広げていく形でどんどん機能を追加しています。
現在の Bot
横に広げて機能を拡張していったところ、現在では 7,945行に。
❯ cloc .
117 text files.
112 unique files.
16 files ignored.
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
Go 102 1471 530 7945
この Bot は前述した tvbit-bot をフォークして作成していて、クリーンアーキテクチャのおかげもあり、機能追加がめちゃんこやりやすかった。
脳筋ロジック: 上場/廃止ニュース即売買
CEX のニュースを高頻度にフェッチし、できるだけ早く Taker で良いので即買い・即売りするロジックです。
これは難しいこと考えず、まさに脳筋です。
今回はこのロジックがうまいこと成功していること、競合が多ければ多いほど圧が増えるので公開しようと思いました。
(もちろん僕より速いと困るんですが…。)
BWEnews より早いよ!
みなさんがよく使う BWEnews より早く検知、注文もできます。
BWEnews で通知がきたのが 00:07 頃
で、ワイの Bot が買い注文出したのが 00:05:54
1分以上早く検知し、注文することができました。
ただし、Coinbase は厄介なので BWEnews が運悪かっただけかもしれません。
他のニュースソースは大体同じぐらいか、それよりは数秒早い程度です。
ちなみに、BWEnews もこの脳筋 Bot と同じようなことをしているっぽく昨今の Binance インサイダーでお気持ちを表明していたのはこれの被害者になったんだと思います。
僕は被害に遭わずに済みました。
脳筋だけどすこしむずかしいところ
この Bot は他サービス(BWEnews など)を利用せず、自前で CEX に対しリクエストを送ってニュース情報を引っ張ってきています。
さて、むずかしいところが2つあります。
- 429 Too Many 問題
- ネットワークレイテンシ
- ニュース受信から、注文を出すまでの速さ
429 Too Many 問題
高頻度でリクエストを送っていたら、もちろん 429 が返ってきます。
私の環境は仕事柄、、6拠点に散らばったコンテナオーケストレーションなクラスタ(Nomad, Kubernetes の簡素版みたいなやつ)を組んでいるので、コンシューマ向けの IP が4つ + Oracle Cloud 2リージョンあります。
なので各拠点のノードにプロキシサーバーを建てて、「429 が返ってきたときはこのプロキシを通す」みたいなことをしています。
VPS などを借りると SNAT IP はデータセンター判定などになったりして苦労するかと思います。
ネットワークレイテンシ
今回の脳筋 Bot に限らず、Bot ではネットワークレイテンシが重要になります。
例えば NURO(東京)からのレイテンシはこんな感じです。
※ ping100回 / p99
Oracle Cloud(大阪)からのレイテンシはこんな感じ。
Oracle Cloud(大阪)から各 CEX のレイテンシは概ね NURO より速いんですが、ある CEX(オレンジ色) では NURO より4倍遅い箇所があります。
IaaS なのにコンシューマ向けのネットワークより遅い場合があるんです。不思議ですね。
この原因は経路を見れば分かるんですが、Oracle Cloud(大阪)の場合は無駄な経路が発生します。
この CEX の場合、Akamai を利用しており、Akamai に到達するまで 2-8 ホップも経路があります。
1 masked (masked) 0.221 ms 140.91.218.30 (140.91.218.30) 0.182 ms 140.91.218.47 (140.91.218.47) 0.223 ms
2 175.129.54.110 (175.129.54.110) 1.020 ms 1.052 ms 1.000 ms
3 175.129.54.109 (175.129.54.109) 1.134 ms 1.152 ms 1.187 ms
4 27.85.228.5 (27.85.228.5) 1.969 ms 27.85.129.229 (27.85.129.229) 1.665 ms 106.139.192.49 (106.139.192.49) 2.411 ms
5 27.85.227.133 (27.85.227.133) 9.336 ms 27.85.227.165 (27.85.227.165) 9.728 ms 27.85.227.129 (27.85.227.129) 7.854 ms
6 27.85.227.149 (27.85.227.149) 9.776 ms 27.86.45.242 (27.86.45.242) 7.688 ms 27.86.121.34 (27.86.121.34) 7.537 ms
7 106.153.229.78 (106.153.229.78) 7.945 ms 7.817 ms 7.768 ms
8 ae2.nttdata-tyo2.netarch.akamai.com (23.56.142.143) 16.492 ms 16.432 ms 106.153.229.78 (106.153.229.78) 7.774 ms
9 a23-193-170-48.deploy.static.akamaitechnologies.com (23.193.170.48) 7.683 ms 7.623 ms 7.718 ms
逆に NURO だと 3-6 ホップで Akamai へ到達してます。
1 192.168.20.1 (192.168.20.1) 0.219 ms 0.213 ms 0.208 ms
2 masked.tkyc511.ap.nuro.jp (masked) 9.879 ms 10.004 ms 11.053 ms
3 92.203.139.108 (92.203.139.108) 2.039 ms 2.659 ms 2.320 ms
4 219.98.228.217 (219.98.228.217) 1.340 ms 1.337 ms 1.265 ms
5 202.213.193.56 (202.213.193.56) 2.011 ms 1.945 ms 2.005 ms
6 101.203.89.88 (101.203.89.88) 4.663 ms 4.337 ms 4.332 ms
7 a23-193-170-106.deploy.static.akamaitechnologies.com (23.193.170.106) 0.973 ms 1.906 ms 1.783 ms
では、Oracle Cloud(東京)だとどうでしょうか?
1 140.91.206.136 (140.91.206.136) 0.319 ms 140.91.206.96 (140.91.206.96) 0.281 ms 140.91.206.104 (140.91.206.104) 0.223 ms
2 101.203.89.147 (101.203.89.147) 1.215 ms 1.124 ms 0.974 ms
3 101.203.89.88 (101.203.89.88) 7.232 ms 7.172 ms 7.109 ms
4 a23-192-46-35.deploy.static.akamaitechnologies.com (23.192.46.35) 1.686 ms 1.621 ms 1.578 ms
なんとオレンジ色の CEX へ 2ホップで Akamai に到達しています。
しかしよく見てみると Oracle Cloud(大阪)に比べ赤、青、緑の CEX はレイテンシが悪化しています。
このように VPS のリージョンによって計測し、自分がどこの CEX に重きを置いて Bot を動かすかによって利用するネットワークを使い分けています。
Bot を動かしていなくても各拠点から、CEX 宛に ping を継続的に送信することでネットワークの健全性を可視化しています。
ニュース受信から注文を出すまでの間
逐次処理では流石に遅いので、あらゆるところで並行処理をしています。
※ 実際にはこの Bot 用に論理コアを占有させて、できるだけ並列処理にできるようにしています。
注文を出すのに必要な情報は下記が必要です。
- トークン名
- そのトークンがすでにどこに上場しているか
- 注文量
- 注文出す直前の価格
- 余力
- サイド
- TP
- SL
- 先物なら乗数の情報
この中でニュースを受信する前から、前もって知ることができる情報は
- そのトークンがすでにどこに上場しているか
- 余力
- 先物の乗数
です。
この3つの情報は常に更新し続け、注文をできるだけ早くしています。
注文量は注文を出す直前の価格が必要なので、事故らないためにも直前の価格に重きをおいてます。
ニュースを受信したら下記のメソッドを持っている CEX クライアントでチャネルを駆使して早いもの順で注文を試みます。
type OrderCreator interface {
GetMarket() Market
GetSymbol(currency string) string
GetPrice(ctx context.Context, symbol string, isSpot bool) (float64, error)
CanOrder(symbol string) (isSpot, isPerp bool, err error)
CalculateTPSL(order *Order, currentPrice float64) (tp string, sl string, err error)
CalculateQty(qtyRatio float64, referencePrice float64, order *Order) (qtyStr string, qty float64, err error)
CreateOrderWithTPSL(ctx context.Context, order *Order) ([]*Order, error)
}
ライブラリの改修
MEXC の 野良 Go SDK は、外部から http.Client を注入できず、リクエストをするたびに 3-way ハンドシェイクが発生している状態でした。
外部から http.Client を注入できるようにし速度を改善したりしました。
Bot のインフラと死活監視
物理的に4つある拠点(物理 + VM) + Oracle Cloud 2リージョンそれぞれにサーバーが何台かおり、VPN で透過的に通信できるようになっています。
物理のワーカーノードには Proxmox の上に Nomad というコンテナオーケストレーション基盤を建てて6拠点で1つのクラスタとして管理しています。
この脳筋 Bot もコンテナ化され、一番物理マシンが強いところにデプロイされ、この Bot のためだけに8論理コアを占有させてます。
VPSって遅くないですか?
煽り見出しですいません。
多くのパブリッククラウドが提供している VPS では Intel/AMD のマイクロコードレベルの脆弱性(Spectre/Meltdown)対策のために、Linux カーネルでデフォルトで有効になっている緩和策が入っています。
この緩和策により性能やレイテンシが落ちているので弊データセンターの物理マシン、VM ではこの緩和策を全てオフにしています。
- name: Disable mitigations
block:
- name: Udpate Grub
replace:
path: /etc/default/grub
regexp: "^GRUB_CMDLINE_LINUX=.*"
replace: 'GRUB_CMDLINE_LINUX="mitigations=off"'
register: grub
- name: Update grub
command: update-grub
- name: Reboot
reboot:
when: grub.changed
この緩和策無効策は VM でも行うことでさらなるパフォーマンスの向上が見られます。
まあもっと速めるならコンテナ化しないでバイナリを物理マシンでそのまま動かしたほうが良いんですが、それはちょっと…
ベンチマーク
Xeon E5-2650 v4 * 2 = 24C/48T のマシンで緩和策が有効(デフォルト)と mitigations=off
で比較してみます。
デフォルト
Dhrystone 2 using register variables 965350794.8 lps (10.0 s, 1 samples)
Double-Precision Whetstone 220633.1 MWIPS (10.0 s, 1 samples)
Execl Throughput 21789.9 lps (29.3 s, 1 samples)
File Copy 1024 bufsize 2000 maxblocks 8619549.0 KBps (30.0 s, 1 samples)
File Copy 256 bufsize 500 maxblocks 2461921.0 KBps (30.0 s, 1 samples)
File Copy 4096 bufsize 8000 maxblocks 16739574.0 KBps (30.0 s, 1 samples)
Pipe Throughput 13110321.3 lps (10.0 s, 1 samples)
Pipe-based Context Switching 2561480.8 lps (10.0 s, 1 samples)
Process Creation 51018.9 lps (30.0 s, 1 samples)
Shell Scripts (1 concurrent) 96885.2 lpm (60.0 s, 1 samples)
Shell Scripts (8 concurrent) 13076.5 lpm (60.1 s, 1 samples)
System Call Overhead 7196864.4 lps (10.0 s, 1 samples)
System Benchmarks Index Values BASELINE RESULT INDEX
Dhrystone 2 using register variables 116700.0 965350794.8 82720.7
Double-Precision Whetstone 55.0 220633.1 40115.1
Execl Throughput 43.0 21789.9 5067.4
File Copy 1024 bufsize 2000 maxblocks 3960.0 8619549.0 21766.5
File Copy 256 bufsize 500 maxblocks 1655.0 2461921.0 14875.7
File Copy 4096 bufsize 8000 maxblocks 5800.0 16739574.0 28861.3
Pipe Throughput 12440.0 13110321.3 10538.8
Pipe-based Context Switching 4000.0 2561480.8 6403.7
Process Creation 126.0 51018.9 4049.1
Shell Scripts (1 concurrent) 42.4 96885.2 22850.3
Shell Scripts (8 concurrent) 6.0 13076.5 21794.1
System Call Overhead 15000.0 7196864.4 4797.9
========
System Benchmarks Index Score 14709.5
mitigations=off
Dhrystone 2 using register variables 959471308.0 lps (10.0 s, 1 samples)
Double-Precision Whetstone 225145.2 MWIPS (9.7 s, 1 samples)
Execl Throughput 22549.7 lps (29.5 s, 1 samples)
File Copy 1024 bufsize 2000 maxblocks 15310998.0 KBps (30.0 s, 1 samples)
File Copy 256 bufsize 500 maxblocks 6576763.0 KBps (30.0 s, 1 samples)
File Copy 4096 bufsize 8000 maxblocks 18223260.0 KBps (30.0 s, 1 samples)
Pipe Throughput 44328487.6 lps (10.0 s, 1 samples)
Pipe-based Context Switching 4277788.6 lps (10.0 s, 1 samples)
Process Creation 52266.4 lps (30.0 s, 1 samples)
Shell Scripts (1 concurrent) 99526.8 lpm (60.0 s, 1 samples)
Shell Scripts (8 concurrent) 14002.2 lpm (60.1 s, 1 samples)
System Call Overhead 48334652.5 lps (10.0 s, 1 samples)
System Benchmarks Index Values BASELINE RESULT INDEX
Dhrystone 2 using register variables 116700.0 959471308.0 82216.9
Double-Precision Whetstone 55.0 225145.2 40935.5
Execl Throughput 43.0 22549.7 5244.1
File Copy 1024 bufsize 2000 maxblocks 3960.0 15310998.0 38664.1
File Copy 256 bufsize 500 maxblocks 1655.0 6576763.0 39738.7
File Copy 4096 bufsize 8000 maxblocks 5800.0 18223260.0 31419.4
Pipe Throughput 12440.0 44328487.6 35633.8
Pipe-based Context Switching 4000.0 4277788.6 10694.5
Process Creation 126.0 52266.4 4148.1
Shell Scripts (1 concurrent) 42.4 99526.8 23473.3
Shell Scripts (8 concurrent) 6.0 14002.2 23337.0
System Call Overhead 15000.0 48334652.5 32223.1
========
System Benchmarks Index Score 23156.7
mitigations=off
にすることで性能は 1.5倍ほど向上します。
この性能向上は CPU の世代が古ければ古いほど躊躇になりますが、割と最新世代でも効果があります。
安物の VPS では今回のベンチマークでも用いた Xeon E5 v4 を利用しているところが多いのでやってみて損はないかもしれません。
パフォーマンスとログの監視
死活に関しても Nomad に任せているので、コンテナが死ねば勝手に再起動が行われ、ノードが死んだとしてもクラスタ化されているので勝手に復活してくれます。
弊データセンターは Grafana を利用しているので、アプリケーションのメトリクスは OpenTelemetry で、ログは Loki で、すべて Grafana 上で完結することができています。
Grafana と OpenTelemetry のおかげで各エンドポイントごとのレイテンシなんかも取れます。
今と怖いこと
TP に届く精度をあげたい
トークンは概ね、Tier1 なりキムチパンプによって暴騰するんですがものによっては思ったより飛ばないトークンもあります。
TP に届かずにそのまま SL で切られることが過去に1度ありました。
これはどう改善すべきか考えないといけないところです。
野良 SDK, クライアント
これは Bot に限らず、開発全般において言えることなのですが、野良 SDK は個人的に怖いです。
コントリビューター、コミッターが大損してその腹いせに悪意のあるコードを注入されたり… とかを考えちゃいます。
絶対無くはないし、なんなら僕が開発していた tvbit-bot にも悪意のあるコードを入れて新バージョンをリリースしても気づく人は中々いないんじゃないでしょうか。
なので僕がやっていることは、一度目grepして問題ないかを確認したのち、フォークして管理しています。
アップデートする際も、差分がかんたんに見られるのでオススメです。
ニュースタイトルのシンボル名がたまに変わる
例えば Bybit
Bybit is excited to announce the upcoming listing of Streamflow (STREAM) on our Spot trading platform!
の場合であれば () を取って、STREAM がシンボル名です。
しかし
Bybit is excited to announce the listing of PENGU on our Spot trading platform!
のように () がないパターンもあります。
これらをすべて網羅できるような正規表現を考える必要があります。
なので、テストコードがどんどん増えていきます…
{
name: "正常ケース: 新規上場 通貨",
description: "Bybit is excited to announce the upcoming listing of Streamflow (STREAM) on our Spot trading platform!",
want: []string{"STREAM"},
expectErr: false,
},
{
name: "正常ケース: 新規上場 通貨",
description: "Bybit is excited to announce the listing of PENGU on our Spot trading platform!",
want: []string{"PENGU"},
expectErr: false,
},
{
name: "正常ケース: 新規上場 通貨",
description: "Bybit is excited to announce the upcoming listing of FLUID on our Spot trading platform!",
want: []string{"FLUID"},
expectErr: false,
},
他の機能たち
この上場・廃止即売買機能以外もあります。
Arkham
Arkham でエッジを見つけ Webhhook アラートから売買する機能
市況が変わり、エッジじゃなくなったので現在は停止中
inflow
ある CEX ホットウォレットに大量の入金があったときに売買する機能
Arkham に対応していないチェーンなので explorer をたくさん叩いて監視
listed
エアドロで降ってきたトークンを即売りするやつ
最近はなんだか即売りしないほうが良かったりするので使ってない
mining
マイニングしたゴミトークンを指定価格になったら買い板にぶつける機能
Radiant, Meow, Cortex 周りを掘ってました 🔨
news
今回紹介したロジック
rsi
叩くとインジの数値を返してくれるサービスを利用し、n足 RSI が n になったら売買する機能
このブル相場で閾値に達しず、まだ発火したことないけど、見る限り良さそう
tv
TradingView の Webhhook アラートから売買する機能(tvbit-bot にあった機能)
まとめ
インサイダーには勝てない