見出し画像

SFDというゲームの解説

いつもお世話になっております。botter老人会代表のHohetoです。

もういいでしょ、ということで2020年のこのツイートを回収しようと思いました。

この記事は「仮想通貨botter Advent Calendar 2023」の9日目の記事です。たくさんの記事が集まっていますので、他の記事もぜひご覧になってください。

SFDって何?という方へ

ここ数年で参入した方の中には、SFDが何であるかすら知らない方も多いでしょう。
SFDとは、bitFlyerが提供している「ビットコインFX(正式名称Lightning FX、以下FXと呼ぶ)」の中で発生するSwap For Differenceという特殊な手数料体系のことです。

SFDについてその歴史や出来事も併せて記事にしようかとも思ったのですが、よくよく考えてみるともはや知る必要のない知識であり、改めて詳しく説明するようなものではないと思いました。

ですので、既に知っている人を対象に記事を書きます。
SFDがなにやらよく分からないという方達は、すみませんが「老人たちが何やら騒いでいるなあ」と横目で眺めてそっとしておいて頂けると幸いです。

SFDというゲームの基本

さて、ビットコインFXでは現物価格とFX価格が5%以上開いたときに、乖離を広げる方向の注文が約定した際には新規建て・決済問わずSFDを徴収されます。
逆に、乖離を狭める方向の注文が約定した際には新規建て注文に限りSFDを受け取ることができます。

手数料率は乖離が5%進むごとに段階的に変動します(参考)。SFDボットたちの戦いはこの手数料率が切り替わるポイントで発生します。

例えば現物価格が100万円でFX価格が105万円近辺(乖離率5%近辺)になっているとき、ボットの基本的な動きは以下のようになります。

①新規建ての売り指値を、SFD価格(100万円×1.05=105万円)で出す。
この注文が約定したとき、
→SFDを受け取ることができたら成功。+1ポイント。
→SFDを受け取れなかったら失敗。±0ポイント。

② ①で建てた売りポジションを決済する買い指値を、SFD価格-1円(104万9999円)で出す。
この注文が約定したとき、
→SFDを支払わずに済んだら成功(買い抜け成功)。±0ポイント。
→SFDを支払ってしまったら失敗(買い抜け失敗)。-1ポイント。

※筆者はSFDを支払わずに売りポジションを解消することを「買い抜けする」と呼んでおり、本記事でもそのように使います。

SFDボットは最低限「①の成功数>②の失敗数」になるように実装します。

SFD botter界隈では「SFDファクター」という謎のファクターが共通認識となっていました。
これは「受け取ったSFDの総額/支払ったSFDの総額」により算出されますが、単純化して「①の成功数/②の失敗数」で計算しても概ね算出できます。
「6回SFDを受け取れて5回SFDを支払った」としたらSFDファクターは1.2前後を期待できるでしょう。

強SFD botterに見えている光景。SFD列に注目。

このゲームの肝とは?

話はここからです。
SFDボットを作ったことのある人なら、現物の価格が更新されてからそれがSFD価格に反映されるまでにタイムラグがあることは、経験的に知っているでしょう。

現物価格が下がったとき(tick down)を考えます。
現物価格:100万円→99万5000円

このとき、SFD価格も×1.05した値に変化します。
SFD価格:105万円→104万4750円

現物価格が下がった瞬間、SFD価格も下がるはずです。
しかしこのとき、104万4750円~104万9999円に取り残されている買い板に対してすぐに売り注文をぶつけて約定させてもSFDを受け取れないケースが頻発します。これにより、現物価格→SFD価格の反映にタイムラグが存在することに否応なく気付きます。

この謎のタイムラグに対してどのような行動を取るか?がこのゲームの肝となります。

なぜタイムラグが発生するのか?

前節で述べたようなタイムラグが発生する理由として、以下の2つが考えられます。

  • 約定情報以外の情報を参照してSFD価格を算出している

  • 約定情報を参照していたとしても、逐次的ではなく定期的に(あるいは不定期に)SFD価格を算出している

そこで頭に浮かぶのがTicker情報です。以下はTickerのRealtime APIのドキュメントからの引用です。

配信内容
Tickerの内容に更新があったとき、配信されます。
配信効率などの合理的理由により、配信頻度が制限されています。
最新の最終取引価格を正確に取得したい場合は、約定配信を利用してください。

https://bf-lightning-api.readme.io/docs/realtime-ticker より

この内容から、Ticker情報は約定ごとには更新されていない様子が伝わってきます。ちなみにRealtime APIではTicker情報は1秒弱に1回程度配信されますが、その間隔には多少ばらつきがあります(次節参照)。

そこで「SFD価格はこのTicker情報を元に計算されているのでは?」「あるいは直接の参照先がTicker情報でなかったとしても、Ticker情報の更新と同じようなロジックやタイミングで更新されるのでは?」というアタリをつけます。

Ticker情報はRealtime APIで配信されてきますが、この「確定済みのTicker情報」を受信した後にSFD価格を計算して発注する、ということをやっていては当然競争に勝つことはできません。

そこで、次回のTicker更新時刻を予測しよう、という発想になります。

Ticker更新時刻の予測モデルの構築

Realtime APIのTickerチャンネルを流れてくるTickerデータを観察します。

Realtime APIのTickerチャンネルに流れてくる情報(一部)

それぞれのデータにはtick_idというIDが割り振られており、歯抜けの状態で受信できます。また、それぞれのデータには生成時刻であろうtick_timestampという情報も付与されています。

配信されてくる各データのtick_id間隔tick_timestamp間隔のヒストグラムをプロットしてみます。

tick_id間隔のヒストグラム
tick_timestamp間隔のヒストグラム

tick_id間隔はかなりばらつきがありますが、tick_timestamp間隔を見るとおよそ1秒弱の間隔で生成されたものが配信されている様子が分かります。

Realtime APIで配信されてこないTickerデータもあるでしょう(実際にREST APIで取得可能)が、少なくとも次に配信される予定のTickerデータについてはその生成時刻を予測できそうです。
ということで、「前回配信されたTickerデータのtick_timestamp」から「次回配信される予定のTickerデータのtick_timestamp」までの間隔(これ以降、次回tick_timestamp間隔、と呼ぶことにします)を予測するモデルを作ることにします。

この「次回tick_timestamp間隔に影響を与えそうなデータ」として真っ先に上げられるのが現物とFXの約定履歴です。
駆け足になりますが、Tickerデータと約定履歴を併せて確認したところ以下のような知見を得られました。

  • 次回tick_timestamp間隔は、現物の約定回数や約定ボリュームとは明確な関係が見られない。

  • 同様に、FXの約定回数や約定ボリュームとも明確な関係は見られない。

  • 直近数十回のtick_timestamp間隔の平均を取ることで、大まかな次回tick_timestamp間隔を求めることができる。

  • 前回のtick_timestamp間隔の予測誤差と次回の予測誤差に微妙な相関がある。

  • Tickerデータが配信遅延を起こしているとき、次回tick_timestamp間隔が大きくなる傾向がある。

要はbitFlyerのサーバ内部の処理遅延と関係がありそうです。

これらの知見をもとに、簡単な線形モデルで予測モデルを構築します。すると、以下のような予測結果を得ることができました。

次回tick_timestamp間隔の予測値と実測値

外れ値で相関係数が上がっている感じはありますが、適当に作ったモデルの割にはいけそうな気がします。

こうしてある程度の精度で「次回tick_timestamp間隔」を予測できるようになったので、「前回受信したTickerデータのtick_timestamp」にこれを加えて「次回配信予定のTickerデータのtick_timestamp」の予測値を出します。
この時刻を次の「Ticker更新時刻(最低でもそこまでにはTickerが更新されるであろう時刻)」とします。

ここから、「Ticker更新時刻=新SFD価格の反映時刻」と仮定してロジック作成に移ります。

本節の内容は様々な仮定・推測の上に成り立っているため、実際と異なる可能性があります。
特に「次に配信されるTickerデータのtick_timestamp」を「新SFD価格の反映時刻」としましたが、これ以前にSFD価格が更新される可能性もありますので注意が必要です。

筆者 注

ロジック構築

それでは、「新SFD価格の反映時刻が分かっている」という仮定の下、ベストなロジックを構築していきます。

まずはRealtime APIで現物のTicker情報と約定情報の両方を受信します。
Ticker情報を受信するたびに、そのtick_timestampを前回tick_timestampとして保持しておきます。

現物の約定情報を受信したタイミングで、モデルが予測した次回tick_timestamp間隔前回tick_timestampに加えて、この約定価格がSFD価格に反映される時刻を計算します。そして適切なタイミング(後述)で発注処理とキャンセル処理を行います。

その際、価格が上がったか(tick up)下がったか(tick down)、またポジション(=売りポジション)を持っているかに応じて処理する内容が変わってきます。

A. 現物価格が前回の約定から上がった場合(tick up時)

tick up時の立ち回り

A-①新規売り注文の発注
売り注文を新SFD価格に出します。
tick upの場合、この新SFD価格の価格帯はそもそもSFDを受け取ることができる価格帯です。ですので新SFD価格の反映時刻は気にせず最速で発注します。
このタイミングで発注すると「現物操作組 or 現物板観測組」よりも後ろに並ぶことになりますが、A-③のDelayキャンセルを行うことで特に問題にならなくなります
「現物板観測組 or 現物操作組」については後述します。

A-②決済買い注文のDelay発注
売りポジションを持っている場合、新SFD価格が反映される「直後」のタイミングまでDelayを挟んでから、その時点で取り残されている売り板を買いテイク指値(新SFD価格-1円のところまで)で食いにいきます
タイミングが早すぎると約定しますがSFDを徴収されてしまいます
タイミングが遅すぎると約定しません。他のSFDボットに先に売り板を食われる&新たなSFD価格-1円に先に買い板を出されるので、その後ろに並ぶことになります。

繰り返しますが、発注タイミングが早いとSFDを徴収されます。ここでSFDを徴収される割合が高いと全体の収益を圧迫しますので、無理をしすぎないタイミングまでDelay時間を多めに取ります。

A-③前回出した新規売り注文のDelayキャンセル
tick upの場合、前回出した売り注文の価格帯がSFDを受け取れない価格帯に変化します。ぼ~っと注文をそのままにしていたら他のボットに買いをぶつけられて買い抜けされてしまいますが、これを逆手にとります。
新SFD価格が反映される「直前」のタイミングまでDelayを挟んでから注文をキャンセルします。これによって反映時刻の予測精度がよくないボットや猪突猛進タイプのボット(SFDを徴収されてもよいから買い板の最前列に並ぶためにテイクしてくるタイプのボット)に自分の売り注文を食わせて、SFDを受け取ります。

B. 現物価格が前回の約定から下がった場合(tick down時)

tick down時の立ち回り

B-①決済買い注文の発注
売りポジションを持っている場合、買い注文を新SFD価格-1円に出します。
A-①と同様、tick downの場合、この新SFD価格の価格帯はそもそもSFDを徴収されない価格帯です。ですので新SFD価格の反映時刻は気にせず最速で発注します。
このタイミングで発注するとやはり「現物操作組 or 現物板観測組」よりも後ろに並ぶことになりますが、B-③のDelayキャンセルを行うことで特に問題にならなくなります

B-②新規売り注文のDelay発注
新SFD価格が反映される「直後」のタイミングまでDelayを挟んでから、その時点で取り残されている買い板を売りテイク指値(新SFD価格のところまで)で食いにいきます
タイミングが早すぎると約定しますがSFDを受け取れません
タイミングが遅すぎると約定しません。他のSFDボットに先に買い板を食われる&新たなSFD価格に先に売り板を出されるので、その後ろに並ぶことになります。

A-②と異なり、タイミングが早くてもSFDを受け取れないだけで済みます(といっても買い抜けが失敗するリスクが発生してしまうので無闇に売りポジションを持つべきではありません)。
Delay時間をA-②よりも短めにして、リスクをとってSFDを受け取りにいきます

B-③前回出した決済買い注文のDelayキャンセル
tick downの場合、前回出した買い注文の価格帯がSFD価格帯に変化します。ぼ~っと注文をそのままにしていたら他のボットに売りをぶつけられてSFDを徴収されてしまいますが、これも逆手にとります。
新SFD価格が反映される「直前」のタイミングまでDelayを挟んでから注文をキャンセルします。これによって反映時刻の予測精度がよくないボットや猪突猛進タイプのボット(SFDを受け取れなくてもいいから売り板の最前列に並ぶためにテイクしてくるタイプのボット)に自分の買い注文を食わせて、買い抜けします。

C. 現物価格が前回の約定と変わらなかった場合

特に何もしません。


ロジックの骨子は以上です。
当然ですが、発注やキャンセルをAPIリクエストしてから板に反映されるまでのギャップを考慮する必要があります。また、Delay中に他の約定が発生してSFD価格が動いた場合どうするか、というような細かな話がありますが割愛します。

結局この予測が正しく機能したかは知る術がなかったのですが、今思えば収益の源泉は予測精度ではなく、A-②、A-③、B-②、B-③のDelay時刻の個別調整(SFDを受け取るときはギリギリを攻めて買い抜けするときは余裕を持たせる)にあっただけかもしれません。
「一生懸命予測して勝てるようになったけど実は勝因は予測力ではなく別のところにあった」はbotterあるあるです。

筆者 注

他にも、担がれたときどうするかとか、急激に上昇する相場での立ち回りとか、遅延がひどいときどうするかとか、APIリミットとどう付き合うか、などのTipsがありますが、このあたりはそれぞれのSFD botterが自分のスタイルに合わせて決めている感じがあります。正解がどうこう、というよりは好み次第といった感じでしょう。

残りのトピックとして「現物操作」と「強化学習的なアプローチ」について触れておきます。

現物操作組と現物板観測組について

現物マーケットで板を出す、あるいは注文を約定させるなどして意図的にSFD価格を変更するタイプのボット(現物操作組)も存在しました。

しかし、現物操作を行っても大した収益は上げることができず、自然と撤退したケースがほとんどだと思われます。
現物操作ボットにできることは、

  • ①予めFX側で売り板を出す→自分のFX板が新SFD価格になるように現物をtick upさせる

  • ②予めFX側で買い板を出す→自分のFX板が新SFD価格-1円になるように現物をtick downさせる

くらいです(タイムラグを予測できていない場合、tick down時の売りとtick up時の買いは出せません)。
①や②を行えば、たしかに新SFD価格の板の先頭に並ぶことはできるかもしれませんが、この手法は(特に最近は)約定回数が少なく思うように稼げません。一度ポジションを持ってしまうとなかなか決済できず、ポジション損益を被りやすいというデメリットもあります。

現物のベスト板を見てFX側で先に板を出すボット(現物板観測組)もいますが、同様にさほど収益化できていないと思われます。
余談ですがSFD最盛期は、この現物板観測ボットを混乱させるために現物板に0.001枚の板を出したり引っ込めたりするボットも存在していました。

強化学習的なアプローチについて

Delay時間を強化学習的なアプローチでリアルタイムで調整する、という手法は有効かもしれません。
前述のように、Tickerが更新されるまでのタイムラグはbitFlyerのマッチングエンジンの遅延と関わりがあるように見受けられます。市況に応じてタイムラグが変動する可能性がある、ということです。

このような状況で、一定時間Delayを挟んで発注したとき、SFDを受け取れなかった(あるいは支払った)場合にDelay間隔を長めに調整、SFDを受け取れた(あるいは支払わなくて済んだ)場合にDelay間隔を短めに調整するようにすれば、自動で最適化できるかもしれません。

ただこれをやるには「自分の注文がSFDを受け取ったか支払ったかの情報」をREST APIで取得する必要があるため、APIリミットを圧迫します。
Web専用のWebsocketなど非公式なものを使えばREST APIなしで取得できるかもしれませんが、筆者はノータッチでした。

SFDボットの変遷

本noteで説明したロジックは、明らかに競合となるSFDボットを食い物にしています。筆者の以前のnoteで以下のように書きましたが、それは上述のようなロジックが含まれていたためです。

実は、筆者のSFDbotには「質の良くないSFDbotからSFDを徴収するようなロジック」が含まれています。他の強botterのSFDbotにも、おそらく同様の仕組みが存在するでしょう。

https://note.com/hht/n/n61e6ecefd059 より

似たようなロジックで運用しているSFDボットも複数いるはずです。

SFDの最初期は、競合となるSFDボットが少ない&収益源となる一般利用者の成行注文が多かったため、「どうにかしてSFD板に一番乗りを試みる」という単純なロジックのボットがほとんどでした。早乗りするために、現物の板を見たり現物を操作するボットが生まれました。

しかし、その後有料noteが販売されるなどしてSFDボット同士の板乗り競争が激化しました。
加えてSFDの長期化によって一般利用者の成行注文が少なくなったため、一部のSFDボットはターゲットを「自分以外のSFDボット」に切り替えました。
上述のDelayキャンセルの手法はその典型です。

noteなどに書かれたロジックをそのまま利用して作ったSFDボットのほとんどは、恐らく早い段階で損失を出して停止していると思います。

こうしてSFDボットの淘汰が進み、生き残った少数のSFDボットたちでSFDを取ったり取られたりという戦いが続いている、というのが今のSFD界隈の現状だと思われます。

最後に注意喚起しておきますが、本noteで紹介した予測モデルやロジックをそのまま使って新たにSFDボットを実装するのはやめたほうがいいです。
より強いbotterに何らかの対策をされたり穴を突かれたりして、養分にされてしまう可能性が高いです(リメンバードテンくん)。

まとめ

今回のnoteではSFDという仕組みの良し悪しではなく、SFDのゲーム性とその攻略方法の1つを解説してみました。

ちなみに筆者は最近はSFDボットを動かしていません。理由は以下です。

  • 取引量・流動性が減って「5%の壁」を貫通しやすくなった。

  • 5%で張り付いているときも約定機会が少なく、SFD収益が減るとともにポジション損益の影響が大きくなった。

  • 強SFD botterだけが残り、SFDファクターが悪化している。

稼働すれば多少は利益が出るとは思いますが、冒頭に書いたとおり「もういいかな」と思い停止しています。

次のATH相場が来たとしたら、ビットコインFXが多少なりとも盛り上がり、再びSFDボットの収益性が上がる可能性もなくはないでしょう。

しかし、そのような時期が来るまでには流石にbitFlyerさんもSFDを廃止し、きちんとした設計に変更してくれるでしょう、という期待の意味も込めて記事化しました。
bitFlyerさんからは以下のようなアナウンスが出ています。首を長くして待ちましょう。

当社は現在、Lightning FXの取引価格と暗号資産現物取引価格の連動性をさらに強化するため、より抜本的なLightning FXの設計変更案の具体化を進めております。上記のとおり変更するSFD比率の適用を即効性の高い乖離防止策として実施する一方、中長期的な施策についても後日改めてお知らせできるよう鋭意進めてまいります。

https://bitflyer.com/pub/20230428-announcement-change-of-SFD-rate-ja.pdf より

現在、Lightning FX(ビットコインFX)のサービス改善に向けて取り組んでおり、来春の新商品・サービス導入を目指しております。

https://bitflyer.com/pub/20231201-Service_termination_Lightning_Futures_ja.pdf  より

本記事は以上となります。皆様お楽しみ頂けたでしょうか。
界隈のbotterたちが綴るAdvent Calendarは明日以降もまだまだ続きますので、お楽しみに!

それではよきbotterライフを!

この記事が気に入ったらサポートをしてみませんか?