国内CEXでは一番BTCの流動性がある取引所.

https://bitflyer.com/ja-jp/

BitFlyer Crypto CFD

2024/03/22からリブランディングした.

📝CFD

BTC-CFD/JPY

  • 最少lot: 0.001 BTC, これは24/10/21から.

もっとも流動性がある📝ビットコインFX.

Lighting FXとの違い

ちょいちょい現物とFXの差分が開きすぎるのとSFDを狙ったbotが原因だと思う.

https://bitflyer.com/ja-jp/s/crypto-cfd

bitFlyer Lighting FXbot

2024/03/21に廃止. 新サービスはBitFlyer Crypto CFD.

よく📝ビットコインFX(BTC-FX)と呼ばれるようなBTCのレバレッジ取引市場をbitFlyerではligithing FXという. 対応銘柄はビットコインのみ.

公式: Lightning FX (ビットコイン FX)とは

Lightning FX とは、証拠金を預入れ、主に差金決済によるビットコインの売買が行えるサービスです。レバレッジをかけることにより資金効率の良い投資を行うことができます。

  • レバレッジ2倍.
  • 最小発注量は0.01BTC.
  • 取引所手数料は無料.
  • 証拠金:
    • レバレッジ2倍の場合は注文金額 x 50%.
    • レバレッジ1倍の場合は注文金額のみ.

たとえば1BTCが30万だとすると. レバレッジ2倍では最小注文量0.01ということは15000円で注文をかけることができるわけ.

📈黒マチャート(現bitFlyer公式チャート)がLighting FXのトレード画面で利用されている.


ここでbitFlyer FXとは何なのかについて議論している.

ref. ビットコイン証拠金取引 - これからの「お金」の話をしよう

Histories

  • 2021/04/21から最大レバレッジは4倍から2倍へ.
  • 2019/04/19から最大レバレッジは15倍から2倍へ.

bitFlyer Futures FX

  • 最小発注数量は 0.001 BTC.

🔌bitFlyer API

bitFlyerを相手にしたbot開発の知見を貯める.

  • bitFlyer Lighting FXが取引手数料無料(現物はお金かかる).
    • ただし証拠金を予めいれておく必要がある.
  • 最小ロット: 0.001BTC

概論はこっち📝仮想通貨bot開発

📝bitFlyer Lighting API

libraries

🔍bitFlyer HTTP API Specs

API制限

  • 同じIPアドレス: 5 分間で 500 回
  • 0.1 以下の数量の注文: 1 分間で 100 回
  • Private API: 5 分間で 500 回
  • 下記のPrivate API: 合計で5 分間で 300 回
    • 新規注文を出す(POST /v1/me/sendchildorder
    • 新規の親注文を出す(特殊注文)(POST /v1/me/sendparentorder)
    • すべての注文をキャンセルする(POST /v1/me/cancelallchildorders)

see also. ✅API制限を回避するには?

指値注文

time_inf_forceで執行注文オプションが指定可能. GTC/IOC/FOK. defaultはGTC.

成行注文

POST /v1/me/sendchildorderでリクエストすると, child_order_acceptance_idのみが即時リターンする. これは仮の値なので, 注文一覧(GET /v1/me/getchildorders )や約定一覧(GET /v1/me/getexecutions)で結果を確認する.

GET /v1/me/getchildordersのレスポンス. priceは0.0で, average-priceに値がはいっていることに注意.

{:expire-date = "2022-09-23T11:08:58"
 :time-in-force = "GTC"
 :cancel-size = 0.0
 :outstanding-size = 0.0
 :child-order-state = "COMPLETED"
 :child-order-acceptance-id = "JRF20220824-110858-xxx"
 :size = 0.01
 :average-price = 2914681.0
 :total-commission = 0.0
 :id = 26921xxxxx
 :executed-size = 0.01
 :product-code = "FX_BTC_JPY"
 :side = "SELL"
 :child-order-date = "2022-08-24T11:08:59"
 :price = 0.0
 :child-order-id = "JFX20220824-110859-xxx"
 :child-order-type = "MARKET"}

成行注文は分割されて約定することもおおい(部分約定). 部分約定しても全て同一の注文ID(order_acceptance_id)で記録される.

ただし約定履歴取得では分割された履歴が取得できるものの, 注文履歴取得だと部分約定は取得できず, average-priceも計算されているように見える.

ref: BitflyerFXでCCXTを使って成行注文を出しその執行価格を取得する方法

約定履歴

GET /v1/me/getchildordersの取得結果. idはorder-idとは異なる. dateはmsecまで取得できる.

{:id = 2379649xxx
 :side = "SELL"
 :price = 2914681.0
 :size = 0.01
 :exec-date = "2022-08-24T11:08:59.153"
 :child-order-id = "JFX20220824-110859-058xxxF"
 :commission = 0.0
 :child-order-acceptance-id = "JRF20220824-110858-045xxx"}

Ticker

GET /v1/getticker, GET /v1/ticker.

{ :ltp = 2788286.0
  :tick-id = 320000452
  :best-ask-size = 0.07801252
  :total-ask-depth = 229.08360093
  :market-ask-size = 0.0
  :best-bid = 2788287.0
  :state = "RUNNING"
  :market-bid-size = 0.0
  :best-bid-size = 0.01
  :volume = 10762.07551354
  :product-code = "FX_BTC_JPY"
  :total-bid-depth = 388.3831962
  :timestamp = "2022-08-29T21:04:58.707"
  :best-ask = 2789325.0
  :volume-by-product = 10759.33251354}

✅bitFlyer遅延問題と対策

bitFlyerのサーバ処理が遅延することによって引き起こされる諸問題と対策.

この問題はなかなか深刻な問題でありみんな悩んでいる. このページの分類がわかりやすい.

ref. BitflyerFXで注文が消えたり、約定拒否や二重注文になる場合の対策コード

  • 注文遅延: 指値注文は受理されたが未約定/建玉一覧に未反映で数十秒-数分経過.
  • スレ違い: 指値注文のキャンセル失敗. リクエスト自体は正常応答.
  • 注文消失: 出したはずの注文が消える.
  • 二重注文: 注文が通信エラーのためリトライすると実は前回の注文が受理済み.

問題点は, POSTリクエストの正常終了はサーバ側の処理の正常終了を意味しないということ. サーバ側に届いたらサーバは即時レスポンスで200を返すがそのあとの処理はサーバの負荷に従って順次処理される.

そのため, 対策としてPOSTリクエスト後でchild_order_acceptance_idが帰ってきたらそのIDに対してステータスを取得してステータスの確認 & child_order_idを手に入れること. child_order_idが割り当てられるとそのacceptance_idはサーバで処理されたことになる.

💡理論的な在庫管理と現実の遅延という技術的課題

前半は一般的な📝在庫リスクの話. 後半がbitFlyerの遅延とその対策.

マーケット・メイクにおける在庫管理と実運用上の懸念点|Hoheto (仮想通貨botter)|note


この視点は面白い. 理論的には, Avellaneda Market Makingに従ってその公式通りに算出したreservation-priceに挿せばいいのだが, 実際問題としては遅延問題があるので理論を現実に落とし込むにはエンジニアリングが必要という話.

市場実勢価格連動なマーケット・メイキング

一般論としては市場実勢価格連動というマーケットメイクにおける指値戦略として議論されている. つまり市場のトレンドを考慮したマーケットメイクの指値決定.

💡板ステータス監視

bitFlyerの負荷状態を取得できる. pingのようなものでありより詳細な状態が取れる. mmbotはトレンドに弱いしサーバ状態が忙しいなら注文も通りづらいためとてたほうがいいかも, という戦略.


  • GET /v1/gethealth: 取引所の状態
  • GET /v1/getboardstate: 板の状態
    • NORMAL: 取引所は稼動しています
    • BUSY: 取引所に負荷がかかっている状態です。
    • VERY BUSY: 取引所の負荷が大きい状態です。
    • SUPER BUSY: 負荷が非常に大きい状態です。発注は失敗するか、遅れて処理される可能性があります。
    • NO ORDER: 発注が受付できない状態です。
    • STOP: 取引所は停止しています。発注は受付されません。

ref. https://peing.net/ja/q/152a527c-b602-4fc3-a960-dbf525e44a88

Busy異常検出で指値all cancel, 替玉成行close.

💡通信タイムアウト対策

通信エラーに対しては通信タイムアウトの値をデフォルトの10秒から30秒に伸ばしたら効果があったとの口コミあり.

BitflyerFXで注文が消えたり、約定拒否や二重注文になる場合の対策コード

ccxtのデフォルトタイムアウト値は10秒らしい(ソース未確認).

ref. 📝SocketTimeout/Connection Timeout/Connection Request Timeout

💡IFDOCO注文

特殊注文のひとつとしてIFDOCO注文をサポートしている. しかしTwitterで検索するとバグりまくりなので自分で実装したほうがいいとか. バグというのは遅延問題がここでも表面化しているのかもしれない.

嘘のようで本当にあった実例

スナフキンさんのBot勉強会プレゼンの11スライド目の見出しが面白い.

  • キャンセル処理の1分後にキャンセル処理w
  • 注文発注後30秒待っても板に乗らないにも関わらず150秒後に注文執行w

✅bitFlyer REST API Tips

仮説が多いので間違っている可能性もあるが, 適宜わかったことをupdateする.

正常終了

status=200, parase-reason: OKとなるのでstatus=200で判断する.

post 系の場合, 正常終了するとbodyが空のことがあるので注意.

注文に対するpost requestは情報取得して成功判断

open/close/cancelのような注文を操作するリクエストはリクエストを出したレスポンスからはその通信が成功したかどうかがわからないので, 別途状態取得のAPI(GET /v1/me/getchildordersで状態を確認する必要がある.

たとえば, すでにCOMPLETEDした注文にしてCANCELをなげてもレスポンスは正常終了になる. {:status 200, :reason-phrase “OK”, :body nil}

post requestの正常終了を依頼の正常終了と判断してはいけない. 情報取得をしないと判断には使えない.

order status CANCELEDとは部分約定

cancelのAPIでcancelをしたあとに 注文情報を取得しようとするとなにもかかってこない(正常終了).

部分約定が発生したとき, そのIDの注文情報を取得しようとすると, CANCELEDというステータスになっている.

つまり, 部分約定が発生した結果最低注文量の0.01を下回ると自動的にキャンセルされる.

💡エラー時の振る舞い

statusが200以外のときは, bodyに詳細情報が入っているので文字列JSONを解析して中身を取り出す.

status/error_message/dataの3種類の情報がある. statusはマイナスの数値でありhttp statusとは違うエラーコードなので注意.


エラーコード一覧, 拾い物.

ref. [bitFlyer] エラーコード一覧【随時更新】|ああああ|note

{"status": -1, "error_message": "Over API limit per minute", "data" :null}
{"status": -2, "error_message": "Under maintenance", "data" :null}
{"status": -100, "error_message": "Invalid product", "data": null}
{'status': -101, 'error_message': 'Invalid type of order', 'data': None}
{"status": -102, "error_message": "無効な売買コードです。", "data": null}
{"status": -103, "error_message": "価格は整数でご入力ください。", "data": null}
{"status": -104, "error_message": "Invalid order amount", "data": null}
{"status": -106, "error_message": "価格が低すぎます。", "data": null}
{"status": -107, "error_message": "The price is too high.", "data": null}
{'status': -111, 'error_message': 'Order not found', 'data': None}
{"status": -115, "error_message": "Not enough parameters.", "data": nil}
{"status": -121, "error_message": "利用規約に同意してください。", "data": null}
{"status": -122, "error_message": "Empty request body", "data": null}
{"status": -125, "error_message": "Invalid offset price.", "data": nil}
{'status': -153, 'error_message': '最低取引数量は 0.01 BTC です', 'data': None}
{"status": -156, "error_message": "Execution history is limited to the most recent 31 days.","data": null}
{"status": -200, "error_message": "Insufficient funds.", "data": null}
{'status': -203, 'error_message': '注文数の上限に達しています。', 'data': None}
{"status": -204, "error_message": "Market state is closed.", "data": null}
{'status': -205, 'error_message': '注文に必要な証拠金が不足しています。', 'data': None}
{"status": -208, "error_message": "Order is not accepted. Please try again later.", "data": null}
{'status': -500, 'error_message': 'Key not found', 'data': None}
{'status': -500, 'error_message': 'Invalid signature','data': None}
{'status': -500, 'error_message': 'Non-hexadecimal signature', 'data': None}
{'status': -501, 'error_message': 'タイムアウトしました。もう一度ログインしてください。', 'data': None}
{"status": -503, "error_message": "デモモードではご利用いただけません。", "data": null}
{'status': -508, 'error_message': '異常な発注があったため、注文を一定期間制限しています。', 'data': None}
{'status': -509, 'error_message': 'Your ordering has been limited due to submitting an irregular number of orders.', 'data': None}

✅注文情報を修正/更新するには?

edit_orderはない. 注文の修正はないので, キャンセルして再注文する.

✅注文状況を確認するには?

新規注文を出すと, child-order-acceptance-idが帰ってる. このIDを元に状況取得のAPIを叩く(GET /v1/me/getchildorders).

{:child-order-acceptance-id "JRF20220813-060xxx-0xxxxx"}

💡bitFlyerにはPost Only 注文がない

機能としてない. そのため指値は成行で約定することはある.

ref. Post Only注文

といっても, bitFlyerFXは指値注文も成行注文手数料がないのでtakerをmakerのみにする旨味はないかも.

💡執行数量条件とは

執行数量条件は注文を出すときに指定できる(POST /me/vi/sendchildorder).

3種類の注文オプションが指定できる.

FOKのcancelの挙動としては, requestに対して, child-order-acceptance-idが変える. このidを元にステータス取得をすると空の配列が変える. からの配列がcancelを示す.


ref. https://lightning.bitflyer.com/docs/specialorder

💡親注文と子注文の違いは?

注文オプションでロジックを指定した注文を発注すると, その注文を親注文(parent order)という. (POST /v1/me/sendparentorder)

注文種別は3種類ある.

ref. 🔗bitFlyer特殊注文

💡child-order-idとchild-order-acceptance-idの違いは?

{:child-order-id             "JFX20220813-074643-xxxxxxF"
 :child-order-acceptance-id  "JRF20220813-074643-xxxxxx"}

決定的な資料がないので以下は推測.

child-order-acceptance-idはAPIで注文を出したときに即時に帰ってくるID. これを利用してAPIではその後の処理ができる(ステータス取得/注文キャンセル).

child-order-idは注文を出したAPIとは別に情報取得APIで注文状況を取りに行ったときに現れる(/me/v1/getchildorders). また画面表示で表示されるのもchild-order-idであり, child-order-acceptacne-idは表示されない.

おそらく即時レスポンスのために発行される一時的なもと, その後システムでいろいろ処理したあとに付与されるIDがあり, なぜそうなっているのかはシステムの設計の話でよくわからない. ただしclientから注文を操作する場合, child-acceptance-idを覚えておけば困ることはないのでこれを一意のIDで扱っていい.


Twitterで検索をかけると, responseにchild-order-acceptance-idがないケースや{buf,sell}_child_order_acceptance_idが約低履歴に現れるなど, よくわからない仕様.

https://twitter.com/search?q=child_order_acceptance_id&src=typed_query&f=live

バグなのか? よくわからずにけっこうみんな悩んでいる.


child-acceptance-idが発行されたけどchild-order-idが付与されてないということはサーバ側の処理がキューイングされている状態だとすると, acceptance-idに対して追加でpost処理をすると遅延問題に発展する可能性あり.

✅bitFlyer遅延問題と対策


このwebsocketのSpecには注文ID, 注文の受付IDという記述がある.

https://bf-lightning-api.readme.io/docs/realtime-child-order-events

✅メンテナンス時間対策

bitFlyerは(2022現在), 毎日4:00-4:10が定時メンテナンスのためこの時間にbotを停止する必要がある.

ref. メンテナンスについて教えてください

メンテナンス中は新規注文, 約定は不可. 価格の取得はできる.

またこれとは別に不定期メンテナンスもあるので正確にメンテナンス回避するためには APIによる取引所の状態取得が必要. GET /v1/gethealthはメンテナンス中はSTOPではなくNORMALを返すことに注意.

getheatlth vs getboardstate

状態取得APIには2種類がある.

  • GET /v1/gethealth: 取引所の稼動状態
  • GET /v1/getboardstate: 板の状態

それぞれどう違うのか調査中.

healthがサーバの負荷(BUSY)状態, boardstateが取引可能状態(メンテナンス)なのかも.

過去チャートの取得(OHLCV)

bitFlyer APIでは過去チャート(OHLCV)を取得することはできない(約定履歴から自分で自作は可能).

バックテストなどをしたいときは, 自分でticker apiを叩いてためておくなり cryptowatchなどの外部サイトから取得する.

💡約定履歴からOHLCを自作する

約定履歴の仕組み(exections)

ref. GET /v1/execitions

約定履歴REST APIについて, 一つの履歴=id(一意の連番)の取得は1回のみという仕様のよう. これはつまりあるidを一度のリクエストで取得したら次のリクエストではそのidが取得できない. このことによって, ローカルでid管理をしないといけない. また1度の取得では500件まで.

ref. [二分探索]少ないAPIリクエストで効率的にbitFlyer約定履歴を検索する|Nagi|note

idは時系列に対する連番のため, countとbefore/afterで取得を調整.

count: 結果の個数を指定します。省略した場合の値は 100 です。 before: このパラメータに指定した値より小さい id を持つデータを取得します。 after: このパラメータに指定した値より大きい id を持つデータを取得します。

bitFlyer電話問題

取引量がおおいと警告の電話がかってくるらしい… 2019の話.

https://twitter.com/akagamiv2/status/1089489183763099648

サーバはMicrosoft Azure VM

bitFlyer_statusというbitFlyerの障害状況を発信するTwitterアカウントでは, bitFlyerに障害が発生すると, Microsoft Azure の障害が原因!Microsoft Azure の障害が原因!と全力アピールする.

https://twitter.com/bitFlyer_status

bitFlyer開発副部長によるbitFlyerの業務内容紹介動画. 55分より.

【オンライン配信】TOKYO BLOCKCHAIN TECH MEETUP 2022/ブロックチェーンエンジニアのためのテック系勉強会 - YouTube

サーバの手前にCDNがいるらしい. pingで叩くと2022現在はxxx.deploy. static.akamaitechnologies.comという文字が見えるのでアカマイ・テクノロジーズと推測.

;; https://twitter.com/zephel01_vc/status/996800530641911808?t=9E6lIhS_fOQjir1ch9ZKfA&s=19

bitFlyerは手前にCDNが入ってるのでそれをpingだけではダメで処理時間を計るなどしないとダメなため一度簡単なbotで処理時間を計ってみたいですね。 azure鯖を前に触った感じだと遅かったですね。

この場合, pingではなくhttpingが必要らしい.

;; https://twitter.com/patten_san/status/1215577240500457472

今日初めて知ったこと pingではapiサーバーまでの正確な応答時間は測れない httping api.bitflyer.***とかのように、httpingを使うと良い。pingの場合とは値が全然違うさらに、httping https://api.bitflyer.**のように、プロトコルも指定すべき さらに、v1/getboard等まで指定すると良いかも

AWS Cloud9の各リージョンとConohaからbitFlyerまでのpingとhttpingの測定結果(2020年8月)|moka|note

$ httping -c 10 https://bitflyer.com/v1/ticker

まあhttpingでなくても, ステータス取得のHTTP GET Requestとかでもいい.

.jpと.comのどちらが通信速度は速いか?

api.bitflyer.jpとapi.bitflyer.comの2つのendpointがあり,.comより.jpのほうが速いのでは?という噂が流れてそれを検証したらしい.

[Bitflyer API].jpと.comの違い+α|J4Np|note

結論は, .comが速いらしい. そして, API Docだと.comが載っているのでなにも問題なかった.

なお自分の手元のPCでのhttpingの結果はどちらも変わらなかった.

bitFlyer 非公式API

Webブラウザが利用しているAPIをつかって注文を出すと通りやすくなるとか.

以下の記事の出来事8あたり.

BTC自動売買は儲かるのか~BTC自動売買に影響を与えた10の出来事~|UKI|note


Private APIについて法で禁止されているわけではないが大抵はアプリの利用規約で禁止されている. あらゆるものの中身をねじ外してはいけない世界ならば人権の否定だし人類は不自由であり進化もしない.

利用規約では禁止されていて, 破るとそのプラットホームから追放だ. bitFlyerの禁止事項で書かれているようで, 使ってはいけない. わたしも使わない.

ネット記事だとちらほら情報はある(そのうちを2つのnote記事は最近削除されたのを見た).

endpoint的には, https://lighting.bitflyer.com/api/trade かな? これと公式API endpointをhttpingで比較しても遅いんだよな. 計測方法が不十分なのかも.


🔖Private API

bitFlyer: ハースーbot

端数処理の脆弱性を狙った処方だが, 今はもう通用しない気がする.

0.00000001以下ででエラーする.

400, Enter the size in units of 0.00000001 BTC.(-104)

Bitflyerの端数処理およびハスーbotに関する考察・対策 - 誰が損しているのか.- マクロは3:2:5

bitFlyer強制ロスカット時の約定Idの特徴

未検証だが一応メモ.

仮想通貨界隈で学んだこと。それを踏まえたEA再開の戦略 - FX自動売買(EAトレード)運用日記

そしてこのヒゲが出る理由を探るのですが、APIを使って約定データを取得すると約定データにある特徴が出ていることに気づきます。約定データのIDを調べていると、ヒゲが出るときの約定IDには違うパターンになっているデータが多く見られるんですね。そして、この違う約定IDのパターンのデータはこれはおそらく強制ロスカットの約定データだろうと推測します。

ただしこれはbitFlyerのレバレッジ倍率が15倍だったときの話で2022現在は2倍のなでそもそも強制ロスカットは発生しているのかという課題がある.

bitFlyer websocket Specs

bitFlyer Realtime APIのこと, websocketでリアルタイム通信.

https://bf-lightning-api.readme.io/docs?ljs=ja

通常のjson-rpc(wssで始まるendpoint)と, Socket.IOの2種類が用意されている.

約定履歴

https://bf-lightning-api.readme.io/docs/realtime-executions

Responseメッセージ注目ポイント.

  • idは一意の連番が振られる(法則性不明).
  • :messageには配列が入り, 同じ秒数の注文がまとめてはいっている.
  • sideが空文字になることがある. 板寄せ時に約定した場合空文字列となる仕様.
{:jsonrpc "2.0",
 :method "channelMessage",
 :params
 {:channel "lightning_executions_FX_BTC_JPY",
  :message
  [{:id 24xxxxx468,
    :side "SELL",
    :price 2862254.0,
    :size 0.03,
    :exec-date "2022-xx-xxTxx:57:29.1832374Z",
    :buy-child-order-acceptance-id "JRFxxxxxxxx-xxxxxx-xxxxxx",
    :sell-child-order-acceptance-id "xxxxxxxx-xxxxxx-xxxxxx"}
   {:id 24xxxxx469,
    :side "SELL",
    :price 2862023.0,
    :size 0.038,
    :exec-date "2022-xx-xxTxx:57:29.1988896Z",
    :buy-child-order-acceptance-id "JRFxxxxxxxx-xxxxxx-xxxxxx",
    :sell-child-order-acceptance-id "JRFxxxxxxxx-xxxxxx-xxxxxx"}
   {:id 24xxxxx470,
    :side "SELL",
    :price 2862023.0,
    :size 0.02712863,
    :exec-date "2022-xx-xxTxx:57:29.2146584Z",
    :buy-child-order-acceptance-id "JRFxxxxxxxx-xxxxxx-xxxxxx",
    :sell-child-order-acceptance-id "JRFxxxxxxxx-xxxxxx-xxxxxx"}]}}

Ticker

板の変化があると配信される, 板の統計情報スナップショット.

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

{:ltp 2864774.0,
 :tick-id 3xxxxxxx,
 :best-ask-size 0.012,
 :total-ask-depth 192.53978609,
 :market-ask-size 0.0,
 :best-bid 2864688.0,
 :state "RUNNING",
 :market-bid-size 0.0,
 :best-bid-size 0.01,
 :volume 5175.54343141,
 :product-code "FX_BTC_JPY",
 :total-bid-depth 390.48972861,
 :timestamp "2022-10-xxTyy:zz:16.7394838Z",
 :best-ask 2864883.0,
 :volume-by-product 5175.54343141}

LTP(last Trade Price)は約定価格/現在価格. 注意書きで配信効率によっては配信頻度が制限され, 正確な情報は約定配信を参照してほしいという記述が興味深い.

stateは板の状態であり, /v1/getboardstateで取得できるものとすると, 板の状態がwebsocketでも取得できる(i.e.RESTによる監視は不要)ということか?(調査中).

microsecondのズレはあるものの, だいたい1秒間に2回, つまり500msの間隔で配信されているように見える.

板情報のスナップショット

https://bf-lightning-api.readme.io/docs/realtime-board-snapshot

配信効率などの合理的理由により配信頻度が制限されるとの注意書きあり. 更新のたびに情報を取得する場合はは板情報の差分を参照とのこと.

asks, bids の配列の順序は保証していないため, 必要に応じてソートしてくださいとのこと.

{:mid-price 3020957.0,
 :bids
 [{:price 3020667.0, :size 0.023}
  {:price 3020508.0, :size 0.08}
 
  ....
 
  {:price 2944600.0, :size 0.01}
  {:price 2943500.0, :size 0.01}
  {:price 2943200.0, :size 0.01}],
 :asks
 [{:price 3021247.0, :size 0.18}
  {:price 3021248.0, :size 0.18}
  {:price 3021249.0, :size 0.18}
 
  ...
 
  {:price 3110014.0, :size 0.01}
  {:price 3111111.0, :size 0.163}
  {:price 3111500.0, :size 0.62}]}

体感で, 5秒に一回配信されている気がする. 取得出来る内容はRESTで取得できるものと同じ.

板情報の差分

板情報に更新があるとその差分を配信.

https://bf-lightning-api.readme.io/docs/realtime-board

  • size は価格に対応する注文の合計数量.
  • 価格に対応する注文が約定や失効等で板から消えた時size: 0 として配信.
  • 板寄せ時の成行注文が約定した時price: 0 の差分として配信.
{:mid-price 3014825.0,
 :bids
 [{:price 3003328.0, :size 0.0}
  {:price 3014278.0, :size 0.0}
  {:price 3014539.0, :size 0.01}
  {:price 3014541.0, :size 0.01}],
 :asks
 [{:price 3017596.0, :size 0.0}
  {:price 3015057.0, :size 0.0}
  {:price 3015163.0, :size 0.0}]}

注文イベント

このchannelの購読には認証が必要.

https://bf-lightning-api.readme.io/docs/realtime-api-auth https://bf-lightning-api.readme.io/docs/realtime-child-order-events

  • 配信は配列でやってくる.
  • priceがint型.

event-type: イベントの種類

  • ORDER
  • ORDER_FAILED
  • CANCEL
  • CANCEL_FAILED
  • EXECUTION
  • EXPIRE

child-order-type: 子注文の種類

  • LIMIT: 指値注文
  • MARKET: 成行注文

event-type: ORDER

{:expire-date "2022-11-19T18:20:08",
 :child-order-acceptance-id "JFX20221028-1300XX-XXXXXXF",
 :size 0.01,
 :product-code "FX_BTC_JPY",
 :side "BUY",
 :event-date "2022-10-28T13:00:09.042143Z",
 :price 2974000,
 :event-type "ORDER",
 :child-order-id "JFX20221028-1300XX-XXXXXXF",
 :child-order-type "LIMIT"}

event-type: CANCEL

{:product-code "FX_BTC_JPY",
 :child-order-id "JFX20221028-1300XX-XXXXXXF",
 :child-order-acceptance-id "JRF20221028-1300XX-XXXXXXF",
 :event-date "2022-10-28T13:00:27.1013388Z",
 :event-type "CANCEL",
 :price 2974000,
 :size 0.01}

event-type: EXECUTION

  • outstanding-size: 未約定数量
  • commision: 手数料
  • sfd: SFD徴収料
{:outstanding-size 0,
 :exec-id 240xxxxxx,
 :child-order-acceptance-id "JRF20221028-1300XX-XXXXXXF",
 :commission 0,
 :size 0.01,
 :sfd 0,
 :product-code "FX_BTC_JPY",
 :side "BUY",
 :event-date "2022-10-31T10:54:57.3471524Z",
 :price 3084563,
 :event-type "EXECUTION",
 :child-order-id "JFX20221028-1300XX-XXXXXXF"}

event-type: CANCEL_FAILED

  • child-order-idが空に注意.
{:product-code "FX_BTC_JPY",
 :child-order-id "",
 :child-order-acceptance-id "JRF20221028-1300XX-XXXXXXF",
 :event-date "2022-10-31T10:52:27.1867387Z",
 :event-type "CANCEL_FAILED"}

指値注文正常系

指値注文の場合は, 注文が通ると配列のなかに1つのorderデータが入っている.

  • child-order-type: LIMIT
  • event-type: ORDER

child-order-id/child-order-acceptance-idが帰ってくる. acceptance_idの一意性は保証されない可能性がある.

✅websocket 約定履歴acceptance_id消失問題と対策

指値注文が約定すると, event-type=EXECUTIONが返ってくる. idの一致を確認する.

  • event-type: EXECUTION

成行注文正常系

成行注文の場合は, 注文が通ると配列のなかに2つのorderデータが入っている.

child-order-id/child-order-acceptance-idはともに同じ.

  • 1つめ
    • child-order-type: MARKET
    • event-type: ORDER
  • 2つめ
    • event-type: EXECUTION

キャンセル正常系/異常系: CANCEL/CANCEL_FAILED

キャンセルリクエストが受理されるとevent-type: CANCELが返る.

約定済(EXECUTION)の注文に対するキャンセルリクエストはCANCEL_FAILEDが返る. しばしば, EXECUTIONとCANCEL_FAILEDはセットで流れてくる.

  • 1つめ
    • event-type: EXECUTION
  • 2つめ
    • event-type: CANCEL_FAILED

bitFlyer websocket API Topics

private API認証

https://bf-lightning-api.readme.io/docs/realtime-api-auth

ハマりポイントは, timestampだけStringではなくNumberの型(JavaだとLong)を指定するところ.

サンプルコードリンク集

⚖Socket.ioとJSON-RPCの比較

検索情報から比較した記事.

Node.jsで仮想通貨の自動取引JSON-RPC 対 Socket.io | 謎の技術研究部

Socket.IOとJSON-RPCの両方に接続して先に受信したデータで処理を行うというHackをしている. 利用者の数によってスピードが変わるのかも.

JSON-RPCとSocketIOによるwebsocketの2重接続化モジュール (BFS-X専用高速化モジュール)|くもすけ|note

bitFlyer websocketでTwitter検索すると, 時々途切れるという話題があるが, websocketの仕様でありそれを改良したのがSocket.IOなのかもしれない(あとで検証).

⚖板情報スナップショットと板情報の差分

websocket経由の板情報取得には2週類あるので, 比較をする.

スナップショットは5秒ごとの配信であり, 差分はリアルタイム. 5秒以内の最新情報がほしいか否かが使いわけのポイントとなる.

✅websocket 約定履歴acceptance_id消失問題と対策

websocket約定履歴で約定検出するもののREST APIや画面からみると実は約定してなかった問題.

ref. https://twitter.com/reo3313/status/1098283696728068097

child_order_acceptance_idは一意であることが保証されないらしい. 重複がある.

ref. https://twitter.com/reo3313/status/1099877415705239553

重複と消失の関係がいまいち見えないものの, 対策としてはREST APIで取得したものでなければ信頼できないということかな?

bifFlyer: ポジション自炊

REST GET /v1/me/getpositionsで取得する内容をRESTを叩かずにリアルタイムに把握.

websocketを使って流れてきたRealtimeAPIの約定履歴と自分の発注履歴を突き合わせてポジションを自炊管理. 途中でズレてしまう場合を救うために, 定期的にREST(getpositions)も叩いて上書き.

部分約定も再現できるとか.

✅bitFlyer: 板自炊と約定判定

websocket経由の約定情報取得がたまに3-5sec遅延する. しかしだいたい1sec以内には配信される.

;; https://twitter.com/peihsun3/status/1572055929461567489

約定通知より板更新の方が速く飛んでくるなら、best ask/bid値参照して約定判定した方が速そうだな、と思うなど。

more: 板自炊

✅bitFlyer: websocket約定履歴遅延問題と対策

まずは自分でも計測してみる.

🔗bitflyerの約定通知の遅延を測った|pe|note

くもすけ流にJSON-RPC/Socket.IOの2回線の早いものガチ.

⚖Socket.ioとJSON-RPCの比較

Orderbookの方を自炊してそこから約定判定することでそもそもwebsocket経由で取得しない.

✅板自炊と約定判定

サーバが高負荷状態ならば諦める. 取引中断.

✅bitFlyer遅延問題と対策

🤖SFDbot(bitFlyer)

SFDとは, Swap for Differenceの略であり, bitFlyer独自用語. 従って, SFDBotとは, bitFlyerで動くbotと考えていい.

bitFlyerのLightning 現物(BTC/JPY)とLightning FX の価格乖離の縮小を目的としたSFDの手数料を取るbot.

bitFlyer SFD手数料

現物BTCとlighting FX BTCの価格差乖離を防ぐ目的で, 現物とLightingの価格差が5%以上乖離したときに, その乖離を戻すようなオープンポジションの注文にインセンティブを与えるという仕組み.

これによって, 価格差乖離を抑制することが目的だった. Lightning FXは先物取引ではないが, 仕組みとしては📝資金調達率(Funding Rate/FR)と同じような設計となっている.


計算式も以下のページにある.

手数料一覧・税 | 【bitFlyer(ビットフライヤー)】

Lightning FX 取引価格と Lightning 現物(BTC/JPY)取引価格が 5% 以上乖離している場合、約定ごとに以下の Lightning FX SFD が発生します。 SFD は約定ごとに発生し、ポジションの決済時に清算されます。ただし、決済注文の約定時には、付与いたしません。

「価格乖離 (%)」は以下の式で計算されます。

価格乖離 (%) = (Lightning FX 取引価格 ÷ Lightning 現物 (BTC/JPY)最終取引価格 − 1)× 100

「SFD」は以下の式で計算されます。

SFD(円)= 取引数量 × Lightning FX 取引価格 × SFD 比率

検査結果はアプリではモバイル見当たらないがWeb UIではリアルタイムに見れる.

アルゴリズム

ヘッジパターンとネイキッドショートパターンの2つがある.

アンチbitFlyer SFDのススメ|UKI|noteより.

・リベートを貰うためにポジションが0以下で+5%価格乖離以上の水準でショートする。・ポジションをニュートラルに戻すために+5%価格乖離未満の水準で買い戻す。

乖離値5%の計算は, 現物価格LTP x 1.05. この場合, 切り上げ(ceil)がSFDしきい値の5%以上となる. 切り捨て(floor)は5%未満となる.

(defn calc-sfd
  "現物価格LTP x 1.05.
  この場合, 切り上げ(ceil)がSFDしきい値の5%以上となる.
  切り捨て(floor)は5%未満となる."
  [ltp-spot]
  (let [sfd (* ltp-spot 1.05)]
    [(Math/ceil sfd) (Math/floor sfd)]))

💡bitFlyer SFD両建て

bF両建て戦略. SFD発動中にbitflyerFX: ショート, 現物ロングでこのまま価格乖離の縮小を待つ. すると, 価格変動リスクを抑えることができる. 5%のときにポジションをつくり, 4.9%のときにポジション解消.

問題は一度5%を超えると, いつ戻るのかはわからないので1日で回収とはならないかも.

https://twitter.com/azuma_bitcoin/status/1374410488717537289

📝両建て

💡SFD発動中は買いからのポジションオープン決済で損をする

買いポジションオープンでSFD手数料をとられ, 売り決済でSFD手数料はもらえない. すると, 買いの方向からのポジション構築で必ず損をする. 売りポジションオープンでSFD手数料をもらえ, 買い決済でSFD手数料をとられるとプラマイ0になるので問題ない.

つまり, SFDはじまるとbitFlyerのビットコインFXはオワコン市場になる. GMOもあるのに, それでもこの特殊ルール市場で取引したいひとは誰?

大口注文による価格操作

アンチbitFlyer SFDのススメ|UKI|note

SFD手数料は価格差 5%から発動するため, たとえば現物LTP * 1.05に大口の指値をいれておき, 別アカウントでその指値板を0.01の単位でテイクしつづけることによって, 自作自演でSFDリベートがもらえるという穴があった.

これを利用するbotが横行した結果, bitFlyerに対する不信感も高まり, bitFlyer現物BTC市場は取引板がスカスカな市場になってしまった.


こっ, これは…

https://twitter.com/boocham_bot/status/1372498745988767745

Insights

🔗References

Sample Codes

🔗References

題材がbitFlyerなのでbitFlyer固有の課題に対してヒントが得られる.

Samples