请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  量化平台 帖子:3365825 新帖:0

期权假期策略

交易资深人士发表于:5 月 10 日 06:28回复(1)

一、跨式结构

贸易战以来,特朗普的大嘴巴给A股带来各种“惊喜”。如何利用这种惊喜盈利呢?

我们知道,跨式结构或者宽跨(勒式)结构可以在波动较大的环境下盈利。买入跨式结构,如果标的价格不波动,则亏损期权时间价值(亏theta),若价格波动大,无论涨跌,则获得一定比例的股票波动收益。(赚delta)
staddle.png
那么如果在假期前的最后交易日买入跨式或者宽跨式结构,在假期过完第一天平仓了结,会有什么结果。

二、跨在哪里?
平值期权几乎是不存在的,交易所期权的执行价格都落在整数位,比如2.8,2.85,2.9等,而标的价格可以在任何位置。所以期权跨在哪里需要事先定下规则。原则上,希望左右两边尽量对称,保持平衡。

如果现价离最近的执行价格不高于0.01,则选取最近的执行价格建立跨式结构。
举个例子,S=2.903,位于2.9和2.95之间,距离最近的执行价格是2.9,且不超过0.01,那么在2.9处建立跨式结构。

如果现价离最近的执行价格超过0.01,则选取临近两边的执行价格建立宽跨结构。
举个例子,S=2.92,位于2.9和2.95之间,距离最近的执行价格2.9超过了阈值0.01,那么在2.9和2.95建立宽跨结构。即买入执行价格为2.95认购期权,买入执行价格为2.9的认沽期权。

合约期限上,选择远月合约。因为theta会随着到期临近快速衰减。选取近月合约在下半月成本过高且存在到期问题,而选取下季、隔季合约期限过长,影响获利。

三、回测结果
回测起始于贸易战开始后,结果如下:
1.png

四、收益与仓位管理
上图列出了每次跨假期开平仓的收益率,那么如果是一个组合产品,实际考虑的收益率是如何的呢?

对于衍生品投资,风险管理要比追逐高收益更重要。所以,维持现金,使得每一次掷硬币都能保持相同的筹码,才能使我们策略长期执行下去。举例来讲,假如说我有20万,每次我都会花10万块购买期权,剩余的钱以现金形式存在。很明显,本策略的最大回撤远不到50%,所以,可以保证每次开仓我都可以购买10万块钱的期权。

基于上述策略,我们的收益就可以表示为每次收益之和,再除以2,自3月底至今,收益接近18%(非年化)。

五、2.0
回测的目的在于在用尽可能简单的办法证明在这个充满不确定的市场中跨式组合是可以盈利的。在回测的过程中,发现了一些实际问题,在此抛砖引玉,为2.0埋下伏笔。

  1. 策略并不是亏theta,赚delta这么简单,(可能这个应该叫赚gamma,anyways),实际上vega的影响也是非常大的。比如说3月23日,贸易战开始,当日出现了巨大的跳空下跌,市场恐慌,隐含波动率陡增,再过了一个平静的周末后,期权价格出现了大幅回落。
    这时候就多亏了一份vega。还有年初2月9日的市场三连崩,导致期权价格陡升,就有同样的问题。如果想要跳过这些坑的话,我们“事后诸葛”的得出一条经验,如果隐含波动率短期内快速上升,那么就不要再采取购买宽胯策略。就好比股价暴涨暴跌之后会回调。引入波动率,是提高模型收益的一个思路。

2.天然的,Put的delta要比Call小,从绝对值来看。手中期权并非持有到期,所以更希望持有一个delta neutral的组合。所以,如果股价接近左边执行价格但又不是十分靠近的时候,不如把put跨在右边。这么做应该会提高收益,因为过去半年,股市处于下跌过程,把put跨在右边,put更加实值了,delta负的更多了,在下跌行情中是好事。这个问题最合理的修正方法就是通过调整put和call的数量,构建一个中性组合。不过,我更倾向于现在,简单粗暴,适合告诉普通投资者,也多少避免些调参之嫌疑。

六. 函数
代码中的部分方程可能会协助你的期权策略研究

get_50ETF_option 获得当日所有50ETF期权信息

get_target_option获得构建跨式组合的期权信息,
term=0近月,1远月,2下季,3隔季,
strategy=0 找最近执行价格建立跨式
strategy=1 就是文中提到的宽胯
strategy=2 就是再宽一点

get_50ETF_price_byday 获取某日某期权的收盘价格

七、买Vs卖
今年事件多,买宽胯能赚钱,若是平静的一年,岂不是赚不到钱?
买跨式属于“花小钱,中大奖”。卖跨式属于“赚小钱,亏大钱”。假如硬要逼着一生一世坚持一个策略,选哪个取决于信仰。先抛弃那些回测,毕竟期权历史在中国并不长,其次,业务初期期权定价也不合理。我们从另一个角度来“胡扯”这个问题。
有人说,长期来看,卖权更赚钱,因为卖权人承受更多的不确定性,更大的波动性,所以需要风险补偿。
有人说,长期来看,买权更赚钱,因为历史上的黑天鹅事件远比人们想象的要多。
对于这个问题,想起一句老话,“占小便宜吃大亏”,“吃亏是福”。不知道说这话的老人是不是炒期权的。

import datetime
import jqdata
from jqdata import *
from jqdata import jy
import numpy as np
import pandas as pd
from scipy import stats
import seaborn as sns
import matplotlib.pyplot as plt

# 获得交易日日期序列
end_date=datetime.date.today()
start_date=datetime.date(2018,3,25)
date_list=list(jqdata.get_trade_days(start_date=start_date,end_date=end_date))
                         
# 获得假期前一个工作日
open_date_list=[]
close_date_list=[]
for i in range(len(date_list)-1):
    if (date_list[i+1]-date_list[i]).days!=1:
        open_date_list.append(date_list[i])
        close_date_list.append(date_list[i+1])
def get_50ETF_option(date,dfSignal=False):
    S0=get_price('510050.XSHG',start_date=date,end_date=date,fields=['close']).values[0][0]
    r=0.03

    q=query(jy.Opt_DailyPreOpen).filter(jy.Opt_DailyPreOpen.TradingDate==date,jy.Opt_DailyPreOpen.ULAName=='50ETF')
    df=jy.run_query(q).loc[:,['ContractCode','TradingCode','StrikePrice','ExerciseDate']]
    exercise_date_list=sorted(df['ExerciseDate'].unique())

    key_list=[]
    Contract_dict={}
    Price_dict={}
    impVol_dict={}

    for exercise_date in exercise_date_list:

        #获得T型代码
        df1=df[df['ExerciseDate']==exercise_date]
        #去除调整合约
        check=[]
        for i in df1['TradingCode']:
            x=True if i[11]=='M' and i[6]=='C' else False
            check.append(x)   
        df_C=df1[check][['ContractCode','StrikePrice']]
        df_C.index=df_C.StrikePrice.values
        del df_C['StrikePrice']
        df_C.columns=['Call']
        df_C=df_C.sort_index()

        #去除调整合约
        check=[]
        for i in df1['TradingCode']:
            x=True if i[11]=='M' and i[6]=='P' else False
            check.append(x)   
        df_P=df1[check][['ContractCode','StrikePrice']]
        df_P.index=df_P.StrikePrice.values
        del df_P['StrikePrice']
        df_P.columns=['Put']
        df_P=df_P.sort_index()

        dfT=pd.concat([df_C,df_P],axis=1)
        exercise_date=datetime.datetime.strptime(str(exercise_date)[:10],'%Y-%m-%d')
        exercise_date=datetime.date(exercise_date.year,exercise_date.month,exercise_date.day)

        Contract_dict[exercise_date]=dfT

        #T型价格
        q=query(jy.Opt_DailyQuote).filter(jy.Opt_DailyQuote.TradingDate==date)
        df2=jy.run_query(q).loc[:,['ContractCode','ClosePrice']]
        df2.index=df2['ContractCode'].values
        del df2['ContractCode']
        dfPrice=dfT.copy()
        dfPrice['Call']=df2.loc[dfT.loc[:,'Call'].values,:].values
        dfPrice['Put']=df2.loc[dfT.loc[:,'Put'].values,:].values
        dfPrice=dfPrice

        Price_dict[exercise_date]=dfPrice
        
        key_list.append(exercise_date)

    strike_price=list(Contract_dict[key_list[0]].index)
    atm_index=list(abs(np.round(S0-strike_price,3))).index(min(abs(np.round(S0-strike_price,3))))
    atm_K=strike_price[atm_index]    
            
    if dfSignal:
        value_list=[]
        for key,value in Contract_dict.items():
            value['exercise_date']=key
            value_list.append(value)
        Contract_df=pd.concat(value_list).sort('exercise_date')

        value_list=[]
        for key,value in Price_dict.items():
            value['exercise_date']=key
            value_list.append(value)
        Price_df=pd.concat(value_list).sort('exercise_date')

        return Contract_df,Price_df,key_list,S0  
        
    return  Contract_dict,Price_dict,key_list,S0

def get_target_option(Contract_dict,Price_dict,key_list,S0,term=0,strategy=1):
# Contract合约数据  Price价格数据  key_list是四个合约的到期日 
#S0当前标的价格 term远近合约选择 strategy策略
    
    expiry_date=key_list[term]
    Contract=Contract_dict[expiry_date]
    Price=Price_dict[expiry_date]
    strike_price=list(Contract.index)

#选择跨式期权位置   

    check=min(abs(np.round(S0-strike_price,3)))
    if check<0.01:
        strategy=0
    else:
        strategy=strategy
    
    if strategy==0:    
                
        atm_index=list(abs(np.round(S0-strike_price,3))).index(min(abs(np.round(S0-strike_price,3))))
        atm_k=strike_price[atm_index]  
        result=pd.DataFrame(index=['Strike','ticker','Price'],columns=['Call','Put'])
        result.loc['Strike','Call']=atm_k
        result.loc['Strike','Put']=atm_k
        result.loc['ticker','Call']=Contract.loc[atm_k,'Call']
        result.loc['ticker','Put']=Contract.loc[atm_k,'Put']
        result.loc['Price','Call']=Price.loc[atm_k,'Call']
        result.loc['Price','Put']=Price.loc[atm_k,'Put']
          
    if strategy==1:
        
        #print(S0)
        for i in range(len(strike_price)-1):
            if strike_price[i]<=S0 and strike_price[i+1]>S0:
                #print(i,i+1)
                k1=strike_price[i]
                k2=strike_price[i+1]
        
        #print(strike_price)
        #print(S0,k1,k2)
        result=pd.DataFrame(index=['Strike','ticker','Price'],columns=['Call','Put'])
        result.loc['Strike','Call']=k2
        result.loc['Strike','Put']=k1
        result.loc['ticker','Call']=Contract.loc[k2,'Call']
        result.loc['ticker','Put']=Contract.loc[k1,'Put']
        result.loc['Price','Call']=Price.loc[k2,'Call']
        result.loc['Price','Put']=Price.loc[k1,'Put']
        
    if strategy==2:
        
        for i in range(len(strike_price)-1):
            if strike_price[i]<=S0 and strike_price[i+1]>S0:
                k1=strike_price[i-1]
                k2=strike_price[i+2]
        
        result=pd.DataFrame(index=['Strike','ticker','Price'],columns=['Call','Put'])
        result.loc['Strike','Call']=k2
        result.loc['Strike','Put']=k1
        result.loc['ticker','Call']=Contract.loc[k2,'Call']
        result.loc['ticker','Put']=Contract.loc[k1,'Put']
        result.loc['Price','Call']=Price.loc[k2,'Call']
        result.loc['Price','Put']=Price.loc[k1,'Put']    
        
    return result

def get_50ETF_price_byday(date,Code):
    q=query(jy.Opt_DailyQuote).filter(jy.Opt_DailyQuote.TradingDate==date,jy.Opt_DailyQuote.ContractCode==Code)
    df=jy.run_query(q).loc[:,['ContractCode','ClosePrice']]
    price=df.loc[0,'ClosePrice']
    return price
N=len(open_date_list)
columns=['t','St','CK','PK','Callcode','Putcode','Callt','Putt','T','ST','CallT','PutT']
df=pd.DataFrame(index=list(range(N)),columns=columns)

for i in range(N):
    
    open_date=open_date_list[i]       
    Contract_dict,Price_dict,key_list,S0=get_50ETF_option(open_date,dfSignal=False)
    table=get_target_option(Contract_dict,Price_dict,key_list,S0,1,1)
    
    df.loc[i,'t']=open_date   
    df.loc[i,'St']=S0
    df.loc[i,'CK']=table.loc['Strike','Call']
    df.loc[i,'PK']=table.loc['Strike','Put']
    Callcode=table.loc['ticker','Call']
    Putcode=table.loc['ticker','Put']
    df.loc[i,'Callcode']=Callcode
    df.loc[i,'Putcode']=Putcode
    df.loc[i,'Callt']=table.loc['Price','Call']
    df.loc[i,'Putt']=table.loc['Price','Put']
    
    close_date=close_date_list[i]
    
    df.loc[i,'T']=close_date
    df.loc[i,'ST']=get_price('510050.XSHG',start_date=close_date,end_date=close_date,fields=['close']).values[0][0]
    df.loc[i,'CallT']=get_50ETF_price_byday(close_date,Callcode)
    df.loc[i,'PutT']=get_50ETF_price_byday(close_date,Putcode)
/opt/conda/lib/python3.5/site-packages/sqlalchemy/dialects/mysql/base.py:1936: SAWarning: MariaDB (10, 2, 6) before 10.2.9 has known issues regarding CHECK constraints, which impact handling of NULL values with SQLAlchemy's boolean datatype (MDEV-13596). An additional issue prevents proper migrations of columns with CHECK constraints (MDEV-11114).  Please upgrade to MariaDB 10.2.9 or greater, or use the MariaDB 10.1 series, to avoid these issues.
  "series, to avoid these issues." % (mdb_version, ))
df['ret']=(df['CallT']+df['PutT'])/(df['Callt']+df['Putt'])-1
df
.dataframe thead tr:only-child th { text-align: right; } .dataframe thead th { text-align: left; } .dataframe tbody tr th { vertical-align: top; }
t St CK PK Callcode Putcode Callt Putt T ST CallT PutT ret
0 2018-03-30 2.713 2.75 2.7 10001288 10001296 0.0838 0.0926 2018-04-02 2.702 0.0741 0.0998 -0.0141723
1 2018-04-04 2.694 2.7 2.7 10001287 10001296 0.093 0.0962 2018-04-09 2.711 0.0991 0.0866 -0.0184989
2 2018-04-13 2.717 2.75 2.7 10001288 10001296 0.0771 0.077 2018-04-16 2.655 0.0527 0.1082 0.0441272
3 2018-04-20 2.637 2.65 2.6 10001286 10001294 0.068 0.0549 2018-04-23 2.658 0.0772 0.0411 -0.0374288
4 2018-04-27 2.642 2.65 2.65 10001167 10001169 0.1098 0.0932 2018-05-02 2.646 0.1124 0.092 0.00689655
5 2018-05-04 2.634 2.65 2.6 10001167 10001240 0.1048 0.077 2018-05-07 2.674 0.1224 0.059 -0.00220022
6 2018-05-11 2.716 2.75 2.7 10001131 10001140 0.074 0.067 2018-05-14 2.745 0.0797 0.0487 -0.0893617
7 2018-05-18 2.733 2.75 2.7 10001131 10001140 0.064 0.0411 2018-05-21 2.736 0.0603 0.0375 -0.0694577
8 2018-05-25 2.654 2.65 2.65 10001339 10001348 0.0877 0.0618 2018-05-28 2.668 0.0921 0.0538 -0.0240803
9 2018-06-01 2.643 2.65 2.65 10001339 10001348 0.0832 0.0747 2018-06-04 2.686 0.1008 0.0558 -0.00823306
10 2018-06-08 2.652 2.65 2.65 10001339 10001348 0.0771 0.0644 2018-06-11 2.663 0.0846 0.0602 0.0233216
11 2018-06-15 2.678 2.7 2.65 10001340 10001348 0.0645 0.054 2018-06-19 2.612 0.0431 0.0976 0.187342
12 2018-06-22 2.598 2.6 2.6 10001338 10001347 0.0642 0.0592 2018-06-25 2.548 0.0413 0.0883 0.0502431
13 2018-06-29 2.495 2.5 2.5 10001390 10001399 0.0842 0.0816 2018-07-02 2.402 0.0516 0.1525 0.231001
14 2018-07-06 2.425 2.45 2.4 10001389 10001397 0.0759 0.0751 2018-07-09 2.496 0.1052 0.0402 -0.0370861
15 2018-07-13 2.509 2.5 2.5 10001390 10001399 0.0858 0.0715 2018-07-16 2.49 0.072 0.0747 -0.0673872
16 2018-07-20 2.54 2.55 2.5 10001391 10001399 0.0597 0.0448 2018-07-23 2.569 0.0751 0.036 0.0631579
17 2018-07-27 2.575 2.6 2.55 10001241 10001276 0.08 0.0713 2018-07-30 2.587 0.0849 0.0645 -0.0125578
18 2018-08-03 2.457 2.45 2.45 10001311 10001312 0.0965 0.0824 2018-08-06 2.455 0.0956 0.0829 -0.00223589
19 2018-08-10 2.551 2.55 2.55 10001275 10001276 0.091 0.083 2018-08-13 2.526 0.0778 0.0933 -0.0166667
20 2018-08-17 2.42 2.45 2.4 10001311 10001358 0.0726 0.0692 2018-08-20 2.458 0.088 0.0528 -0.00705219
21 2018-08-24 2.523 2.55 2.5 10001448 10001456 0.073 0.0716 2018-08-27 2.571 0.0933 0.0524 0.00760719
22 2018-08-31 2.521 2.55 2.5 10001448 10001456 0.0713 0.071 2018-09-03 2.512 0.0698 0.0733 0.00562193
23 2018-09-07 2.491 2.5 2.5 10001447 10001456 0.0822 0.084 2018-09-10 2.466 0.0722 0.0938 -0.00120337
24 2018-09-14 2.475 2.5 2.45 10001447 10001455 0.0652 0.0544 2018-09-17 2.452 0.0489 0.0603 -0.0869565
25 2018-09-21 2.632 2.65 2.6 10001450 10001458 0.0542 0.045 2018-09-25 2.6 0.0419 0.055 -0.0231855
26 2018-09-28 2.659 2.65 2.65 10001475 10001484 0.1075 0.0802 2018-10-08 2.533 0.0533 0.1492 0.0788492
27 2018-10-12 2.494 2.5 2.5 10001472 10001481 0.098 0.0863 2018-10-15 2.463 0.0816 0.1041 0.00759631
28 2018-10-19 2.491 2.5 2.5 10001472 10001481 0.0892 0.086 2018-10-22 2.599 0.1499 0.0452 0.113584
29 2018-10-26 2.516 2.55 2.5 10001314 10001322 0.1108 0.1014 2018-10-29 2.431 0.0781 0.1415 0.0348728
30 2018-11-02 2.594 2.6 2.6 10001315 10001324 0.1116 0.1117 2018-11-05 2.558 0.1009 0.1306 0.0367219
31 2018-11-09 2.48 2.5 2.45 10001313 10001332 0.112 0.0865 2018-11-12 2.49 0.1124 0.0826 -0.0176322
sum(df['ret'])
0.3555466432564187
 

全部回复

0/140

达人推荐

量化课程

    移动端课程