每日期权风险数据整理

histDayGreeksIVOpt50ETF(Date(2015,10,12)).head()

  1. | | Call | Call-Put | Put |
  2. | --- | -- |
  3. | | close | iv | delta | theta | gamma | vega | rho | strike | spot | forward | close | iv | delta | theta | gamma | vega | rho |
  4. | expDate | | | | | | | | | | | | | | | | | |
  5. | 2015-10-28 | 0.4331 | 0.4790 | 0.9830 | -0.1615 | 0.1779 | 0.0208 | 0.0842 | 1.85 | 2.288 | 2.283 | 0.0015 | 0.4793 | -0.0170 | -0.1059 | 0.1783 | 0.0208 | -0.0019 |
  6. | 2015-10-28 | 0.3821 | 0.4772 | 0.9692 | -0.2310 | 0.2949 | 0.0343 | 0.0850 | 1.90 | 2.288 | 2.283 | 0.0029 | 0.4765 | -0.0306 | -0.1725 | 0.2939 | 0.0341 | -0.0034 |
  7. | 2015-10-28 | 0.3335 | 0.4485 | 0.9568 | -0.2740 | 0.4144 | 0.0453 | 0.0859 | 1.95 | 2.288 | 2.283 | 0.0040 | 0.4473 | -0.0428 | -0.2129 | 0.4124 | 0.0450 | -0.0048 |
  8. | 2015-10-28 | 0.2874 | 0.4218 | 0.9381 | -0.3289 | 0.5861 | 0.0603 | 0.0862 | 2.00 | 2.288 | 2.283 | 0.0058 | 0.4220 | -0.0620 | -0.2690 | 0.5866 | 0.0604 | -0.0069 |
  9. | 2015-10-28 | 0.2420 | 0.2613 | 0.9773 | -0.1349 | 0.4175 | 0.0266 | 0.0929 | 2.05 | 2.288 | 2.283 | 0.0077 | 0.3873 | -0.0849 | -0.3130 | 0.8130 | 0.0768 | -0.0094 |
  10. 期权的隐含波动率微笑
  11. + 下图中,竖直虚线表示当日的标的50ETF收盘价
  12. + 实际上计算PCIVD就是仅仅考虑竖直虚线附近的平值期权
  13. + 看跌看涨隐含波动率微笑曲线中间的 Gap 的变化,正是我们关注点
  14. ```py
  15. histDayPlotSmileVolatilityOpt50ETF(Date(2015,10,12))

每日期权风险数据整理 - 图1

  1. def histDayPCIVD50ETF(date):
  2. ## PCIVD: Put Call Implied Volatility Diff;
  3. ## 看跌看涨期权隐含波动率价差,选取平值附近的近月和次近月合约构建
  4. ## 看跌和看涨期权的隐含波动率指数,PCIVD即为两指数之差。
  5. # Uqer 计算期权的风险数据
  6. opt = histDayGreeksIVOpt50ETF(date)
  7. # 下面展示波动率微笑
  8. exp_dates = np.sort(opt.index.unique())[0:2]
  9. pcivd = pd.DataFrame(0.0, index=map(Date.toDateTime, [date]), columns=['nearPCIVD','nextPCIVD'])
  10. pcivd.index.name = 'date'
  11. ivd = []
  12. for epd in exp_dates:
  13. opt_epd = opt[opt.index==epd]
  14. opt_epd[('Call-Put', 'diffKF')] = np.abs(opt_epd[('Call-Put', 'strike')] - opt_epd[('Call-Put', 'spot')])
  15. opt_epd = opt_epd.set_index(('Call-Put', 'strike'))
  16. opt_epd.index.name = 'strike'
  17. opt_epd = opt_epd.sort([('Call-Put', 'diffKF')]).head(2)
  18. ivd_epd = opt_epd[('Put', 'iv')].mean() - opt_epd[('Call', 'iv')].mean()
  19. ivd.append(ivd_epd)
  20. pcivd.ix[Date.toDateTime(date)] = ivd
  21. return pcivd
  22. def histDayPCIVD50ETF_check(date):
  23. ## PCIVD: Put Call Implied Volatility Diff;
  24. ## 看跌看涨期权隐含波动率价差,选取平值附近的近月和次近月合约构建
  25. ## 看跌和看涨期权的隐含波动率指数,PCIVD即为两指数之差。
  26. # Uqer 计算期权的风险数据
  27. opt = histDayGreeksIVOpt50ETF(date)
  28. # 下面展示波动率微笑
  29. exp_dates = np.sort(opt.index.unique())[0:2]
  30. pcivd = pd.DataFrame(0.0, index=map(Date.toDateTime, [date]), columns=['nearPCIVD', 'nearPutIV', 'nearCallIV','nextPCIVD', 'nextPutIV', 'nextCallIV'])
  31. pcivd.index.name = 'date'
  32. ivd = []
  33. for epd in exp_dates:
  34. opt_epd = opt[opt.index==epd]
  35. opt_epd[('Call-Put', 'diffKF')] = np.abs(opt_epd[('Call-Put', 'strike')] - opt_epd[('Call-Put', 'spot')])
  36. opt_epd = opt_epd.set_index(('Call-Put', 'strike'))
  37. opt_epd.index.name = 'strike'
  38. opt_epd = opt_epd.sort([('Call-Put', 'diffKF')]).head(2)
  39. ivd_epd = opt_epd[('Put', 'iv')].mean() - opt_epd[('Call', 'iv')].mean()
  40. ivd.append(ivd_epd)
  41. ivd.append(opt_epd[('Put', 'iv')].mean())
  42. ivd.append(opt_epd[('Call', 'iv')].mean())
  43. pcivd.ix[Date.toDateTime(date)] = ivd
  44. return pcivd
  45. def histPCIVD50ETF(beginDate, endDate):
  46. begin = Date.fromDateTime(beginDate)
  47. end = Date.fromDateTime(endDate)
  48. cal = Calendar('China.SSE')
  49. dates = cal.bizDatesList(begin, end)
  50. pcivd = pd.DataFrame()
  51. for dt in dates:
  52. pcivd_dt = histDayPCIVD50ETF(dt)
  53. pcivd = concat([pcivd, pcivd_dt])
  54. pcivd['nearDiff'] = pcivd['nearPCIVD'].diff()
  55. pcivd['nextDiff'] = pcivd['nextPCIVD'].diff()
  56. return pcivd
  57. def histPCIVD50ETF_check(beginDate, endDate):
  58. begin = Date.fromDateTime(beginDate)
  59. end = Date.fromDateTime(endDate)
  60. cal = Calendar('China.SSE')
  61. dates = cal.bizDatesList(begin, end)
  62. pcivd = pd.DataFrame()
  63. for dt in dates:
  64. pcivd_dt = histDayPCIVD50ETF_check(dt)
  65. pcivd = concat([pcivd, pcivd_dt])
  66. pcivd['nearPutDiff'] = pcivd['nearPutIV'].diff()
  67. pcivd['nearCallDiff'] = pcivd['nearCallIV'].diff()
  68. pcivd['nextPutDiff'] = pcivd['nextPutIV'].diff()
  69. pcivd['nextCallDiff'] = pcivd['nextCallIV'].diff()
  70. return pcivd

计算PCIVD

  • 期权自15年2月9号上市
  • 此处计算得到的数据可以用在后面几条策略中 结果中的列分别为:

  • nearPCIVD:当月PCIVD

  • nextPCIVD:次月PCIVD
  • nearDiff:当月PCIVD与前一日值的变化量
  • nextDiff:次月PCIVD与前一日值的变化量
  1. ## PCIVD计算示例
  2. start = datetime(2015, 2, 9) # 回测起始时间
  3. end = datetime(2015, 10, 12) # 回测结束时间
  4. pcivd = histPCIVD50ETF(start, end)
  5. pcivd.tail()
nearPCIVDnextPCIVDnearDiffnextDiff
date
2015-09-290.155400.159150.026600.0073
2015-09-300.102050.14915-0.05335-0.0100
2015-10-080.088450.10645-0.01360-0.0427
2015-10-090.083200.10375-0.00525-0.0027
2015-10-120.046350.07065-0.03685-0.0331

2.1 结合使用当月、次月 PCIVD 的择时策略

策略思路:考虑当月 PCIVD 和 次月 PCIVD 的日变化量

  • 当月 PCIVD 和 次月 PCIVD 同时变小(当月和次月的 PCIVDDiff 同时小于0),则今天全仓50ETF
  • 否则,清仓观望
  1. start = datetime(2015, 2, 9) # 回测起始时间
  2. end = datetime(2015, 10, 8) # 回测结束时间
  3. benchmark = '510050.XSHG' # 策略参考标准
  4. universe = ['510050.XSHG'] # 股票池
  5. capital_base = 100000 # 起始资金
  6. commission = Commission(0.0,0.0)
  7. refresh_rate = 1
  8. # pcivd = histPCIVD50ETF(start, end)
  9. def initialize(account): # 初始化虚拟账户状态
  10. account.fund = universe[0]
  11. def handle_data(account): # 每个交易日的买入卖出指令
  12. fund = account.fund
  13. # 获取回测当日的前一天日期
  14. dt = Date.fromDateTime(account.current_date)
  15. cal = Calendar('China.IB')
  16. last_day = cal.advanceDate(dt,'-1B',BizDayConvention.Preceding) #计算出倒数第一个交易日
  17. last_day_str = last_day.strftime("%Y-%m-%d")
  18. # 计算买入卖出信号
  19. try:
  20. # 拿取PCIVD数据
  21. pcivd_near = pcivd.nearDiff.loc[last_day_str]
  22. pcivd_next = pcivd.nextDiff.loc[last_day_str]
  23. long_flag = True if pcivd_near < 0 and pcivd_next < 0 else False
  24. except:
  25. long_flag = False
  26. if long_flag:
  27. # 买入时,全仓杀入
  28. try:
  29. approximationAmount = int(account.cash / account.referencePrice[fund] / 100.0) * 100
  30. order(fund, approximationAmount)
  31. except:
  32. return
  33. else:
  34. # 卖出时,全仓清空
  35. order_to(fund, 0)

每日期权风险数据整理 - 图2

PCR 和 PCIVD 的良好择时效果表明,虽然回测时间短,但它们均可以通过期权市场的信息来给出在现货市场的买卖择时信号,必要时建议我们空仓

3. 监视最近 PCR 和 PCIVD 走势

  • 每日监视 PCR 和 PCIVD 近期走势,指导次日操作
  • 如果 PCR 和 PCIVD 的值降低,那么我们就在第二天买入
  1. cal = Calendar('China.IB')
  2. # Dates
  3. end = Date.todaysDate()
  4. end = cal.advanceDate(end,'-1B',BizDayConvention.Preceding) # 这里结束点选择昨天,因为DataAPI的今日数据要到收盘后比较晚才能拿到;实际中可以自己调整
  5. start = cal.advanceDate(end,'-15B',BizDayConvention.Preceding) # 开始点为七天前
  6. ## 计算 PCR 和 PCIVD
  7. start = start.toDateTime()
  8. end = end.toDateTime()
  9. hist_pcr = histPCR50ETF(start, end) # 计算PCR
  10. hist_pcivd = histPCIVD50ETF(start, end) # 计算PCIVD
  11. hist_pcr[['nearVolPCR', 'nearValuePCR']].plot(style='s-')
  12. hist_pcivd[['nearPCIVD', 'nextPCIVD']].plot(style='s-')
  13. <matplotlib.axes.AxesSubplot at 0x852ba90>

每日期权风险数据整理 - 图3

每日期权风险数据整理 - 图4

PCIVD 图中,近月期权的 PCIVD 在行权日为0,需要注意;行权日附近,可以以次近月期权的 PCIVD 走势为参考