其实画期权最主要的是数据,既可以伪造,也可以用真实的,如果画出来的图和脑海里应该呈现的不一样,要不画错了,要不就代表当时具有很大的套利空间。
因此,我们接下来会通过两种工具分别用假数据与真数据来介绍如何画期权收益图。
我们首先来使用excel来绘制图,由于手里没有真实的数据,但我们又迫切需要这么一张图,那我们就可以通过伪造数据来做到。
因为我们伪造的数据都是理想情况下且简化过的,因此画出来的图会非常平滑,与教科书里的一模一样。
一张期权的收益主要受到以下三个因素影响:
因此我们只需设计这三个变量即可。
我们可以在excel里设定固定的行权价
与权利金
,将到期日价格一个个列出来,如下表所示:
平值行权价格 | 8 | 权利金 | 1 |
---|
标的到期价格 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
---|
接下来就可以计算不同到期日价格情况下的收益情况了,最基础的4种期权:买入看涨、买入看跌、卖出看涨、卖出看跌。
取其中一种情况解释,具体收益有两段,一段是一条导数为±1的射线,另一端是导数为0的射线(理想情况下)。
假设以215美元,买入2020-10-6到期、行权价为8750的看跌期权
意味着,我在2020-3-6,有使用8750美金的价格卖出1个比特币的权利。
最终价格 | 过程 | 最终收益 |
---|---|---|
上涨至9000 | 不行使权利 | -215 |
上涨至10000 | 不行使权利 | -215 |
下跌至8000 | (8750-8000)-215 | 535 |
下跌至500 | (8750-500)-215 | 8035 |
因此我们在写excel的函数的时候需要考虑两种情况,我们这里以if函数为例,买入看涨期权的写法如下:
=IF((-($B$1-B$2)-$D$1)<-$D$1, -$D$1, (-($B$1-B$2)-$D$1)), 其中B1为行权价,B2为到期日价格(此处B不是固定的,可向后延伸为C2, D2…, 分别代表不同的到期日),D1为权利金。
于是我们的数据全都伪造好了,正确与否,画图便知:
而略微复杂的期权,是这四种基础期权的叠加,如我们的买入跨式期权,就是买入看涨和买入看跌期权的组合,直接相加就可以画出来。
但更复杂的就涉及到不同行权价、不同权利金、不同到期日的共同组合,需要涉及更复杂的数据,包括虚值期权、实值期权等等。
上文的数据是我们自己伪造的,那么真实世界的数据是什么样子的?
我们接下来用一种数字货币来看看。
数据:OKEX交易所-EOSUSD期权合约
EOS是数字货币的一种,也是让作者套进去很久的数字货币.
我们来把它分为几个步骤:
想要获取少量的数据还是很简单的,okex的官方API接口就可以达成这个目的,这是他的API文档网址(也许需要外网,但以下代码获取数据确实需要)。
我们提供两种方法:
ccxt是一个集成了众多数字货币交易所API的第三方库,这是他的github网址。
我们找到我们需要的函数,按照其说明,只要把其中的underlying
改成我们需要的ID即可,同时需要的话加上到期日,如图所示,这一项并非必需项。因此我们就用python的request库访问,并整理成DataFrame.
不难发现获得的数据类型还是很多的。
import pandas as pd
import requests
url = 'https://www.okex.com/api/option/v3/instruments/EOS-USD/summary?delivery=200829'
context = requests.get(url)
print(pd.DataFrame(context.json()).head())
以下是输出结果:
ask_vol best_ask best_ask_size best_bid best_bid_size bid_vol \
0 0 0.0005 5 0
1 3.125 0.001 123 0
2 0 0.0005 5 0
3 2.8125 0.001 123 0
4 0 0
delta estimated_price gamma high_24h ... low_24h \
0 0.8592260574 0 -0.4961600626 0 ... 0
1 -0.0080590691 0 0.3711250638 0 ... 0
2 0.86536487 0 -0.138287936 0 ... 0
3 -0.0179810921 0 0.7450580261 0 ... 0
4 0.8629635948 0 0.4487230883 0 ... 0
lowest_sell mark_price mark_vol open_interest realized_vol theta \
0 0.0005 0.13281285 2.4201 0 0 -0.0006391405
1 0.0005 0.00015885 2.4201 0 0 -0.0006472155
2 0.0005 0.11698047 2.4201 0 0 -0.001289014
3 0.0005 0.0003873 2.4201 0 0 -0.0012961114
4 0.0005 0.10139366 2.4201 0 0 -0.0023321264
timestamp vega volume_24h
0 2020-08-29T02:40:03.000Z 0.0000053487 0
1 2020-08-29T02:40:03.000Z 0.0000053487 0
2 2020-08-29T02:40:03.000Z 0.0000107113 0
3 2020-08-29T02:40:03.000Z 0.0000107113 0
4 2020-08-29T02:40:03.000Z 0.0000193238 0
[5 rows x 24 columns]
ccxt需要首先创建交易所,不同的交易所有不同的标识,他的github上有支持的交易所名称。
ccxt封装了许多常见的功能为固定代码,同时因为不同交易所支持的功能不一样,我们也可以拼接函数,比如我们目前用的,将option后面的除了V3之外的单词全部首字母大写,即可使用,例子可以看下面的代码。
得到之后我们将其进行整理,他们ID的样式是EOS-USD-200829-2.70-C
,因此取最后一位作为期权的类型(看涨/看跌),按‘-’分割取倒数第二位作为其行权价,最后按照行权价排序即可。
同时为了让数据更直观一些,我们选取期权合约名称
、买一价
、卖一价
、期权类型
、行权价
展示,而且后面两个是我们自己写的。
import ccxt
import pandas as pd
exchange = ccxt.okex() # 创建交易所
params = {'underlying': 'EOS-USD',
'delivery': '200829'} # 所需参数
df = exchange.optionGetInstrumentsUnderlyingSummary(params=params) # 抓取数据
df = pd.DataFrame(df, dtype=float) # 输出为dataframe
df['type'] = df['instrument_id'].str[-1] # 取期权类型
df['strike'] = df['instrument_id'].str.split('-').str[-2] # 取行权价
df = df[['instrument_id', 'best_bid', 'best_ask', 'type', 'strike']]
df.sort_values('strike', inplace=True)
print(df)
df.to_csv(r'C:\Users\xxx\Python\options\show_options.csv', index=False)
exit()
以下是输出结果:
instrument_id best_bid best_ask type strike
0 EOS-USD-200829-2.70-C 0.0005 C 2.70
1 EOS-USD-200829-2.70-P 0.001 P 2.70
2 EOS-USD-200829-2.75-C 0.0005 C 2.75
3 EOS-USD-200829-2.75-P 0.001 P 2.75
4 EOS-USD-200829-2.80-C C 2.80
5 EOS-USD-200829-2.80-P 0.0015 P 2.80
6 EOS-USD-200829-2.85-C C 2.85
7 EOS-USD-200829-2.85-P 0.0015 P 2.85
8 EOS-USD-200829-2.90-C C 2.90
9 EOS-USD-200829-2.90-P 0.002 P 2.90
10 EOS-USD-200829-2.95-C C 2.95
11 EOS-USD-200829-2.95-P 0.002 P 2.95
12 EOS-USD-200829-3.00-C C 3.00
13 EOS-USD-200829-3.00-P 0.0025 P 3.00
14 EOS-USD-200829-3.05-C 0.0115 0.0335 C 3.05
15 EOS-USD-200829-3.05-P 0.0015 0.0045 P 3.05
17 EOS-USD-200829-3.10-P 0.0055 0.01 P 3.10
16 EOS-USD-200829-3.10-C 0.0095 0.013 C 3.10
18 EOS-USD-200829-3.15-C 0.004 0.0055 C 3.15
19 EOS-USD-200829-3.15-P 0.0065 0.0295 P 3.15
20 EOS-USD-200829-3.20-C 0.003 C 3.20
21 EOS-USD-200829-3.20-P 0.0185 0.046 P 3.20
22 EOS-USD-200829-3.25-C 0.0025 C 3.25
23 EOS-USD-200829-3.25-P P 3.25
25 EOS-USD-200829-3.30-P P 3.30
24 EOS-USD-200829-3.30-C 0.002 C 3.30
27 EOS-USD-200829-3.35-P P 3.35
26 EOS-USD-200829-3.35-C 0.0015 C 3.35
28 EOS-USD-200829-3.40-C 0.0015 C 3.40
29 EOS-USD-200829-3.40-P P 3.40
30 EOS-USD-200829-3.45-C 0.0015 C 3.45
31 EOS-USD-200829-3.45-P P 3.45
32 EOS-USD-200829-3.50-C 0.001 C 3.50
33 EOS-USD-200829-3.50-P P 3.50
34 EOS-USD-200829-3.55-C 0.001 C 3.55
35 EOS-USD-200829-3.55-P 0.0005 P 3.55
这一步主要是为了确定行权价与权利金,可以随便找一个行权价及其对应的权利金,也可以通过自己的特定计算来选取行权价及其权利金,甚至可以省略本步和上一步,但为了凸显不是伪造数据,因此步骤略显复杂。
我们这里选取3.10
作为行权价,原因在于其权利金较为丰厚。
另外,上面的表格存在许多缺失值,这是因为现实世界的期权流动性不一定特别好,尤其是这种非主流期权合约的非主流数字货币。
接下来要绘制的是以价格为x轴,收益为y轴的四种基本期权的图。
由于我们遇到的实际情况里,看跌期权与看涨期权各自有不同的权利金,为了区分我们使用groupby
将其分层分别计算,权利金我直接手动复制粘贴了,接下来各自期权的计算逻辑同excel,但注意根据真实数据需要乘以其合约乘数100。
计算完毕后再把数据合并到一起,就可以绘制出一张由真实数据产生的期权收益图了。
import pandas as pd
import numpy as np
import ccxt
from pyecharts.charts import *
from pyecharts import options as opts
df = pd.DataFrame()
exchange = ccxt.okex() # 建立交易所
df = pd.read_csv(r'C:\Users\xxx\Python\options\show_options.csv')
params = {'instrument_id': 'EOS-USD'} # 参数
price = float(pd.DataFrame(exchange.indexGetInstrumentIdConstituents(params=params)).loc['last', 'data']) # 获取现价
df['price'] = np.sort(np.append(np.linspace(price-0.5, price+0.5, 18), np.linspace(price-0.5, price+0.5, 18)))
df['strike'] = float(3.10)
# 计算数据
Call = df.groupby('type').get_group('C')
Put = df.groupby('type').get_group('P')
# 买入看涨
Call['call_buy'] = -(Call['strike'] - Call['price']) * 100 - Call['best_ask'] * 100
Call.loc[Call['price'] < Call['strike'], 'call_buy'] = - Call['best_ask'] * 100
# 买入看跌
Put['put_buy'] = (Put['strike'] - Put['price']) * 100 - Put['best_ask'] * 100
Put.loc[Put['price'] > Put['strike'], 'put_buy'] = - Put['best_ask'] * 100
# 卖出看涨
Call['call_sell'] = (Call['strike'] - Call['price']) * 100 + Call['best_bid'] * 100
Call.loc[Call['price'] < Call['strike'], 'call_sell'] = Call['best_bid'] * 100
# 卖出看跌
Put['put_sell'] = -(Put['strike'] - Put['price']) * 100 + Put['best_bid'] * 100
Put.loc[Put['price'] > Put['strike'], 'put_sell'] = Put['best_bid'] * 100
# 再次整理
df2 = Call[['type', 'strike', 'price', 'call_buy', 'call_sell']]
df2['put_buy'] = Put['put_buy'].tolist()
df2['put_sell'] = Put['put_sell'].tolist()
# 画图
fig1 = (Line(init_opts=opts.InitOpts(width='900px', height='400px'))
.add_xaxis(df2['price'])
.add_yaxis('买入看涨', df2['call_buy'], color='red', linestyle_opts=opts.LineStyleOpts(width=2))
.add_yaxis('买入看跌', df2['put_buy'], color='green', linestyle_opts=opts.LineStyleOpts(width=2))
.add_yaxis('卖出看涨', df2['call_sell'], color='black', linestyle_opts=opts.LineStyleOpts(width=2))
.add_yaxis('卖出看跌', df2['put_sell'], color='blue', linestyle_opts=opts.LineStyleOpts(width=2))
.set_series_opts(label_opts=opts.LabelOpts(is_show=False))
.set_global_opts(xaxis_opts=opts.AxisOpts(min_=2.5), yaxis_opts=opts.AxisOpts(min_=-40, max_=40)))
fig1.render_notebook()
以下是输出结果: