Whirlpool mathematics
Whirlpool (や Uniswap v3) の価格と $${tick}$$ の変換についてのメモです。
なお、Whirlpool の SDK ではこれらの処理をやってくれるヘルパークラスがあるので、実装の詳細を知らずとも変換は可能です。
tick から価格 P
数学的背景
$$
\begin{array}{}
P &=& 1.0001^{tick} \\
\sqrt{P} &=& \sqrt{1.0001^{tick}} \\
&=& \sqrt{1.0001}^{tick} \\
\end{array}
$$
実装
get_sqrt_price_positive_tick
get_sqrt_price_negative_tick
実装上のキーポイント
べき乗を分解する。
$${tick = 203}$$ の場合を例にする。
なお、203 は 128+64+8+2+1 なので 2 進数では 11001011 となる。
$$
\begin{array}{rcl}
\sqrt{P} &=& \sqrt{1.0001}^{tick} \\
&=& \sqrt{1.0001}^{203} \\
&=& \sqrt{1.0001}^{128+64+8+2+1} \\
&=& \sqrt{1.0001}^{128} \cdot \sqrt{1.0001}^{64} \cdot \sqrt{1.0001}^{8} \cdot \sqrt{1.0001}^{2} \cdot \sqrt{1.0001}^{1} \\
\end{array}
$$
このように分解すると、事前に $${\sqrt{1.0001}^{2^n}}$$ を計算しておけば、あとは掛け算で実装できる。
Whirlpool における $${tick}$$ の範囲は [-443636, 443636]。
443636 は 19 ビットの 1101100010011110100 であるため、事前に計算しておくのは $${\sqrt{1.0001}^{2^{18}}}$$ までで足りる。
この程度の数なら十分ハードコードできる。
なお、マイナス方向の場合も同様に分解する。
$${tick = -203}$$ の場合を例にする。
$$
\begin{array}{rcl}
\sqrt{P} &=& \sqrt{1.0001}^{tick} \\
&=& \sqrt{1.0001}^{-203} \\
&=& \frac{1}{\sqrt{1.0001}^{203}} \\
&=& \frac{1}{\sqrt{1.0001}^{128+64+8+2+1}} \\
&=& \frac{1}{\sqrt{1.0001}^{128}} \cdot \frac{1}{\sqrt{1.0001}^{64}} \cdot \frac{1}{\sqrt{1.0001}^{8}} \cdot \frac{1}{\sqrt{1.0001}^{2}} \cdot \frac{1}{\sqrt{1.0001}^{1}} \\
\end{array}
$$
同様に、事前に $${\frac{1}{\sqrt{1.0001}^{2^n}}}$$ を計算しておけば、あとは掛け算で実装できる。
価格 P から tick
数学的背景
$$
\begin{array}{rcl}
tick&=&\lfloor\log_{1.0001}{P}\rfloor \\
&=&\lfloor\log_{\sqrt{1.0001}}{\sqrt{P}}\rfloor\\
\end{array}
$$
実装
tick_index_from_sqrt_price
実装上のキーポイント
2 を底として計算する
下記のように変換する。
$$
\begin{array}{rcl}
tick &=&\lfloor\log_{\sqrt{1.0001}}{\sqrt{P}}\rfloor\\
&=&\lfloor\log_{\sqrt{1.0001}}2 \cdot \log_{2}\sqrt{P} \rfloor\\
\end{array}
$$
$${\log_{\sqrt{1.0001}}2}$$ は定数になるため、あとは $${\log_{2}\sqrt{P}}$$ を計算すればよい。
2 を底とした対数の計算方法はこちらで説明されている。
整数部と小数部を別々に計算する
$${\log_{2}\sqrt{P}}$$ を計算する上で、整数部と小数部を別々に考える。
例えば、$${\log_{2} \sqrt{169}}$$、つまり $${\log_{2} 13}$$ を解こうとすると、$${2^3 \lt 13 \lt 2^4}$$ は自明であるため、解が $${3 + \alpha (0 \lt \alpha \lt 1)}$$ になることはわかる。
$$
\begin{array}{rcl}
\log_{2} 13 &=&\log_{2} 2^3 \cdot 2^\alpha \\
&=&3 + \log_{2}2^\alpha \\
\end{array}
$$
整数部が 3 とわかったので、あとは小数部である $${\alpha}$$ を求めればよくなった。
$$
\begin{array}{rcl}
\log_{2} 13 &=&3 + \log_{2}2^\alpha \\
\log_{2} 13 - 3 &=& \log_{2}2^\alpha \\
\log_{2} \frac{13}{2^3} &=& \alpha \\
\\
\alpha &=& \log_{2} \frac{13}{2^3} \\
\end{array}
$$
この $${\alpha}$$ の計算ロジックは Wikipedia にも載っているものだが、$${\alpha}$$ を 2 進数で表して、その小数部の各ビットを 1 つずつ解き明かしていると解釈できる。
次の2つの性質を使いながら、値が判明している真数を操作・観察しながら、対数側である $${\alpha}$$ のビットを決定していく流れを示す。
真数に対する2乗が、対数($${\alpha}$$) を2倍にする
対数から1を引くと、真数が 1/2 になる
この結果から、$${\alpha}$$ の2進数表現は 0.1011・・・ となることがわかり、0.6875 (0.5 + 0.125 + 0.0625) 以上であるとわかる。4 ビットではまだ精度が不足しているが、この処理を進めていけば、十分な精度の結果を得ることができる。
この記事が気に入ったらサポートをしてみませんか?