仮想通貨botter Advent Calendar 2024 裏 23日目の裏記事です。

@rarirureluis といいます。

みんなのレベルが高すぎる件について

他の botter を見ていると、mmbot で DEX で裁定パスを算出して利鞘を得ていたり、Atomic Arbitrage をやっていたり、機械学習を使って色々やっていたり…

そんな難しいことできないよぉ…ということでもっと簡単に稼げないかなと考えた結果、良いロジックを思いつきました。

ワイの Bot の生い立ち

Bot ととの出会いは、インターネットで見かけた Bitflyer の CB Bot でした。

これは他の方が無料で公開していて、Python で書かれていました。

こんなかんたんなコードで、サーキットブレーカーが動けばお金になるかあと思い Go でリファクタリングしたのがきっかけです。

【コード公開】公開されていた CB Bot のコードを別言語でチューニングしてみた【勝率99.9%サーキットブレーカー】|rarirureluis

ネットを巡回していたところ CB Bot というものが存在することを知り、公開されていたコードをリファクタリングしてみたというお話と、実際のコードも無料で公開します。 【コード公開】絶対に勝てる仮想通貨botはCBbot【勝率99.9%サーキットブレーカー】 サーキットブレーカーbot(CBbot)を作りました。作ってみた感想は99.9%勝てるbotであるということです。 コピペ yameteeeee.com よろしければツイッターのフォローをしていただけると助かります🙇‍♂ @rarirureluis どれぐらい早くなったか 実際にベンチマークを取り

その後 TradingView のアラート(Webhook)に JSON を埋め込み、 CEX に注文を行う tvbit-bot というものを OSS で公開しました。

GitHub – rluisr/tvbit-bot: tvbit-bot is TradingView webhook handler for Bybit.

tvbit-bot is TradingView webhook handler for Bybit. – rluisr/tvbit-bot

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

ワイ Bot 00:05:54 注文

1分以上早く検知し、注文することができました。

ただし、Coinbase は厄介なので BWEnews が運悪かっただけかもしれません。

他のニュースソースは大体同じぐらいか、それよりは数秒早い程度です。

ちなみに、BWEnews もこの脳筋 Bot と同じようなことをしているっぽく昨今の Binance インサイダーでお気持ちを表明していたのはこれの被害者になったんだと思います。

方程式新闻 BWEnews ⚡️🚀 on X (formerly Twitter): “[not a news] To insider traders on Binance perp listings, please sell your bag slower in the future. This crash that you caused on WHY and CHEEMS is 100% -ev for everyone participant in the game, you are ruining the sentiment of the trades.[不是新闻] 对于币安 perp… / X”

not a news] To insider traders on Binance perp listings, please sell your bag slower in the future. This crash that you caused on WHY and CHEEMS is 100% -ev for everyone participant in the game, you are ruining the sentiment of the trades.[不是新闻] 对于币安 perp…

僕は被害に遭わずに済みました。

脳筋だけどすこしむずかしいところ

この 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って遅くないですか?

煽り見出しですいません。

【特集】 Spectre V2対策による性能低下を緩和する「Retpoline」の効果を確認する

Microsoftは今年3月1日にリリースした更新プログラムであるKB4482887で、”Retpoline”と呼ばれる性能低下緩和策を有効にしたことを発表した。

多くのパブリッククラウドが提供している 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 にあった機能)

まとめ

インサイダーには勝てない