手把手教你 DVOL

news/2024/12/22 19:44:36/

773182dadace7eaecf32020bfa756cd6.jpeg

分享本文在朋友圈的读者可获得本文数据和 Python 代码。留个言说已分享(不用截屏)我相信你,我会发给你百度盘下载链接。

本文长度为 6393,建议阅读 32 分钟

题图:SignalPlus Dashboard

0

引言


Deribit volatility (DVOL) 指数是 Deribit 制定的加密货币期权的波动率指数,它衡量的是 30 天的向前看 (forward-looking) 预期波动率 (implied volatility 从期权价格隐含计算出来),而不是向后看 (backward-looking) 实际波动率 (realized volatility 从标的价格时间序列计算)。

ea42e4a5efb9759f1f24775bfcea7f4e.jpeg

DVOL 是一个年化波动率的预期。粗略来讲,要获得 BTC 价格每日的预期变动,只需将 DVOL 值除以 19 (365 的平方根) 即可。 例如,DVOL 等于 57 (其实指的是 57%) 相当于 3% 的日波动。

和老规矩一样,在前戏王中我会介绍推导所需要的知识点;在理论皇中我会根据不同市场惯例给出不同的波动率指数表达式,如股票市场的 VIX 和币圈市场的 DVOL;在实践狼中我会讲解如何用 Python 来计算 DVOL。

LET'S GO!

1

前戏王


1.1

方差期望

假设标的资产的 SDE 和经过伊藤定理变形后的 SDE 如下:

97ef141e633372cb0d67080940c8548f.png

第 2 个公式减去第 1 个公式后,两边乘以 2 得到:

1ebba83bc6c014deaba7d6d2b1ef923a.png

两边从 0 到 T 求积分得到:

a9c27ad6ff43ef09d354bc412a88095f.png

两边求期望得到:

f56a442fc1905cf80f6bd605022afd89.png

上式中的 F0,T 是标的资产在 T 时到期的远期价格,而左边的期望值年化后 (除以 T) 就是方差,即

911c929dbe1d287ac03736733f4ef32f.png

1.2

Carr & Madan 公式

Carr & Madan 公式是本文证明需要的核心内容,公式如下:

661efa20e165b1ae7a419ce07d1bb403.png

该公式咋一看很复杂,实际上就是利用了泰勒展开。在实操时,可把 g(ST) 看成某个金融产品的支付函数,右边积分项里有 put 和 call 的支付函数,这样对于任何一个金融产品,其支付函数都可以用一系列的 put 和 call 来合成。 

为了推导波动率指数,我们选取对数合约作为这个金融产品。

推导见 https://gregorygundersen.com/blog/2023/01/26/carr-madan/。

1.3

对数合约

对数合约 (log contract) 是一种奇异期权 (exotic option),它根据其标的资产价格的对数进行支付。对数合约的收益是单一资产价格的非线性函数。这种类型的期权使投资者表达对未来波动的看法。

给定对数合约的支付函数 g(x) = ln(x/S*),其一阶导数和二阶导数为

3611ccac7f3ae1939286a146ac36e4fc.png

注意到小节 1.1 最后得到公式里红色那一项就是对数合约的期望,利用对数性质 ln(ab) = ln(a) + ln(b) 做以下的恒等变换:

d8d8c93fbee2f1e851fab7eb4c5117cf.png

代入 Carr & Madan 公式得到

7280f878d91dc1e7ebcb5fffdd66f9ce.png

两边求期望可计算出

6fd85c577d771a713c66eb10a2aa4a48.png

将上面结果代入对数合约得到,

81820ec963fde8924c149ff75edc383e.png

将对数合约推导结果代入方差表达式得到,

354eb9d3dac5119b04101e19cd4bc130.png

这个公式就是方差的期望的精确数学表达式。

2

理论皇

根据上节最后一个公式根据不同资产市场的惯例做一些调整就能得到股票市场上的 VIX 公式和加密货币市场的 DVOL 公式。

2.1

VIX 公式

上节最后一个公式的推导和模型无关 (model-independent)

15a64d84fdee54a6ce95e868025f4e7b.png

但是用在实操上,还需要做几个近似:

首先远期价格 F0,T 是通过先找到最接近价中 (at-the-money, ATM) 的看涨期权 c0 和看跌期权 p0,再用平价公式 (put-call parity) 计算出来的:

f0c4ab3868f1fe9f8633e5c90c5f5b27.png


红色的项可根据当 x ≈ 0 有 ln(1+x) ≈ x - 0.5x做进一步的化简

e43afc161198ba5c63d947e182f90ae5.png

在实操时,通常 S* 选一个离 F0,T 很近但是小于它的值,那么 1 - F0,T/S* 是一个很接近 0 的数,因此上面的近似关系成立。


绿色的项可用累加近似积分

42b89c2eace0a26d0772119e35715a0a.png

由于 S* ≈ F0,T ,那么上式中的看跌期权 p(Ki), Ki ≤ F0,T 和看涨期权 c(Kj), Kj ≥ F0,T  都是价外期权 (out-of-the-money, OTM)。这样可操作的 VIX 指数表达式为

84a175385d8cfd5431421af7a5370884.png


实际上 VIX 是衡量 30 天的波动率的指标,因此会根据离 30 天最近的两个到期日的期权计算出 σ2VIX(T1) 和 σ2VIX(T2),再做一个线性插值,

25436d505e2845f7a58e45b8f1ea0ef5.png

2.2

DVOL 公式

DVOL 是 Deribit 发布的关于加密货币的波动率指数,Deribit 上面的期权有两大特征:

    1. 期权是币本位而不是 U 本位

    2. 标的是期货价格而不是现货价格

根据上述两个特征我们需要更改两处地方

1. 计算远期价格 F0,T 的公式需要改,因为期权是币本位,因此需要用 F0,T 转换成 U 本位,而且去掉 r,公式改成

a9c531d9014525ea379c74d140253a19.png

这个和 Deribit 官网上 DVOL 白皮书的公式吻合:

3b3fa95cb155353f20f162bc026a8e67.jpeg

2. 计算 strike constribution 的地方也要改:

493b235927ba835acefe51855a9d5967.png

这个也和 Deribit 官网上 DVOL 白皮书的公式吻合:

1ee00bb88cd4814c044bac3aadaaa541.jpeg

最终方差的公式变成

25ecc57f3f4a86a7a1b8f84e6a84ea6a.png

同理 DVOL 是衡量 30 天的波动率的指标,因此会根据离 30 天最近的两个到期日的期权计算出 σ2DVOL(T1) 和 σ2DVOL(T2),再做一个线性插值,

0e2d21b43ef69a7e42ae66eb2ae57f1f.png

3

实践狼


3.1

数据预处理

在 31-Apr-2023 (精确时间 UTC 10:42) 时从 Deribit 上截取的离 30 天之后最近的两个到期日期权的信息。 

28-Apr-2023 到期的期权信息 (28 天)

c2a1305fe3f5a135996f602a1f50ea2c.png

26-May-2023 到期的期权信息 (56 天)

5939508305bf931b7f4b41f3971b0948.png

截图的数据比较乱,好消息时可以从 Deribit Options 页面上下载 csv 格式的数据,点击下图右上角的红框即可。 这样就可以下载 28-April-2023 和 26-May-2023 的数据了 (注意当你们在未来下载时,要根据当天的 30 天后来选择最近两个到期日)。

f67a9ca9ac06f110cb7f73c06760478b.jpeg

计算 DVOL 只需要不同 strike 对应 call 和 put 的 bid | mid | ask,额外还需要 delta 值来过滤数据。从上图下载的 csv 数据可处理成下图的样子。

3b94baaef5c4dbeb244c2a5dd1c81cba.png

接下来用 Python 来计算 DVOL。

3.2

数据读取

首先引入需要的库 datetime, numpy 和 pandas。

from datetime import datetime
import pandas as pd
import numpy as np

上图的数据已存成 excel 里的两个 sheets,用 pd.read_excel() 将 sheet_name 参数设为 None 可以读取所有 sheets 中的数据。注意 header 参数设为 [0, 1] 是为了将数据转换成两层列标签的 DataFrame。第一层列标签是 "calls", "puts";第二层列标签是 "Delta", "Bid", "Mid", "Ask"。

data = pd.read_excel( "data.xlsx", sheet_name=None, header=[0,1], index_col=0 )
data

18bf4293c33b498ee85cc79e6e9a2236.png

丢弃掉 delta 小于 5% 的数据,因为对应的 OTM call 和 put 的期权值太小,用它们来计算波动率会“歪曲”波动率市场深度。

写一个 cutoff() 函数来过滤 delta 小于 5% 的数据。

def cutoff( pdf, delta_cutoff=0.05 ):return pdf[ (pdf["Calls"]["Delta"] >= delta_cutoff) & (pdf["Calls"]["Delta"] <= 1-delta_cutoff) ]

注意,这里 delta < 0.05 是指的 delta 绝对值,由于 call delta 为正,put delta 为负,那么就是过滤掉 

    • delta 小于 0.05 的 call

    • delta 大于 -0.05 的 put (即 delta 大于 0.95 的 call)

过滤完的数据可视化如下图所示。

d5dd6ed8d2a59c05a474d6294a80f210.png


3.3

计算远期价格

根据小节 2.2,远期价格是通过先找到最接近价中 (at-the-money, ATM) 的看涨期权和看跌期权,再用平价公式 (put-call parity) 计算出来的。写一个 get_forward() 函数来实现。

def get_forward( pdf ):abs_call_minus_put = np.abs(pdf["Calls"]["Mid"] - pdf["Puts"]["Mid"])idx = np.argmin(abs_call_minus_put)call_mid = pdf["Calls"]["Mid"].iloc[idx], put_mid = pdf["Puts"]["Mid"].iloc[idx]strike_min = pdf.index[idx]forward = strike_min / (1-call_mid + put_mid)strike_cutoff = strike_min if forward > strike_min else pdf.index[idx+1]return strike_cutoff, forward

代码解释如下:

    • 第 2-3 行计算 call 和 put 的 mid 差的绝对值

    • 第 4 行找到最小绝对值的索引

    • 第 5-6 行根据索引找出对应的 call 和 put 作为 ATM call 和 ATM put

    • 第 7 行根据索引找出对应的 strike 作为 ATM strike

    • 第 8 行用平价公式计算 forward

    • 第 9 行将最接近 forward 但比 forward 小的一个 strike 设为 strike cutoff

上述计算 forward 的过程的可视化如下图:

c5f78cb4465dc3aa77ee51f7440f2c11.png


3.4

计算方差

接下来根据小节 2.2 的公式来计算方差:

63520645311c685ec363d38cdb05654b.png

首先写一个 get_strike_width() 函数来计算公式里面的 

Δ

K,公式如下:

236a26a85149c34dfece3ef8976381a0.png

代码很简单无需进一步的解释。

def get_strike_width( strike ):strike_width = np.zeros(strike.shape)strike_width[1:-1] = 0.5*(strike[2:]-strike[:-2])strike_width[0] = strike[1] - strike[0]strike_width[-1] = strike[-1] - strike[-2]return strike_width

接下来写一个 get_variance() 函数来实现以下公式:

bd798ad08bf650e666d1cb82b752bf57.png

def get_variance( pdf, strike_cutoff, forward, t ):strike = pdf.index.valuesstrike_width = get_strike_width( strike )otm_call = pdf["Calls"]["Mid"][strike >= strike_cutoff]otm_put = pdf["Puts"]["Mid"][strike <= strike_cutoff]strike_price = pd.concat([otm_put, otm_call], axis=1).mean(axis=1)strike_contribution = strike_price * strike_width / (strike**2) * forwardvariance = (2*strike_contribution.sum() - (forward/strike_cutoff-1)**2) / treturn variance

代码解释如下:

  • 第 2 行获取所有 strike 数据

  • 第 3 行用计算每个 strike 对应的行权价格的宽度

  • 第 4-5 行利用 strike_cutoff 来找到 OTM call (舍弃所有 strike 小于 strike_cutoff 的 call) 和 OTM put (舍弃所有 strike 大于 strike_cutoff 的 put)

  • 第 6 行将 OTM call 和 OTM put 横向拼接,并求均值,这么做的原因是在 strike cutoff 处 同时有 call 和 put,因此求一个均值当作期权价格

  • 第 7 行根据公式计算 strike contribution

    b8cd3ab58fa97e1eb478e139e67353ae.jpeg

  • 第 8 行根据公式计算 variance

上述计算 variance 的过程的可视化如下图:

f209c9d2f14d3926c792f519ee85358a.png

3.6

插值 DVOL

最后将所有函数放入 get_dvol() 函数中,对两个到期日的期权数据分别计算出方差,然后线性插值出 30 天的方差,最后开根号得到 DVOL。

cbf359395ca6ac750c6fa64d71d59d64.png

def get_dvol( data, today ):t_list, variance_list = [], []for expiry, df in data.items():df = cutoff( df )strike_cutoff, forward = get_forward( df )t = (datetime.strptime(expiry,"%d%b%y") - datetime.strptime(today,"%d%b%y")).days / 365variance = get_variance( df, strike_cutoff, forward, t )t_list.append(t)variance_list.append(variance)t1, t2 = t_listv1, v2 = variance_listdvol = ((t1*v1*(t2-30/365)/(t2-t1) + t2*v2*(30/365-t1)/(t2-t1)) * 365/30)**0.5return dvol*100

最后代入具体数据得到在 31-Mar-2023 计算的 DVOL 值是 57.98。

today = "31MAR23"
get_dvol(data, today)
57.9761828940359

插值 DVOL 过程的可视化如下:

a42d6b57b6295fc954f78338f2c0ec9f.png

3.7

更多细节

Deribit 在计算 DVOL 时还有三个小细节需要处理:

EMA 平滑

上节计算出来的 DVOL 其实上叫 DVOL.RAW,每秒计算一次。但为了过滤噪声和平滑数据,Deribit 取最后 240 个点的指数移动平均线 (EMA) [通过调节参数 EMAPeriod] 以获得波动率指数 DVOL 的最终值。

7a960225f1bec36bf2b44d7eaef790b1.png

从上图不难看出,平滑后的 DVOL (淡蓝线) 比平滑前 DVOL.Raw (黑色) 少了很多 spikes。

计算 Instrument Price

我们在小节 3.1 中使用的 mid price 是已经处理好的,真正要用的 Instrument Price 还需要做下图的操作:

eda69e1ddd4ab9f8f3805a74365651d3.png

首先定义基差为 Depth Ask – Depth Bid,定义阈值 c = max(min(MaxSpreadBidRatio * DepthBid, MaxSpreadWidth), MinSpreadWidth)

    • 当基差小于 c 时,Instrument Price 可用 Mid Depth Price 来代表。

    • 当基差大于 c 时,Instrument Price 用 VWAP Trade Price 来代表。

      • 如果 VWAP Trade Price 不存在,Instrument Price 用 60 秒到90 秒前的 Past Mark Price 来代表。

        • 如果 Past Mark Price 也不存在,Instrument Price 用 Current Mark Price 来代表。

计算 Mid Depth Price

中间深度价格 = (深度卖价 + 深度买价) / 2,两者都被计算为 VWAP,直到达到至少 2 BTC [通过调节参数 DepthVolume] 的累计交易量,并总共使用 5 个层级 [通过调节参数 DepthLevels]。从第一个层级开始,每个层级只能比上一个层级相差 1 个 tick(即 0.0005 BTC)。如果这些级别的总交易量少于 2 BTC,则将在后面再创建 1 个层级,剩余的交易量为 2 BTC。具体例子如下图所示:

eb19fd978eb53319f4de3b7db35f80e7.png

在 Bid 边,注意到加入了 0.1500 和 0.1490,因为要确保每个层级相差 0.0005 BTC。此外 0.1485 已经到第 5 层级了,累计交易量只有 1.3 BTC,因此再用 0.7 BTC 的 0.1480 来补满。 在 Ask 边,前两级累计交易量已经 2.2 BTC 了,因此补充一个 0.1605 的层级,并且在 0.1610 层级把交易量调成 0.8 BTC 使得总数为 2 BTC。

计算深度买价 Depth Bid 为 

f1d454364760ff780788e78bcb12dc34.png

计算深度卖价 Depth Ask 为

4e0ed4e20362f5055f5428f914811be0.png

那么中间深度价格 = (深度卖价 + 深度买价) / 2 = 0.1544625。

4

总结


本文从 Carr & Madan 公式和对数合约开始,推导出方差期望的精确数学表达式,接着根据币圈的市场惯例得到 DVOL 的表达式,最后给出一套 Python 实现计算 DVOL 的流程。

19d74a202fc26434c4b27adb5a8f3c83.png

以后我会分享更多和 crypto market 相关的硬核技术内容。同学们也可关注我们的官网 https://www.signalplus.com/,以及加入以下我们的社群。

972b1964b03d01ffd3b3d5dd7f9f1ff4.png


http://www.ppmy.cn/news/48250.html

相关文章

Qt音视频开发37-识别鼠标按下像素坐标

一、前言 在和视频交互过程中,用户一般需要在显示视频的通道上点击对应的区域,弹出对应的操作按钮,将当前点击的区域或者绘制的多边形区域坐标或者坐标点集合,发送出去,通知其他设备进行处理。比如识别到很多人脸,用户单击某个人脸后指定对该人脸进行详细的信息查询等;…

记录webpack安装

在安装完 yarn以后&#xff0c;yarn安装_阿巴资源站的博客-CSDN博客 然后开始安装webpack 看网上有人说npm install webpack-cli -g没成功&#xff0c;于是没试直接跳过坑 sudo npm install webpack -g --unsafe-permtrue --allow-root 然后提示&#xff1a; webpack -v On…

RabbitMQ:消息中间件

文章目录 概念管理界面简介4中常见交换器类型1.Direct交换器:2.Fanout交换器3.Topic交换器4.headers交换器 对象类型消息传递同步等待使用代码创建队列待续...... 概念 在微服务架构中项目之间项目A调用项目B 项目B调用项目C项目C调用项目D。。 用户必须等待项目之间内容依次的…

【闲聊杂谈】HTTPS原理详解

HTTPS和HTTP的区别 HTTP虽然使用极为广泛, 但是却存在不小的安全缺陷, 主要是其数据的明文传送和消息完整性检测的缺乏, 而这两点恰好是网络支付, 网络交易等新兴应用中安全方面最需要关注的。 关于 HTTP的明文数据传输, 攻击者最常用的攻击手法就是网络嗅探, 试图从传输过程…

亚马逊广告运营常见问答

同一个广告组中&#xff0c;建议投放多少个关键词呢&#xff1f; 同一广告组下我们建议投放的关键词至多不超过50个。 1.如关键词设置过少&#xff08;且前期无法用数据佐证其精准引流效果时&#xff09;&#xff0c;有可能导致广告曝光量较低&#xff1b; 2.如关键词设置过多…

SAS学习第9章:卡方检验之适合性检验与独立性检验

卡方检验就是统计样本的实际观测值与理论推断值之间的偏离程度&#xff0c;实际观测值与理论推断值之间的偏离程度就决定卡方值的大小&#xff0c;如果卡方值越大&#xff0c;二者偏差程度越大&#xff1b;反之&#xff0c;二者偏差越小&#xff1b;若两个值完全相等时&#xf…

查询练习:按等级查询

建立一个 grade 表代表学生的成绩等级&#xff0c;并插入数据&#xff1a; CREATE TABLE grade (low INT(3),upp INT(3),grade char(1) );INSERT INTO grade VALUES (90, 100, A); INSERT INTO grade VALUES (80, 89, B); INSERT INTO grade VALUES (70, 79, C); INSERT INTO …

U-Boot 命令使用

进入 uboot 的命令行模式以后输入“help”或者“&#xff1f;”&#xff0c;然后按下回车即可查看当前 uboot 所 支持的命令&#xff0c;如图 所示&#xff1a; 我们输入“help(或?) 命令名”既可以查看命令的详细用法&#xff0c;以“bootz”这 个命令为例&#xff0c;我们输…