# 领口看涨策略-商品主力合约 # 参考资料: # - 原始策略来源: https://www.joinquant.com/view/community/detail/bec4688fd7998652edfd929a4ef1f4df # - 研究网址: https://www.joinquant.com/research?target=research&url=/user/75474983526/notebooks/Options/%E9%A2%86%E5%8F%A3%E7%9C%8B%E6%B6%A8%E7%AD%96%E7%95%A5-%E5%95%86%E5%93%81%E4%B8%BB%E5%8A%9B%E5%90%88%E7%BA%A6.ipynb # TODO: 添加领口看涨策略商品主力合约相关代码 import jqdata from jqdata import * import pandas as pd import numpy as np import datetime import matplotlib.pyplot as plt from datetime import datetime, timedelta plt.rcParams['font.sans-serif']=['SimHei'] plt.rcParams['axes.unicode_minus'] = False def is_last_day_of_month(date_str): # 将字符串转换为日期对象 date_obj = datetime.strptime(date_str, '%Y-%m-%d') # 获取下一个日期对象 next_date_obj = date_obj + timedelta(days=1) # 判断是否为下个月的第一天,如果是,则当前日期为月末 return date_obj.month != next_date_obj.month def get_last_day_of_month(date_str): # 将字符串转换为日期对象 date_obj = datetime.strptime(date_str, '%Y-%m-%d') # 获取下个月的第一天日期对象 next_month_first_day = datetime(date_obj.year, date_obj.month + 1, 1) # 从下个月的第一天减去一天,得到当前月的月末日期对象 last_day_of_month = next_month_first_day - timedelta(days=1) # 返回月末日期的字符串形式 return last_day_of_month.strftime('%Y-%m-%d') #获取期权 def getContract(code,date,type="CO"): #CO为认购,PO为认沽 q_contract_info = query(opt.OPT_CONTRACT_INFO.code, opt.OPT_CONTRACT_INFO.trading_code, opt.OPT_CONTRACT_INFO.name, opt.OPT_CONTRACT_INFO.exercise_price, opt.OPT_CONTRACT_INFO.last_trade_date, # 行权价格,最后交易日 opt.OPT_CONTRACT_INFO.list_date ).filter(opt.OPT_CONTRACT_INFO.underlying_symbol == code, opt.OPT_CONTRACT_INFO.contract_type == type, # 看涨期权 ) contract_info = opt.run_query(q_contract_info) commodity_cls = get_price(code, date, date, fields=['close']).values[0][0] if type == 'CO': contract_info['price_spread'] = contract_info['exercise_price'] - commodity_cls else: contract_info['price_spread'] = commodity_cls - contract_info['exercise_price'] if contract_info['price_spread'].max() > 0: contract_info = contract_info[contract_info['price_spread'] > 0] # 选出虚值期权 contract_info = contract_info.sort_values('exercise_price') else: # 全是实值期权 contract_info = contract_info.sort_values('exercise_price', ascending=False) return(contract_info['code'].iloc[0]) #获取期权价格等信息 def getContractPrice(code,last_trade_date,type='CO'): q_contract_info = query(opt.OPT_CONTRACT_INFO.code, opt.OPT_CONTRACT_INFO.trading_code, opt.OPT_CONTRACT_INFO.name, opt.OPT_CONTRACT_INFO.exercise_price, opt.OPT_CONTRACT_INFO.last_trade_date, # 行权价格,最后交易日 opt.OPT_CONTRACT_INFO.list_date ).filter(opt.OPT_CONTRACT_INFO.code == code, opt.OPT_CONTRACT_INFO.contract_type == type, # 期权类型 opt.OPT_CONTRACT_INFO.last_trade_date >= last_trade_date ) contract_info = opt.run_query(q_contract_info) return(contract_info) #处理合约切换标记 def contractChange(holding_contract,main_list,error_date): data2 = pd.DataFrame(holding_contract) data2.columns = ['holding_contract'] data2 = data2.reindex(columns=['holding_contract','close','last_close']) data2 = data2.drop(error_date) main_list = main_list.drop(error_date) last_contract = holding_contract.iloc[0] #记录上个持仓 for i in range(0,len(data2.index)): t = data2.index[i] if last_contract == data2.loc[t,'holding_contract']: #期权未换仓 q_price = query(opt.OPT_DAILY_PRICE.code, opt.OPT_DAILY_PRICE.date, opt.OPT_DAILY_PRICE.close, ).filter(opt.OPT_DAILY_PRICE.code==last_contract, opt.OPT_DAILY_PRICE.date==t) price = opt.run_query(q_price)['close'][0] data2.loc[t,'close'] = price else: #合约换仓 q_price = query(opt.OPT_DAILY_PRICE.code, opt.OPT_DAILY_PRICE.date, opt.OPT_DAILY_PRICE.close, ).filter(opt.OPT_DAILY_PRICE.code==data2.loc[t,'holding_contract'], opt.OPT_DAILY_PRICE.date==t) price = opt.run_query(q_price)['close'][0] data2.loc[t,'close'] = price #收盘价,旧 q_price = query(opt.OPT_DAILY_PRICE.code, opt.OPT_DAILY_PRICE.date, opt.OPT_DAILY_PRICE.close, ).filter(opt.OPT_DAILY_PRICE.code==last_contract, opt.OPT_DAILY_PRICE.date==data2.index[i-1]) price = opt.run_query(q_price)['close'][0] data2.loc[t,'last_close'] = price last_contract = data2.loc[t,'holding_contract'] return(data2) def optionProfit(data2,price_gap,fee): #计算平仓期权的收益 opt_ret2 = pd.Series(0,index=data2.index) pre_close2 = data2['close'].iloc[0] for t in data2.index[1:]: if data2.isna().loc[t,'last_close']: #未换仓,last为空 opt_ret2[t] = -price_gap*(data2.loc[t,'close'] - pre_close2) else: opt_ret2[t] = -price_gap*(data2.loc[t,'last_close'] - pre_close2) - fee #手续费 pre_close2 = data2.loc[t,'close'] return(opt_ret2) # 设置各项参数 # 合约代码 symbol = 'AU' #手续费 fee = 5 #合约价差 price_gap = 10 #起始时间 starttime = '2023-03-01' endtime = '2024-01-01' SUBJECT_MATTER = get_dominant_future(symbol,date = starttime) #获取交易时间和时间间隔(频率:月) #根据不同交易日分割月份 #指定回测的起始时间 trade_days = pd.Series(index=jqdata.get_trade_days(starttime,endtime)) trade_days.index = pd.to_datetime(trade_days.index) ##持仓情况 #主力合约列表 main_list = pd.Series(index=trade_days.index) main_list[trade_days.index[0]] = SUBJECT_MATTER holding_contract_CO = pd.Series(index=trade_days.index) #获取首个认购持仓合约 contract = getContract(SUBJECT_MATTER,trade_days.index[0],type='CO') holding_contract_CO[trade_days.index[0]] = contract print(holding_contract_CO[:3]) #获取首个认沽持仓合约 holding_contract_PO = pd.Series(index=trade_days.index) contract = getContract(SUBJECT_MATTER,trade_days.index[0],type='PO') holding_contract_PO[trade_days.index[0]] = contract print(holding_contract_PO[:3]) #循环访问每一个交易日,判断交易情况 #规则:判断当前主力合约对应的期权,持有略虚值看涨期权,待行权价低于现价的95%时,平仓原期权合约,重新开仓略虚值看涨期权 error_date =[] pre_hold_CO = holding_contract_CO[0] pre_hold_PO = holding_contract_PO[0] for i in range(1,len(trade_days)): pre_day = trade_days.index[i-1] cur_day = trade_days.index[i] cur_main = get_dominant_future(symbol,date = cur_day) #当前主力合约 pre_main = get_dominant_future(symbol,date = pre_day) #上一个交易日的主力合约 main_list[cur_day] = cur_main if cur_main != pre_main: #主力合约切换 contract_CO = getContract(cur_main,cur_day,type='CO') contract_PO = getContract(cur_main,cur_day,type='PO') else: pre_cls = get_price(cur_main, cur_day, cur_day, fields=['pre_close']).values[0][0] contract_info_CO = getContractPrice(pre_hold_CO,cur_day,"CO") pre_exercise_price_CO = contract_info_CO contract_info_PO = getContractPrice(pre_hold_PO,cur_day,"PO") pre_exercise_price_PO = contract_info_PO if contract_info_CO.empty | contract_info_PO.empty: error_date.append(cur_day) continue else: pre_exercise_price_CO = pre_exercise_price_CO['exercise_price'][0] pre_exercise_price_PO = pre_exercise_price_PO['exercise_price'][0] if pre_cls * 0.95 >= pre_exercise_price_CO: contract_CO = getContract(pre_main,cur_day) else: contract_CO = pre_hold_CO if pre_cls <= 0.95 * pre_exercise_price_PO: contract_PO = getContract(pre_main,cur_day,type='PO') else: contract_PO = pre_hold_PO holding_contract_CO[cur_day] = contract_CO pre_hold_CO = contract_CO holding_contract_PO[cur_day] = contract_PO pre_hold_PO = contract_PO holding_contract_CO = holding_contract_CO.fillna(method='ffill') holding_contract_PO = holding_contract_PO.fillna(method='ffill') data_list_CO = contractChange(holding_contract_CO,main_list,error_date) data_list_PO = contractChange(holding_contract_PO,main_list,error_date) #计算平仓期权的收益 opt_ret_CO = optionProfit(data_list_CO,price_gap,fee) opt_ret_PO = optionProfit(data_list_PO,price_gap,fee) opt_ret = opt_ret_CO + opt_ret_PO print(opt_ret) #计算持仓收益 commodity_price = [get_price(main_list[t],data_list_CO.index[t],data_list_CO.index[t],fields=['close'])['close'][0] for t in range(0,len(data_list_CO.index))] commodity_price = pd.Series(commodity_price,index=data_list_CO.index) commodity_ret = commodity_price.diff(1).fillna(0) commodity_ret #计算净值 init_asset2 = commodity_price.iloc[0]*price_gap ass2 = init_asset2 + (commodity_ret + opt_ret).cumsum() pfl_ret2 = (ass2/ass2.shift(1) - 1).fillna(0) pfl_nv2 = (1 + pfl_ret2).cumprod() pfl_nv2 #绘制净值图 plt.figure(figsize=(30,20)) plt.plot(commodity_price/commodity_price.iloc[0], label='现货净值') plt.plot(pfl_nv2, label=symbol+'领式看涨策略净值') plt.legend(loc='upper left', fontsize='large') plt.xlabel('时间',size=12) plt.ylabel('净值',size=12) plt.show()