forked from qmhedging/poboquant
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path50ETF与期权对冲策略 50ETF and Options Hedging
307 lines (206 loc) · 11 KB
/
50ETF与期权对冲策略 50ETF and Options Hedging
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# coding:utf-8
#!/usr/bin/env python
from PoboAPI import *
import datetime
import numpy as np
#50ETF 和 50ETF期权的对冲交易,当ETF隐含波动率较高时就买50ETF并做空50ETF看涨期权
#用真格量化 python实现,在真格量化上运行,如果有问题 可加群 726895887 咨询
#真格量化可访问 https://quant.pobo.net.cn
#这是一个卖空波动率的50ETF期权策略样例,在日线级别测试
#run with Python 2.7
#开始时间,用于初始化一些参数
def OnStart(context) :
print "system starting..."
#设定一个全局变量品种
#context.param['ratio']
#登录交易账号,需在主页用户管理中设置账号,并把证券测试替换成您的账户名称
context.myaccOPT = None #初始化期权账户
if context.accounts.has_key("回测期权") :
print "登录交易账号[回测期权]"
if context.accounts["回测期权"].Login() :
context.myaccOPT = context.accounts["回测期权"]
context.myaccSTC = None #初始化股票账户
if context.accounts.has_key("回测证券") :
print "登录交易账号[回测证券]"
if context.accounts["回测证券"].Login() :
context.myaccSTC = context.accounts["回测证券"]
def OnMarketQuotationInitialEx(context, exchange,daynight):
if exchange == 'SHSE' and daynight == 'day':
print '上证所白盘行情开盘时重登交易账号'
context.myaccOPT.Logout()
context.myaccOPT.Login()
context.myaccSTC.Logout()
context.myaccSTC.Login()
g.code0 = "510050.SHSE" #证券品种为50ETF
g.etfpos=0 #初始化50ETF持仓数量
g.optpos=0 #初始化期权持仓数量
#g.ontradedealcount=0
#ratio=1.5
#订阅实时数据,用于驱动OnQuote事件
SubscribeQuote(g.code0) #订阅510050 实时行情
#SubscribeBar(g.code0, BarType.Day) #订阅 510050 日K线
#盘前查持仓
option1 = PBObj()
option1.buysellflag = '1' # 查期权的卖开头寸
posOPT=context.myaccOPT.GetPositions(option1)
if len(posOPT)>0:
g.optpos=posOPT[0].availvolume
print "ontradedeal 查到期权空头 "+str(g.optpos)
option2 = PBObj()
option2.buysellflag = '0' # 查50ETF的买开头寸
posETF=context.myaccSTC.GetPositions(option2)
if len(posETF)>0:
g.etfpos=posETF[0].volume
print "ontradedeal 查到50ETF 多头 "+str(g.etfpos)
#获取期权合约,包括call和put合约
def Getop(code): #获取虚值期权合约,虚值一档
dyndata = GetQuote(code)
#获取标的价格并计算实值期权价格
now1 = dyndata.now
now50 = round(now1,1) + 0.05
#计算期权合约的到期年月
cutime = GetCurrentTime()
if cutime.day >15 and cutime.month<12:
tim = cutime.month + 1
month_time = datetime.datetime(month=tim, year=cutime.year,day = 20) #当月日期大于15日,且当月在12月之前,则取下月
elif cutime.day >15 and cutime.month==12: #当月日期大于15日期额当月为12月 取次年1月
tim = 1
yea = cutime.year + 1
month_time = datetime.datetime(month=tim, year=yea,day = 20)
else: #其他情况取当月
month_time = cutime
#获取指定档位看涨看跌期权合约
atmopc = GetAtmOptionContract(code,month_time,now50,0) #取得期权合约
atmopp = GetAtmOptionContract(code,month_time,now50,1)
return atmopc,atmopp
def GetDaystoExpire(op):#计算期权到期日
info1 = GetContractInfo(op)
kill = info1['行权到期日']
cutime = GetCurrentTime()
c = cutime.date()
n = (kill - c).days #距离到期的时间
return n
#实时行情事件,当有新行情出现时调用该事件
def OnQuote(context, code) :
#获取最新行情
dyndata0 = GetQuote(g.code0)
#获取最新价
now0 = dyndata0.now
ratio=1.0#just a random ratio,try find a better one
# atmop = GetAtmOptionContract('510050.SHSE',datetime.datetime(2018,12,1),2.55,0,'A') #GetQuote("510050.SHSE")
# print "A atmop "+str(atmop)+str(GetContractInfo(atmop)["名称"])
# atmop1 = GetAtmOptionContract('510050.SHSE',datetime.datetime(2018,12,1),2.55,0,'M')
# print "M atmop "+str(atmop1)+str(GetContractInfo(atmop1)["名称"])
# atmop2 = GetAtmOptionContract('510050.SHSE',datetime.datetime(2018,12,1),2.55,0)
# print " atmop "+str(atmop2)+str(GetContractInfo(atmop2)["名称"])
#获取看涨看跌期权合约代码
opc,opp = Getop(g.code0)
print "opc:"+str(opc)
#获取期权行情
dyndata1 = GetQuote(opc)
tradingtime=GetQuote(g.code0).time
tradingdate=GetCurrentTime().date()
print "tradingtime ",tradingtime
print "tradingdate ",tradingdate
if dyndata0 :
#打印最新价
log.info("510050最新价: " + str(now0))
#获取50ETF的历史数据
klinedata0 = GetHisData(g.code0, BarType.Day)
#获取历史数据参数设置
CalObj = CreateCalcObj()
option = PBObj()
option.EndDate = GetCurrentTime()
option.Count = 30 #取30个日线数据点
#计算30日历史数据
klist = GetHisDataByField(g.code0, BarType.Day, "close", option)
#计算波动率
if len(klist)>0:
Kl = np.array(klist, dtype=np.double)
HisVola=CalObj.GetVolatility(Kl)
print HisVola
# 0 for call, 1 for put
# 0 for stock opt,1 for etf opt, 2 for futures opt
# here is the 510050 price
OptDirection=0 #看涨期权
AssetType=0 #标的为股票
AssetPrice=now0 #标的资产价格
print "AssetPrice ",AssetPrice
StrikePrice=round(now0,1) + 0.05 # 计算期权行权价
InterestRate=4.35*0.01 # 无风险利率
OptInfo = GetContractInfo(opc) #查询期权信息
ExpDates=OptInfo['行权到期日'] #获取期权的到期日
tradingdate=GetCurrentTime().date() #当前日期
ExpireinYear=(ExpDates - tradingdate).days / 365.0 #距离到期时间(以年为单位)
OptionPrice=dyndata1.now #当前期权价格
ImpliedVola=CalObj.GetImpliedVolatility(
OptDirection,AssetType,AssetPrice,StrikePrice,HisVola,
InterestRate,ExpireinYear,OptionPrice) #获取隐含波动率
balOPT = context.myaccOPT.AccountBalance #获取账户资金状况
posmarginOPT=balOPT.MarketValue
balSTC = context.myaccSTC.AccountBalance #获取账户资金状况
posmarginSTC=balSTC.MarketValue
posmargin=posmarginOPT + posmarginSTC
pos = context.myaccOPT.GetPositions()
poslength=len(pos) #获取当前持仓合约个数
########检查关注期权合约是否已经变化 opc 与 positions #############
if poslength>0:
for i in pos:
if i.contract<>opc:#关注合约已经改变,如果关注合约改变且持有改变前的期权合约,则全部平仓
OptClosePrice=GetQuote(i.contract)
OptCloseVolume=i.availvolume
print "期权合约变更 平仓 "+str(i.contract)+" 手数"+str(OptCloseVolume)
print "ETF跟随已经变更的期权平仓 "+str(g.code0)+" 手数 "+str(g.etfpos)
context.myaccOPT.InsertOrder(i.contract, BSType.BuyClose,max(OptClosePrice.now+0.0001,0.0001), OptCloseVolume)
context.myaccSTC.InsertOrder(g.code0, BSType.SellClose,max(dyndata0.now-0.001,0.001), g.etfpos)
#如果配置好交易账号了,可以根据条件下单,需把下面中的证券测试账号换成您设置的账号名称
if ImpliedVola>0.23 and poslength==0 and context.myaccOPT and context.myaccSTC and posmargin<=250000 and 100*10000*dyndata1.now/(dyndata0.now*10000)>=ratio:
# 50ETF隐含波动率大于23%就卖出50ETF购12月2600,买入50ETF
print "open positions with IV at "+str(ImpliedVola)
print "trading day "+str(tradingtime)
print "期权持仓市值 "+str(posmargin)
#做空期权,做多50ETF
context.myaccOPT.InsertOrder(opc, BSType.SellOpen, max(dyndata1.now-0.0001,dyndata1.falllimit), 100)
context.myaccSTC.InsertOrder(g.code0, BSType.BuyOpen, dyndata0.now+0.001, 10000)
if ImpliedVola<0.18 and poslength>0 and context.myaccOPT and context.myaccSTC :
# 50ETF隐含波动率小于18%就买平50ETF购12月2600,卖出50ETF,获利平仓
print "Close positions,take profit with IV"+str(ImpliedVola)
print "trading day "+str(tradingtime)
context.myaccOPT.InsertOrder(opc, BSType.BuyClose, dyndata1.now+0.0001, g.optpos)
context.myaccSTC.InsertOrder(g.code0, BSType.SellClose, dyndata0.now-0.001, g.etfpos)
if ImpliedVola>0.30 and poslength>0 and context.myaccOPT and context.myaccSTC :
# 50ETF隐含波动率大于30%就买平50ETF购12月2600,卖出50ETF,止损平仓
print "sell close the spread,cut loss with IV "+str(ImpliedVola)
print "trading day "+str(tradingtime)
context.myaccOPT.InsertOrder(opc, BSType.BuyClose, dyndata1.now+0.0001, g.optpos)
context.myaccSTC.InsertOrder(g.code0, BSType.SellClose, max(dyndata0.now-0.001,0.001), g.etfpos)
if poslength>0:
#平仓快要到期的期权头寸和相应50ETF头寸
for i in pos:
if GetDaystoExpire(i.contract)<=3:
OptClosePrice=GetQuote(i.contract)
OptCloseVolume=i.availvolume
print "期权到期平仓 "+str(i.contract)+" 手数"+str(OptCloseVolume)
print "ETF跟随到期的期权平仓 "+str(g.code0)+" 手数 "+str(g.etfpos)
context.myaccOPT.InsertOrder(i.contract, BSType.BuyClose,max(OptClosePrice.now+0.0001,0.0001), OptCloseVolume)
context.myaccSTC.InsertOrder(g.code0, BSType.SellClose,max(dyndata0.now-0.001,0.001), g.etfpos)
#委托回报事件,当有委托回报时调用
def OnOrderChange(context, AccountName, order) :
#打印委托信息,id是编号,volume是数量,详细见API文档
print "委托编号: " + order.id + " 账号名称: " + AccountName
print "Vol: " + str(order.volume) + " Price: " + str(order.price)
def OnTradeDeal(context, AccountName, trade) :
# g.ontradedealcount=g.ontradedealcount+1
# print "成交回报 第"+str(g.ontradedealcount)
option1 = PBObj()
option1.buysellflag = '1' # 查期权的卖开头寸
posOPT=context.myaccOPT.GetPositions(option1)
if len(posOPT)>0:
g.optpos=posOPT[0].availvolume
print "ontradedeal 查到期权空头 "+str(g.optpos)
option2 = PBObj()
option2.buysellflag = '0' # 查50ETF的买开头寸
posETF=context.myaccSTC.GetPositions(option2)
if len(posETF)>0:
g.etfpos=posETF[0].volume
print "ontradedeal 查到50ETF 多头 "+str(g.etfpos)