06_50ETF期权备兑认购策略.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. # 50ETF-期权-备兑看涨策略
  2. # 参考资料:
  3. # - 原始策略来源: https://www.joinquant.com/view/community/detail/2b40f724dcea54aaa06419a46517f3db
  4. # - 研究网址: https://www.joinquant.com/research?target=research&url=/user/75474983526/notebooks/Options/50ETF-%E6%9C%9F%E6%9D%83-%E5%A4%87%E5%85%91%E7%9C%8B%E6%B6%A8%E7%AD%96%E7%95%A5.ipynb
  5. # TODO: 添加50ETF期权备兑看涨策略相关代码
  6. import jqdata
  7. from jqdata import *
  8. import pandas as pd
  9. import numpy as np
  10. import datetime
  11. import matplotlib.pyplot as plt
  12. plt.rcParams['font.sans-serif']=['SimHei']
  13. plt.rcParams['axes.unicode_minus'] = False
  14. #获取交易时间和时间间隔(频率:月)
  15. trade_days = pd.Series(index=jqdata.get_trade_days('2025-03-01','2025-06-10'))
  16. trade_days.index = pd.to_datetime(trade_days.index)
  17. month_split = list(trade_days.resample('M',label='left').mean().index) + [pd.to_datetime('20250610')]
  18. month_split
  19. ##传统备兑开仓策略
  20. holding_contract2 = pd.Series(index=trade_days.index)
  21. #获取首个持仓合约
  22. q_contract_info = query(opt.OPT_CONTRACT_INFO.code,
  23. opt.OPT_CONTRACT_INFO.trading_code,
  24. opt.OPT_CONTRACT_INFO.name, #合约代码,合约交易代码,合约简称
  25. opt.OPT_CONTRACT_INFO.exercise_price, opt.OPT_CONTRACT_INFO.last_trade_date,
  26. opt.OPT_CONTRACT_INFO.list_date
  27. ).filter(opt.OPT_CONTRACT_INFO.contract_type == 'CO', #看涨期权
  28. opt.OPT_CONTRACT_INFO.exchange_code == 'XSHG', #上交所
  29. opt.OPT_CONTRACT_INFO.last_trade_date > month_split[0], #时间-到期月开始
  30. opt.OPT_CONTRACT_INFO.last_trade_date < month_split[1], #时间-到期月结束
  31. opt.OPT_CONTRACT_INFO.list_date < trade_days.index[0]) #在交易前上市
  32. # 对应的期权列表
  33. contract_info = opt.run_query(q_contract_info)
  34. # 要先使用trading_code前6位为'510050'过滤
  35. contract_info = contract_info[contract_info['trading_code'].str[:6] == '510050']
  36. # 获取etf第一个交易日的收盘价etf_cls,price_spread是行权价exercise_price和etf价格的差
  37. etf_cls = get_price('510050.XSHG',trade_days.index[0],trade_days.index[0],fields=['close']).values[0][0]
  38. contract_info['price_spread'] = contract_info['exercise_price'] - etf_cls
  39. if contract_info['price_spread'].max() > 0:
  40. #选出认购虚值期权
  41. contract_info = contract_info[contract_info['price_spread'] > 0]
  42. contract_info = contract_info.sort_values('exercise_price')
  43. else: #全是认购实值期权
  44. contract_info = contract_info.sort_values('exercise_price',ascending=False)
  45. holding_contract2[trade_days.index[0]] = contract_info['code'].iloc[0]
  46. newest_exercise_price = contract_info['exercise_price'].iloc[0]
  47. newest_expire_date = contract_info['last_trade_date'].iloc[0]
  48. print(f"newest_exercise_price:{newest_exercise_price}")
  49. print(f"newest_expire_date: {newest_expire_date}")
  50. print(f"first trading day: {trade_days.index[0]}")
  51. print(f"etf_cls: {etf_cls}")
  52. # 循环访问每一个交易日,判断交易情况
  53. # 规则:持有略虚值看涨期权,待行权价低于现价的 95% 时,平仓原期权合约
  54. # 重新开仓略虚值看涨期权;到期前1天移仓换月至次月合约
  55. for t in trade_days.index[1:]:
  56. the_date = pd.to_datetime(get_trade_days(end_date=pd.to_datetime(newest_expire_date),count=2)[0])
  57. print(f"processing day: {t} data, newest_expire_date: {newest_expire_date}, the_date: {the_date}")
  58. #到期前一天
  59. if t >= the_date:
  60. print(f"{t} 晚于 the_date: {the_date} 日期")
  61. #寻找month_idx
  62. for month_idx in range(len(month_split)):
  63. if month_split[month_idx] >= t:
  64. break
  65. q_contract_info = query(opt.OPT_CONTRACT_INFO.code, opt.OPT_CONTRACT_INFO.trading_code, opt.OPT_CONTRACT_INFO.name,
  66. opt.OPT_CONTRACT_INFO.exercise_price, opt.OPT_CONTRACT_INFO.last_trade_date,
  67. opt.OPT_CONTRACT_INFO.list_date
  68. ).filter(opt.OPT_CONTRACT_INFO.contract_type == 'CO', #看涨期权
  69. opt.OPT_CONTRACT_INFO.exchange_code == 'XSHG', #上交所
  70. opt.OPT_CONTRACT_INFO.last_trade_date > month_split[month_idx], #时间-到期月开始
  71. opt.OPT_CONTRACT_INFO.last_trade_date <= month_split[month_idx+1], #时间-到期月结束
  72. opt.OPT_CONTRACT_INFO.list_date < t) #在交易前上市
  73. contract_info = opt.run_query(q_contract_info)
  74. print(f"contract_info in the 1st step: {contract_info.head()}")
  75. etf_cls = get_price('510050.XSHG',t,t,fields=['close']).values[0][0] #现货收盘价
  76. contract_info['price_spread'] = contract_info['exercise_price'] - etf_cls
  77. print(f"contract_info in the 2nd step: {contract_info.head()}")
  78. if contract_info['price_spread'].max() > 0:
  79. contract_info = contract_info[contract_info['price_spread'] > 0] #选出虚值期权
  80. contract_info = contract_info.sort_values('exercise_price')
  81. else: #全是实值期权
  82. contract_info = contract_info.sort_values('exercise_price',ascending=False)
  83. print(f"contract_info in the 3rd step: {contract_info.head()}")
  84. if contract_info['last_trade_date'].iloc[0] >= newest_expire_date:
  85. holding_contract2[t] = contract_info['code'].iloc[0]
  86. newest_exercise_price = contract_info['exercise_price'].iloc[0]
  87. newest_expire_date = contract_info['last_trade_date'].iloc[0]
  88. else:
  89. print(f"{t} 早于 newest_expire_date: {newest_expire_date} 日期")
  90. #获取昨日50etf收盘价
  91. pre_cls = get_price('510050.XSHG',t,t,fields=['pre_close']).values[0][0]
  92. if pre_cls*0.95 >= newest_exercise_price: #原虚值变为实值,重新开仓略虚值期权
  93. #寻找 month_idx
  94. for month_idx in range(len(month_split)):
  95. if month_split[month_idx] >= t:
  96. break
  97. q_contract_info = query(opt.OPT_CONTRACT_INFO.code, opt.OPT_CONTRACT_INFO.trading_code, opt.OPT_CONTRACT_INFO.name,
  98. opt.OPT_CONTRACT_INFO.exercise_price, opt.OPT_CONTRACT_INFO.last_trade_date, #行权价格,最后交易日
  99. opt.OPT_CONTRACT_INFO.list_date
  100. ).filter(opt.OPT_CONTRACT_INFO.contract_type == 'CO', #看涨期权
  101. opt.OPT_CONTRACT_INFO.exchange_code == 'XSHG', #上交所
  102. opt.OPT_CONTRACT_INFO.last_trade_date > month_split[month_idx-1], #时间-到期月开始
  103. opt.OPT_CONTRACT_INFO.last_trade_date <= month_split[month_idx], #时间-到期月结束
  104. opt.OPT_CONTRACT_INFO.list_date < t) #在交易前上市
  105. contract_info = opt.run_query(q_contract_info)
  106. etf_cls = get_price('510050.XSHG',t,t,fields=['close']).values[0][0]
  107. contract_info['price_spread'] = contract_info['exercise_price'] - etf_cls
  108. if contract_info['price_spread'].max() > 0:
  109. contract_info = contract_info[contract_info['price_spread'] > 0] #选出虚值期权
  110. contract_info = contract_info.sort_values('exercise_price')
  111. else: #全是实值期权
  112. contract_info = contract_info.sort_values('exercise_price',ascending=False)
  113. if contract_info['last_trade_date'].iloc[0] >= newest_expire_date:
  114. holding_contract2[t] = contract_info['code'].iloc[0]
  115. newest_exercise_price = contract_info['exercise_price'].iloc[0]
  116. newest_expire_date = contract_info['last_trade_date'].iloc[0]
  117. holding_contract2 = holding_contract2.fillna(method='ffill')
  118. holding_contract2
  119. data2 = pd.DataFrame(holding_contract2)
  120. data2.columns = ['holding_contract']
  121. data2 = data2.reindex(columns=['holding_contract','close','last_close'])
  122. last_contract = holding_contract2.iloc[0] #记录上个持仓
  123. for t in data2.index:
  124. if last_contract == data2.loc[t,'holding_contract']: #期权未换仓
  125. #收盘价
  126. q_price = query(opt.OPT_DAILY_PRICE.code, opt.OPT_DAILY_PRICE.date, opt.OPT_DAILY_PRICE.close,
  127. ).filter(opt.OPT_DAILY_PRICE.code==last_contract,
  128. opt.OPT_DAILY_PRICE.date==t)
  129. price = opt.run_query(q_price)['close'][0]
  130. data2.loc[t,'close'] = price
  131. else:
  132. #收盘价,新
  133. q_price = query(opt.OPT_DAILY_PRICE.code, opt.OPT_DAILY_PRICE.date, opt.OPT_DAILY_PRICE.close,
  134. ).filter(opt.OPT_DAILY_PRICE.code==data2.loc[t,'holding_contract'],
  135. opt.OPT_DAILY_PRICE.date==t)
  136. price = opt.run_query(q_price)['close'][0]
  137. data2.loc[t,'close'] = price
  138. #收盘价,旧
  139. q_price = query(opt.OPT_DAILY_PRICE.code, opt.OPT_DAILY_PRICE.date, opt.OPT_DAILY_PRICE.close,
  140. ).filter(opt.OPT_DAILY_PRICE.code==last_contract,
  141. opt.OPT_DAILY_PRICE.date==t)
  142. price = opt.run_query(q_price)['close'][0]
  143. data2.loc[t,'last_close'] = price
  144. last_contract = data2.loc[t,'holding_contract']
  145. data2
  146. #计算卖出期权的收益
  147. opt_ret2 = pd.Series(0,index=data2.index)
  148. pre_close2 = data2['close'].iloc[0]
  149. for t in data2.index[1:]:
  150. if data2.isna().loc[t,'last_close']: #未换仓,last为空
  151. opt_ret2[t] = -10000*(data2.loc[t,'close'] - pre_close2)
  152. else:
  153. opt_ret2[t] = -10000*(data2.loc[t,'last_close'] - pre_close2) - 5 #手续费5元
  154. pre_close2 = data2.loc[t,'close']
  155. opt_ret2
  156. #计算持仓收益
  157. etf_price = get_price('510050.XSHG',trade_days.index[0],trade_days.index[-1],fields=['close'])['close']
  158. etf_ret = 10000*etf_price.diff(1).fillna(0)
  159. etf_ret
  160. #计算净值
  161. init_asset2 = etf_price.iloc[0]*10000
  162. ass2 = init_asset2 + (etf_ret + opt_ret2).cumsum()
  163. pfl_ret2 = (ass2/ass2.shift(1) - 1).fillna(0)
  164. pfl_nv2 = (1 + pfl_ret2).cumprod()
  165. pfl_nv2
  166. #绘制净值图
  167. plt.figure(figsize=(8, 5))
  168. plt.plot(etf_price/etf_price.iloc[0], label='50ETF现货净值')
  169. plt.plot(pfl_nv2, label='备兑看涨策略净值')
  170. plt.legend(loc='upper left', fontsize='large')
  171. plt.xlabel('时间',size=12)
  172. plt.ylabel('净值',size=12)
  173. plt.show()