見出し画像

Bid & Ask Ratio Strength [TradingLite Indicator]

TradingLite用のBid & Ask Ratio Strengthというインジケーターを作りました。

ザックリ言うとオーダーブックの買い板(需要)と売り板(供給)の不均衡を色分けして表示するインジケーターです。


元ネタはTRDRのOrder Book Depth Orvelayです。

TRDRのインジケーターの強みは、何といっても各CEXのデータを合算したAggregationオーダーブックを扱えることです。
アラートも設定できて携帯からも見やすいので便利なトレードツールの一つです(回し者感)。


対して当インジケーターは、LitScriptの制約により単一取引所のものしか表示できないので、TRDRの劣化版といった位置付けになります。
唯一差別化できている点は、集計するオーダーブックの範囲を自由に設定できるあたりでしょうか。

一例として特定の深度にいるマーケットメイカーのものと思われる板を除外して、それ以外の深度にある板の不均衡を見たい場合などに集計範囲の指定が生きてくると思います。



 Bid & Ask Ratio Strengthのなかみ

Orderbookとは

オーダーブックの解説をBinanceアカデミーから引用しようと思っていたら日本語ページが削除されてしまっていたので、代わりにPhemexアカデミーの解説を貼り付けておきます。


以下はTradingLiteで見られるBinanceのBTCUSDT無期限先物のオーダーブックです。

価格帯別に出ている買い板と売り板のサイズを集計したグラフで、画像の場合は10ドル単位で現在価格より上に出ている売り板の枚数と現在価格より下に出ている買い板の枚数が集計・表示されています。


こちらはBinanceのBTCUSDT現物のオーダーブックです。

以降は一定範囲の買い板の集合体をBids、売り板の集合体をAsksと呼んでいきます。


インジケーターではBest Bid(最良買い気配)とBest Ask(最良売り気配)から指定した範囲をDepth1、Depth2、Depth3と階層分けし、階層別にBidsとAsksの不均衡を計算しています。


Orderbook Imbalanceとは

当インジケーターや一部ツールではBid & Ask Ratioと呼んでいますが、Orderbook Imbalanceという名称がメジャーだと思われます。
計算式は以下のような感じです。

Ratio = (Bids - Asks) / (Bids + Asks)

指定した範囲内の買い板の比率が高い(=需要が強い)ほど+1に、売り板の比率が高い(=供給が強い)ほど-1に近付く指標です。
もう少し掘り下げたい方は、以下の記事や論文を漁ってみてください。


名前的に紛らわしいですがbotterの記事でたびたび紹介されている杉原慶彦「執行戦略と取引コストに関する 研究の進展」(2012)にあるorder flow  imbalanceとは別物になります。



インジケーターの導入方法

このインジケーターでは板情報を扱っているため、GOLDプランが推奨条件となります。

  1. チャート画面上部の「INDICATORS」をクリック

  2. 「Community Indicators」をクリック

  3. 「Bid & Ask Ratio Strength」というインジケーターを探して選択

  4. 「+ Add to Chart」をクリックしてチャートにインジケーターを追加



設定値の説明

インジケーターの「Settings」アイコンをクリックすると設定画面が開きます。


Depth

集計するオーダーブックの範囲を指定します。

画像ではそれぞれBest BidまたはBest Askから上下に

  • Depth1: 0 % ~ 1.0 %

  • Depth2: 1.0 % ~ 2.0 %

  • Depth3: 2.0 % ~ 5.0 %

で範囲指定しています。
チェックボックスを外すと指定した範囲のデータは非表示となります。


Filter

表示するRatioの範囲を絞り込みます。

画像ではRatioが+0.3以上または-0.3以下のみ表示されるようになります。

Ratio Filterなし
Ratio Filterあり

Orderbook Sizeは計算に使用したBidsとAsksの板の合計サイズが指定した数値以上の場合のみ表示されるようになります。

この場合は1万枚以上の板で計算したRatioしか表示されません。
流動性のない場所でのRatio検出を防止したい場合に役立ちます。

Ratio Filter + Size Filterあり


Ratio Colors

Ratioの数値毎の表示色を指定します。

もっと細かく色分けしたら表示がクッソ重たくなったので、このぐらいにしておきました。
自分の分かりやすい色に変更してお使いください。


Line Colors

インジケーターの背景色と区切り線の色を指定します。



使用例

先ほど紹介した英文の記事では、Best BidとBest Askのみで計算した不均衡と価格の方向性に相関はないと結論付けられています。


ただ、

  • BidsとAsksの集計深度を増やしていくと不均衡と将来の価格変動との相関は高くなる

  • 不均衡とその直後に発生した成行注文の増加は価格変動の予測に役立つ

  • 時間の経過とともに予測精度は落ちていく

といった記述が見られます。

英文記事は秒足レベルのお話ですが、とりあえず短期足(+成行指標)で見てみましょう。

最近追加された公式インジケーターの「Bar Statistics」が各種数値を一覧で見るのに便利なので、コイツと一緒に見ていきます。


一時期CoinbaseのBTCUSD現物でインジケーターがバチバチに効いていて、最近は微妙な感じですが以下の設定で見てみます。

Coinbase BTCUSD現物の設定

上のチャートでは、価格の下落後に需要が強くなり、やや強めの成行買いも連続して一旦底打ちしています。

他には供給が強めの状態が続き、更に供給が強くなって価格が下落していくケースなど。
これは違うと思いますが、こういった変化点では価格操縦を試みるスプーフィングが起こっている可能性もあります。


次はBinanceのBTCUSDT無期限先物を見てみましょう。
年末とFUDの影響か板がスッカスッカなので、RatioのFilterを下げておきます。

Binance BTCUSDT無期限先物の設定

否定される方もいると思いますが、相場によってプレイヤーが入れ替わるので、インジケーターの設定も取引所や相場環境にフィッティングさせるべきだと考えています。

画像左側の黄色枠のように、Ratio単体で短期的な天井を取っていけそうな場面もあります。
デリバの場合は画像右側のように、ロングの清算が出た&需要が強くなったところのリバ取りを狙うというのもアリかもしれません。


他にはもっと範囲を広げてスイング視点での需給を観察するとか。

これを見るとCoinbaseのDepth10%~15%にはかなり強い需要がありそうだと分かります。


・・・だんだん面倒くさくなってきたので、このへんで切り上げてもよろしいでしょうか。
気が向いた時に追記するかもしれません。


そもそもインジケーターの使用方法なんて制作者が強制するものでもないので、好きなように使っていただいて且つ利益を出していただけるなら、これに勝る喜びはありません。

いい感じの言葉をひねり出したところでそろそろ限界なので〆ます。
インジケーターの不具合を見つけた際にはご連絡いただければ幸いです。



LitScript(2023/11/21追記)

もうすぐTradingLiteのサブスクが期限切れとなるためコードを公開しておきます。

//@version=1
study("Bid & Ask Ratio Strength")

const showD1 = input("Show Depth1 [0~Depth1 %]", true)
const depth1 = input("Depth1(%)", 1, minval=0, maxval=100, step=0.1)
const showD2 = input("Show Depth2 [Depth1~Depth2 %]", true)
const depth2 = input("Depth2(%)", 2, minval=0, maxval=100, step=0.1)
const showD3 = input("Show Depth3 [Depth2~Depth3 %]", true)
const depth3 = input("Depth3(%)", 5, minval=0, maxval=100, step=0.1)

header("Filter")
const plusRatioFilter = input("Plus Ratio", 0.30, minval=0.00, maxval=1.00, step=0.01)
const minusRatioFilter = input("Minus Ratio", -0.30, minval=-1.00, maxval=0.00, step=0.01)
const sizeFilter = input("Orderbook Size", 0, minval=0, maxval=1000000, step=10)

header("Ratio Colors")
const plusColorLevel1 = input("< +0.5", #81c78430 )
const plusColorLevel2 = input("< +0.7", #81c78460 )
const plusColorLevel3 = input("< +0.9", #4caf50ff )
const plusColorLevel4 = input(">= +0.9", #1b5e20ff )
const minusColorLevel1 = input("> -0.5", #f77c8030 )
const minusColorLevel2 = input("> -0.7", #f77c8060 )
const minusColorLevel3 = input("> -0.9", #f23645ff )
const minusColorLevel4 = input("<= -0.9", #801922ff )

header("Line Colors")
const backgroundColor = input("Background Color", #00000000 )
const borderColor = input("Border Color", #9598a1ff )

var bestBid = bid_spread(0)
var bestAsk = ask_spread(0)

var bids1 = showD1 ? bid_sum(close - bestBid * (depth1 / 100), bestBid - bestBid * (0 / 100)) : 0
var asks1 = showD1 ? ask_sum(bestAsk + bestAsk * (0 / 100), close + bestAsk * (depth1 / 100)) : 0
var bids2 = showD2 ? bid_sum(close - bestBid * (depth2 / 100), bestBid - bestBid * (depth1 / 100)) : 0
var asks2 = showD2 ? ask_sum(bestAsk + bestAsk * (depth1 / 100), close + bestAsk * (depth2 / 100)) : 0
var bids3 = showD3 ? bid_sum(close - bestBid * (depth3 / 100), bestBid - bestBid * (depth2 / 100)) : 0
var asks3 = showD3 ? ask_sum(bestAsk + bestAsk * (depth2 / 100), close + bestAsk * (depth3 / 100)) : 0

var total1 = bids1 + asks1
var total2 = bids2 + asks2
var total3 = bids3 + asks3
var ratio1 = (bids1 - asks1) / (total1)
var ratio2 = (bids2 - asks2) / (total2)
var ratio3 = (bids3 - asks3) / (total3)

var filteringRatio1 = 
	ratio1 >= 0 && ratio1 >= plusRatioFilter && total1 >= sizeFilter ? ratio1 : 
	ratio1 < 0 && ratio1 <= minusRatioFilter && total1 >= sizeFilter ? ratio1 : 0
var filteringRatio2 = 
	ratio2 >= 0 && ratio2 >= plusRatioFilter && total2 >= sizeFilter ? ratio2 : 
	ratio2 < 0 && ratio2 <= minusRatioFilter && total2 >= sizeFilter ? ratio2 : 0
var filteringRatio3 = 
	ratio3 >= 0 && ratio3 >= plusRatioFilter && total3 >= sizeFilter ? ratio3 : 
	ratio3 < 0 && ratio3 <= minusRatioFilter && total3 >= sizeFilter ? ratio3 : 0


fill(0, depth1, color=
	filteringRatio1 == 0 ? backgroundColor : 
	filteringRatio1 >= 0.9 ? plusColorLevel4 : 
	filteringRatio1 >= 0.7 ? plusColorLevel3 : 
	filteringRatio1 >= 0.5 ? plusColorLevel2 : 
	filteringRatio1 > 0 ? plusColorLevel1 : 
	filteringRatio1 > -0.5 ? minusColorLevel1 : 
	filteringRatio1 > -0.7 ? minusColorLevel2 : 
	filteringRatio1 > -0.9 ? minusColorLevel3 : minusColorLevel4)
fill(depth1, depth2, color=
	filteringRatio2 == 0 ? backgroundColor : 
	filteringRatio2 >= 0.9 ? plusColorLevel4 : 
	filteringRatio2 >= 0.7 ? plusColorLevel3 : 
	filteringRatio2 >= 0.5 ? plusColorLevel2 : 
	filteringRatio2 > 0 ? plusColorLevel1 : 
	filteringRatio2 > -0.5 ? minusColorLevel1 : 
	filteringRatio2 > -0.7 ? minusColorLevel2 : 
	filteringRatio2 > -0.9 ? minusColorLevel3 : minusColorLevel4)
fill(depth2, depth3, color=
	filteringRatio3 == 0 ? backgroundColor : 
	filteringRatio3 >= 0.9 ? plusColorLevel4 : 
	filteringRatio3 >= 0.7 ? plusColorLevel3 : 
	filteringRatio3 >= 0.5 ? plusColorLevel2 : 
	filteringRatio3 > 0 ? plusColorLevel1 : 
	filteringRatio3 > -0.5 ? minusColorLevel1 : 
	filteringRatio3 > -0.7 ? minusColorLevel2 : 
	filteringRatio3 > -0.9 ? minusColorLevel3 : minusColorLevel4)
plot(depth3, linewidth=0.5, color=borderColor)
plot(depth2, linewidth=0.5, color=borderColor)
plot(depth1, linewidth=0.5, color=borderColor)
plot(0, linewidth=1, color=borderColor)

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